Webhooks
Signed HTTPS deliveries to your server when something happens in Outlevo: a job finishes, a company qualifies, a lead replies, a meeting is booked.
Subscribe
curl -s -X POST https://api.outlevo.com/v1/webhooks -H "authorization: Bearer $OUTLEVO_KEY" -H "idempotency-key: $(uuidgen)" \
-H "content-type: application/json" -d '{ "url": "https://your.app/api/outlevo-webhook", "events": ["lead.replied", "company.qualified"] }'
The answer carries the signing secret once: keep it as OUTLEVO_WEBHOOK_SECRET. HTTPS and public addresses only; the key needs webhooks:manage. You can also add one in the Outlevo chat or at app.outlevo.com → Developers.
What a delivery looks like
POST /api/outlevo-webhook
content-type: application/json
user-agent: Outlevo-Webhooks/1.0
outlevo-signature: t=1760000000,v1=5f2b…
outlevo-event-id: 3f1c…
outlevo-event-type: lead.replied
{ "id": "3f1c…", "type": "lead.replied", "created_at": "2026-10-21T09:41:00.000Z", "data": { "campaign_id": "…", "lead_id": "…", "contact_id": "…", "company_id": "…", "external_id": "site-17", "reply_text": "…", "at": "…" } }
Verify every delivery
- Compute HMAC-SHA256 with your secret over
"<t>.<raw body>"and compare it, in constant time, with eachv1in the header. Accept if any matches. - Reject when the timestamp is more than 300 seconds from now.
- Dedupe on
Outlevo-Event-Id: a retry carries the same id. - Answer 2xx fast and do the work in a queue. A delivery times out after 10 seconds.
With the SDK:
import { verifyWebhook } from '@outlevo/sdk';
const event = await verifyWebhook(rawBody, req.headers['outlevo-signature'], process.env.OUTLEVO_WEBHOOK_SECRET!);
Without it, the ten-line check in Node and PHP is in llms.txt; a complete Node receiver is templates/webhook-receiver.ts.
Retries, tests and rotation
- A delivery that does not get a 2xx is retried after 1 min, 5 min, 30 min, 2 h, 12 h, then marked failed.
GET /v1/webhooks/{id}/deliveriesshows each attempt. POST /v1/webhooks/{id}/testsends a signedwebhook.testdelivery at once and answers with your endpoint's status code.POST /v1/webhooks/{id}/rotate-secretreturns a new secret. For 24 hours the header carries a signature for each secret, and either one verifies.- In the sandbox, deliveries are signed with
whsec_sandbox_outlevo_test_only_000000, so a receiver can be tested end to end.
Events
| Group | Events |
|---|---|
| job | job.completed job.failed |
| company | company.qualified company.disqualified company.created |
| contact | contact.verified contact.discovered |
| revenue_action | revenue_action.proposed revenue_action.blocked revenue_action.executed revenue_action.approved revenue_action.failed revenue_action.outcome_observed |
| handoff | handoff.created handoff.accepted handoff.rejected |
| campaign | campaign.status_changed |
| audience | audience.ready |
| outcome | outcome.recorded |
| usage | usage.threshold |
| competitor | competitor.added competitor.confirmed competitor.changed competitor.account_linked |
| lead | lead.sent lead.replied lead.bounced lead.unsubscribed lead.answered |
| meeting | meeting.booked |
| evidence | evidence.added |
Lead events carry campaign_id, lead_id, contact_id, company_id, external_id and at; lead.replied adds reply_text, lead.bounced adds hard, lead.answered adds by (autopilot or person) and meeting.booked adds starts_at, ends_at, title, booking_ref and outcome_id. Payload schemas for every event are in the API reference.