Skip to main content

Connect services with webhooks

TOW webhooks connect a workspace to scripts, agents, build systems, and other services without polling. There are two directions:

  • An Incoming webhook is a receiver URL. An external producer calls TOW, and TOW durably routes the request to one or more automations.
  • An Outgoing webhook is an event subscription. TOW sends selected app events to an HTTPS destination with an HMAC signature, retries, delivery history, and replay.

These records share administration and activity screens, but they intentionally have different credentials and execution semantics. An incoming automation execution is not an outgoing event subscription.

Open Webhooks settings

Organisation owners and administrators can manage every webhook under Organisation Settings → Integrations → Webhooks. A project administrator can manage project-owned webhooks under Project Settings → Integrations. A project-owned webhook has an immutable project scope and cannot expose organisation-wide destination details or activity.

Select New webhook, then choose Receive a request or Send TOW events. TOW shows every newly issued secret exactly once. Copy it into a secret manager before closing the dialog.

Receive an external request

Create an Incoming webhook when an external producer should start TOW automation flows. One receiver can route to as many as 25 automations. Each connected automation must use the Incoming webhook trigger and must be in the receiver's permitted scope.

Bearer authentication is the simplest option:

curl --request POST 'https://tow.example.com/api/hooks/v1/in/<endpoint-id>' \
--header 'Authorization: Bearer whin_<key-id>_<secret>' \
--header 'Idempotency-Key: build-2026-08-12-42' \
--header 'Tow-Event: build.completed' \
--header 'Content-Type: application/json' \
--data '{"build_id":42,"status":"passed"}'

The default receiver requires Idempotency-Key. A repeated key with the same body returns the original receipt with Tow-Duplicate: true and creates no duplicate automation runs. Reusing a key with different content returns 409 Conflict.

TOW also accepts an HMAC mode. Send Tow-Delivery-Id, Tow-Request-Timestamp, and Tow-Signature. Compute each v1 signature as the lowercase hexadecimal HMAC-SHA256 of these exact bytes:

<timestamp>.<delivery_id>.<raw_request_body>

The timestamp must be within the configured clock-skew window, which defaults to five minutes.

Receivers accept one occurrence per POST. Supported bodies are JSON and +json, form-urlencoded, UTF-8 text/XML, and CloudEvents 1.0 structured or binary mode. Multipart, compressed bodies, and CloudEvents batches are rejected. The default body limit is 256 KiB and the default per-receiver rate is 60 requests per minute.

The event type comes from a valid CloudEvent type, then Tow-Event, and otherwise defaults to generic. An accepted response is asynchronous:

{
"receipt_id": "0198...",
"status": "accepted",
"duplicate": false
}

The Activity tab records accepted, duplicate, unrouted, and skipped dispatches. It links each successful dispatch to its Automation run. Replaying an incoming receipt uses the retained parsed request and the automation's current definition, so review the flow before replaying an operation with side effects.

The Overview tab can send a retained sample request through the same durable routing path. TOW warns before doing this because every matching automation runs normally and may cause side effects; the receipt and resulting runs are marked as tests.

In new automations, request data is available under {{webhook.request.body.*}}. TOW never exposes Authorization, cookies, signature material, proxy headers, or secret-like query parameters to the automation context. Administrators may explicitly expose additional safe header or query names through the management API.

Send TOW events

Create an Outgoing webhook, enter its HTTPS destination, select its project scope, and select exact event types. Select all current expands into today's explicit values; a future event type is never silently added to an existing subscription.

The destination should acknowledge quickly and move expensive work to its own queue. Any 2xx response succeeds. TOW waits up to five seconds, does not follow redirects, and retries transport failures plus 408, 409, 425, 429, and 5xx responses. The default six-attempt schedule is immediate, then approximately 1 minute, 10 minutes, 1 hour, 6 hours, and 24 hours, with positive jitter. A valid Retry-After on 429 or 503 is honored up to 24 hours.

Deliveries are at least once and are not globally ordered. Deduplicate by Tow-Delivery-Id or Idempotency-Key. The same logical delivery ID and request body are retained across automatic attempts.

Every request includes:

Content-Type: application/json; charset=utf-8
User-Agent: TOW-Webhooks/1
Tow-Webhook-Id: <integration UUID>
Tow-Event: ticket.updated
Tow-Event-Id: <event UUID>
Tow-Delivery-Id: <delivery UUID>
Tow-Attempt: 1
Tow-Payload-Version: 1
Tow-Request-Timestamp: 1786531200
Tow-Signature: t=1786531200,v1=<hex digest>
Idempotency-Key: <delivery UUID>

Verify Tow-Signature over the exact raw request bytes before parsing JSON. The signed bytes are:

<timestamp>.<delivery_id>.<raw_request_body>

Parse all v1 values and accept any one that verifies. During the default 24-hour rotation overlap, TOW signs with both the new and retiring secret. Use a constant-time comparison and reject timestamps outside five minutes. Tow-Attempt is operational metadata and is not part of the signature.

Health, replay, and retention

The first exhausted or terminal logical delivery marks a webhook as failing. A later successful live delivery clears the failure streak. Five consecutive logical failures auto-disable it; 410 Gone disables it immediately. A destination that violates the live network policy enters suspended_security and requires administrator or operator action.

Pausing is a forward-looking stop, not a backlog mode. TOW cancels deliveries that have not completed and does not capture events that occur while the webhook is paused. Resume the webhook before the changes you want delivered; use replay only for retained deliveries that were captured before the pause.

Tests and replays are diagnostic: they are allowed while paused or auto-disabled and do not damage endpoint health. A replay keeps the original event ID, event time, payload version, and captured body, creates a new delivery ID, uses the current URL and current signing secret, and adds Tow-Replay: true. Filters are not re-evaluated.

By default, encrypted payloads and bounded response previews are retained for 14 days, delivery metadata for 30 days, and response bodies are capped at 4 KiB. Privacy erasure and hard-delete workflows may purge a retained payload earlier. Once the payload is unavailable, it cannot be replayed.

Access and encrypted projects

Every webhook has a stable, non-human integration principal. It survives creator removal, and TOW rechecks that principal's current access when an event is captured or replayed. Project roles define ordinary reach. Security-level records and private documents or goals require explicit grants in the webhook's Overview tab. Revoking a grant invalidates queued work created under the old access revision.

End-to-end encrypted projects remain observable only through server-readable planning fields. TOW never sends ciphertext envelopes, keys, encrypted custom values, protected text, filenames, file bytes, or attachment download URLs. changed_fields may report that a protected field changed, while redacted_fields records what was omitted. The payload's encryption.content_included value is false.

Use a PAT or MCP connection to hydrate an event when an external agent needs current state. The caller's live permissions still apply.

See Webhook event and payload reference for the complete v1 catalog and envelope.