Integrations
The exact endpoints an automation platform is built on — triggers, actions, and the subscription API behind them
There is no first-party Sendly app in the Zapier, Make or n8n marketplaces yet. What there is instead is the thing those apps are made of: a documented REST API with a stable machine-readable spec, and a webhook subscription API that lets a platform register and tear down its own listeners.
This page is that surface, named exactly. Every row is an operation in
the OpenAPI document, and the operationId column is the
name a generated client will give it.
Authenticating
An API key, sent as Authorization: Bearer sk_…. Create one in Settings → API keys.
Build against a test-mode key (sk_test_…) while you are wiring things up. A test key is the
same credential with one thing taken away: it may only send from the project's sandbox address,
whose recipients are limited to the project's own verified members, so a loop in a half-finished
integration cannot put mail in a customer's inbox. See
API keys for the prefix scheme and what the mode does and does not change.
Triggers
Triggers are outbound webhooks. Sendly's webhook API is shaped for the REST-hook pattern that
Zapier and n8n both use: the platform calls createWebhook when a user turns a trigger on, and
deleteWebhook when they turn it off.
| Operation | Method and path | What it is for |
|---|---|---|
createWebhook | POST /api/webhooks | Subscribe. Takes a URL and the event types to receive. |
deleteWebhook | DELETE /api/webhooks/{id} | Unsubscribe. |
listWebhooks | GET /api/webhooks | The project's existing subscriptions. |
getWebhook | GET /api/webhooks/{id} | One subscription. |
updateWebhook | PATCH /api/webhooks/{id} | Change the URL, the event list, or the status. |
rotateWebhookSecret | POST /api/webhooks/{id}/rotate-secret | New signing secret. |
listWebhookCalls | GET /api/webhooks/{id}/calls | Delivery history — the first place to look when a trigger stops firing. |
Which event types a subscription may name is a fixed list, and it is not the same list a workflow triggers on. That distinction has its own page: Event vocabularies. Read it before you hardcode a name.
Actions
Sending
| Operation | Method and path | Notes |
|---|---|---|
v1SendEmail | POST /api/v1/emails | The current send endpoint. |
sendEmail | POST /api/emails | The older path, still supported. |
sendEmailBatch | POST /api/emails/batch | Many messages in one request. |
listEmails | GET /api/emails | For a "find a message" search step. |
getEmail | GET /api/emails/{id} | Status of one message. |
Audience
| Operation | Method and path | Notes |
|---|---|---|
upsertContact | POST /api/contacts/upsert | The right action for a sync: create or update by email, idempotent. |
createContact | POST /api/contacts | Fails on a duplicate. |
listContacts | GET /api/contacts | Cursor-paginated. |
getContact | GET /api/contacts/{id} | |
updateContact | PATCH /api/contacts/{id} | |
deleteContact | DELETE /api/contacts/{id} | |
bulkCreateContacts | POST /api/contacts/bulk | For an import step rather than a per-record one. |
subscribeToList | POST /api/lists/{id}/subscribe | |
unsubscribeFromList | POST /api/lists/{id}/unsubscribe |
Suppression
Any integration that sends on a customer's behalf has to respect this list, and any integration that syncs an audience outward should read it.
| Operation | Method and path |
|---|---|
listSuppressions | GET /api/suppression |
addSuppression | POST /api/suppression |
checkSuppression | GET /api/suppression/{email} |
removeSuppression | DELETE /api/suppression/{email} |
Campaigns and events
| Operation | Method and path | Notes |
|---|---|---|
v1CreateCampaign | POST /api/v1/campaigns | |
v1SendCampaign | POST /api/v1/campaigns/{id}/send | |
v1GetCampaignStats | GET /api/v1/campaigns/{id}/stats | |
v1TrackEvent | POST /api/v1/events | Record a custom event, which can fire a workflow. |
v1ListEvents | GET /api/v1/events |
Building an n8n community node
The spec at https://docs.sendly.now/openapi.json is the released contract, and it is what a node
generator should be pointed at.
Three properties it holds, which are what a generated node depends on:
- every operation has an
operationId; - they are unique across the whole document;
- they are lower camel case with no punctuation, so they survive being pasted into generated source.
Those three are enforced by a test, not by convention, and so is the fact that the
operationIds on this page keep pointing at the same method and path. An operationId is not
visible in a curl command, so renaming one breaks every generated client without failing
anything loudly — which is exactly why it is pinned.
Zapier and Make
Both are built the same way: a REST-hook trigger backed by createWebhook / deleteWebhook, and
actions backed by the tables above. The signature on the delivered payload is what the trigger
should verify — see Webhooks for the header and the scheme.
Segment
There is no published Segment destination. A destination built against this API would map:
identify→upsertContact;track→v1TrackEvent;- a suppression or unsubscribe signal →
addSuppressionorunsubscribeFromList.
What is not here yet
Said plainly, so nobody builds on an assumption:
- No first-party apps. Nothing is published in the Zapier, Make, n8n or Segment catalogues. Everything above is what someone else can build with today.
- No campaign-level webhook events. A subscription can hear about individual messages, not
about a campaign starting or finishing. A platform that wants "when a campaign completes" has
to poll
v1GetCampaignStats. - Two paths, one API. Newer resources are versioned under
/api/v1/…; contacts, templates, domains, webhooks and suppression are documented at their unversioned/api/…paths. Both are in the same spec and both are supported. Generate from the spec rather than assuming a prefix.
MCP Server
Connect Claude, or any MCP client, to your Sendly account so an AI agent can work in it — with the permissions you choose, and the irreversible ones only if you tick them
Event vocabularies
Sendly has two event name lists — one a webhook subscription accepts, one a workflow triggers on — and they are not the same