Developers

Webhooks

Each webhook step gets its own endpoint verified by bearer token or HMAC (GitHub, Stripe, plain). Covers immediate, last-step and respond-step modes.

The developer-facing contract behind the webhook trigger. Each webhook step gets its own endpoint:

POST https://<your-instance>/webhooks/<webhook-id>

Authentication

Two verification methods, set on the step:

  • Bearer: Coworkly mints the token; send it as Authorization: Bearer …. Rotating the token on the step invalidates the old one immediately.
  • HMAC: for senders that sign payloads. Formats: github (X-Hub-Signature-256), stripe (Stripe-Signature), or plain (hex digest of the body; custom header name supported).

Unauthenticated deliveries get 401 and never touch a workflow.

Response modes

mode behavior caller sees
immediate ack now, run async 202 {"ok": true, "execution_id": …}
last step block until the run ends 200 + the terminal step's output
respond step block until a respond to webhook step fires that step's status/headers/body; the rest of the run continues in background

Other codes worth handling: 404 means an unknown id or the workflow isn't active (deliberately indistinguishable); 500 means the run failed before responding; 504 means the response timeout elapsed (the workflow keeps running; only the HTTP wait gave up).

Sample capture

While a webhook step is capturing, one authenticated delivery is accepted even if the workflow isn't active. It returns 202 {"ok": true, "captured": true} and records the payload shape without running anything.

App events (platform-routed)

Each event arrives normalized: payload.text, user, channel, channel_type, ts, thread_ts, mentioned (the bot was @-mentioned or DM'd) and bot_thread (a reply in a thread the bot is already part of, so a conversation continues without re-mentioning it). Gate on mentioned or bot_thread for an assistant.

A reaction (event_type: "reaction_added") adds reaction (the emoji name), item_user (who wrote the reacted-to message) and item_type; its channel, ts and thread_ts point at that message, so a step can reply in its thread or read it back. The bot's own reactions never come back as events.

The webhook step's App event source is a different transport with the same trigger: instead of a per-step URL, a published Coworkly app (Slack first) receives every event for the connected account at one platform endpoint, POST /webhooks/inbound/:provider, and Coworkly finds the connections authorized for that account (team_id for Slack, the installation for GitHub, organizationId for Linear), and in each connection's own workspace fires only the triggers bound to that connection. Nothing is exposed per workflow, so there is no URL or secret to manage.

Each provider delivery is signature-verified with the app's own secret before parsing (fail-closed), deduplicated by the provider's event id, and, for Slack and GitHub, filtered through a bot-loop guard so the bot can never trigger itself. Linear shows a workflow's changes as the connected person's, so a Linear workflow that edits its own trigger issue guards on payload.changed instead. The same endpoint family serves the platform SMS and email triggers (/inbound/surge, /inbound/resend).

Delivery semantics

Coworkly doesn't re-request a failed inbound delivery; retries are the sender's job (most webhook senders retry on non-2xx). Once a delivery is accepted (202), execution retries happen internally. The endpoint is rate-limited per source IP.