longrun.
Receive webhook updates
Build with LongRun / Receive webhook updates

Receive webhook updates

Choose which endpoints receive each run’s result.

1. Connect an endpoint

In Settings → Webhooks, add your public HTTPS receiver. Copy the generated signing secret when it is shown, then copy the webhook ID.

2. Select destinations

Pass webhook_ids to choose endpoints for a run. An empty array disables delivery. Omitting it on a workflow run uses the published selection; omitting it on a direct action sends no webhook.

Select a receiver for this run
curl --request POST "https://api.uselongrun.app/v1/pdf/html-to-pdf" \
  --header "Authorization: Bearer ${API_KEY}" \
  --header "Content-Type: application/json" \
  --data "$(jq -n --arg WEBHOOK_ID "${WEBHOOK_ID}" '{
  "inputs": {
    "html": "<h1>Hello</h1>"
  },
  "webhook_ids": [
    $WEBHOOK_ID
  ]
}')"
Explicitly disable delivery
{
  "inputs": {
    "html": "<h1>No webhook</h1>"
  },
  "webhook_ids": []
}

3. Verify and acknowledge

Verify X-LongRun-Signature using HMAC-SHA256 over event_id.timestamp. followed by the exact request body bytes. The signature is v1= plus a hexadecimal digest. Use X-LongRun-Event-Id and X-LongRun-Timestamp, reject timestamps over five minutes from your clock, and compare signatures in constant time. In the Node.js example, rawBody must be the original Buffer from the HTTP request, headers must use lowercase keys, and secret is the signing secret from Settings. Reject the request if this returns false; only then parse its JSON.

Verify a webhook · Node.js
import { createHmac, timingSafeEqual } from 'node:crypto';

function verifyWebhook(rawBody, headers, secret) {
  const eventId = headers['x-longrun-event-id'];
  const timestamp = headers['x-longrun-timestamp'];
  const signature = headers['x-longrun-signature'];
  if (!Buffer.isBuffer(rawBody) || typeof eventId !== 'string' ||
      !/^[a-zA-Z0-9_-]+$/.test(eventId) ||
      !Number.isSafeInteger(Number(timestamp)) ||
      Math.abs(Date.now() / 1000 - Number(timestamp)) > 300 ||
      typeof signature !== 'string' || !/^v1=[a-f0-9]{64}$/.test(signature)) return false;
  const expected = 'v1=' + createHmac('sha256', secret)
    .update(eventId + '.' + Number(timestamp) + '.')
    .update(rawBody).digest('hex');
  return timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}

4. Handle retries

Return 2xx promptly and deduplicate by event ID. Initial deliveries allow up to eight attempts. HTTP 408, 429, 5xx and transient delivery failures retry with exponential delays; other rejected HTTP responses fail without automatic retries. A manual retry adds another eight-attempt allowance. A delivery failure does not change the run result. View delivery history in Settings → Webhooks.

5. Check delivery

Submit a sample, then open Settings → Webhooks to inspect its delivery history. Use the returned run id below to check the run independently. Successful processing and successful webhook delivery are separate outcomes.

Check the run
curl --request GET "https://api.uselongrun.app/v1/runs/${RUN_ID}" \
  --header "Authorization: Bearer ${API_KEY}"