Webhooks
Webhooks send each entry of the admin audit trail (the Audit Log page) to a URL you control the moment it is recorded: client created, updated or deleted, secret rotated, registration approved or rejected, change set applied, and so on.
Manage them under Settings > Integrations > Webhooks. That page is for instance admins and organization owners and admins. Webhooks belong to the organization they were created in.
Adding a webhook
Section titled “Adding a webhook”Give the URL, optionally a description, and choose All events or particular ones. The signing secret is shown once, when you add the webhook: copy it then. Send test posts a webhook.test event and shows the outcome. You can switch a webhook off, or delete it, at any time. Up to 10 per organization.
The URL must use https and may not contain credentials. By default Taco refuses to send to private, loopback or link-local addresses (checked again when connecting, so a host can’t change its answer in between) and never follows redirects.
What is sent
Section titled “What is sent”A POST with a JSON body:
{ "id": "0d4f…", "type": "client.secret.rotate", "created_at": "2026-10-02T10:00:00.000Z", "organization_id": "org_…", "status": "success", "actor": { "id": "usr_…", "email": "admin@example.com" }, "target": { "type": "client", "id": "abc123" }, "details": {}}type is the audit action (client.create, client.secret.rotate, registration.approve…; the Audit Log filter lists them), status is success or failure. The admin’s IP address and user agent are not sent.
Headers: X-Taco-Event, X-Taco-Delivery (unique per delivery), X-Taco-Timestamp (Unix seconds) and X-Taco-Signature.
Verifying the signature
Section titled “Verifying the signature”The signature is v1= followed by the hex HMAC-SHA256, keyed with your signing secret, of <timestamp>.<raw body>. Recompute it on the raw body, compare in constant time, and reject old timestamps to stop replays.
import { createHmac, timingSafeEqual } from 'node:crypto'
function verify(secret, headers, rawBody) { const expected = `v1=${createHmac('sha256', secret) .update(`${headers['x-taco-timestamp']}.${rawBody}`) .digest('hex')}` const given = Buffer.from(headers['x-taco-signature'] ?? '') return ( given.length === expected.length && timingSafeEqual(given, Buffer.from(expected)) )}Delivery and retries
Section titled “Delivery and retries”Any 2xx response counts as delivered. Otherwise (or on a timeout of 10 seconds) Taco retries after 1 minute, 5 minutes, 30 minutes, 2 hours and 12 hours, then gives up after 6 attempts. Retries run from a scheduled task every minute (a cron trigger on Cloudflare). A delivery can arrive more than once: use X-Taco-Delivery to ignore repeats. Deliveries are kept 30 days; the settings page shows the recent ones.