• Customermates logo
    CustomermatesDocumentation
  • Introduction
  • Comparison
Getting Started
  • Quickstart
  • Core Concepts
  • From Pipedrive
Connect your AI
  • Connect Claude Code
  • Connect Claude Desktop
  • Connect Codex
  • Connect Cursor
  • Connect Gemini
  • Connect ChatGPT
Integrations
  • MCP
  • Webhooks
  • OpenAPI 3.1.0
  • N8N
Self-Hosting
  • Get Started
  • Architecture & Security
Reference
  • MCP Tool Catalog
  • Filter Syntax
  • API Keys
  • Go back
  1. Introduction
  2. Webhooks

Webhooks

Subscribe to CRM events, verify signatures, debug failed deliveries, and the full event catalog in one place.

Pick events, give us an HTTPS URL, we POST JSON there every time something changes. Re-send failed deliveries from the UI or through MCP. Verify signatures with HMAC-SHA256 over the raw body. Every event with its payload shape is listed at the bottom of this page.

Why webhooks

Webhooks turn your CRM into the source of truth your other tools listen to. No polling, no stale data.

Common uses:

  • Push new contacts into your email sequencer.
  • Open a ticket in your support tool when a deal is lost.
  • Trigger a finance workflow when a deal is marked won.
  • Sync to your data warehouse in near-real-time.

Create a webhook

UI: Company → Webhooks → New.

MCP: create_webhook with url, events[], optional description, secret, enabled.

{
  "url": "https://hooks.example.com/customermates",
  "events": ["contact.created", "contact.updated", "deal.updated"],
  "secret": "use-a-random-string-from-a-password-manager",
  "enabled": true
}

The URL must be HTTPS. HTTP is rejected.

What you receive

Every delivery is a POST with Content-Type: application/json. Every delivery follows the same envelope:

{
  "event": "<event-name>",
  "data": {
    "userId": "<who-triggered-it>",
    "companyId": "<tenant>",
    "entityId": "<affected-record>",
    "payload": { /* event-specific */ }
  },
  "timestamp": "<iso-8601>"
}

The userId is the user that caused the write, which includes a user acting through an API key.

The changes record is only present on *.updated events. It names every field that actually moved, with previous and current values.

Example, contact.updated:

{
  "event": "contact.updated",
  "data": {
    "userId": "u_123",
    "companyId": "c_abc",
    "entityId": "ct_xyz",
    "payload": {
      "contact": {
        "id": "ct_xyz",
        "firstName": "Max",
        "lastName": "Mustermann",
        "notes": { /* Tiptap JSON */ },
        "organizationIds": ["org_1"],
        "userIds": [],
        "dealIds": ["deal_1", "deal_2"],
        "customFieldValues": [
          { "columnId": "col_stage", "value": "won" }
        ]
      },
      "changes": {
        "organizationIds": {
          "previous": [],
          "current": ["org_1"]
        }
      }
    }
  },
  "timestamp": "2026-04-22T10:00:00.000Z"
}

The changes record

Each key in changes is a field name whose value moved. previous is what was there before, current is what is there now.

Arrays of relationship ids compare as sets. Objects compare by deep equality. Scalar fields compare by value.

If a write does not actually change any field, the event is suppressed. You will not see no-op *.updated events.

Signature verification

If you set a secret, every outgoing request includes:

X-Webhook-Signature: <hex>

Where <hex> is HMAC-SHA256(secret, rawRequestBody), lowercase hex, no prefix. Recompute on your side and compare in constant time.

Node.js example:

import crypto from "crypto";

function verify(rawBody: string, received: string, secret: string) {
  const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received));
}

Retries and failed deliveries

Every delivery is recorded. See them in the UI (Company → Webhook Deliveries) or through list_webhook_deliveries.

A delivery is considered failed if the HTTP status is outside the 2xx range or the request times out.

Retry a failed delivery through the UI or resend_webhook_delivery with the delivery id. A new delivery record is created; the original is unchanged.

Customermates does not automatically retry failed deliveries. If your receiver is down, trigger retries from the UI or pipe list_webhook_deliveries through a monitoring script.

Ordering and concurrency

Deliveries are not strictly ordered across events. Two contact.updated events a millisecond apart can arrive out of order. Use the timestamp in the payload to resolve, or treat deliveries as idempotent messages keyed by entityId.

Notes are JSON, not markdown

The notes field in payloads is Tiptap JSON, the same structure the editor stores. To render as markdown on your side, run it through a Tiptap-compatible serializer. In MCP tools, get_entities with include: "withNotes" returns notes pre-serialized to markdown.

Deleted payloads

Delete events carry only the id, not the final state of the record. If you need the final state for a delete hook (for archiving, say), subscribe to *.updated as well and keep a local mirror.

Debugging tips

  • Use webhook.site for a throwaway receiver during integration testing.
  • The list_webhook_deliveries tool supports searchTerm on url and event name.
  • If your receiver returns a 500, Customermates records the response body so you can see exactly what happened.

Event catalog

15 events across the five entity types, all with the envelope above.

EventWhen it firesPayload keys
contact.createdA contact is created via UI, API, MCP, or importcontact
contact.updatedAny contact field changescontact, changes
contact.deletedA contact is deletedcontactId
organization.createdAn organization is createdorganization
organization.updatedAny organization field changesorganization, changes
organization.deletedAn organization is deletedorganizationId
deal.createdA deal is createddeal
deal.updatedAny deal field changesdeal, changes
deal.deletedA deal is deleteddealId
service.createdA service is createdservice
service.updatedAny service field changesservice, changes
service.deletedA service is deletedserviceId
task.createdA task is createdtask
task.updatedAny task field changestask, changes
task.deletedA task is deletedtaskId

Next

  • MCP tool catalog: programmatic webhook management.
  • n8n integration: drop webhooks into visual workflows.
Why webhooks
Create a webhook
What you receive
The changes record
Signature verification
Retries and failed deliveries
Ordering and concurrency
Notes are JSON, not markdown
Deleted payloads
Debugging tips
Event catalog
Next