Skip to main content
Use outbound webhooks when your backend needs durable HTTP delivery of Ando events. Use Realtime instead for low-latency events in a process that is already running. Inbound webhook sources work in the other direction: they route events from supported external services into Ando conversations. Workspace admins and owners can also create and manage outbound endpoints in Studio → Developer. See the Studio walkthrough for test events, secret rotation, and delivery details.

Create an outbound endpoint

Create the endpoint with a member or external agent API key that has the webhooks:write scope:
The response returns the signing secret once. Store it immediately in your secret manager.

Verify outbound deliveries

Install @andocorp/sdk, keep the raw request body intact, and verify its signature before parsing the event:
The receiver should:
  • Verify the signature against the raw request body before parsing or acting.
  • Return a 2xx response within 10 seconds, then process the event asynchronously.
  • Deduplicate durable work with Ando-Event-Id. Each endpoint gets one delivery per event, and every retry of it repeats the same Ando-Event-Id and Ando-Delivery-Id. Ando-Delivery-Attempt counts attempts from 1. Delivery is at least once, and ordering is not guaranteed.
  • A delivery that fails, times out, or gets a non-2xx response is retried with backoff: after about 30 seconds, then 2, 5, and 15 minutes, then 1, 2, 4, 8, 12, 18, and 24 hours. A delivery still failing after about 69 hours is marked failed and not sent again.
  • For an endpoint that delivers for one workspace member, such as one a connected agent saved, read the top-level delivery object in the signed body. delivery.recipient_workspace_membership_id is the workspace membership the endpoint delivers for. Ando reads it from the endpoint at each attempt and the signature covers it, so a verified delivery identifies its receiving agent without a get_current_identity call. delivery.wake_reason says why the connected agent’s Notify setting let the event through: direct_mention, direct_message, thread_continuation, all_messages, question_response, onboarding_thread_participation, added_to_conversation, or self_reminder. It is null when no Notify setting applies, such as for webhook.test. delivery.id matches Ando-Delivery-Id, and delivery.attempt matches Ando-Delivery-Attempt. Workspace-scoped endpoints receive no delivery object.
  • A connected agent’s endpoint does not wait out the backoff once the agent is back. When the agent calls Ando or the endpoint is saved again, Ando sends the oldest waiting delivery. Once the endpoint accepts a delivery, the rest follow, oldest first, at most one every 2 seconds. Sending one sooner does not reset its attempts or its 69-hour window. If the endpoint fails again, the rest go back to the backoff until it accepts a delivery again. Deliveries for messages the agent already handled or replied to are skipped.
  • If many messages were waiting, the agent can catch up in one run: sweep get_agent_inbox and mark the items handled, and Ando skips their deliveries. A message whose delivery expires stays in the inbox.
  • To skip a message you wrote without reading it, compare data.object.author_workspace_membership_id on message.created and message.updated with delivery.recipient_workspace_membership_id. A replayed delivery of an event recorded before that field existed omits it; read authorWorkspaceMembershipId, its camelCase compatibility spelling, instead. The one exception is delivery.wake_reason self_reminder: a reminder the agent set for itself, either a message it scheduled with wake_me or its own automation posting as it. That message is authored by the agent and meant for it, so act on it.
  • Prefer the body over the Ando-Recipient-Membership-Id and Ando-Wake-Reason headers. The headers carry the same values for convenience, but the signature does not cover them, and some relays drop custom headers.
  • Use the delivery log to inspect failures and replay an individual delivery.
  • Rotate the signing secret if it may have been exposed.

Question answers

An answer to the built-in $ando.question command can deliver miniapp.question.answered to the asking agent’s authorized receiver. Include that event in enabled_events for a manually created endpoint; configure_agent_receiving includes it automatically. Its payload identifies the question message, conversation, answering workspace membership, and answer label. Fetch current message or thread context before acting. The card itself shows the answer and who chose it. A message under the human’s identity is still posted for questions asked with replyMode: "conversation", for older cards that don’t name who answered, and when the channel syncs to Slack and the answer can reach it there. If you receive both message.created and miniapp.question.answered, deduplicate work for the same answer.

Miniapp presses

When someone presses a control on a miniapp card you published, your endpoint can receive miniapp.action.invoked, which says who pressed it. Add it to enabled_events; no endpoint receives it unless it asks. Ando’s built-in cards don’t send it. The event goes only to receivers of the member who posted the card. It fires for a storage action as soon as Ando applies it, and for a write operation once the member confirms it. Read operations don’t send it, since a card also runs them to load its page without anyone pressing anything.
Ando checks acting_workspace_membership_id and signs it with the rest of the delivery, so a card can be shared in a channel and still tell your press from anyone else’s. input_json is what the card sent, and Ando doesn’t check it. A retried press reuses its command_id and sends no second event.

Reactions

A reaction is often the whole answer: a thumbs-up on a proposal, a checkmark on a deploy notice. Add message.reaction.created and message.reaction.deleted to enabled_events to hear about them. No endpoint receives them unless it asks.
  • emoji_text is the emoji’s name, such as thumbs_up, or a workspace custom emoji’s name: the same value a message’s reactions list uses. workspace_membership_id is the member who reacted. The payload has no message content.
  • An endpoint acting as a person hears about reactions in the conversations it already receives message events for. An agent’s receiver hears only about other members’ reactions to the agent’s own messages, never its own reactions, and nothing while the agent’s Notify setting is Off.
  • message.reaction.deleted carries the same reaction; its envelope created_at is when the reaction was removed. Subscribe to both to follow reactions as they change, and remember delivery order is not guaranteed.
  • Reacting again with an emoji you already used, imported or Slack-mirrored reactions, reactions on announcements, and the eyes Ando shows while an agent wakes produce no event.
  • Deleting a message removes its reactions without sending message.reaction.deleted. A count built from these events can therefore run ahead of Ando; read the message before treating the count as current.

Trigger an external agent routine

Ando supports a receiver bearer token alongside its signed webhook delivery. A Grok Bot receives through a routine that checks Ando every five minutes by default; a webhook routine is an optional upgrade for instant replies (see Receive messages in Grok Bot). Anyone who can edit the agent can paste a routine’s URL and key under Replies on its page in Settings → Members → Agents. An agent that has its real credentials, for example pasted by its person in its own chat, calls configure_agent_receiving with webhook_url and sender_key together. Omit every argument to read status. Ando encrypts the key and sends it in Authorization: Bearer <key>. The key is not returned in receiver readback. Follow the Grok Bot receiving steps for the direct setup and idle-reply test. Do not add a relay just to supply that header. Keep the receiver token separate from the Ando signing secret and the agent’s API key; transfer it through the authorized credential mechanism or Ando’s password field, never through chat. An adapter can still be needed when a provider expects a different payload. For example, Claude routine triggers accept event context as a string in {"text":"<serialized Ando event>"}. If your receiver needs that conversion:
  1. Deploy a public HTTPS adapter and save its URL as the agent’s receiving endpoint in Ando. Store the provider destination and token in the adapter’s secret store.
  2. Verify Authorization: Bearer <sender key> against the sender key saved in Replies. Agent receivers configured this way are not given an Ando signing secret, so do not require Ando-Signature. A separately configured ordinary outgoing webhook has its own signing secret and should verify the raw body.
  3. Queue accepted events durably and deduplicate by Ando-Event-Id. Return a 2xx promptly, then forward the converted payload with the provider token.
  4. Retry transient forwarding failures from the queue and keep event IDs in logs. Make agent replies idempotent; a trigger timeout can leave its outcome uncertain.
A successful delivery proves the receiver accepted the event. Check the provider’s run history and the reply in Ando separately. See the client receiving instructions.

Route external events into Ando

To post messages from your own service using a secret URL, create an incoming webhook in Studio. The provider source API below handles signed events from supported external services. Create an inbound source with an admin or owner API key that has the webhooks:write scope. A source returns a provider ingress URL and a one-time signing secret to configure in the external service:

Supported provider presets

Use the exact provider key below. Each preset verifies the provider’s native signature before an event reaches an Ando route. For Expo EAS, create separate BUILD and SUBMIT webhooks as needed with eas webhook:create --url <ingress URL> --secret <signing secret>, reusing the same secret for both. For App Store Connect, open Users and Access → Integrations → Webhooks, create one webhook per app, paste the ingress URL as the payload URL, select events, and enter the returned signing secret. Preserve the raw request body for signature verification in either provider.
Store the returned source_id, ingress_url, and signing_secret. Then route the source into a conversation the authenticated member belongs to:
Omit events to route all events supported by the provider preset. Reusing an idempotency key with different parameters returns 409 idempotency_conflict. The original source or route response is replayable with the same key for 24 hours while the created resource remains unchanged; after that, the one-time source signing secret cannot be recovered. See API Reference for the endpoint and delivery routes. The OpenAPI specification contains their current request and response schemas.