[{"data":1,"prerenderedAt":147},["ShallowReactive",2],{"doc:\u002Freference\u002Fwebhooks\u002Fenvelope":3},{"page":4,"toc":140,"updated":146},{"path":5,"title":6,"seoTitle":7,"description":8,"blocks":9,"examples":128},"\u002Freference\u002Fwebhooks\u002Fenvelope","Event envelope","Webhook Event Envelope, Headers and Signature","Every Fingerly webhook is an HTTPS POST with the same headers, the same signature scheme and the same versioned JSON envelope around its data.",[10,13,18,42,44,47,69,72,74,80,87,90,116,119,124],{"type":11,"text":12},"p","Every webhook Fingerly sends has the same shape: four headers you can act on, and a JSON envelope whose `data` depends on the event type.",{"type":14,"level":15,"text":16,"id":17},"heading",2,"Event types","event-types",{"type":19,"columns":20,"rows":24},"table",[21,22,23],"Type","Sent when","Environments",[25,29,32,35,39],[26,27,28],"[`identification.completed`](\u002Freference\u002Fwebhooks\u002Fidentification-completed)","Any identification finishes.","`live`, `test`",[30,31,28],"[`visitor.suspect`](\u002Freference\u002Fwebhooks\u002Fvisitor-suspect)","An identification reaches the `high` level.",[33,34,28],"[`identification.refused`](\u002Freference\u002Fwebhooks\u002Fidentification-refused)","An identify request is refused.",[36,37,38],"[`billing.status_changed`](\u002Freference\u002Fwebhooks\u002Fbilling-status-changed)","Your organization starts or stops accepting traffic.","`live`",[40,41,28],"[`usage.daily_settled`](\u002Freference\u002Fwebhooks\u002Fusage-daily-settled)","A day of usage is settled.",{"type":11,"text":43},"`webhook.test` is sent only when you choose **Send test event** for an endpoint. You cannot subscribe to it, and its `data` holds only `endpoint_id`. Ignore event types you do not handle.",{"type":14,"level":15,"text":45,"id":46},"Headers","headers",{"type":48,"fields":49},"fields",[50,54,57,60,63,66],{"name":51,"type":52,"text":53},"Content-Type","string","`application\u002Fjson`.",{"name":55,"type":52,"text":56},"User-Agent","`Fingerly-Webhooks\u002F1.0`.",{"name":58,"type":52,"text":59},"X-Fingerly-Event-ID","The envelope's `id`.",{"name":61,"type":52,"text":62},"X-Fingerly-Event-Type","The envelope's `type`, so you can route before parsing.",{"name":64,"type":52,"text":65},"X-Fingerly-Timestamp","Unix seconds when this attempt was signed. It changes on every retry.",{"name":67,"type":52,"text":68},"X-Fingerly-Signature","`sha256=` followed by the lowercase hex HMAC-SHA256 signature. While a [secret is rotated](\u002Fdocs\u002Fwebhooks#rotate-a-secret), one such value per secret, separated by commas.",{"type":14,"level":15,"text":70,"id":71},"Signature","signature",{"type":11,"text":73},"The signature is an HMAC-SHA256 over the timestamp, a full stop, and the raw request body, keyed with the endpoint's signing secret. Use the whole secret, `whsec_` prefix included, as the key.",{"type":75,"samples":76},"code",[77],{"label":70,"lang":78,"code":79},"text","signed_payload = X-Fingerly-Timestamp + \".\" + raw_body\nsignature      = \"sha256=\" + hex(hmac_sha256(signing_secret, signed_payload))",{"type":81,"items":82},"list",[83,84,85,86],"Compare signatures in constant time.","Split the header on commas and accept the delivery if any signature matches.","Reject a timestamp more than five minutes from your clock.","Verify the exact bytes you received. Parsing and re-serialising the JSON changes them.",{"type":14,"level":15,"text":88,"id":89},"Envelope","envelope",{"type":48,"fields":91},[92,95,98,102,105,108,111],{"name":93,"type":52,"text":94},"id","The event's ID, a UUIDv7. The same on every retry: deduplicate on it. Also sent as `X-Fingerly-Event-ID`.",{"name":96,"type":52,"text":97},"type","Always `\u003Cevent type>`.",{"name":99,"type":100,"text":101},"version","integer","The envelope version, `1`.",{"name":103,"type":52,"text":104},"organization_id","Your organization.",{"name":106,"type":52,"text":107},"environment","`live` for production traffic, `test` for staging and development.",{"name":109,"type":52,"text":110},"created_at","When the underlying fact happened, RFC 3339 in UTC. Not when it was delivered.",{"name":112,"type":113,"text":114,"children":115},"data","object","The event's data. Its fields are listed on each event's page.",[],{"type":14,"level":15,"text":117,"id":118},"Compatibility","compatibility",{"type":81,"items":120},[121,122,123],"New fields may be added to `data` and to the envelope within version `1`. Ignore fields you do not know.","New event types are only delivered to endpoints that subscribe to them.","Do not rely on the order of JSON keys.",{"type":125,"tone":126,"text":127},"callout","tip","Route on `X-Fingerly-Event-Type` and store the raw body first. Processing it from your own queue keeps your endpoint fast and lets you replay events after a bug fix.",{"titles":129,"request":131,"response":135},{"request":45,"response":130},"Payload",[132],{"label":45,"lang":133,"code":134},"http","POST \u002Fwebhooks\u002Ffingerly HTTP\u002F1.1\nContent-Type: application\u002Fjson\nUser-Agent: Fingerly-Webhooks\u002F1.0\nX-Fingerly-Event-ID: 01a0a851-0c4e-7f23-b8d1-6e2f94c0a7b5\nX-Fingerly-Event-Type: identification.completed\nX-Fingerly-Timestamp: 1789551672\nX-Fingerly-Signature: sha256=6f1c0a3e9b…",[136],{"label":137,"lang":138,"code":139},"Body","json","{\n  \"id\": \"01a0a851-0c4e-7f23-b8d1-6e2f94c0a7b5\",\n  \"type\": \"identification.completed\",\n  \"version\": 1,\n  \"organization_id\": \"01a0a7f2-3c18-7b40-8d2e-5f6a9b1c0d37\",\n  \"environment\": \"live\",\n  \"created_at\": \"2026-09-16T09:41:12Z\",\n  \"data\": {\n    \"request_id\": \"01a0a84b-e6a2-7c09-9f51-0b3d7a26c8e4\",\n    \"…\": \"…\"\n  }\n}",[141,142,143,144,145],{"id":17,"text":16,"level":15},{"id":46,"text":45,"level":15},{"id":71,"text":70,"level":15},{"id":89,"text":88,"level":15},{"id":118,"text":117,"level":15},"2026-09-17T16:57:29.000Z",1789667797872]