Skip to main content
Sente pushes events to an HTTPS endpoint you register: inbound mail (message.received) and run state changes (run.blocked, run.completed, run.failed). Every delivery carries your organization’s webhook secret in the X-Sente-Secret header.

Register an endpoint

POST /v1/webhooks with { url, events, identityId? }. Omit identityId for an org-wide webhook; set it to scope deliveries to one identity. Registration is idempotent per (url, identity scope) — registering the same pair again does not create a duplicate.
The secret is returned at registration only — GET /v1/webhooks never includes it. It is org-level: one secret verifies every delivery to your organization, across all webhooks and identities. Store it like an API key.
URL requirements: the URL must be http(s) and resolve to a public address. Loopback, private-range (RFC 1918), link-local, and cloud-metadata addresses are rejected at registration (400) and re-checked before every delivery.

Verifying deliveries

Compare the X-Sente-Secret header against your stored secret with a timing-safe compare, then ack fast:

Events

The run.* events are single-attempt: if your endpoint is down at that moment, the notification is lost. GET /v1/runs/:id is the backstop — poll it if you must not miss a terminal state. See the runs API reference.

message.received

A thin notification — it has no body. Fetch the full message (body and annotation) with GET /v1/messages/:id / sente.messages.get(id):

run.blocked

Fired when a register, login, or connect run needs a human — a CAPTCHA, an SMS/phone step, a payment step, a confirm-before-submit pause, or an MFA code only the account owner can receive. The payload carries everything needed to route a person to the gate:
  • code — why the run blocked: SUBMIT_CONFIRMATION_REQUIRED, CAPTCHA_REQUIRED, MISSING_FIELD, PAYMENT_REQUIRED, PHONE_REQUIRED, LOGIN_UNCONFIRMED, or (connect runs) TOTP_REQUIRED / MFA_REQUIRED.
  • liveViewUrl — the interactive browser view where a person clears the step.
  • actionUrl — a dashboard deep link to the run page (live view, submit, and resume in one place).
  • holdExpiresAt — the run holds for roughly 10 minutes, then auto-fails with BLOCKED_TIMEOUT if not resumed. A notification should tell the human how long they have.
Not fired for a user-initiated pause (manual intervention from the dashboard) — the user already knows. See Runs and human takeover for the block/resume flow.

run.completed / run.failed

run.failed is the same shape with status: "failed", the error code/detail set, and result: null.

Desktop notifications without a webhook

sente watch is the pull-based counterpart for a human at a machine — a daemon that polls and fires an OS desktop notification the moment any run blocks, with the live-view URL and the sente run resume <id> command:

Testing locally

No public URL needed during development — the CLI relays inbound mail to a local handler with the exact same payload and X-Sente-Secret header: