# Webhooks

> Register HTTPS receivers, verify timestamped webhook signatures and understand the currently implemented event coverage.

Canonical: https://theaimart.co/docs/webhooks
Updated: 2026-10-08 · Guide version: 1.1

The Webhooks dashboard manages signed deliveries to HTTPS receivers you control. Event-name registration does not establish that every named product event is emitted.

## Register and test a receiver

1. Open [Webhooks](/Dashboard/Webhooks), add a public HTTPS URL and select the events you need.
2. Save the endpoint and protect its signing secret. Authorized list responses can return that secret, so do not share dashboard exports.
3. Use Test only with a receiver you control. Inspect the actual delivery status and receiver logs.

```json
{"url":"https://example.com/hooks/theaimart","events":["payout.requested"]}
```

The routes are `GET /webhooks`, `POST /webhooks`, `DELETE /webhooks/{id}` and `POST /webhooks/{id}/test`. Management requires a backend session. At most 10 endpoints may be registered per account; test deliveries are limited to 20 per hour.

## Verify deliveries

The envelope uses `id`, `event`, `data` and `timestamp`. The timestamped header is `X-Theaimart-Signature-V2: t=<unix-seconds>,v1=<hex-HMAC>`. Verify HMAC-SHA256 of the timestamp, a dot and the exact raw request body using the endpoint secret. Reject malformed signatures and timestamps outside your replay window. Deduplicate `X-Theaimart-Delivery` before performing an action.

The legacy `X-Theaimart-Signature` signs only the body and cannot establish freshness. Prefer the timestamped V2 contract. Do not parse and reserialize the body before checking the signature. This Node.js helper validates the V2 header against a five-minute replay window:

```js
import { createHmac, timingSafeEqual } from 'node:crypto';

export function verifyWebhook(rawBody, header, secret, nowSeconds = Math.floor(Date.now() / 1000)) {
  if (!Buffer.isBuffer(rawBody) || typeof header !== 'string' || !secret) return false;
  const parts = /^t=(\d{1,12}),v1=([a-fA-F0-9]{64})$/.exec(header);
  if (!parts) return false;
  const timestamp = Number(parts[1]);
  if (!Number.isSafeInteger(timestamp) || Math.abs(nowSeconds - timestamp) > 300) return false;
  const expected = createHmac('sha256', secret).update(`${parts[1]}.`).update(rawBody).digest();
  return timingSafeEqual(expected, Buffer.from(parts[2], 'hex'));
}
```

Pass the untouched request bytes and your endpoint secret. Only after verification, parse the envelope and atomically record its delivery ID in durable storage before applying side effects. This helper does not implement delivery-ID storage, secret rotation or an HTTP server. Return a failure response for an invalid signature.

## Coverage and troubleshooting

The reviewed backend emits `payout.requested` and provides a signed test delivery. It does not establish install, purchase, review, scan, subscription or release event delivery. No 24-hour retry guarantee is implemented here. The outbound request has a 10-second timeout and rejects unsafe destinations and redirects. A failed test needs investigation at the receiver; registering another event name will not add a missing emitter.

## Next

See [Payouts & Finance](/docs/payouts-and-finance), [Automation](/docs/automation) and [REST API Reference](/docs/rest-api-reference).
