Skip to main content
Reintersect talks to your app with signed HTTPS POST requests to one endpoint. A self-hosted app receives them at the endpoint URL it declares; a hosted app receives them inside Reintersect’s runtime. The SDK verifies and routes every request for you. This page explains what it checks, so you can reason about failures and implement the protocol yourself if you need to.

Two lanes

The full list of interactive delivery types and their payloads is in the delivery protocol.

Signatures

Every request follows the Standard Webhooks specification and carries these headers: Your app’s signing secret is in Settings → Developer → Apps → your app, under the app’s credentials. Rotating it starts a 24 hour grace period in which Reintersect signs with both the old and the new secret, so you can deploy the new value without dropping requests. Hosted apps are redeployed with the new secret automatically.

Verification in the SDK

The SDK verifies every request before your code runs, with WebCrypto, on every runtime it supports. It rejects a request when:
  • the signature doesn’t match any signature in webhook-signature, or
  • webhook-timestamp is more than 5 minutes away from the current time.
Rejected requests get a 401 response and never reach your handlers. The SDK also remembers each webhook-id for 10 minutes on the same instance. A repeated lifecycle delivery is answered with { "duplicate": true } without running your handler again, and a repeated interactive delivery, such as a replayed button click, is refused with a 400 (replayed). Where the secret comes from:

Verifying without the SDK

Any Standard Webhooks library works. With the standardwebhooks package:
verify throws when the signature or timestamp is invalid. Verify the raw body before you parse it; re-serialized JSON doesn’t match the signature.

Interactive deliveries

When a member clicks a button, Reintersect sends a block.action delivery and waits up to 5 seconds. Your app answers in one of two ways.

Answer within 5 seconds

Respond 200 with the result: the next state, a notice, field errors and so on. With the SDK, that is whatever your action handler returns; the SDK merges the state, renders the block and sends both back in the same response. The member sees the new block as soon as the response arrives. If your app doesn’t answer in time, answers with an error, or returns a result that fails validation, Reintersect keeps the last good version of the block and shows the member an inline error. Interactive deliveries are never retried, because the member may already have moved on or clicked again.

Defer and push later

When the work takes longer, such as a deployment, acknowledge first and push the result when it is ready. With the SDK, return a quick result and continue in waitUntil:
Inside an action, reintersect is authorized by the delivery’s interaction token, so the push works without extra credentials. Without the SDK, respond 202 Accepted with an empty body, then push the result within 15 minutes. The block.action payload includes an interactionToken. Send it as a bearer token to update or replace that block’s state:
An interaction token is valid for 15 minutes and only for the block the action ran on. After it expires, use the install’s credential, which needs the blocks:write scope.

Commands and message actions

Slash commands and message actions defer the same way, except the 202 body says who sees your app thinking: { "visibility": "private" } for only the member who ran it, or { "visibility": "public" } for everyone there. Then send the answer within 15 minutes to POST /v0/invocations/respond with the delivery’s interactionToken in the body and your install token as the credential. With the SDK, return defer(task, { visibility }) from the handler, or defer() and call reintersect.invocations.respond when your work finishes. A run takes one answer; the token is refused after that or after 15 minutes.

Idempotency

Authorization

Before it sends an action, Reintersect checks that:
  • the member can see and write in the circle;
  • the block’s audience includes them;
  • the block isn’t final;
  • the action is rendered in the block’s current revision;
  • the install is active.
It then sends the member as actor in the signed body. Your app still decides what that member may do in your own system. Use actor.memberId to look up the matching account on your side before you deploy, delete or pay for anything. audience hides components from people; it doesn’t stop a crafted request, and your server is the last check.

Network rules

  • Self-hosted endpoints must use HTTPS on the default port. Reintersect doesn’t deliver to private, loopback or link-local addresses.
  • Reintersect doesn’t follow redirects. An endpoint that answers 3xx fails the delivery.
  • Reintersect never sends your app its cookies or a member’s session. The only credentials in a request are the signature headers, a short-lived install token in the body (installToken, valid for one hour) and, for actions, an interaction token for that block.
  • Hosted apps can only call the hosts they list in egress. Reintersect’s gateway sets the install token on their calls to the Reintersect API. See Reintersect-hosted apps.

Reintersect-signed tokens

Webhook deliveries are always signed with Standard Webhooks. Separately, MCP servers can opt in to receive a short-lived JWT that Reintersect signs for each tool call, so they can identify the member without running their own OAuth. See MCP authentication. Context that Reintersect adds to MCP calls under _meta is a convenience hint and doesn’t prove identity, so authorize with the verified token and never with _meta.