Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.usehasp.com/llms.txt

Use this file to discover all available pages before exploring further.

Outbound webhooks notify external services when events happen in your workflow app.

Setup

  1. Open your workflow app and go to Webhooks in the sidebar.
  2. On the Outgoing tab, click Add endpoint.
  3. Enter the destination URL, select the events to subscribe to, and save.
  4. Copy the signing secret — you’ll use it to verify incoming requests.

Events

EventWhen it fires
record.createdA single record is created
record.updatedA record is updated
record.deletedA record is deleted
record.bulk_createdRecords are created via a bulk operation
schema.updatedThe entity schema is modified

Delivery

Requests are delivered asynchronously. Hasp retries failed deliveries up to 5 times with exponential backoff:
AttemptDelay after previous failure
1Immediate
210 seconds
360 seconds
45 minutes
530 minutes
(final)2 hours
A delivery is considered successful if the endpoint returns any 2xx status code within 10 seconds. Redirects are not followed.

Request Format

Content-Type: application/json
X-Hasp-Webhook-Id: <delivery-ulid>
X-Hasp-Signature-256: sha256=<hmac-hex>
User-Agent: Hasp-Webhook/1.0
Body envelope:
{
  "id": "01JQDELIVERY0000000000000",
  "event": "record.created",
  "app_id": "01JQAPP00000000000000000",
  "entity_key": "tasks",
  "timestamp": "2026-03-22T01:31:46+00:00",
  "data": { ... }
}

Verifying Signatures

Every delivery includes an X-Hasp-Signature-256 header with an HMAC-SHA256 signature signed with your endpoint’s secret.
import { createHmac, timingSafeEqual } from 'crypto';

function verifySignature(body, secret, signatureHeader) {
  const expected = 'sha256=' + createHmac('sha256', secret)
    .update(body)
    .digest('hex');

  const a = Buffer.from(signatureHeader);
  const b = Buffer.from(expected);
  if (a.length !== b.length) return false;
  return timingSafeEqual(a, b);
}

app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['x-hasp-signature-256'];
  if (!verifySignature(req.body, process.env.WEBHOOK_SECRET, sig)) {
    return res.status(401).send('Invalid signature');
  }
  res.sendStatus(200);
});
Compute the signature against the raw request body bytes, not a parsed JSON object. Use a constant-time comparison to prevent timing attacks.

Idempotency

The X-Hasp-Webhook-Id header contains the delivery ULID. Store this value and check for duplicates before processing — network failures can cause the same delivery to arrive more than once.

Rotating the Signing Secret

Go to the webhook detail view and click Rotate secret. The previous secret remains valid for 7 days after rotation.