Anessa was previously called Insurely. You may still see the Insurely name in parts of our documentation while we complete the transition.

Collection webhooks

Get notified the moment a collection reaches a final state, instead of polling the status endpoint. A webhook is a lightweight signal — it tells you which collection the event is about and how it ended, so you can fetch the result from the Insurely API.

One endpoint receives every event type. Each delivery carries an eventType so you can tell them apart:

eventTypeWhen it is sent
COLLECTION_STATUSA collection reached a terminal status.
COLLECTION_TRANSACTION_HISTORYThe full transaction history for a wealth collection has been fetched. Only sent if full transaction history is enabled for your client.

How it works

You register an HTTPS endpoint and receive a signing secret. A customer admin can do this in the Insurely Hub, on your client's Webhook tab, which also shows the secret. Without Hub access, send the endpoint to support@insurely.com.

When one of your collections reaches a terminal status, or its transaction history is ready, we POST a small JSON event to your endpoint, signed with your secret.

You verify the signature, respond 2xx quickly, and then fetch the result: GET /collections/{collectionId}/status (and the data endpoints) for a COLLECTION_STATUS event, GET /collections/{collectionId}/wealth/transactions for a COLLECTION_TRANSACTION_HISTORY event.

Webhooks complement the /status endpoint — they don't replace it. Treat the webhook as a "go fetch" signal; /status remains the source of truth.

The payload

Every delivery is a JSON body with these fields:

FieldTypeDescription
collectionIdstringThe collection the event is about. Use it to fetch via the API.
eventTypestringCOLLECTION_STATUS or COLLECTION_TRANSACTION_HISTORY. Decides what status means.
statusstringFor COLLECTION_STATUS: the terminal status (see the table below). For COLLECTION_TRANSACTION_HISTORY: COMPLETED or FAILED.
eventIdstring (UUID)Unique id for this event. Use it to de-duplicate (see Idempotency).
timestampstring (ISO-8601)When the event was generated.
Delivery
POST https://your-endpoint.example.com/insurely-webhook
Content-Type: application/json
X-Insurely-Event-Id: 6f1c2e9a-3b1d-4a77-9b2e-2c0a8f3d4e55
X-Insurely-Event-Type: COLLECTION_STATUS
X-Insurely-Signature: 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08

{
  "collectionId": "c0ffee00-1234-5678-9abc-def012345678",
  "eventType": "COLLECTION_STATUS",
  "status": "COMPLETED",
  "eventId": "6f1c2e9a-3b1d-4a77-9b2e-2c0a8f3d4e55",
  "timestamp": "2026-06-11T09:21:44.512Z"
}
Delivery: transaction history
POST https://your-endpoint.example.com/insurely-webhook
Content-Type: application/json
X-Insurely-Event-Id: 0b7d4c1e-8a2f-4e0b-9c3d-5f6a7b8c9d0e
X-Insurely-Event-Type: COLLECTION_TRANSACTION_HISTORY
X-Insurely-Signature: 2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae

{
  "collectionId": "c0ffee00-1234-5678-9abc-def012345678",
  "eventType": "COLLECTION_TRANSACTION_HISTORY",
  "status": "COMPLETED",
  "eventId": "0b7d4c1e-8a2f-4e0b-9c3d-5f6a7b8c9d0e",
  "timestamp": "2026-06-11T09:24:02.118Z"
}

Branch on eventType before reading status. With full transaction history enabled, a single collection produces two events at the same URL with the same collectionId: first COLLECTION_STATUS, then COLLECTION_TRANSACTION_HISTORY. A receiver that ignores eventType and sees "status": "FAILED" on the second event would wrongly mark the whole collection as failed, when only the transaction history fetch failed.

The payload deliberately carries no collected data or PII — fetch the collection from the API after you receive the event.

Headers

HeaderDescription
X-Insurely-SignatureLowercase-hex HMAC-SHA256 of the exact request body, keyed by your signing secret. Verify this.
X-Insurely-Event-IdSame value as eventId in the body; your idempotency key.
X-Insurely-Event-TypeSame value as eventType in the body, so you can route before parsing it.
Content-Typeapplication/json

Status values

COLLECTION_STATUS

A webhook is sent for every terminal status — i.e. any state where the collection will receive no further updates: success, failure, and states that need end-user action. The status field is one of:

GroupValues
CompletedCOMPLETED, COMPLETED_PARTIAL, COMPLETED_EMPTY
FailedFAILED, FAILED_PDF_PARSE, FAILED_PDF_USER_INPUT, THIRD_PARTY_ERROR
Action neededINCORRECT_CREDENTIALS, AUTHENTICATION_TIMEOUT, AUTHENTICATION_CANCELLED, AUTHENTICATION_CONFLICT, AUTHENTICATION_MISMATCH, TWO_FACTOR_METHOD_SELECTION_TIMEOUT, KYC_FORM, CONTACT_FORM, CUSTOMER_ENROLLMENT_REQUIRED, COLLECTION_INPUT_TIMEOUT

Treat status as a stable, webhook-specific vocabulary. For the authoritative, fully-detailed status, call /status. New values may be added over time — handle unknown values gracefully.

COLLECTION_TRANSACTION_HISTORY

Sent once per wealth collection, after the collection itself has finished, when full transaction history is enabled for your client.

ValueMeaning
COMPLETEDThe full transaction history is stored. Fetch it with GET /collections/{collectionId}/wealth/transactions.
FAILEDThe transaction history could not be fetched, or the collection itself did not complete. There is nothing to fetch; stop waiting.

If this event never arrives for a collection, the transaction history was not available for that provider. Don't wait for it indefinitely.

Verifying the signature

Compute the HMAC-SHA256 of the raw request body bytes using your signing secret, render it as lowercase hex, and compare it (in constant time) to the X-Insurely-Signature header. Reject the request if it doesn't match.

Node.js
const crypto = require("crypto");

function isValid(rawBody, signatureHeader, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody, "utf8")   // the EXACT received bytes — do not re-serialize
    .digest("hex");
  const a = Buffer.from(signatureHeader || "", "utf8");
  const b = Buffer.from(expected, "utf8");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
Python
import hashlib, hmac

def is_valid(raw_body: bytes, signature_header: str, secret: str) -> bool:
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature_header or "")

Verify against the raw body your framework received, not a re-serialized object — JSON re-encoding can change bytes (key order, whitespace) and break the signature.

Idempotency

Delivery is at-least-once: you may receive the same event more than once (e.g. if your endpoint is slow to acknowledge and we retry). De-duplicate on X-Insurely-Event-Id — process each id once.

A single collection can emit more than one webhook over its lifecycle: several COLLECTION_STATUS events (for example COMPLETED_PARTIAL then COMPLETED), and a COLLECTION_TRANSACTION_HISTORY event if enabled. Each is a distinct event with its own eventId, so don't collapse different events by collectionId.

Retries & failures

  • Respond with any 2xx to acknowledge. Anything else — non-2xx, a timeout, or a connection error — is treated as a failed delivery.
  • Failed deliveries are retried with exponential backoff over several attempts. After they're exhausted we stop retrying that event; you can still reconcile via /status.
  • We do not follow redirects, and we apply request timeouts — point the webhook directly at your final HTTPS endpoint.
  • Acknowledge fast (do the heavy work asynchronously). A slow endpoint looks like a failure and gets retried.

Requirements & best practices

  • Endpoint must be HTTPS and publicly reachable (no internal/private addresses).
  • Always verify the signature before trusting an event.
  • Branch on eventType before interpreting status.
  • De-duplicate on eventId.
  • Use the webhook as a trigger to fetch; rely on /status as the source of truth.
  • Keep your signing secret secret. Contact us to rotate it if exposed.

Last updated on