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
clientIdandclientSecret). 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.clientReferenceis your stable primary key, but the paged list does not return it, so the routine match is onemail. 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
emaillookup is the create-or-update decision. The reasoning behind the whole loop is in Create and manage learners.
The flow, in order:
- Get the ProSolution export as a CSV that Power Automate can read.
- Authenticate once and reuse the token for the whole run.
- Page
GET /v1/learnersand build a lookup keyed onemail. - For each export row: create the ones that are new, update the ones that changed.
- 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')}andContent-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:
- Create the flow and let its trigger generate a URL.
- Register that URL as a webhook subscription for event
100. - On each delivery, verify the signature and de-duplicate on
DataId. - 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).
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-sha256header, an HMAC of the body computed with yoursecret. 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 theDataIdvalues 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.
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
429back-off guidance, are in Troubleshooting. One Power-Automate-specific point: the built-in retry policy on the HTTP action handles429back-off for you.
Where to go next¶
- How the systems fit together: the recommended shape across all three phases, with named example systems.
- Automate with low-code tools: the wider low-code patterns, and the two Power Automate gotchas (custom-connector auth and OpenAPI 2.0 import) to plan for.
- Create and manage learners: the reconcile loop in full, why the list matches on email, and the tutor dependency.
- Webhooks: the delivery model, the eight events, and signature verification.
- Keep in sync: the full webhooks-versus-polling order and the SFTP fallback.
- Retrieve and render results: every result code decoded, and how to present a profile responsibly.