~/sendslice

API reference

Everything SendSlice does, and the shortest path from nothing to an email in your inbox.

Five minutes, start to inbox

Three calls. The first two you do once.

# 1. make an account. the key comes back once — keep it.
curl -X POST https://api.sendslice.com/signup -H 'content-type: application/json' \
  -d '{ "email": "you@example.com" }'

# 2. click the link in the email we just sent you.

# 3. send.
curl -X POST https://api.sendslice.com/send \
  -H 'authorization: Bearer ss_live_...' \
  -H 'content-type: application/json' \
  -d '{ "to": "you@example.com", "subject": "Hello",
        "template_id": "welcome", "data": { "first_name": "Sam" } }'

There is no HTML in step 3, and there never will be. How the message looks is set once in the builder and applies to every send after it.

A new account is in the sandbox: mail goes from our domain with a visible test banner, and only to addresses you have confirmed. That is why step 3 sends to you. Verify a domain to lift it.

Authentication

Every authenticated call takes your key either way:

authorization: Bearer ss_live_...
or
x-api-key: ss_live_...

We store a hash of your key, not the key. That means nobody who steals our database can send as you — and also that we cannot read it back to you if you lose it. Treat anything done with your key as done by you.

When something is wrong

Every failure is the same shape, with a stable code to branch on and a request_id to quote at us.

{
  "error": {
    "code": "send_cap",
    "message": "This account has sent its 50 emails for the month. The allowance resets on the 1st.",
    "details": { "plan": "sandbox", "sends_allowed": 50, "window": "month" },
    "request_id": "req_8c1f..."
  }
}
CodeStatusWhat it means
validation_error400The body did not match the schema. details names the field.
unauthorized401Missing or unrecognised API key.
send_cap403The month's allowance is spent. It resets on the 1st.
unverified_recipient403Sandbox account sending to an address it has not confirmed.
recipient_cap403Sandbox account is at its limit of confirmed addresses.
suppressed403That address bounced or complained. We will not mail it again.
not_found404No such route or resource.
rate_limited429Too many requests. Back off and retry.

Endpoints

POST /signup no key needed

Create an account. The API key comes back once and is never retrievable again — we store only a hash of it. A brand kit can be set in the same call.

curl -X POST https://api.sendslice.com/signup \
  -H 'content-type: application/json' \
  -d '{ "email": "you@example.com", "name": "Your app" }'
{
  "customer_id": "cus_9f2c...",
  "api_key": "ss_live_...",   // shown once
  "plan": "sandbox",
  "message": "Check you@example.com and click the confirmation link."
}
GET /confirm?token=... no key needed

The link in the confirmation email. Until it is clicked the account cannot send. Lost it? POST /confirm/resend with your key.

POST /send key required

Send one email. Give exactly one of template_id or html — both, or neither, is a 400. data merges into a template and is meaningless alongside raw html.

curl -X POST https://api.sendslice.com/send \
  -H 'authorization: Bearer ss_live_...' \
  -H 'content-type: application/json' \
  -d '{
    "to": "sam@example.com",
    "subject": "Reset your password",
    "template_id": "password-reset",
    "data": { "first_name": "Sam", "reset_url": "https://your.app/r/abc" }
  }'
{ "send_id": "snd_4a81...", "status": "sent" }

Optional: reply_to. The look of the message is not in this call and never will be — it comes from your brand kit.

GET /sends key required

Your send history: recipient, subject, template, time, status and any error. Message bodies are not stored, so they are not here.

POST /brand-kit key required

Set how your email looks. One flavour sets every knob at once; any individual field you also pass wins over the flavour.

curl -X POST https://api.sendslice.com/brand-kit \
  -H 'authorization: Bearer ss_live_...' \
  -H 'content-type: application/json' \
  -d '{ "flavor": "pecan", "logo_url": "https://your.app/logo.png" }'

Flavours: blueberry apple cherry pecan key-lime lemon-meringue pumpkin. Or open the builder and click through them.

GET /brand-kit key required

What is currently saved. Returns the documented defaults if you never set one.

POST /domains key required

Claim a sending domain. Returns the DNS records to publish. Calling it twice for the same domain is safe and tells you the second call created nothing.

curl -X POST https://api.sendslice.com/domains \
  -H 'authorization: Bearer ss_live_...' \
  -H 'content-type: application/json' \
  -d '{ "domain": "mail.your.app" }'

Publish the records, then poll GET /domains. Once one verifies, the test banner stops and you can send to anyone.

GET /domains key required

Every domain you have claimed and whether it has verified yet.

GET /suppressions key required

Addresses that hard-bounced or reported your mail as spam. Sends to them are refused automatically — this endpoint tells you which and why.

GET /templates no key needed

The templates and the fields each one takes: welcome password-reset alert generic-message.

GET /brand-options no key needed

Every value a brand kit accepts — flavours, corner styles, border styles and font pairings — so a UI can be built against it without hardcoding a list.

GET /health no key needed

Liveness, with the deployed revision. /ready also checks storage.

Plans and what they allow

Every plan is metered by the calendar month and resets on the 1st. A send past the allowance is refused, not billed.

PlanMonthSendsSends fromSends to
WhiffFree50SendSlice domain, test banner5 confirmed addresses
Taste$95,000Your own domainAnyone
Slice$1925,000Your own domainAnyone
Whole pie$99250,000Your own domainAnyone

Sandbox is not a trial that expires — it is a mode. It exists so nobody has to touch DNS before seeing a real branded email arrive.