orkasa

Guides

Webhooks

Four state changes Orkasa pushes to you the moment they happen.

Polling a list tells you a deal closed up to a minute late, and costs a request every minute forever. A webhook tells you when it happens.

event_typefires whenpermissionpayload
deal_wonan operation reaches won · sold · rentedoperations:readthe operation
offer_acceptedan offer reaches acceptedoffers:readthe offer
signature_signeda signature document reaches signedsignatures:readthe signature
document_completeda document reaches received or verifieddocuments:readthe document

Each payload is produced by the same serializer as the matching list endpoint, so a field means the same thing whether you polled it or were pushed it. And the permission to subscribe is the resource's own :read — you can only be told about what you could already have fetched.

Subscribe

curl -X POST "$ORKASA_API/hooks" \
  -H "Authorization: Bearer $ORKASA_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "event_type": "deal_won",
    "target_url": "https://hooks.zapier.com/hooks/catch/123/abc/"
  }'

201 with a flat { "id": "…" } — no data envelope, because Zapier stores the whole body as its subscribeData. target_url must be https.

Unsubscribe

curl -X DELETE "$ORKASA_API/hooks/<id>" -H "Authorization: Bearer $ORKASA_KEY"

204, always. A repeated unsubscribe, or one for something already gone, is not an error: a Zap being torn down never fails on that step.

Test without waiting for an event

curl "$ORKASA_API/hooks/sample?event_type=deal_won" \
  -H "Authorization: Bearer $ORKASA_KEY"

Recent rows already in the event's terminal state, in the normal envelope. A REST-hook has no past event to show while you are building the Zap; this fills that step, and doubles as a polling fallback.

How delivery works

Subscribing arms a database trigger, not a poll. On the qualifying transition — and only on the first one — the row is enqueued in an outbox, but only if someone is already listening: with no subscription for that brokerage and event, nothing is written at all. Silence costs nothing.

A cron drains the outbox every minute:

POST <target_url>
x-orkasa-event: deal_won
x-orkasa-signature: sha256=<hmac of the exact body>
content-type: application/json

The signature is HMAC-SHA256 over the exact bytes sent. Zapier does not verify it — its catch URL is itself the secret — but any other consumer should, and the header is simply omitted when no secret is configured.

At most once per target

That is the right default for a consumer that does not deduplicate:

  • every target returned 2xx → delivered;
  • every target failed → retried on the next run, up to 3 attempts. Safe: nobody saw it;
  • some targets succeeded, or attempts ran out → marked delivered with the reason recorded. Retrying would re-POST to the targets that already got it, and a duplicated Zap run is worse than a missed one;
  • the subscription vanished before serialization → nothing to send.

The outbox table is called outbound_webhook_events, not webhook_events: that name was already taken by the inbound log for Stripe, Meta and WhatsApp callbacks. The two are unrelated.