Skip to main content
Rather than polling, let Frayme push updates to you. Frayme delivers signed HTTPS POSTs to a URL configured for your tenant whenever a case decision is reached (or later overridden), when a workflow node asks an external system to act, or when a node emits a notification mid-run.
Your webhook URL, signing secret, secret ID, and retry policy are provisioned for you by the Frayme team. These will move to self-service in the portal.

Events

event_type is always snake_case. Decision events and case.rfi_requested also use snake_case for their top-level identity keys (case_id, tenant_id); the external_callback.dispatch and node.notification events use camelCase (caseId, tenantId). The examples below reflect the exact keys on the wire.

Payload shapes

Decision events

Sent for case.decided, case.pending_review, and case.decision_overridden. The body carries the full decision result so you can act without re-fetching the case.
  • external_customer_id is present only when the case carried one — it is the stable key to correlate the decision back to your own user record.
  • result is the same envelope returned by GET /cases/{caseId}: result.decision is the current decision, result.decisionHistory[0] is the workflow’s initial decision, and later entries are analyst overrides.
  • result.decision.source is one of workflow, risk_evaluation, or analyst.
  • result.workflow_result is write-once — analyst overrides never mutate it.
For a case.decision_overridden event, the new result.decision reflects the analyst’s override and a new entry is appended to result.decisionHistory.

External callback dispatch

Sent for external_callback.dispatch. The receiving system performs its work, then posts the result to the one-time callbackUrl (see Delivering an external callback) before expiresAt.

Node notification

Sent for node.notification when a node configured to notify emits output. payloadType identifies the output schema (for example, sumsub-application-start); output carries the node’s emitted fields, including a pending_handle when the node also exposes pending actions.
payloadType and tag appear only when configured on the node. The output keys depend on the node’s provider and configuration. A Sumsub node that allows retries emits a node.notification with payloadType: sumsub-retry-requested each time the applicant must resubmit — see Data sources → Sumsub → Resubmission retries.

RFI requested

Sent for case.rfi_requested when a request for information is raised on a case — by an analyst in the review console, a workflow node, or your own API call. Frayme never sends the email itself; your systems send it to the customer using this payload. Delivered to your tenant’s default webhook configuration (or the one named by webhook_key).
  • source is one of analyst, workflow, or api — who raised the request. requested_by is a prefixed identifier: analyst:<userId>, node:<nodeId>, or apikey:<keyId>.
  • template_id (when a template was used), cc, and webhook_key are present only when set.
This event carries PIIto, cc, subject, and body are the customer-facing email content. Handle and store it accordingly, and keep it out of your logs.
Deduplicate this event on rfi_id, not webhookId. Re-raising a still-pending RFI legitimately re-publishes the event with a fresh webhookId, so the webhookId rule below would let a duplicate through.

Headers

Every outbound webhook carries these headers.

Verifying the signature

Compute the HMAC over the exact bytes of the request body — do not re-serialize the JSON — and compare it to X-Frayme-Signature in constant time.
  1. Compute HMAC-SHA256 over the raw body bytes; compare hex digests with a constant-time comparison (for example, hmac.compare_digest).
  2. If X-Frayme-Secret-ID is present, select the matching secret from your rotation set before computing the HMAC.
  3. Respond 2xx within the per-delivery timeout (about 10 seconds).

Deduplication

Every outbound webhook carries a unique webhookId (UUID). Delivery is at-least-once — a transient failure or network blip can produce duplicate deliveries with the same webhookId. Persist the webhookId of every event you successfully process, and short-circuit any event whose webhookId you have seen before. The one exception is case.rfi_requested: deduplicate it on rfi_id, because re-raising a still-pending RFI re-publishes with a fresh webhookId.

Retries

A non-2xx response or a timeout is retried with exponential backoff — each wait is twice the last (1s, 2s, 4s, 8s, …) — up to a configured maximum number of attempts (5 by default). After the final attempt the delivery is recorded as failed and not retried further, so treat webhooks as best-effort and reconcile with GET /cases/{caseId} if you must be certain of a case’s current decision.