Webhooks API
Webhooks are HTTP push subscriptions that POST event notifications to a URL you control when analysis and report jobs complete. These endpoints let you manage subscriptions programmatically: register URLs, list and delete them, and verify ownership of the receiving domain. For concepts and setup walkthroughs, see the guides linked at the bottom of this page.
All endpoints on this page require the workspace admin role (workspace owners always qualify). Requests from other workspace roles are rejected with 403 Admin access required for this workspace.
Base URL: https://api.perceive8.com. Authenticate with X-API-Key: pk_live_... or Authorization: Bearer <jwt-or-key>.
POST /v1/webhooks
Create a webhook subscription for the authenticated workspace. A signing secret is generated server-side (64 hex characters) and stored on the webhook; it is used to sign every delivery but is not returned by any endpoint. Creating a webhook also creates (or refreshes) a domain-verification record for the URL's domain and returns it alongside the webhook; if that record cannot be created, domain_verification is null and the webhook is still created.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
url |
string | yes | HTTPS endpoint that receives event POSTs (max 2048 chars) |
events |
array of strings | no | Event names to subscribe to. Defaults to ["analysis.completed"] |
{
"url": "https://yourapp.example.com/webhooks/perceive8",
"events": ["analysis.completed", "report.completed"]
}
Response 200 OK
webhook contains the created subscription; domain_verification contains the DNS instructions for the URL's domain (see Domain verification).
{
"webhook": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"user_id": "1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed",
"url": "https://yourapp.example.com/webhooks/perceive8",
"events": ["analysis.completed", "report.completed"],
"is_active": true,
"domain_verified": false,
"created_at": "2026-08-19T10:00:00Z",
"last_triggered_at": null
},
"domain_verification": {
"id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
"domain": "yourapp.example.com",
"token": "dGhpcy1pcy1hbi1leGFtcGxlLXRva2Vu",
"status": "pending",
"expires_at": "2026-08-22T10:00:00Z",
"txt_record_name": "_perceive8-verify.yourapp.example.com",
"txt_record_value": "perceive8-verify=dGhpcy1pcy1hbi1leGFtcGxlLXRva2Vu"
}
}
curl -X POST https://api.perceive8.com/v1/webhooks \
-H "X-API-Key: pk_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourapp.example.com/webhooks/perceive8",
"events": ["analysis.completed", "report.completed"]
}'
GET /v1/webhooks
List all webhook subscriptions for the authenticated workspace.
Response 200 OK — array of webhook objects:
| Field | Type | Description |
|---|---|---|
id |
uuid | Webhook identifier |
user_id |
uuid | Internal user that created the webhook |
url |
string | Delivery endpoint |
events |
array of strings | Subscribed event names |
is_active |
boolean | Inactive webhooks receive no deliveries |
domain_verified |
boolean | Whether the URL's domain has been verified |
created_at |
datetime | Creation timestamp |
last_triggered_at |
datetime | null | Last successful (2xx) delivery timestamp |
curl https://api.perceive8.com/v1/webhooks \
-H "X-API-Key: pk_live_xxxxxxxxxxxx"
DELETE /v1/webhooks/{webhook_id}
Delete a webhook. Deletion is immediate; the endpoint stops receiving events.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
webhook_id |
path | uuid | Webhook identifier |
Response 204 No Content — empty body. Returns 404 {"detail": "Webhook not found"} if the webhook does not exist in the authenticated workspace.
curl -X DELETE https://api.perceive8.com/v1/webhooks/3fa85f64-5717-4562-b3fc-2c963f66afa6 \
-H "X-API-Key: pk_live_xxxxxxxxxxxx"
Domain verification
Deliveries are only sent to webhooks whose URL domain has been verified (domain_verified: true); deliveries to unverified domains are skipped. Verification works by publishing a DNS TXT record and asking Perceive8 to check it:
POST /v1/webhooks/verify-domainwith the webhook URL → returns a token and the exact TXT record to publish. (This also happens automatically when you create a webhook.)- Publish the TXT record
_perceive8-verify.<domain>with valueperceive8-verify=<token>at your DNS provider. POST /v1/webhooks/confirm-domainwith theverification_id→ Perceive8 performs the DNS lookup and, on a match, marks the domain verified.
The token expires after 72 hours. Requesting verification again for the same domain returns the existing record if already verified, or refreshes the token and expiry (status reset to pending) otherwise. When a domain is confirmed, all webhooks in the workspace whose URL contains that domain are marked domain_verified: true.
POST /v1/webhooks/verify-domain
Initiate (or refresh) domain ownership verification for a webhook URL.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
url |
string | yes | Webhook URL; must be http/https with a valid hostname |
{
"url": "https://yourapp.example.com/webhooks/perceive8"
}
Response 200 OK
| Field | Type | Description |
|---|---|---|
id |
uuid | Verification record identifier — pass it to confirm-domain |
domain |
string | Hostname extracted from the URL |
token |
string | Ownership token to publish in DNS |
status |
string | pending, verified, or expired |
expires_at |
datetime | Token expiry (72 hours after issuance) |
txt_record_name |
string | TXT record name to create: _perceive8-verify.<domain> |
txt_record_value |
string | TXT record value to publish: perceive8-verify=<token> |
Returns 422 {"detail": "Invalid URL: cannot extract domain"} if no hostname can be parsed from url.
curl -X POST https://api.perceive8.com/v1/webhooks/verify-domain \
-H "X-API-Key: pk_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"url": "https://yourapp.example.com/webhooks/perceive8"}'
POST /v1/webhooks/confirm-domain
Perform the DNS TXT lookup and confirm domain ownership. On success, the record's status becomes verified and all workspace webhooks on that domain are marked domain_verified: true.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
verification_id |
string (uuid) | yes | id returned by verify-domain or by webhook creation |
{
"verification_id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"
}
Response 200 OK
{
"verified": true,
"message": "Domain verified successfully"
}
verified is false — with a message explaining why — when the record is not found, the token has expired ("Verification token has expired; please request a new one"), no TXT records exist for _perceive8-verify.<domain>, the expected value is not among them, or the DNS lookup fails. An already-verified domain returns verified: true with "Domain already verified".
GET /v1/webhooks/domains
List all domain-verification records for the authenticated workspace.
Response 200 OK — array of domain-verification objects with the same fields as verify-domain (id, domain, token, status, expires_at, txt_record_name, txt_record_value).
curl https://api.perceive8.com/v1/webhooks/domains \
-H "X-API-Key: pk_live_xxxxxxxxxxxx"
Events
Subscribe to event names in the events array when creating a webhook. The following events are emitted by the platform:
| Event | When sent | Payload fields |
|---|---|---|
analysis.completed |
An audio-analysis pipeline finishes successfully | analysis_id (string), status (string, "completed") |
report.completed |
A report finishes generating for an analysis | report_id (string uuid), analysis_id (string), status (string, "COMPLETED") |
Example analysis.completed delivery body:
{
"analysis_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed"
}
Delivery and signatures
Each event is delivered as an HTTP POST with the JSON payload as the raw body. A webhook receives a delivery only when it is active (is_active: true), its events list contains the event, and its domain is verified. On a 2xx response, the webhook's last_triggered_at is updated.
| Header | Description |
|---|---|
X-Perceive8-Signature |
sha256=<hex> — HMAC-SHA256 of the raw request body, keyed with the webhook's signing secret |
X-Perceive8-Event |
Event name, e.g. analysis.completed |
Content-Type |
application/json |
Verify deliveries by computing HMAC-SHA256 over the exact raw request body with your signing secret and comparing it (constant-time) against the hex digest in X-Perceive8-Signature. See the verification guide linked below for sample code. Delivery uses a 10-second timeout; your endpoint should respond quickly and process asynchronously.
See also
- Webhooks overview — concepts and setup guide
- Verifying signatures — validate
X-Perceive8-Signaturein your receiver - Retry behavior — delivery schedule, retry policy, and idempotency