Jambo

Developer docs

Webhook events

When something meaningful happens in a workspace, Jambo delivers a signed JSON envelope to your HTTPS endpoint. Delivery is at-least-once — dedupe on the envelope id.

Envelope (version 1)

Every delivery — custom webhook endpoints, Zapier REST Hooks, and replays — uses the same shape. Configure endpoints in Dashboard → Settings → Integrations → Webhooks.

Example payload

{
  "id": "550e8400-e29b-41d4-a716-446655440099",
  "type": "submission.created",
  "version": 1,
  "occurredAt": "2026-06-11T10:00:00.000Z",
  "workspaceId": "550e8400-e29b-41d4-a716-446655440000",
  "object": {
    "type": "submission",
    "id": "550e8400-e29b-41d4-a716-446655440001"
  },
  "data": {
    "fullName": "Jane Doe",
    "email": "[email protected]",
    "phone": "+15551234567",
    "message": "I would like a demo.",
    "formId": "00000000-0000-0000-0000-000000000001",
    "formName": "Contact us",
    "contactId": "00000000-0000-0000-0000-000000000002",
    "leadId": "00000000-0000-0000-0000-000000000003",
    "conversationId": "00000000-0000-0000-0000-000000000004",
    "fieldValues": {
      "company": "Example Co",
      "budget": "5k-10k"
    }
  }
}

HTTP headers

HeaderValue
Content-Typeapplication/json
User-AgentJambo-Webhooks/1.0
X-Jambo-Signaturev1=<hex hmac-sha256>
X-Jambo-Timestamp<unix seconds>
X-Jambo-Event-Typesubmission.created
X-Jambo-Event-Id<envelope id>

The signature covers {timestamp}.{rawBody} using HMAC-SHA256 and your endpoint signing secret. Reject requests older than five minutes.

Verify signatures (Node.js)

import crypto from 'node:crypto'

function verifyJamboWebhook(req, secret) {
  const timestamp = req.headers['x-jambo-timestamp']
  const signature = req.headers['x-jambo-signature']
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false
  const expected =
    'v1=' +
    crypto.createHmac('sha256', secret).update(`${timestamp}.${req.rawBody}`).digest('hex')
  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))
}

Event catalog

Subscribe to one or more event types per endpoint. REST Hooks use the same vocabulary via eventTypes on POST /integrations/zapier/hooks.

  • submission.createdForm submission received

    A public form was submitted. Fires once per submission, after the contact and lead are saved. `fieldValues` contains mapped custom field values keyed by mapping target.

    {
      "data": {
        "fullName": "Jane Doe",
        "email": "[email protected]",
        "phone": "+15551234567",
        "message": "I would like a demo.",
        "formId": "00000000-0000-0000-0000-000000000001",
        "formName": "Contact us",
        "contactId": "00000000-0000-0000-0000-000000000002",
        "leadId": "00000000-0000-0000-0000-000000000003",
        "conversationId": "00000000-0000-0000-0000-000000000004",
        "fieldValues": {
          "company": "Example Co",
          "budget": "5k-10k"
        }
      }
    }
  • contact.createdContact created

    A new contact record was created — from a form submission, inbound email, or manual entry.

    {
      "data": {
        "contactId": "00000000-0000-0000-0000-000000000002",
        "fullName": "Jane Doe",
        "email": "[email protected]",
        "phone": "+15551234567",
        "company": "Example Co"
      }
    }
  • contact.updatedContact updated

    An existing contact was edited or enriched via import.

    {
      "data": {
        "contactId": "00000000-0000-0000-0000-000000000002",
        "fullName": "Jane Doe",
        "email": "[email protected]",
        "phone": "+15551234567",
        "company": "Example Co"
      }
    }
  • message.receivedInbound message received

    A channel-agnostic inbound message (web_form, email, or future whatsapp). `message` mirrors `contentText`; contact fields are included when the message is linked to a contact.

    {
      "data": {
        "messageId": "00000000-0000-0000-0000-000000000005",
        "conversationId": "00000000-0000-0000-0000-000000000004",
        "contactId": "00000000-0000-0000-0000-000000000002",
        "leadId": "00000000-0000-0000-0000-000000000003",
        "channelType": "web_form",
        "contentText": "Hi, following up on my enquiry.",
        "message": "Hi, following up on my enquiry.",
        "email": "[email protected]",
        "fullName": "Jane Doe",
        "phone": "+15551234567"
      }
    }
  • message.sentOutbound message sent

    An outbound reply was delivered from the inbox or agent (team member or AI). Includes `authorType`, `authorLabel`, optional `subject`, and contact enrichment when available.

    {
      "data": {
        "messageId": "00000000-0000-0000-0000-000000000005",
        "conversationId": "00000000-0000-0000-0000-000000000004",
        "contactId": "00000000-0000-0000-0000-000000000002",
        "leadId": "00000000-0000-0000-0000-000000000003",
        "channelType": "email",
        "contentText": "Thanks for reaching out — happy to help with pricing.",
        "message": "Thanks for reaching out — happy to help with pricing.",
        "authorType": "ai",
        "authorLabel": "AI agent",
        "subject": "Re: Demo request",
        "email": "[email protected]",
        "fullName": "Jane Doe",
        "phone": "+15551234567"
      }
    }
  • task.createdTask created

    A new task was created — manually or by the inbound agent. Includes the full task snapshot plus a preformatted `message` note for tools that ingest plain text.

    {
      "data": {
        "taskId": "00000000-0000-0000-0000-000000000010",
        "title": "Follow up with Jane",
        "description": "Send the rate card and confirm availability.",
        "status": "open",
        "priority": "medium",
        "source": "agent",
        "actionType": "open_conversation",
        "href": "/dashboard/inbox/00000000-0000-0000-0000-000000000004",
        "assigneeMemberId": "00000000-0000-0000-0000-000000000011",
        "assigneeEmail": "[email protected]",
        "createdByMemberId": "00000000-0000-0000-0000-000000000011",
        "conversationId": "00000000-0000-0000-0000-000000000004",
        "leadId": "00000000-0000-0000-0000-000000000003",
        "contactId": "00000000-0000-0000-0000-000000000002",
        "contactName": "Jane Doe",
        "messageId": null,
        "email": "[email protected]",
        "fullName": "Jane Doe",
        "phone": "+15551234567",
        "dueAt": "2026-06-15T09:00:00.000Z",
        "completedAt": null,
        "createdAt": "2026-06-11T10:00:00.000Z",
        "updatedAt": "2026-06-12T10:05:00.000Z",
        "message": "[Task created] Follow up with Jane\nStatus: open\n/dashboard/inbox/00000000-0000-0000-0000-000000000004\n\nSend the rate card and confirm availability."
      }
    }
  • task.updatedTask updated

    An existing task was edited (title, status, assignee, due date, etc.).

    {
      "data": {
        "taskId": "00000000-0000-0000-0000-000000000010",
        "title": "Follow up with Jane",
        "description": "Send the rate card and confirm availability.",
        "status": "done",
        "priority": "medium",
        "source": "agent",
        "actionType": "open_conversation",
        "href": "/dashboard/inbox/00000000-0000-0000-0000-000000000004",
        "assigneeMemberId": "00000000-0000-0000-0000-000000000011",
        "assigneeEmail": "[email protected]",
        "createdByMemberId": "00000000-0000-0000-0000-000000000011",
        "conversationId": "00000000-0000-0000-0000-000000000004",
        "leadId": "00000000-0000-0000-0000-000000000003",
        "contactId": "00000000-0000-0000-0000-000000000002",
        "contactName": "Jane Doe",
        "messageId": null,
        "email": "[email protected]",
        "fullName": "Jane Doe",
        "phone": "+15551234567",
        "dueAt": "2026-06-15T09:00:00.000Z",
        "completedAt": "2026-06-12T10:05:00.000Z",
        "createdAt": "2026-06-11T10:00:00.000Z",
        "updatedAt": "2026-06-12T10:05:00.000Z",
        "message": "[Task updated] Follow up with Jane\nStatus: done\n/dashboard/inbox/00000000-0000-0000-0000-000000000004\n\nSend the rate card and confirm availability."
      }
    }
  • task.deletedTask deleted

    A task was permanently deleted. The payload is the last snapshot before deletion.

    {
      "data": {
        "taskId": "00000000-0000-0000-0000-000000000010",
        "title": "Follow up with Jane",
        "description": "Send the rate card and confirm availability.",
        "status": "open",
        "priority": "medium",
        "source": "agent",
        "actionType": "open_conversation",
        "href": "/dashboard/inbox/00000000-0000-0000-0000-000000000004",
        "assigneeMemberId": "00000000-0000-0000-0000-000000000011",
        "assigneeEmail": "[email protected]",
        "createdByMemberId": "00000000-0000-0000-0000-000000000011",
        "conversationId": "00000000-0000-0000-0000-000000000004",
        "leadId": "00000000-0000-0000-0000-000000000003",
        "contactId": "00000000-0000-0000-0000-000000000002",
        "contactName": "Jane Doe",
        "messageId": null,
        "email": "[email protected]",
        "fullName": "Jane Doe",
        "phone": "+15551234567",
        "dueAt": "2026-06-15T09:00:00.000Z",
        "completedAt": null,
        "createdAt": "2026-06-11T10:00:00.000Z",
        "updatedAt": "2026-06-12T10:05:00.000Z",
        "message": "[Task deleted] Follow up with Jane\nStatus: open\n/dashboard/inbox/00000000-0000-0000-0000-000000000004\n\nSend the rate card and confirm availability."
      }
    }

Delivery semantics

  • Deduplication: use envelope id — we may deliver more than once.
  • Retries: network errors, timeouts, HTTP 5xx, and 429 are retried with backoff (up to six attempts: 1m → 5m → 15m → 15m → 15m). Other 4xx responses are terminal.
  • Auto-pause: after five consecutive abandoned deliveries, an endpoint is paused until you resume it in settings.
  • Test events: use the “Send test” button in webhook settings for a test.ping connectivity check (not part of normal subscriptions).