Skip to main content

Webhooks

The Platform GraphQL API supports webhooks so your backend receives a real-time HTTP notification when a relevant event occurs for brokers in your tree.

Registering a webhook

Use the registerWebhook mutation to subscribe a URL to one or more events. Registering the same URL a second time merges the event set — you will not create duplicates.

mutation RegisterWebhook($input: RegisterWebhookInput!) {
registerWebhook(input: $input) {
id
url
events
createdAt
}
}
{
"input": {
"url": "https://your-platform.example/webhooks/lla",
"events": ["OFFER_CREATED", "OFFER_UPDATED", "REWORK_CREATED", "REWORK_UPDATED", "POLICY_ISSUED"],
"verificationSecret": "your-hmac-secret"
}
}

Listing registered webhooks

query Webhooks {
webhooks {
id
url
events
createdAt
}
}

Removing a webhook

mutation UnregisterWebhook($input: UnregisterWebhookInput!) {
unregisterWebhook(input: $input) {
__typename
... on Success {
status
}
... on WebhookNotFoundError {
message
}
}
}
{
"input": {
"url": "https://your-platform.example/webhooks/lla"
}
}

Events

OFFER_CREATED

Fires when an offer is created for any broker in your tree — including offers created directly in the Liechtenstein Life insurance system, not only offers submitted through this API.

Payload

{
"contractID": "PR-123456",
"offerId": "abc123",
"brokerLifewareId": "196856",
"event": "offer_created"
}
FieldTypeDescription
contractIDstringThe policy / offer reference in the insurance system. Use this as the contractID in offerStatus queries.
offerIdstringInternal offer identifier.
brokerLifewareIdstringLifeware ID of the broker who owns the offer.
event"offer_created"Literal event name.
Timing

The notification fires when the offer is first seen by the sync pipeline — not necessarily at the instant of creation in the insurance system. Lifeware-direct offers appear when the offer-sync scheduler picks them up.

OFFER_UPDATED

Fires when an already-known offer changes a meaningful field — its offer status, signature status, or application / cancellation date. It does not fire on routine re-syncs that change nothing.

Payload

{
"contractID": "PR-123456",
"offerId": "abc123",
"brokerLifewareId": "196856",
"changedFields": ["offerStatus", "signatureStatus"],
"event": "offer_updated"
}
FieldTypeDescription
contractIDstringPolicy / offer reference.
offerIdstringInternal offer identifier.
brokerLifewareIdstringLifeware ID of the broker who owns the offer.
changedFieldsstring[]Which fields moved since the previous sync (e.g. offerStatus, signatureStatus).
event"offer_updated"Literal event name.

REWORK_CREATED

Fires when a rework — a request from the insurer for missing information or a correction before the offer can proceed — is opened on an offer/policy in your tree.

Payload

{
"taskId": "3f1c0b7e-1a2b-4c3d-8e9f-000000000000",
"contractID": "PR-123456",
"reason": "Passport copy for the insured person is missing",
"status": "WAITING",
"openedOn": "2026-06-30T00:00:00.000Z",
"closedOn": null,
"event": "rework_created"
}
FieldTypeDescription
taskIdstringUnique rework identifier.
contractIDstringPolicy reference the rework belongs to.
reasonstringWhy the rework was raised.
status"WAITING" | "FROZEN" | "FINISHED"WAITING = awaiting your response; FROZEN = you replied, internal verification pending; FINISHED = resolved.
openedOnstringDate the rework opened (date-only, serialized as a midnight-UTC ISO timestamp).
closedOnstring | nullDate the rework closed, or null while still open.
event"rework_created"Literal event name.

REWORK_UPDATED

Same payload shape as REWORK_CREATED, with event: "rework_updated". Fires when an existing rework changes — typically a status transition (WAITING → FROZEN → FINISHED). The closing transition arrives as a final REWORK_UPDATED with status: "FINISHED" and a non-null closedOn.

After it closes, the rework no longer appears in the openReworks query or in offerStatus.openReworks — both of which list only WAITING reworks. The webhook is therefore the only way to observe FROZEN and FINISHED transitions.

Timing

Rework events fire when the rework-sync pipeline observes the change. If the related offer has not yet been synced to your tree, the notification is sent on a later sync cycle once the offer is known.

POLICY_ISSUED

Fires once, when an offer/application in your tree is issued as a policy in the insurance system. Like the other events, this is detected by the sync pipeline, so it covers policies issued outside this API too.

Payload

{
"contractID": "PR-123456",
"offerId": "abc123",
"brokerLifewareId": "196856",
"event": "policy_issued"
}
FieldTypeDescription
contractIDstringThe issued policy reference. Query it via offer / offerStatus.
offerIdstringInternal offer identifier the policy was issued from.
brokerLifewareIdstringLifeware ID of the broker who owns the policy.
event"policy_issued"Literal event name.
note

After issuance, offerStatus(contractID).status reports ISSUED. The event fires only on the first transition to issued, not on subsequent re-syncs.

Verifying webhook signatures

Every delivery includes an x-hmac header: a hex-encoded HMAC-SHA256 of the raw request body, keyed with your verification secret.

Pass your verification secret in the verificationSecret field when registering a webhook. Keep it secret — it is the only mechanism that proves a delivery came from us.

Example — Node.js / TypeScript

import { createHmac, timingSafeEqual } from "crypto"

function isValidWebhook(
body: string,
signature: string,
secret: string,
): boolean {
const expected = createHmac("sha256", secret)
.update(body)
.digest("hex")
return timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected),
)
}

Always compare with a constant-time function (timingSafeEqual) to prevent timing attacks.

Delivery details

PropertyValue
MethodPOST
Content-Typeapplication/json
x-webhook-idUnique delivery identifier
x-hmacHMAC-SHA256 signature (hex)
User-Agentprosperity-solutions/1.0.0 (+https://api.prosperity.app/partner/v1/webhook)
Retry policy5 attempts with exponential back-off (initial delay 5 s)

Return 2xx to acknowledge receipt. Any other status code triggers a retry. Failed deliveries are retained; contact support if you need to replay a specific delivery.

Delivery guarantees

Webhooks are a best-effort, at-least-once notification: a delivery may be retried (so the same event can arrive more than once) and, in rare cases, may be missed entirely during an infrastructure incident. Design your integration accordingly:

  • Reconcile, don't rely solely on webhooks. Treat the GraphQL queries as the source of truth — on receipt, and periodically as a safety net, fetch the authoritative state with offer(contractID) / offerStatus(contractID) (and openReworks for reworks). A missed webhook then self-heals on the next reconciliation.
  • Be idempotent. The same event may be delivered more than once. Deduplicate on the natural id for the event — offerId for offers, taskId for reworks, contractID for POLICY_ISSUED — and make your handler safe to run twice.
  • Don't assume ordering. Events are not guaranteed to arrive in the order they occurred. Use the queried state (e.g. offerStatus.status, the rework status) as authoritative rather than inferring it from the sequence of webhooks.
note

Deduplicate on the payload's natural id (above), not on x-webhook-id — that header identifies a delivery, and the same underlying event can be delivered under different ids.

Access requirement

Your API key must have the WEB_HOOK_MANAGEMENT permission to register, list, or remove webhooks. Contact your integration support contact if this permission is not active on your key.