Webhooks
Receive signed alert deliveries on your own HTTPS endpoint
A webhook destination receives alerts and report notifications as alert.v3
JSON:
intelligence.materialwhen material intel matches an alert's filters.rank.enteredwhen a project or topic enters your selected ranking.report.publishedwhen a subscribed new topic report is published. Its compactevent.reportobject contains the report id and the account-authorized REST API URL; fetch that URL with your API key for the full report.
Set up
- Open Integrations and add a webhook with a public HTTPS endpoint.
- Registration sends a
destination.testrequest. Return thechallengevalue from its JSON body as the plain-text response body. To try the flow before your receiver exists, registerhttps://docs.aixbt.tech/api/webhook-echofirst. - Store the signing secret when it appears. It is shown once.
- Select the webhook on the alerts or reports you want delivered.
Verify every request
Read the AIXBT-Webhook-Timestamp and AIXBT-Webhook-Signature headers and
check them against the exact raw request body with your stored secret. Do not
parse and re-serialize the body before verifying:
import { createHmac, timingSafeEqual } from 'node:crypto'
export function verifyAixbtWebhook(
rawBody: Buffer,
timestamp: string, // AIXBT-Webhook-Timestamp header
signature: string, // AIXBT-Webhook-Signature header
secret: string, // shown once in Integrations
): boolean {
const sentAt = Number(timestamp)
if (!Number.isSafeInteger(sentAt) || sentAt < 1) return false
if (Math.abs(Date.now() / 1000 - sentAt) > 300) return false
const expected = createHmac('sha256', secret)
.update(`${timestamp}.`)
.update(rawBody)
.digest('hex')
return signature.split(',').some(part => {
const digest = part.trim().split('=')[1] ?? ''
return (
/^[a-f0-9]{64}$/.test(digest) &&
timingSafeEqual(Buffer.from(digest, 'hex'), Buffer.from(expected, 'hex'))
)
})
}The signature header carries one entry, or two separated by a comma for 24
hours after you rotate the secret, so your stored secret keeps verifying
straight through a rotation. The AIXBT-Webhook-Event,
AIXBT-Webhook-Event-ID, and AIXBT-Webhook-Delivery-ID headers tell you
what arrived without parsing the body.
Respond
Return 2xx after accepting a delivery. 408, 429, and 5xx responses
are retried, honoring a bounded Retry-After; other 4xx responses are not.
A 410 disables the destination, and repeated 401 or 404 responses pause
it until a test succeeds.
Process deliveries
Delivery is at-least-once and can arrive late or out of order. Deduplicate by
the delivery.id body field; an intentional replay repeats it with a higher
delivery.replayCount. Acknowledge an unknown event type with a 2xx so
adding support later never causes a retry loop.