Skip to content

Webhook Sensor

What it ingests

Arbitrary events from your own integrations — incidents, notes, deploys, or anything else you can POST as JSON. The source is webhook-driven: your producer POSTs to Corveil, so there's no outbound polling and firewalled systems work the same way Jenkins does.

This is one sensor. Zoom transcripts, Gong, Greenhouse, Salesforce, and the local file watcher remain separate catalog entries and are not bundled here.

Credential

None to create. The webhook token is generated server-side at create time and stored as a hash. The plaintext token is returned once in the create response so the UI can show you the full webhook URL — copy it then; it isn't shown again.

HMAC signatures are out of scope for v1. The URL token is the auth.

Config fields

The only config field is optional.

KeyNotes
source_labelHuman-readable name shown in the UI, e.g. pager. Display-only; not used for auth.

The Add-sensor form also offers a default entity type picker. Leave it blank to land each POST as an Event. Set it to retarget the primary entity (Meeting, CreativeWork, Issue, …) without changing the producer payload.

Wire-up

POST /api/sensors

json
{
  "sensor_type": "webhook",
  "name": "Pager",
  "config": {
    "source_label": "pager"
  }
}

The response includes the generated webhook URL + token. Save it. If you lose it, Edit sensor → Get webhook URL (or POST /api/sensors/{id}/rotate-webhook) issues a new URL and immediately invalidates the previous one.

Payload

POST /webhooks/webhook/{token} with Content-Type: application/json. Maximum body size is 256 KiB.

json
{
  "id": "unique-in-this-sensor",
  "title": "required",
  "summary": "",
  "content": "",
  "url": "",
  "actor_email": "",
  "actor_name": "",
  "occurred_at": "2026-09-05T12:00:00Z",
  "event_type": "event",
  "category": "document"
}
FieldRequiredNotes
idyesUnique per sensor. Aliases: external_id. Stored as webhook:{id} (no double-prefix if you already send that). Replaying the same id is a 200, not a new row.
titleyesDisplay name of the resulting entity (truncated at 500 characters).
summarynoShort description; falls back to title.
content / bodynoLonger body text.
urlnoLink back to the source system.
actor_email / emailnoResolved to a Person when present.
actor_name / namenoDisplay name; falls back to the email local-part.
occurred_atnoRFC3339 or unix seconds (milliseconds if > 1e12). Omitted → ingest time.
event_type / typenoLowercase slug ^[a-z][a-z0-9_]{0,63}$. Default event.
categorynodocument (default), code, conversation, task, meeting, or file. Unknown values become document.

Extra JSON fields are ignored.

Provider-side setup

Point your producer at the webhook URL from the create response. A typical curl:

bash
curl -sS -X POST "$WEBHOOK_URL" \
  -H "Content-Type: application/json" \
  -d '{"id":"inc-42","title":"Pager fired","actor_email":"alice@acme.com"}'

A 200 means the event was accepted (including an idempotent replay of the same id). 401 is an unknown or rotated token. 400 is a malformed or oversized body. 429 is rate limiting (120 requests/minute per IP before lookup; 600 per sensor after).

Verify

POST a payload, then check Sensors for a recent "received" time and the ontology for an Event (or whatever default entity type you picked) named after title. If nothing arrives, rotate the URL and update the producer — the previous token stops working immediately.