Webhooks
Webhooks deliver real-time notifications when data changes in your
Journeybee workspace. Use them to sync leads into your CRM, trigger a
Zap, notify an ops channel, or drive any downstream workflow.
Webhooks today run through the connector subscription API (below). A more
developer-oriented flow — including raw-signature HMAC, retries, and a UI for
managing endpoints — is planned for a later release.
Quick start
Subscribe your endpoint to an event:
curl -X POST https://api.journeybee.io/v1/webhooks/subscriptions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"integration_id": 42,
"event": "leads.create",
"url": "https://hooks.example.com/journeybee"
}'
Response:
{
"id": "42.leads.create.1713300000000",
"integration_id": 42,
"event": "leads.create",
"url": "https://hooks.example.com/journeybee",
"active": true,
"message": "Webhook subscription created successfully"
}
Your endpoint will now receive a POST for every matching event.
Supported events
| Event | Description |
|---|
leads.create | A lead was created |
leads.update | A lead was updated |
leads.delete | A lead was deleted |
leads.notes_create | A note was added to a lead |
leads.notes_update | A lead note was updated |
leads.notes_delete | A lead note was deleted |
deals.create | A deal was created |
deals.update | A deal was updated |
deals.delete | A deal was deleted |
partners.create | A partner was added |
partners.update | A partner was updated |
partners.contact_create | A partner contact was created |
partners.contact_update | A partner contact was updated |
payments.processing | A payout was initiated |
payments.completed | A payout completed |
payments.failed | A payout failed |
mdf.payout_completed | An MDF claim payout completed |
mdf.payout_failed | An MDF claim payout failed |
Subscription API
Create a subscription
POST /v1/webhooks/subscriptions
Body:
| Field | Type | Required | Description |
|---|
integration_id | integer | yes | The integration this subscription attaches to |
event | string | yes | One of the supported events above |
url | string | yes | The HTTPS endpoint to POST events to |
description | string | no | Free-text note for your own records |
The integration_id identifies which integration row the subscription
hangs off. For Zapier-style self-serve subscriptions, use the public
Journeybee connector integration ID. Ask
support if you’re unsure which
integration to use.
Delete a subscription
POST /v1/webhooks/subscriptions
Send DELETE with the hookUrl body field set to the subscription
id returned at create time:
curl -X DELETE https://api.journeybee.io/v1/webhooks/subscriptions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "hookUrl": "42.leads.create.1713300000000" }'
Event delivery
Every subscribed event generates a single POST to your URL with:
Content-Type: application/json — the request body is a JSON
payload described below.
Authorization: Bearer <token> — a JWT signed with your
integration’s UUID as the secret. Verify this before processing the
payload.
Example payload — leads.create
{
"lead": {
"uuid": "b1a9d5c3-...",
"created_at": "2026-04-16T09:12:00.000Z",
"updated_at": "2026-04-16T09:12:00.000Z",
"status": "new",
"source": "form",
"company_name": "Acme Corp",
"email": "buyer@acme.test",
"phone_number": "+1-555-0100",
"first_name": "Jane",
"last_name": "Buyer",
"partnership_name": "Northwind Referrals",
"partnership_type": "referral",
"created_by_user_uuid": "8e7d...",
"created_by_user_email": "rep@northwind.test",
"created_by_user_first_name": "Riley",
"created_by_user_last_name": "Rep",
"custom_fields": [
{
"uuid": "cf-...",
"label": "Region",
"type": "select",
"options": [
{ "id": 1, "label": "EMEA" },
{ "id": 2, "label": "AMER" }
],
"value": "EMEA",
"custom_field_value_uuid": "cfv-..."
}
],
"tags": [{ "uuid": "tag-...", "label": "Hot" }],
"assigned_users": [
{
"uuid": "u-...",
"email": "ae@acme.test",
"first_name": "Alex",
"last_name": "AE"
}
],
"assigned_contacts": []
},
"configuration": []
}
Other events follow the same pattern — the entity key matches the
resource (deal, partner, contact, etc.).
Verifying requests
The Authorization header contains a JWT signed with HS256 using your
integration UUID as the shared secret. Decode and verify it to
confirm the request is genuine.
Node.js (jsonwebtoken)
import jwt from "jsonwebtoken";
const INTEGRATION_UUID = process.env.JOURNEYBEE_INTEGRATION_UUID;
app.post("/webhook", (req, res) => {
const header = req.headers.authorization ?? "";
const token = header.startsWith("Bearer ") ? header.slice(7) : null;
if (!token) return res.status(401).end();
try {
const claims = jwt.verify(token, INTEGRATION_UUID, {
algorithms: ["HS256"],
});
// claims = { company_uuid, user_uuid, event_id, external_settings, api_key }
processEvent(claims.event_id, req.body);
res.status(200).end();
} catch {
res.status(401).end();
}
});
Token claims
| Claim | Description |
|---|
company_uuid | The Journeybee company the event belongs to |
user_uuid | The user that triggered the event (falls back to an admin user) |
event_id | The event name (e.g. lead_created, deal_updated) |
api_key | Optional API key for callbacks into Journeybee |
external_settings | Your stored integration settings, including authorisation[] |
Always verify the JWT before acting on a payload. The integration UUID is the
shared secret — do not commit it to source control and do not expose it in
client-side code.
Delivery semantics
- At-most-once: the worker sends one POST per event. There are no
automatic retries on 5xx responses today, so your endpoint must return
2xx quickly and queue work asynchronously.
- Timeouts: slow endpoints (>5s) may be dropped. Return a 2xx as
soon as you’ve persisted the event, then process it out of band.
- Ordering: events are not guaranteed to arrive in order. Use the
updated_at field on the payload to reconcile.
- Duplicates: duplicates are rare but possible. Key your idempotency
off the entity
uuid plus event_id.
Retries, signed timestamps, and a delivery log are on the roadmap (Platform
phase 15). If you need guaranteed delivery today, poll the relevant list
endpoints on an interval and reconcile against your local state.
Troubleshooting
-
Receiving 401s? Double-check that you’re verifying with the
integration UUID (not your API key) and using HS256.
-
Not receiving events? Confirm the subscription exists by listing
your current subscriptions:
curl https://api.journeybee.io/v1/webhooks/subscriptions \
-H "Authorization: Bearer YOUR_API_KEY"
-
Want to test delivery? Use a tool like
webhook.site as the
url and trigger an
event (create a test lead, update a partner) from the app.