Skip to main contentSkip to Content
Webhooks

Webhooks

CampaignOS supports both outbound and inbound webhooks for integrating with external systems.

  • Outbound webhooks send data from CampaignOS to your systems when events occur.
  • Inbound webhooks receive data from your systems into CampaignOS.

Outbound Webhooks

How It Works

  1. Create a webhook endpoint with the events you want to subscribe to.
  2. CampaignOS sends a signed POST request to your URL whenever a matching event occurs.
  3. Your server verifies the signature and processes the payload.

Payload Format

All outbound webhook deliveries follow this format:

{ "event": "contact.created", "timestamp": "2026-01-15T10:00:00.000Z", "data": { "contactId": "ct_abc123", "email": "jane@example.com", "firstName": "Jane", "lastName": "Doe" } }

Headers

HeaderDescription
Content-Typeapplication/json
X-Webhook-SignatureHMAC-SHA256 hex digest of the payload body
X-Webhook-EventThe event type (e.g. contact.created)
X-Webhook-DeliveryUnique delivery ID

Signature Verification

Verify the X-Webhook-Signature header using your endpoint’s signing secret:

const crypto = require("crypto"); function verifyWebhook(body, signature, secret) { const expected = crypto .createHmac("sha256", secret) .update(body) .digest("hex"); return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expected) ); }

Inbound Webhooks

How It Works

  1. Create an inbound webhook endpoint in CampaignOS.
  2. Copy the generated URL and signing secret.
  3. Send data from your external systems to this URL.
  4. CampaignOS processes the payload, creates events, and optionally auto-creates contacts.

Endpoint URL

POST /api/webhooks/inbound/{endpointId}

Payload Format

{ "type": "user.signup", "email": "jane@example.com", "firstName": "Jane", "lastName": "Doe", "phone": "+1234567890", "metadata": { "source": "external-crm", "plan": "pro" } }
FieldTypeRequiredDescription
typestringYesEvent type to record (e.g. user.signup, purchase.completed)
emailstringNoContact email — used to find or auto-create a contact
contactIdstringNoCampaignOS contact ID (if known)
firstNamestringNoContact first name (used when auto-creating)
lastNamestringNoContact last name (used when auto-creating)
phonestringNoContact phone (used when auto-creating)
metadataobjectNoAdditional data stored with the event

Auto-Create Contacts

When you include an email in the payload and no matching contact exists in your workspace, CampaignOS automatically creates a new contact with the provided details (email, firstName, lastName, phone). This means you can send events from external systems without pre-creating contacts.

Signing Inbound Requests

If you configure a signing secret, include a signature header:

X-Webhook-Signature: <hmac_hex_digest>

Computed as HMAC-SHA256 of the raw request body using your signing secret.

cURL Example

curl -X POST https://your-domain.com/api/webhooks/inbound/ENDPOINT_ID \ -H "Content-Type: application/json" \ -H "X-Webhook-Signature: YOUR_SIGNATURE" \ -d '{ "type": "user.signup", "email": "jane@example.com", "firstName": "Jane", "metadata": { "source": "crm", "plan": "pro" } }'

Response

{ "received": true, "deliveryId": "del_abc123" }

API Reference

List Webhook Endpoints

GET /api/workspaces/{workspaceId}/webhooks

Lists all configured webhook endpoints with delivery counts.

Auth: Session required

Response:

[ { "id": "wh_abc123", "name": "CRM Sync", "url": "https://hooks.example.com/campaignos", "events": ["contact.created", "message.sent"], "direction": "OUTBOUND", "active": true, "_count": { "deliveries": 1500 }, "createdAt": "2026-01-15T10:00:00.000Z" } ]

Create Webhook Endpoint

POST /api/workspaces/{workspaceId}/webhooks

Auth: Session + ADMIN role required

Request Body:

{ "name": "CRM Sync", "url": "https://hooks.example.com/campaignos", "events": ["contact.created", "contact.updated", "message.sent"], "direction": "OUTBOUND" }
FieldTypeRequiredDescription
namestringNoHuman-readable label (max 100 chars)
urlstringOutbound onlyWebhook destination URL
eventsstring[]NoEvent types to subscribe to
directionstringNoINBOUND or OUTBOUND (default: OUTBOUND)

Response (201): Webhook endpoint with secret (shown only once).

Update Webhook Endpoint

PATCH /api/workspaces/{workspaceId}/webhooks/{webhookId}

Auth: Session + ADMIN role required

Request Body:

{ "name": "Updated Name", "url": "https://new-url.example.com/hook", "events": ["contact.created"], "active": false }

Delete Webhook Endpoint

DELETE /api/workspaces/{workspaceId}/webhooks/{webhookId}

Auth: Session + ADMIN role required. Cascades to all delivery records.

List Deliveries

GET /api/workspaces/{workspaceId}/webhooks/deliveries

Lists webhook delivery logs with pagination and filtering.

Query Parameters:

ParamTypeDescription
pagenumberPage number (default: 1)
limitnumberItems per page (default: 50, max: 100)
endpointIdstringFilter by endpoint
successbooleanFilter by success status
eventstringFilter by event type

Retry Delivery

POST /api/workspaces/{workspaceId}/webhooks/deliveries/{deliveryId}/retry

Auth: Session + ADMIN role required. Only outbound deliveries can be retried.


Webhook Events (All 26 Types)

Contact Events

EventDescriptionPayload Fields
contact.createdNew contact createdcontactId, email, firstName, lastName
contact.updatedContact profile updatedcontactId, email, firstName, lastName
contact.deletedContact permanently removedcontactId, email
contact.mergedTwo contacts merged (anonymous to known)primaryContactId, secondaryContactId, mergedContact

Segment Events

EventDescriptionPayload Fields
segment.enteredContact enters a segmentsegmentId, contactId
segment.exitedContact leaves a segmentsegmentId, contactId

Campaign Events

EventDescriptionPayload Fields
campaign.startedCampaign activatedcampaignId, name, status
campaign.completedCampaign finished for all contactscampaignId, name, totalEnrolled
campaign.contact.enrolledContact enters a campaigncampaignId, contactId
campaign.contact.completedContact finishes a campaign flowcampaignId, contactId, completedAt

Message Events

EventDescriptionPayload Fields
message.sentMessage dispatched to a channelcontactId, channel, messageId, status, campaignId
message.deliveredDelivery confirmed by providermessageId, contactId, channel
message.openedEmail or push openedmessageId, contactId, channel
message.clickedTracked link clickedmessageId, contactId, channel, url
message.bouncedEmail bounced (hard or soft)messageId, contactId, bounceType
message.complainedRecipient marked as spammessageId, contactId
message.failedMessage failed to sendmessageId, contactId, error

Subscription Events

EventDescriptionPayload Fields
unsubscribe.emailContact opts out of emailcontactId, channel, messageId
unsubscribe.pushPush subscription removedcontactId, channel
unsubscribe.telegramContact blocks Telegram botcontactId, channel
unsubscribe.allGlobal unsubscribe from all channelscontactId

Form Events

EventDescriptionPayload Fields
form.submittedForm submission receivedformId, submissionId, fields

Push Events

EventDescriptionPayload Fields
push.subscribedBrowser subscribes to pushcontactId, endpoint
push.clickedPush notification clickedcontactId, messageId

Custom Events

EventDescriptionPayload Fields
event.trackedCustom tracking event ingestedcontactId, eventType, metadata
Last updated on