Skip to main content
Your app subscribes per workspace, with the access token the workspace granted it. Deliveries are signed, retried, logged, and stop on their own when the app is uninstalled or re-authorized with fewer scopes.

Available events

Submissions

submission.created, submission.qualified, submission.updated, submission.deleted

Contacts

contact.created, contact.updated, contact.archived, contact.deleted, contact.tag_added, contact.tag_removed, contact.association_changed, contact.job_changed

Companies

company.created, company.updated, company.deleted, company.restored, company.merged, company.tag_added, company.tag_removed

Deals

deal.created, deal.updated, deal.stage_changed, deal.won, deal.lost, deal.reopened, deal.deleted, deal.restored, deal.merged, deal.tag_added, deal.tag_removed, deal.association_changed, deal.line_item_added, deal.line_item_updated, deal.line_item_removed

Products

product.created, product.updated, product.archived, product.restored

Quotes

quote.created, quote.updated, quote.published, quote.viewed, quote.accepted, quote.voided, quote.deleted

Tasks

task.created, task.updated, task.completed, task.deleted, task.restored

Meetings

meeting.booked, meeting.rescheduled, meeting.cancelled

Calls

call.completed, call.transcribed, call.analyzed

Lists

list.member_added, list.member_removed
Each event’s payload is documented on the events page. GET /api/v1/webhooks/events returns the same catalog, with subscribable telling you which groups the current token may subscribe to.

Scopes your app needs

The read scope is checked twice: when you subscribe (403 insufficient_scope, with required_scopes, if the token lacks it) and on every delivery, against the app’s current installation in that workspace. A workspace that removes a scope from your app, or uninstalls it, stops receiving the matching deliveries without you doing anything. Register the scopes in the Developer Portal and request them during OAuth.

Subscribe after install

Once a workspace has authorized your app, create the subscription with its access token. Do it once per workspace, right after the OAuth callback, and keep the returned id and secret with that workspace’s record.
  • The Idempotency-Key makes the call safe to retry; a replay returns the webhook without the secret.
  • Your app only ever sees the webhooks it registered. GET /api/v1/webhooks with a workspace’s token lists yours in that workspace.
  • Limits: 5 webhooks per workspace while your app is in development, 100 once it is approved. Creation is rate limited to 60 an hour per app per workspace (500 once approved); the response and a 429 carry RateLimit-* headers. One webhook can subscribe to any number of events (X-Orbit-Event and the envelope’s event say which one arrived), so a single subscription per workspace is usually enough, and PATCH with is_active pauses and resumes it without a delete and a new secret.
  • form_id limits a webhook to one form’s submission events. Every other event is workspace-wide, and a webhook with any of them refuses a form filter.
  • filters limits deal events to deals in given pipelines or stages: { "pipeline_ids": [...], "stage_ids": [...] }, checked against where the deal is when the event happens (so deal.stage_changed is judged by the stage the deal moved to). Other events on the same webhook are not filtered. There are no field filters: subscribe to the specific event you need (deal.won rather than deal.updated) to keep deliveries down.
  • The URL’s host is refused only when it points at a private or internal address. A host that does not resolve yet is accepted, so you can register a development endpoint before it is public; deliveries re-check the host every time. Localhost is never reachable from Orbit, so use a tunnel for local testing.
  • A replayed create (same Idempotency-Key) returns the webhook without its secret. If the original response was lost before you stored the secret, delete that webhook and create it again under a new key.

Receiving deliveries

Every delivery is a POST with the same envelope:
team_id tells you which workspace the event belongs to, so one endpoint can serve every installation. actor.app_id is your app’s id (credential.app.id from GET /api/v1/me) when the change came from your own API call, so a sync can skip its own writes; see Changes your app made. source is app for ordinary activity and import for records a bulk import or CRM migration wrote; import events are only delivered once the workspace turned that on (see Imports), so a customer migrating from another CRM does not flood your app, and when they do turn it on you can tell those deliveries apart. Verify X-Orbit-Signature with the secret you stored for that webhook before reading anything else; see Webhook Security.

Delivery, retries and logs

  • Submissions and meetings are delivered as they happen; CRM, call and submission-change events within about a minute.
  • A failed delivery (timeout, connection error, 5xx or 429) is retried with exponential backoff, up to 3 retries by default. Retry-After on a 429 is honoured. Other 4xx responses are final.
  • 10 consecutive failed events switch the webhook off. GET /api/v1/webhooks/:id shows is_active, consecutive_failures and last_error; PATCH with { "is_active": true } turns it back on.
  • Deliveries are at-least-once. De-duplicate on id (also sent as X-Orbit-Event-ID); a retry carries the same one. Order is not guaranteed; use occurred_at.
  • GET /api/v1/webhooks/:id/deliveries lists every attempt, with status codes and errors, for debugging a customer’s installation.

Best practices

Respond quickly

Return a 2xx within a few seconds and process the event in a queue. The default timeout is 30 seconds.

Verify signatures

Validate X-Orbit-Signature with the webhook’s secret before processing.

Handle retries

Store processed event ids so a retried delivery is a no-op.

Fetch what you need

Payloads carry the record that changed, not its related records. Use the API for a deal’s contacts, a call’s transcript or a submission’s form.

Expect imports to be off

Records written by a CSV import, the API’s bulk endpoints or a CRM migration are not delivered unless the workspace’s owner turned on Send imported records to integrations (Settings → Integrations → Connected apps). If your app needs a customer’s existing records, read them through the API instead of waiting for contact.created; when a workspace does turn import delivery on, deliveries carry "source": "import".

Clean up on uninstall

Deliveries stop on their own when a workspace removes your app, and every token you hold for that workspace is revoked (401 invalid_token, refreshes 400 invalid_grant). Set an uninstall URL in the Developer Portal to be told right away: Orbit POSTs { "event": "app.uninstalled", "app_id", "client_id", "team_id", "uninstalled_at", "reason" } there, signed in X-Orbit-Signature with your app’s signing secret (same HMAC-SHA256 scheme as deliveries), with X-Orbit-Event: app.uninstalled and X-Orbit-Timestamp. Answer 2xx. A 429, a 5xx or no answer in time is retried three times; any other 4xx is final. reason is workspace_removed or app_suspended. Revoking the last live token you hold for a workspace ends its installation too, without a notification; while another member’s token is live, the installation stays.

Next steps

Event payloads

Every event’s data

Webhooks API

Create, change, delete, and read the delivery log