Skip to content

Worked example: receiving webhooks with Power Automate

This page solves one specific problem end to end: how to receive a Cognassist webhook when you cannot open an inbound port, using assessment completion as the example. It is the low-code companion to How the systems fit together: that page shows the recommended shape across all three phases, and this one zooms in on the phase-2 hand-off and builds it with Power Automate.

The constraint is common on an on-premises estate: your systems can make outbound calls to Cognassist, but nothing can accept an inbound HTTPS request from the internet. The answer is not to poll. It is to let a low-code flow in your own Microsoft 365 tenant receive the webhook for you. Power Automate's When a HTTP request is received trigger generates a public HTTPS URL, and that URL is what you register with Cognassist. The low-code tool is the receiver, not the integration method; an Azure Logic App with the same trigger works identically.

The example estate is the OneAdvanced stack: ProSolution as the MIS and system of record, with results written back into ProMonitor where tutors work. It runs against one credential set for the whole organisation.

The whole flow, once

sequenceDiagram
    participant MIS as ProSolution
    participant FLOW as Power Automate
    participant COG as Cognassist API
    participant LN as Learner
    participant PM as ProMonitor

    Note over FLOW,COG: One-off setup
    FLOW->>COG: POST /v1/webhooks (register URL, event 100)

    Note over MIS,LN: Scheduled reconcile
    MIS-->>FLOW: Enrolment export (CSV)
    FLOW->>COG: POST /v1/auth
    FLOW->>COG: GET /v1/learners (match on email)
    FLOW->>COG: POST /v1/learners (create new)
    FLOW->>COG: POST .../assessment/invite
    COG-->>LN: Assessment invite email

    Note over LN,PM: Completion, event-driven
    LN->>COG: Completes assessment
    COG->>FLOW: 100 Assessment Completed (webhook)
    FLOW->>FLOW: Verify signature, de-dupe on DataId
    FLOW->>COG: GET .../assessment
    FLOW->>PM: Write profile and results

Two flows do the whole job, and every step above belongs to one of them:

  • A scheduled reconcile flow reads the ProSolution export, matches it against Cognassist, creates the learners that are new, and invites them. This is the get-data-in phase, run on a timer; every call it makes is outbound.
  • A webhook receiver flow whose trigger is When a HTTP request is received. Cognassist calls its URL when a learner finishes; the flow fetches the result and writes it back into ProMonitor. This is the event-driven channel that means you never poll.

Neither flow needs a custom connector or a hosted service. The rest of this page is those two flows, step by step.

Before you start

Two things must be in place first, and neither is a Power Automate step; both are covered on Before you start. The two that bite this integration specifically:

  • API credentials (a clientId and clientSecret). These come from your Cognassist contact, not a self-service form. See Get access.
  • Tutors provisioned in Cognassist. Creating a learner requires the email of a tutor who already exists in Cognassist, and there is no API to create tutors. Provision them once, up front, in the Cognassist app. A create rejected for an unknown tutor is fixed by provisioning, not by a retry.

To rehearse the completion path without touching real learners, invite yourself as a test learner and complete the assessment so the receiver has a real event to react to. Read Test safely first.

The reconcile flow: get learners in

This flow runs on a schedule and brings Cognassist into line with ProSolution on each run: it creates the learners that are new and invites them. It is the standard create-and-reconcile loop. Two facts from the concept pages shape how it lands in Power Automate, and this page links rather than re-deriving them:

  • Why the match is on email. clientReference is your stable primary key, but the paged list does not return it, so the routine match is on email. The split (list rows versus the full learner) is in Core concepts.
  • Why a create is not an update. The API does not upsert, so the email lookup is the create-or-update decision. The reasoning behind the whole loop is in Create and manage learners.

The flow, in order:

  1. Get the ProSolution export as a CSV that Power Automate can read.
  2. Authenticate once and reuse the token for the whole run.
  3. Page GET /v1/learners and build a lookup keyed on email.
  4. For each export row: create the ones that are new, update the ones that changed.
  5. Invite each learner you just created.
Step 1: get the ProSolution export

ProSolution runs on-premises, so the least-friction starting point is a scheduled export to CSV that ProSolution (or a SQL job beside it) drops somewhere Power Automate can read: a SharePoint document library, a OneDrive folder, or an on-premises file share reached through the on-premises data gateway. One row per learner, carrying the fields a create needs (see Create and manage learners for the full list).

Letting the MIS push a file out on a timer, rather than exposing it to inbound calls, keeps every call to Cognassist outbound from your tenant. That is normally the least-friction shape for an on-premises system of record.

Step 2: authenticate and reuse the token

Start the flow with one HTTP action calling POST /v1/auth, store the token in a variable, and reuse it for every later action in the run. Do not call POST /v1/auth per learner.

  • Method: POST
  • URI: https://api.uk.cognassist.com/v1/auth
  • Headers: Content-Type: application/json
  • Body: { "clientId": "@{variables('clientId')}", "clientSecret": "@{variables('clientSecret')}" }

Then a Parse JSON action on the response and a variable that stores accessToken. Reuse it as Authorization: Bearer @{variables('accessToken')} on every later action. Store clientId and clientSecret as secure inputs or pull them from Azure Key Vault, never inline in the flow definition.

The response carries the token and its lifetime in expiresIn (seconds). Read the value rather than hard-coding it; a scheduled run fits comfortably inside one token, so authenticate once at the top. The auth model in full is on Getting started.

Step 3: page the list and build an email lookup

Loop pages with a Do until, calling GET /v1/learners each time:

  • Method: GET
  • URI: https://api.uk.cognassist.com/v1/learners?pageSize=500&pageIndex=@{variables('pageIndex')}
  • Headers: Authorization: Bearer @{variables('accessToken')}

On the first page, read totalRecords so you know the target count. After each page, append the rows to a collection and increment pageIndex. Stop when you have collected totalRecords rows, or when a page returns fewer than pageSize rows. Then build a lookup keyed on email (for example with Select to project email and learnerId).

Confirm whether pageIndex starts at 0 or 1 in the API reference before setting the initial value; the stop condition holds either way. For the exact list projection (the five fields each row carries), the reference is the source of truth.

Step 4: create the missing learners, update the changed ones

Walk the CSV rows inside an Apply to each, with a Condition on whether the email is in the lookup.

Create branch (email not found), an HTTP action:

  • Method: POST
  • URI: https://api.uk.cognassist.com/v1/learners
  • Headers: Authorization: Bearer @{variables('accessToken')} and Content-Type: application/json
  • Body: the learner object

Read learnerId from the 201 response and store it against the ProSolution record, so the receiver flow can write results back later.

Update branch (email found), an HTTP action with Method PUT and URI https://api.uk.cognassist.com/v1/learners/@{...learnerId...}, sending only the fields that changed. A PUT returns 204 No Content.

A minimal create body looks like this; set clientReference from your ProSolution learner id:

curl -X POST https://api.uk.cognassist.com/v1/learners \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "Jane",
    "lastName": "Doe",
    "email": "jane.doe@example.com",
    "gender": "Female",
    "postcode": "NE1 6BF",
    "dateOfBirth": "2000-02-24",
    "clientReference": "PROSOL-12345",
    "primaryTutorEmail": "tutor.name@example.com"
  }'

For the full field list, which fields are required, and validation rules, see Create and manage learners and the API reference.

Step 5: invite each new learner

For each learner in the create branch, after the create succeeds, send the invitation:

  • Method: POST
  • URI: https://api.uk.cognassist.com/v1/learners/@{...learnerId...}/assessment/invite
  • Headers: Authorization: Bearer @{variables('accessToken')}

A successful call returns 202 Accepted and the email is sent asynchronously. Invite only the learners you just created, not every learner in the export: a learner cannot be re-invited within three days, so inviting the whole export on a scheduled run would hit that limit. To start a learner sooner or embed the assessment in your own portal, see Send the assessment.

Never POST without checking the email lookup first

Because the API does not upsert (see the top of this section), the step-3 lookup is not optional: it is what keeps a scheduled run from creating a second record for someone who already exists.

The webhook receiver flow: put the result where it's needed

A learner completes the assessment in their own time, so you do not wait on it. Cognassist announces completion with the 100 Assessment Completed webhook, and this flow reacts. The delivery model, the eight events, and the signature scheme are owned by the Webhooks page; this section shows only the on-premises receiver shape.

The receiver is a Power Automate flow whose trigger is When a HTTP request is received. Power Automate generates a public HTTPS URL for that trigger, and that URL is what you register with Cognassist. This is exactly the receive-a-webhook-without-a-service pattern: the low-code flow stands in for the inbound endpoint a purely on-premises estate cannot open.

The flow, in order:

  1. Create the flow and let its trigger generate a URL.
  2. Register that URL as a webhook subscription for event 100.
  3. On each delivery, verify the signature and de-duplicate on DataId.
  4. Fetch the result the payload points at and write it into ProMonitor.
Steps 1 to 2: create the receiver and register its URL

Create the flow with the When a HTTP request is received trigger. Save it so Power Automate generates the URL. Then subscribe to the completion event with a one-off POST /v1/webhooks, giving that URL, a secret you choose, and the event id (you can also do this in the Cognassist app):

curl -X POST https://api.uk.cognassist.com/v1/webhooks \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://prod-00.westeurope.logic.azure.com/...", "secret": "a-secret-you-choose", "systemEventIds": [100] }'

The receiver keys on just two fields: Data.LearnerId (the learner to fetch) and DataId (for idempotency).

{ "Data": { "LearnerId": "..." }, "DataId": "..." }

Full envelope on Webhooks and the schema in the API reference.

Step 3: verify the signature and de-duplicate

Two checks before acting on any delivery:

  • Verify the signature. Every request carries an x-cognassist-sha256 header, an HMAC of the body computed with your secret. Recompute it and compare; if it does not match, do not process the request. The exact scheme and a worked example are on Webhooks.
  • De-duplicate on DataId. Deliveries can be retried, so the same payload can arrive more than once. Record the DataId values you have processed and skip any you have seen.

There is no subscription-time URL challenge or handshake to implement; the security model is the HMAC signature plus DataId idempotency. The finer delivery guarantees (retry schedule, endpoint timeout, the exact success status to return, any source IP allow-list) are not in the public spec. The Webhooks page collects these in a confirm-with-Cognassist block; build the receiver to be idempotent and forgiving in the meantime.

Step 4: fetch the result and write it back

With a verified, first-seen LearnerId, fetch the result:

  • Method: GET
  • URI: https://api.uk.cognassist.com/v1/learners/@{triggerBody()?['Data']?['LearnerId']}/assessment
  • Headers: Authorization: Bearer @{variables('accessToken')}

Then Parse JSON and branch on the two fields the write-back keys on: status (30 means Completed; a not-completed result has no profile to write yet) and learnerStatus (the headline classification). Write the profile and classification into ProMonitor, where tutors act on it.

{ "status": 30, "learnerStatus": 20, "...": "..." }

The full result object (supportLikelihoodRating, assessmentResults[], and the rest) is on Retrieve and render results, with the schema in the API reference. That page decodes every code and is the one to keep open while you build the write-back mapping; persist the raw integers alongside the text you render, so you can reconcile later.

However you surface the profile, keep the framing right: a cognitive profile is a signal, not a diagnosis. Lead with the learner's strengths, show the whole profile rather than only the identified domains, and frame support as offered, not imposed. Retrieve and render results covers this in full.

One receiver serves every source system

If you create learners from more than one system of record, there is nothing new to build. Point a second scheduled reconcile at the other system's export and reuse the same flow: authenticate, page GET /v1/learners, match on email, create the new learners, and invite them, setting clientReference from that system's learner id.

Completions come back through the same receiver. The 100 Assessment Completed webhook does not say which system created the learner, so look up the learnerId against your stored ids to decide where each write-back goes.

Fallbacks and rate limits

Two general concerns apply to this flow but have canonical homes elsewhere, so this page only points to them:

  • If a webhook genuinely cannot be received, the fallbacks (polling, and SFTP as a limited one-way-in channel for learners) are in Keep in sync.
  • Rate limits, and the token-caching and 429 back-off guidance, are in Troubleshooting. One Power-Automate-specific point: the built-in retry policy on the HTTP action handles 429 back-off for you.

Where to go next