Webhooks

Payloads

The webhook envelope, JSON encoding, realistic sample payloads, and the deleted-event rule.

Last updated Jul 14, 2026

Every webhook is a POST with exactly five top-level keys. No more, no less.

The envelope

KeyTypeDescription
typestringThe entity that changed: contact, company, deal, action, note, call, meeting
reasonstringWhat happened: created, updated, deleted, changed_to_won, … β€” see Events
timestampintegerUnix time in seconds
secretkeystringThe secret you configured, or empty if the in-app form left it blank
dataobjectThe changed resource, nested under its entity key

data is the resource serialized exactly like the API v3 response for that resource β€” the same nested shape you get from GET /api/v3/contacts/{id}.json, GET /api/v3/deals/{id}.json, and so on. If you already parse API responses, you already parse webhook payloads.

data is nested. The resource sits under its entity key: data.contact.first_name, not data.first_name. The record’s id is data.contact.id, data.deal.id, and so on β€” matching the type field. Contact events also carry sibling keys (next_actions, next_action, queued_actions, most_urgent_action) alongside data.contact, just like the API. The one exception is deleted events, where data is flat: { "id": "..." }.

Encoding

Webhooks are delivered as JSON. Content-Type: application/json, and the body is the envelope as a JSON object:

{
  "type": "note",
  "reason": "created",
  "timestamp": 1781426730,
  "secretkey": "your-configured-secret",
  "data": {
    "note": {
      "id": "5afc1b69d556730b580596cb",
      "author": "Jane D.",
      "contact_id": "5ae06ef9d55673108fe8877f",
      "text": "Met Jane at the Eco Conference. Wants a quote for 40 panels.",
      "date": "2026-06-10",
      "linked_deal_id": "",
      "linked_deal_name": "",
      "attachments": [],
      "created_at": "2026-06-10T08:46:10Z",
      "modified_at": "2026-06-10T08:46:10Z"
    }
  }
}

Sample payloads

The samples below are built from the current API v3 schemas and abridged for readability ("...": "..." marks omitted fields). The authoritative field list for each resource is the API reference.

contact

The contact sits under data.contact, with its actions as sibling keys β€” the same shape as GET /api/v3/contacts/{id}.json:

{
  "type": "contact",
  "reason": "updated",
  "timestamp": 1781426730,
  "secretkey": "your-configured-secret",
  "data": {
    "contact": {
      "id": "5aba31ea9007ba0f570c92d4",
      "title": "Ms",
      "first_name": "Jane",
      "last_name": "Doe",
      "job_title": "Operations Lead",
      "starred": false,
      "company_id": "5aba31ea9007ba0f570c92d5",
      "company_name": "Acme Solar",
      "emails": [{ "type": "work", "value": "jane.doe@acmesolar.example" }],
      "phones": [{ "type": "work", "value": "(912) 644-1770" }],
      "urls": [{ "type": "website", "value": "https://acmesolar.example" }],
      "address_list": [],
      "status_id": "5e31e030849d781e837b6ba1",
      "status": "Prospect",
      "lead_source_id": "email_web",
      "lead_source": "Email or Web",
      "background": "",
      "owner_id": "5aba31e99007ba0f570c92a5",
      "tags": ["solar", "q3-pipeline"],
      "custom_fields": [],
      "created_at": "2026-05-12T09:14:02Z",
      "modified_at": "2026-06-10T08:45:30Z"
    },
    "next_actions": [
      {
        "id": "5aeac8789007ba56ffca92b9",
        "assignee_id": "5aaa9b009007ba08c9ebaef7",
        "contact_id": "5aba31ea9007ba0f570c92d4",
        "text": "Email Jane the revised quote",
        "status": "date",
        "date": "2026-06-12",
        "done": false,
        "...": "..."
      }
    ],
    "next_action": { "...": "..." },
    "queued_actions": [],
    "most_urgent_action": { "...": "..." }
  }
}

deal

A status change uses the dynamic changed_to_<status> reason β€” see Events:

{
  "type": "deal",
  "reason": "changed_to_won",
  "timestamp": 1781426741,
  "secretkey": "your-configured-secret",
  "data": {
    "deal": {
      "id": "5aaa9b059007ba08c9ebaf58",
      "contact_id": "5aba31ea9007ba0f570c92d4",
      "owner_id": "5aba31e99007ba0f570c12f7",
      "name": "Solar panels β€” 40 units",
      "text": "Signed PO received.",
      "status": "won",
      "last_stage": 75,
      "close_date": "2026-06-10",
      "amount": 4500.0,
      "months": 1,
      "total_amount": 4500.0,
      "cost": 0.0,
      "date": "2026-05-20",
      "author": "Jane D.",
      "contact_info": {
        "contact_name": "Jane Doe",
        "company": "Acme Solar",
        "...": "..."
      },
      "created_at": "2026-05-20T16:10:45Z",
      "modified_at": "2026-06-10T08:45:41Z",
      "...": "..."
    }
  }
}

The stage and date keys depend on the deal’s status:

  • Pending deals carry stage and expected_close_date.
  • Won and lost deals carry last_stage (the stage the deal closed from) and close_date instead.

action

Completed actions serialize with status: "done", done: true, and a done_at date. There is no date key on a completed action:

{
  "type": "action",
  "reason": "completed",
  "timestamp": 1781426755,
  "secretkey": "your-configured-secret",
  "data": {
    "action": {
      "id": "5aeac8789007ba56ffca92b9",
      "assignee_id": "5aaa9b009007ba08c9ebaef7",
      "contact_id": "5aba31ea9007ba0f570c92d4",
      "text": "Email Jane the revised quote",
      "status": "done",
      "done": true,
      "done_at": "2026-06-10",
      "created_at": "2026-06-09T11:52:09Z",
      "modified_at": "2026-06-10T08:45:55Z"
    }
  }
}

note

{
  "type": "note",
  "reason": "created",
  "timestamp": 1781426770,
  "secretkey": "your-configured-secret",
  "data": {
    "note": {
      "id": "5afc1b69d556730b580596cb",
      "author": "Jane D.",
      "contact_id": "5aba31ea9007ba0f570c92d4",
      "text": "Met Jane at the Eco Conference. Wants a quote for 40 panels.",
      "date": "2026-06-10",
      "linked_deal_id": "",
      "linked_deal_name": "",
      "attachments": [],
      "created_at": "2026-06-10T08:46:10Z",
      "modified_at": "2026-06-10T08:46:10Z"
    }
  }
}

When no deal is linked, linked_deal_id is "" β€” an empty string, never null.

call, meeting, and company events nest the same way: data.call, data.meeting, data.company.

Deleted events

When reason is deleted, data contains only the id, flat β€” no entity key:

{
  "type": "contact",
  "reason": "deleted",
  "timestamp": 1781426790,
  "secretkey": "your-configured-secret",
  "data": {
    "id": "5aba31ea9007ba0f570c92d4"
  }
}

This is always the case β€” it doesn’t matter whether the delete is still undoable. If you need the record’s last state, keep your own copy keyed by id, or treat the delete as a tombstone.

Next

  • Delivery β€” when payloads arrive and what happens when delivery fails.
  • Security β€” verifying secretkey correctly.