Developers
The Sendslay API.
18 endpoints, one header, and a key that can read and write your list but can never send a campaign. Everything below works today — nothing here is planned.
Quick start
Three minutes to your first call.
One base URL, one header. There is no SDK to install and no OAuth dance.
1. Make a key
In your dashboard: Settings → API keys. Tick only the scopes that integration needs. The key is shown once — it is stored hashed, so nobody, including us, can read it back to you.
2. Send it as a Bearer token
Every request goes to https://sendslay.com/api/v1 over HTTPS. Keys start with sk_live_.
3. Your plan has to include API access
API access is on Growth and Scale. On Free and Starter, key creation is refused — and an existing key stops working if the workspace moves down to one of those plans, which is checked on every single request, not just when the key was made. See pricing.
curl -H 'Authorization: Bearer sk_live_…' \
'https://sendslay.com/api/v1/subscribers?pageSize=5'curl -X POST 'https://sendslay.com/api/v1/subscribers' \
-H 'Authorization: Bearer sk_live_…' \
-H 'Content-Type: application/json' \
-d '{
"email": "jane@example.com",
"firstName": "Jane",
"consentSource": "api"
}'Keys belong on your server. A key in browser or mobile code is public the moment it ships — and it carries every scope you ticked.
Scopes
Seven scopes. A key has exactly what you ticked.
A key is not a user — it has no role and no seat. Its scopes are its whole authority, so anything not on this list cannot be done with a key at all.
| Scope | What it lets a key do |
|---|---|
subscribers:read | Read your subscribers, including their email addresses |
subscribers:write | Add, update and delete subscribers |
groups:read | Read your groups |
groups:write | Create and change groups |
campaigns:read | Read campaigns and their results |
suppression:read | Read your suppression list |
suppression:write | Add addresses to your suppression list |
What keys cannot do — on purpose
This list is short because it is meant to be. Each of these is missing for a reason, not because we have not got to it yet.
- Sending a campaign
- There is no campaigns:write scope. Sending is the one thing in this product that cannot be undone: a wrong loop, a leaked key, and half-written email reaches your whole list. That decision stays with a person who has seen the pre-send checks.
- Creating or revoking API keys
- A key can never make another key. Otherwise one leaked key keeps itself alive forever — you revoke the old one, not knowing it already made a new one.
- Billing, domains, senders and team
- These are the workspace owner's decisions and need a signed-in human.
- Deleting someone's data under GDPR
- It runs on one person's request and has no undo. Not work for a script.
- Un-suppressing an address, and deleting groups
- Both are quiet ways to start mailing people who should not be mailed. Do them in the dashboard, where you can see what you are doing.
Reference
All 18 endpoints.
Every path below is relative to the base URL, and every one of them needs the Authorization header. Anything not listed here refuses API keys outright, even with a perfectly valid key.
https://sendslay.com/api/v1Subscribers
Seven endpoints. This is where nearly every integration lives — your app gains a customer, and one call later they are on the list with a consent record attached.
/subscriberssubscribers:readList subscribers, newest first.
Query
- search
- Matches email, first name or last name.
- status
- ACTIVE · UNSUBSCRIBED · BOUNCED · JUNK · UNCONFIRMED
- groupId
- Only members of this group.
- page
- Default 1.
- pageSize
- Default 25, maximum 100.
curl -H 'Authorization: Bearer sk_live_…' \
'https://sendslay.com/api/v1/subscribers?status=ACTIVE&pageSize=50'{
"data": [ … ],
"total": 1284,
"page": 1,
"pageSize": 25
}pageSize must be between 1 and 100 — a larger number is a 400, not a silent cap. Read total and walk the pages.
/subscribers/:idsubscribers:readOne subscriber, with the groups they are in.
curl -H 'Authorization: Bearer sk_live_…' \
https://sendslay.com/api/v1/subscribers/9f1c...{
… every field above, plus:
"memberships": [
{ "groupId": "4b2a...", "group": { "name": "Newsletter" } }
]
}The list endpoint does not include memberships — only this one does. If you need groups for many subscribers, ask per group (GET /groups/:id/subscribers) rather than calling this in a loop.
/subscriberssubscribers:writeAdd a subscriber.
Body
- email *
- Must be a valid address.
- firstName · lastName
- Up to 100 characters each.
- status
- Defaults to ACTIVE.
- fields
- Any JSON object — your own custom fields.
- timezone
- IANA name, e.g. "Asia/Karachi". A wrong name is rejected with 400, not quietly turned into UTC.
- consentSource
- Where permission came from — "api", "signup-form", your own label.
- requireDoubleOptIn
- true sends a confirmation email and creates the subscriber as UNCONFIRMED. No campaign reaches them until they click.
- groupIds
- Up to 50 group ids to join on creation.
curl -H 'Authorization: Bearer sk_live_…' \
-X POST https://sendslay.com/api/v1/subscribers \
-H 'Content-Type: application/json' \
-d '{"email":"jane@example.com","firstName":"Jane","consentSource":"api","requireDoubleOptIn":true}'{
"id": "9f1c...",
"tenantId": "3a7e...",
"email": "jane@example.com",
"firstName": "Jane",
"lastName": "Doe",
"status": "ACTIVE",
"externalCustomerId": null,
"totalOrders": 0,
"totalSpent": 0,
"lastOrderAt": null,
"fields": { "city": "Karachi" },
"timezone": "Asia/Karachi",
"consentSource": "api",
"consentAt": "2026-09-20T09:14:02.881Z",
"consentIp": "203.0.113.7",
"subscribedAt": "2026-09-20T09:14:02.881Z",
"unsubscribedAt": null,
"createdAt": "2026-09-20T09:14:02.881Z",
"updatedAt": "2026-09-20T09:14:02.881Z"
}The IP the call comes from is recorded as consentIp. That record is the only evidence you have if someone files an abuse report, so send consentSource on every call.
/subscribers/:idsubscribers:writeChange a subscriber. Send only the fields you are changing.
Body
- Any field from POST
- All optional here.
- timezone: null
- Clears it — the workspace timezone takes over again.
curl -H 'Authorization: Bearer sk_live_…' \
-X PATCH https://sendslay.com/api/v1/subscribers/9f1c... \
-H 'Content-Type: application/json' \
-d '{"lastName":"Khan","fields":{"plan":"pro"}}'If the address is on your suppression list, status is forced to UNSUBSCRIBED no matter what you send — and unsubscribing always puts the address on that list. So writing status: "ACTIVE" cannot quietly put someone back on the list who asked to be off it.
/subscribers/:idsubscribers:writeDelete a subscriber.
curl -H 'Authorization: Bearer sk_live_…' \
-X DELETE https://sendslay.com/api/v1/subscribers/9f1c...{ "id": "9f1c..." }This removes the row, not the suppression entry. If they unsubscribed or bounced, that address stays suppressed — which is what keeps a re-import from mailing them again.
/subscribers/:id/groupssubscribers:writeAdd a subscriber to one or more groups.
Body
- groupId
- A single group.
- groupIds
- Up to 50. Send either shape.
curl -H 'Authorization: Bearer sk_live_…' \
-X POST https://sendslay.com/api/v1/subscribers/9f1c.../groups \
-H 'Content-Type: application/json' \
-d '{"groupIds":["4b2a...","7c9d..."]}'/subscribers/:id/groups/:groupIdsubscribers:writeRemove a subscriber from one group.
curl -H 'Authorization: Bearer sk_live_…' \
-X DELETE https://sendslay.com/api/v1/subscribers/9f1c.../groups/4b2a...Groups
Five endpoints. A group is a list you put people on by hand or by integration — as opposed to a segment, which is a rule.
/groupsgroups:readList groups with their member counts.
Query
- search
- Matches the name.
- page · pageSize
- Default 1 and 25, max 100.
curl -H 'Authorization: Bearer sk_live_…' \
https://sendslay.com/api/v1/groups{
"data": [
{ "id": "4b2a...", "name": "Newsletter", "description": null,
"memberCount": 812,
"createdAt": "2026-08-02T11:20:00.000Z",
"updatedAt": "2026-09-19T06:02:11.118Z" }
],
"total": 6, "page": 1, "pageSize": 25
}/groups/:idgroups:readOne group, with memberCount.
curl -H 'Authorization: Bearer sk_live_…' \
https://sendslay.com/api/v1/groups/4b2a.../groups/:id/subscriberssubscribers:readThe members of a group.
Query
- Same as GET /subscribers
- search, status, page, pageSize.
curl -H 'Authorization: Bearer sk_live_…' \
'https://sendslay.com/api/v1/groups/4b2a.../subscribers?status=ACTIVE'{
"data": [ … ],
"total": 1284,
"page": 1,
"pageSize": 25
}Note the scope: this returns email addresses, so it needs subscribers:read — not groups:read.
/groupsgroups:writeCreate a group.
Body
- name *
- 1–120 characters, unique in your workspace.
- description
- Up to 500 characters.
curl -H 'Authorization: Bearer sk_live_…' \
-X POST https://sendslay.com/api/v1/groups \
-H 'Content-Type: application/json' \
-d '{"name":"Newsletter"}'/groups/:idgroups:writeRename a group or change its description.
curl -H 'Authorization: Bearer sk_live_…' \
-X PATCH https://sendslay.com/api/v1/groups/4b2a... \
-H 'Content-Type: application/json' \
-d '{"name":"Weekly newsletter"}'Deleting a group is not available to keys — a group is attached to campaigns and forms, and that is a decision for a person looking at the screen.
Segments
Two endpoints, both read-only. A segment is a saved rule ("no opens in 30 days"), so its membership is computed, never stored.
/segmentsgroups:readAll segments, most recently changed first.
curl -H 'Authorization: Bearer sk_live_…' \
https://sendslay.com/api/v1/segments[
{ "id": "1d4f...", "name": "Quiet customers", "description": null,
"match": "ALL", "rules": [ … ],
"createdAt": "…", "updatedAt": "…" }
]This one returns a plain array, not the paginated { data, total } shape — there are rarely many segments.
/segments/:idgroups:readOne segment and its rules.
curl -H 'Authorization: Bearer sk_live_…' \
https://sendslay.com/api/v1/segments/1d4f...Segments use the groups scopes on purpose — both answer the same question, "who is on this list". Creating and changing segments is not available to keys.
Campaigns
Two endpoints, both read-only — this is how you pull results into your own dashboard.
/campaignscampaigns:readList campaigns with their counters.
Query
- search
- Matches name or subject.
- status
- DRAFT · SCHEDULED · SENDING · SENT · FAILED
- page · pageSize
- Default 1 and 25, max 100.
curl -H 'Authorization: Bearer sk_live_…' \
'https://sendslay.com/api/v1/campaigns?status=SENT'{
"data": [
{ "id": "8e3b...", "name": "September update", "subject": "What shipped",
"status": "SENT", "sentAt": "2026-09-14T04:00:07.412Z",
"recipientCount": 1204, "deliveredCount": 1189,
"openedCount": 431, "clickedCount": 96,
"bouncedCount": 15, "unsubscribedCount": 3,
"sender": { "id": "…", "name": "Sendslay", "email": "hello@yourdomain.com" },
"group": { "id": "4b2a...", "name": "Newsletter", "memberCount": 812 } }
],
"total": 18, "page": 1, "pageSize": 25
}openedCount is a soft number everywhere in email, not only here: Apple Mail Privacy Protection opens messages on the reader's behalf. Clicks are the number a human actually produced.
/campaigns/:idcampaigns:readOne campaign, including its HTML body and A/B variants.
curl -H 'Authorization: Bearer sk_live_…' \
https://sendslay.com/api/v1/campaigns/8e3b...Creating, editing, scheduling and sending a campaign are all unavailable to keys. See "What keys cannot do" below — this is the one decision we will not let a script make.
Suppression
Two endpoints. The suppression list is the set of addresses that are never mailed again, whatever any group or import says.
/suppressionsuppression:readList suppressed addresses.
Query
- search
- Matches the address.
- reason
- BOUNCE · COMPLAINT · UNSUBSCRIBE · MANUAL
- page · pageSize
- Default 1 and 25, max 100.
curl -H 'Authorization: Bearer sk_live_…' \
'https://sendslay.com/api/v1/suppression?reason=COMPLAINT'{
"data": [
{ "id": "2c8a...", "email": "bounced@example.com",
"reason": "BOUNCE", "detail": "smtp; 550 5.1.1 user unknown",
"createdAt": "2026-09-11T22:04:51.007Z" }
],
"total": 47, "page": 1, "pageSize": 25
}/suppressionsuppression:writeSuppress an address by hand.
Body
- email *
- The address to stop mailing.
- reason
- MANUAL or UNSUBSCRIBE only. BOUNCE and COMPLAINT belong to Amazon's feedback, and they never come off — the API will not let you write one.
- detail
- Your own note, up to 500 characters.
curl -H 'Authorization: Bearer sk_live_…' \
-X POST https://sendslay.com/api/v1/suppression \
-H 'Content-Type: application/json' \
-d '{"email":"angry@example.com","reason":"MANUAL","detail":"asked us by phone"}'Removing an address from the suppression list is not available to keys. One careless script undoing a complaint is how a sending reputation dies, and it takes months to rebuild.
How it behaves
Things that are true on every endpoint.
Read this once and you will not have to guess again.
Paging is { data, total, page, pageSize }
GET /segments, which returns a plain array. pageSize must be 1–100. Asking for 5,000 is a 400, not a silent 100 — so read total and walk the pages. There is no endpoint that hands you the whole list at once, on purpose: that one call is what takes an API down.An unknown field is a 400, not a shrug
frstName and the whole request is rejected. We would rather fail loudly than accept a typo and drop the value — which is how half a list ends up with no first name and nobody notices for a month.Times are UTC, ISO 8601
timezone on a subscriber is a separate thing — it decides when they receive mail, not how dates are written.Your workspace never has to be named
60 requests per minute, per key
X-RateLimit-Remaining, so a well-behaved script never has to be surprised. Over the limit you get a 429 with Retry-After in seconds. The limit is per key — a separate key per integration gets its own allowance.Errors say what happened
401 Invalid or revoked API key
…or your plan no longer includes API access.
All three read the same on purpose.
403 This API key does not have the
"subscribers:write" scope.
404 Subscriber 9f1c… not found
Also what you get for another
workspace's id — never "forbidden".
409 jane@example.com already exists
429 Too many requests. Retry-After: 37A validation failure is a 400 whose message is an array — one line per field that was wrong.
Make a key and try it.
Settings → API keys, tick two scopes, and your first call works a minute later. API access comes with Growth.
Stuck on something the page does not answer? support@sendslay.com — a person reads it.