Getting started

Authentication

PristineSend uses API keys to authenticate requests. Every API call must include a valid key in the Authorization header.

API keys

Your API key uniquely identifies your workspace. It acts as both an identifier and a secret credential. Each key carries a prefix that tells you which environment it targets:

PrefixEnvironmentBehaviour
ps_live_ProductionSends real email and counts against your sending limits.
ps_test_Sandbox / testRuns the full pipeline but captures sends instead of delivering — free, no reputation impact. See Test mode.

Both prefixes are used the same way — pass the full key as a Bearer token. Examples in these docs use ps_live_; substitute a ps_test_ key to run against the sandbox.

To find your key, go to Dashboard → Settings → API Keys. The key is shown once at creation and can be regenerated at any time.

Using your key

Pass your API key as a Bearer token in the Authorization header of every request:

Authorization: Bearer ps_live_xxxxxxxxxxxxxxxxxxxx

A complete example with curl:

"color:#79c0ff">curl "color:#ff7b72">-X POST https://pristinesend.com/api/v1/send \
  "color:#ff7b72">-H "Authorization: Bearer ps_live_xxxxxxxxxxxxxxxxxxxx" \
  "color:#ff7b72">-H "Content">-Type: application/json" \
  "color:#ff7b72">-d "color:#a5d6ff">'{ "to": "user@example.com", "subject": "Hi", "html": "<p>Hi!</p>" }'
Note: If you omit from, the send uses your workspace's default sender (configured under Settings → Senders). Supplying a from on one of your verified domains — or a sender_id — gives you explicit control.

Scopes

A key can be restricted to the operations it actually needs. When you create one in Settings → Developer you can pick Full access (the default), Send only, Read only, or a custom set. A key handed to a script that only sends receipts has no business reading your audience, and a leaked one then costs you far less.

ScopeGrants
email:sendSend transactional email — POST /send and /send/batch.
email:send_bulkDraft a bulk campaign to a segment. Every bulk send waits for a human to approve it before anyone is emailed. Deliberate-only (see below).
contacts:readList and retrieve contacts.
contacts:writeCreate, update, and delete contacts.
emails:readList sent emails and their delivery status.
domains:readList verified sending domains.
events:readRead the delivery and engagement event feed.
suppressions:readRead the do-not-send list.
suppressions:writeAdd to and remove from the do-not-send list.
deliverability:checkScore email content before sending — POST /deliverability/check.
segments:writeCreate an audience segment from a filter. Deliberate-only (see below).

Call an endpoint without its scope and you get 403 insufficient_scope; the message names the scope you are missing, so an automated caller can report exactly what it needs. Keys created before scopes existed carry full access and are unaffected, and rotating a scoped key keeps its scopes.

Two scopes are deliberate-only. email:send_bulk and segments:write are never granted to an OAuth-connected agent and never seeded as a workspace default — they exist only on a key you mint on purpose. Together they let software author an audience and mail it, so the pairing is the thing worth an explicit decision. Bulk sends still wait for a human to approve them.

Key security

  • Never hard-code your API key in client-side code or commit it to a repository.
  • Store it in an environment variable (e.g. PRISTINESEND_API_KEY).
  • Restrict access to the environment variable in your CI/CD pipeline.
  • If a key is compromised, rotate it immediately from the Settings page.

Rotating keys

Go to Settings → API Keys and click Regenerate. The old key is immediately invalidated. Update your environment variables before rotating to avoid downtime.

Auth errors

All authentication failures return a 401 status:

Missing header
{
  "error": {
    "code": "unauthorized",
    "message": "Missing or invalid Authorization header.",
    "request_id": "req_8f3c9a1b2d4e5f6071829304"
  }
}
Invalid key
{
  "error": {
    "code": "unauthorized",
    "message": "Invalid API key.",
    "request_id": "req_8f3c9a1b2d4e5f6071829304"
  }
}

See the full Error codes reference for a complete list of error responses.