POST, instead of (or alongside) email. Use it to drive a Slack bot, open a ticket, write to a database, or trigger any workflow the moment a watched entity is mentioned or appears as a speaker.
Setting one up is three steps:
Create a webhook connection
A connection is a destination URL plus a generated signing secret, scoped to one project. You create it once and reuse it across alerts.
Attach the connection to an alert
Add a
WEBHOOK notification channel that references the connection’s ID. From then on, every match the alert produces is POSTed to the connection’s URL.Before you start
- Plan — Webhooks are part of the alerts feature, so they require a Team, Business, or Enterprise plan, the same as creating alerts.
- API key — Connection management uses a standard
X-API-Key, bound to a project. Create one on the , and pass that project’s ID in the path. - A public HTTPS endpoint — The destination URL must be
httpsand must resolve to a public address. Private, loopback, and link-local addresses are rejected at create time and re-checked at delivery time.
1. Create a webhook connection
POST the destination URL to the project-scoped connections endpoint. The response includes the secret — this is the only time it is returned, so store it securely.
Response (200)
2. Attach the connection to an alert
A webhook is just another notification channel. Add an entry to an alert’snotifications array with type: "WEBHOOK" and the connection’s id as webhook_connection_id. You can mix it with EMAIL channels on the same alert.
PATCH /v1/alerts/{id}. Remember that notifications, when present in a PATCH, replaces the whole set — include every channel you want to keep. See Create and manage alerts.
The connection must already exist, and its
surface must match the alert’s. A connection created with an API key is PLATFORM; an alert created the same way can only reference a PLATFORM connection. Referencing an unknown or wrong-surface connection returns validation_error.Send a test event
Rather than wait for a real match, trigger a synthetic delivery to check your endpoint end to end — that it receives thePOST, verifies the signature, and parses the body:
POSTs a real-shaped alert.match.created event to every active webhook connection on the alert, carrying the alert’s real id, title, kind, and first watched entity, with illustrative sample match content. The only difference from a live delivery is the envelope’s test field, set to true so your handler can tell it apart and skip real side effects. Nothing is persisted — a test never appears in the alert’s delivery log. A channel whose connection has since been deleted is skipped and doesn’t appear in the results.
The response reports the outcome for each connection:
Response (200)
delivered is true when your endpoint answered 2xx. A non-2xx or unreachable endpoint is reported as delivered: false with the reason in error, and the request still returns 200 — the send is what’s under test, so your endpoint’s own response comes back as data rather than an error. If the alert has no active webhook connection — either none is configured, or every configured connection has been deleted — the endpoint returns 422.
The payload
Particle POSTsContent-Type: application/json with a User-Agent of ParticlePro-Webhooks/1.0. Every payload carries an event_type so you can branch on the shape before parsing the rest.
event_type | Sent for | Body shape |
|---|---|---|
alert.match.created | A REALTIME alert, one POST per match | A single match, with full transcript windows |
alert.digest.created | A DAILY or WEEKLY alert, one POST per digest window | An array of matches, each summarized (no windows) |
| Field | Type | Description |
|---|---|---|
event_type | string | alert.match.created or alert.digest.created. |
delivery_id | string | Identifier for this delivery attempt. Mirrors the X-Webhook-ID header. See idempotency — this is not a stable key across retries. |
timestamp | integer | Unix seconds for the event: the match’s detection time (realtime), or the most recent match in the window (digest). Distinct from the X-Webhook-Timestamp header, which is the send time used in the signature. |
url | string | Deep link to the human-readable delivery page for this match or digest. |
test | boolean | Present and true only on a test delivery; absent on real matches. Branch on it to exercise your parse-and-verify path while skipping real side effects. |
alert | object | The alert that fired: { "id", "title", "kind" }. |
match / matches | object / array | The match (realtime) or list of matches (digest). |
match object is the same shape the REST API returns from GET /v1/alerts/matches/{id}, minus a few REST-only fields: the deliveries array, the deprecated monitor_id, and the episode’s raw podcast_popularity percentile (its human-readable podcast_popularity_badge is kept). The full field reference lives on Alert results; the examples below show the fields you’ll typically act on.
Realtime: a mention match
alert.match.created (ENTITY_MENTION)
ENTITY_MENTION match, mention_count and mention_variants describe how the entity was referenced, and each window’s lines carry the verbatim dialogue with the matched line flagged is_mention. relevance (on_target vs incidental) and llm_summary are added by the summarizer and may be absent if it hasn’t finished or gave up.
Realtime: a speaker appearance
alert.match.created (PODCAST_SPEAKER)
PODCAST_SPEAKER match, roles lists how the entity appeared (GUEST, PANELIST, CORRESPONDENT, AUDIENCE, or SOUNDBITE_SPEAKER), mention_count is the speaker’s line count, and windows may carry clip metadata (clip_id, clip_title, …) when the episode has a clip for that speaker.
Digest
ADAILY or WEEKLY alert sends one POST per window with all of its matches bundled into a matches array. To keep large digests bounded, each match is summarized — transcript windows are stripped, and a single clip_url per match points into its primary window’s audio. Fetch full windows with GET /v1/alerts/matches/{id} when you need them.
alert.digest.created
Verify the signature
Every POST carries three headers. Verify the signature on each delivery before trusting the body — it proves the request came from Particle and was not modified.| Header | Purpose |
|---|---|
X-Webhook-ID | Identifier for this delivery attempt. Mirrors the body’s delivery_id. |
X-Webhook-Timestamp | Unix seconds when the payload was signed (send time). Used in the signature and for replay protection. |
X-Webhook-Signature | v1,<base64> — the HMAC-SHA256 signature. |
"v1," + base64(HMAC-SHA256(signing_secret, that_string)) and compare it to X-Webhook-Signature with a constant-time comparison.
Delivery, retries, and idempotency
- Respond quickly with a 2xx. Any
2xxstatus counts as success. Each POST is given up to 30 seconds end to end; do slow work asynchronously and acknowledge first. - Failures are retried (realtime). A realtime delivery that fails — a non-2xx response, a timeout, or a connection error — is retried with a bounded budget of up to 5 attempts. After that the delivery is marked
ABANDONEDand dropped for that connection. - Redirects are not followed, and a destination that resolves to a non-public address is refused at delivery time. Keep the endpoint on a stable public HTTPS URL.
- A deleted connection is skipped silently at delivery time; the alert’s other channels still fire.
Idempotency
Deliver-at-least-once means you can receive the same match more than once, so make your handler idempotent.Manage connections
List a project's connections (secrets masked)
Get one connection
Rotate the signing secret (returns the new secret once)
Delete a connection (soft delete; referencing alerts skip it)
| Method & path | Reference |
|---|---|
POST /v1/projects/{projectId}/webhooks/connections | Create a webhook connection |
GET /v1/projects/{projectId}/webhooks/connections | List webhook connections for a project |
GET /v1/projects/{projectId}/webhooks/connections/{id} | Get a webhook connection |
POST /v1/projects/{projectId}/webhooks/connections/{id}/secret/rotate | Rotate a webhook connection’s signing secret |
DELETE /v1/projects/{projectId}/webhooks/connections/{id} | Delete a webhook connection |
Related
- Create and manage alerts — create an alert and attach notification channels
- Alerts overview — alert kinds, delivery cadence, and the match model
- Alert results — the full match field reference, also embedded in each payload