Webhooks
Send Connect events to your own systems the moment something happens, and check that each one really came from Nexra.
Updated 11/10/2026
On this page
Webhooks let Nexra tell your other systems when something happens in Connect: a flow run fails, an approval is needed, a connection starts having trouble, a reconciliation finds something to review or a contract obligation falls due. Instead of checking the console, your system receives a message straight away and can raise an alert, open a ticket or start its own process.
Add an endpoint
An endpoint is a web address on a system you own. Open Webhooks in the console and choose Add endpoint:
Name: something your team will recognise, up to 120 characters.
Web address: must start with https://.
Test or Live: a Test endpoint receives events from test-mode runs and test connections. A Live endpoint receives events from live (production) activity. Start with Test while you set things up, then add a Live endpoint.
Events: tick the events it should hear about (up to 30). Each group has Select all in group.
When you add the endpoint, Nexra shows its signing secret once. Copy it and give it to whoever looks after the receiving system; it is not shown again.
Use Send a test event to check the endpoint works. The result appears in its delivery list straight away.
Events
| Event | Sent when |
|---|---|
flow.run.succeeded | A flow run finished and every record went through |
flow.run.failed | A flow run stopped before it could finish |
flow.run.partially_succeeded | A flow run finished, but some records did not go through |
record.dead_lettered | A record could not be processed after several tries and is set aside |
approval.requested | A change is waiting for someone to approve it |
approval.decided | Someone approved or rejected a change |
connection.health_changed | A connection became healthy again or started having trouble |
connection.live_check.failed | Nexra could not reach a connected app during a live check |
integration.paused | Someone paused an integration |
integration.resumed | A paused integration is running again |
integration.suspended | An integration was stopped for safety |
integration.deleted | Someone deleted an integration |
reconciliation.run.completed | A reconciliation finished checking your records |
reconciliation.item.needs_review | A reconciliation found a difference someone should look at |
go_live.completed | An integration finished going live |
obligation.due_soon | A contract obligation comes within its reminder time |
obligation.overdue | A contract obligation passes its due date without being marked done |
workspace.daily_digest | Once a day: the workspace digest, the same summary the console shows |
Once money movement is switched on for your workspace, payment.initiated, payment.completed and payment.failed are offered too (see Stablecoins with Circle and Yellow Card).
Test sends use the type sandbox.test. The console's What the events look like section shows an example of every event.
Obligation reminders
obligation.due_soon and obligation.overdue come from Nexra's daily reminder check (see Obligations and reminders). Each is sent once per due date, to both Test and Live endpoints that choose it, so you can open a ticket or post to a channel without any risk of duplicates for the same date. The message carries the contract reference, counterparty, clause, due date, how often it repeats, the reminder time and a plain sentence such as "Send the monthly volume statement (Gas supply agreement GSA-2024-07, Northfield Power) is due in 14 days, on 31 October 2026." It never names the owner.
{
"id": "evt_8c1d2e3f4a5b6c7d8e9f0a1b",
"type": "obligation.due_soon",
"version": "2026-10-01",
"createdAt": "2026-10-17T06:00:12.000Z",
"mode": "live",
"workspaceId": "cmworkspace01",
"data": {
"obligation": {
"id": "obl_01",
"reference": "Gas supply agreement GSA-2024-07",
"counterparty": "Northfield Power",
"sourceClause": "7.2",
"dueDate": "2026-10-31",
"recurrence": "monthly",
"reminderLeadDays": 14
},
"reminder": {
"kind": "due_soon",
"summary": "Send the monthly volume statement (Gas supply agreement GSA-2024-07, Northfield Power) is due in 14 days, on 31 October 2026."
},
"links": { "obligation": "https://<your console>/dashboard/connect/obligations/obl_01" }
}
}The daily digest (workspace.daily_digest) has an Obligations section listing what is overdue and due soon, with the counts obligationsDueSoon and obligationsOverdue.
What a message looks like
Nexra sends a POST request with a JSON body. Every event shares the same envelope:
| Field | Meaning |
|---|---|
id | The event ID (evt_…). Use it to ignore duplicates. |
type | The event, for example flow.run.failed |
version | The event format version, currently 2026-10-01 |
createdAt | When the event happened |
mode | test or live |
workspaceId | The workspace the event belongs to |
data | Details of what happened |
Example flow.run.failed event:
{
"id": "evt_3f9a1c2b7d4e5f60718293a4",
"type": "flow.run.failed",
"version": "2026-10-01",
"createdAt": "2026-10-10T09:31:12.000Z",
"mode": "live",
"workspaceId": "cmworkspace01",
"data": {
"run": {
"id": "cmexec01run",
"status": "failed",
"trigger": "schedule",
"startedAt": "2026-10-10T09:30:00.000Z",
"completedAt": "2026-10-10T09:31:12.000Z",
"durationMs": 72000
},
"flow": { "id": "cmflow01orders", "name": "Shopify orders to Xero invoices" },
"integration": { "id": "cmint01shop", "name": "Online shop to accounts" },
"counts": { "received": 42, "succeeded": 0, "failed": 42, "skipped": 0 },
"reason": {
"code": "run_deadline_exceeded",
"message": "The run reached its time limit, so Nexra stopped it before sending more."
},
"links": { "run": "https://<your console>/dashboard/connect/runs/cmexec01run" }
}
}Messages contain IDs, names, counts, statuses and links only. They never contain record contents, credentials or secrets; follow the link to see the details in the console.
Each request also carries these headers:
| Header | Value |
|---|---|
Content-Type | application/json |
X-Amakora-Event | The event type |
X-Amakora-Event-Id | The event ID; use it to ignore duplicates |
X-Amakora-Delivery-Id | The delivery's ID |
X-Amakora-Signature | t=<unix seconds>,v1=<hex HMAC-SHA256> |
Verify the signature
Check every message before you act on it. v1 is the hex HMAC-SHA256 of <t>.<raw request body>, made with the endpoint's signing secret. Reject the message if the signature does not match, or if t is more than five minutes old.
import crypto from "node:crypto";
export function verifyNexraSignature(rawBody, header, secret) {
const parts = Object.fromEntries(
header.split(",").map((part) => {
const index = part.indexOf("=");
return [part.slice(0, index).trim(), part.slice(index + 1).trim()];
}),
);
const timestamp = Number(parts.t);
if (!Number.isFinite(timestamp)) return false;
// Reject anything more than 5 minutes old (or from the future).
if (Math.abs(Date.now() / 1000 - timestamp) > 300) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`)
.digest("hex");
const received = Buffer.from(parts.v1 ?? "", "hex");
const wanted = Buffer.from(expected, "hex");
return received.length === wanted.length && crypto.timingSafeEqual(received, wanted);
}Always verify against the raw body exactly as it arrived, before parsing the JSON.
Retries and resending
A delivery counts as delivered when your system answers with any 2xx status within 8 seconds. Do slow work after you answer.
If your system can't be reached, takes too long, or answers 408, 425, 429 or any 5xx, Nexra tries again with growing waits (about 5 seconds, then 10, 20 and so on, up to 5 minutes), up to 5 tries in total.
Any other 4xx answer means your system refused the event, so Nexra stops straight away and doesn't retry.
A message can occasionally arrive more than once or out of order. Use the event ID to ignore duplicates.
Each delivery shows a status, how many tries it had and, when something went wrong, why in plain words:
| What you see | Meaning |
|---|---|
| Waiting to send, Sending | On its way. While it can still be retried, tries show as "2 of 5" |
| Waiting to retry, for example "Your endpoint had an error (503). Nexra will try again." | A try failed with an answer worth retrying |
| Delivered | Your system answered with a 2xx |
| Stopped after 1 try: "Your endpoint refused the event (422), so Nexra stopped." | Your system answered with a 4xx that isn't retried |
| Stopped after 5 tries: "Your endpoint had an error (503). Nexra used all its tries." | Every try failed |
| Stopped: "The endpoint was turned off, so Nexra stopped sending." | The endpoint was turned off before the delivery finished |
Other reasons name the exact problem, such as "Your endpoint took too long to answer", "Nexra couldn't find your endpoint's address" or "Your endpoint's security certificate wasn't accepted".
Fix the problem, then choose Resend on the delivery to send it again with the same event ID and a fresh set of tries.
The delivery list for each endpoint shows the latest 100 deliveries, with the result, each try, the response code, how long it took and exactly what was sent. Endpoints show whether they are healthy or having trouble, and when they last received an event.
Change the signing secret
Choose Change secret on an endpoint to issue a new signing secret. The old secret stops working immediately, so update the receiving system straight away. The new secret is shown once.
Turn an endpoint off
Turn off stops all events to an endpoint and cancels deliveries still waiting to retry. Its delivery history stays in the console, and you can turn it back on at any time.
Only workspace owners, admins and developers can add endpoints, send test events, resend deliveries, change secrets or turn endpoints off. Other members can view endpoints and deliveries; the buttons they can't use are shown switched off, with the reason.