API basics

API overview

Base URL, authentication, errors, and pagination for the REST API.

View as Markdown ↗

Base URL & authentication

Most endpoints authenticate with an API key as a bearer token — create one under Developers → API Keys. Discovery (GET /v1) and signup (POST /v1/signup) are public exceptions. Dashboard session routes under /api/teams do not accept API-key authentication.

Base URL
https://emailbump.com/api/v1
cURL
curl https://emailbump.com/api/v1/contacts \
  -H "Authorization: Bearer ebk_your_key"

Start here

Replace example keys, IDs, domains and recipient addresses with values for your own project. Example addresses are documentation placeholders, not test inboxes. Two calls help you discover the API and check your setup:

cURL
curl https://emailbump.com/api/v1                    # the index — no key needed
curl https://emailbump.com/api/v1/me -H "Authorization: Bearer ebk_your_key"

GET /v1/me reports the selected project, email-confirmation state, verified domains, suggested sender and footer address. sending.ready requires a confirmed account and no sending hold. Use an owned agent inbox, a verified domain, or the shared testing sender when eligible. sending.domain_review_required identifies a domain review hold; sending.sending_held also includes abuse suspensions. Limited shared-domain testing can be available before domain verification only when sending is not held. It is a setup check, not a promise that a particular send will pass consent, suppression, plan-limit and abuse checks. Marketing mail also needs your own postal address.

cURL
{
  "project": { "id": "7a10f0f2-…", "name": "Main", "object": "project" },
  "sending": {
    "ready": true,
    "email_confirmed": true,
    "verified_domains": ["mail.acme.com"],
    "default_from": "hello@mail.acme.com",
    "note": "Ready. `from` must be an address on one of the verified domains."
  },
  "footer": { "address": "12 Bridge Street, Austin TX", "is_your_own_address": true }
}

No account yet

POST /v1/signup creates one from the command line — an email address, no password, no browser:

cURL
curl -X POST https://emailbump.com/api/v1/signup \
  -H "Content-Type: application/json" \
  -d '{ "email": "you@company.com", "name": "Dana", "accept_terms": true }'

It returns a project key for sending and an account key for creating further projects and keys. The account cannot send until the six-character code we email is confirmed with POST /auth/verify-email/code. More on keys.

Two halves, one API

Everything lives under the same base URL, and every reference page says at the top which key reaches it. The split is only about what a resource belongs to:

Project API

What you send and who you send it to — contacts, events, lists, segments, templates, campaigns, flows, transactional email. A project key reaches these directly.

Project resources
Account API

The structure those projects live in — workspaces, projects, project keys, sending domains. Needs an all-access key, and is mostly for agents and CLIs setting an account up.

Account resources

An all-access key can do both: it reaches any project resource too, by naming the project per call with an X-Project-Id header. A project key stays inside its own project. How keys and scopes work.

One exception, because it was on the wrong side of the line: a project key manages its own project's sending domains at /v1/domains — list, add, check. Setting up sending is the first thing a project needs to do, and it shouldn't require the wider credential.

Pagination

Contacts, campaigns, flows, and flow enrollments are paginated: they accept ?limit= (max 100, default 25) and ?offset=, and return { "data": [...], "total", "limit", "offset" }. Lists, segments, and templates are small by nature and return the full set as { "data": [...] }. Other resources have their own pagination contracts; see their reference pages. Inspect attributable requests under Developers → API logs.

Plan limits and usage

GET/v1/limits

The selected project's workspace plan, billable active-subscriber count and pooled fair-use sending allowance. It is not a per-project quota or the separate hard monthly send-cap check; see Billing.

cURL
curl https://emailbump.com/api/v1/limits \
  -H "Authorization: Bearer ebk_your_key"
200 response
{
  "plan": "growth",
  "plan_name": "Growth",
  "contacts": { "count": 8412, "limit": 25000 },
  "sending": {
    "this_month": 31204,
    "included_per_month": 250000,
    "remaining": 218796,
    "over_limit": false
  },
  "features": { "automations": true, "custom_domains": true, "…": true }
}

A 0 contact limit and a null included_per_month both mean unmetered.

Unrecognised fields

On /v1/emails, a field we don’t read is ignored but not silently — the response carries a warnings array naming it. from_name is the one that bites: it’s a real field on campaigns and flows, and here the display name belongs in from ("Acme <hi@acme.com>"). A typo that changes nothing is worth hearing about at the time.

Validate the returned resource

Contact create/upsert and update reject unknown fields with 400, including typos such as first_nmae or unsupported fields such as tags. Other endpoints can still ignore unknown fields: use the documented request shape and check the returned resource. A successful status alone does not prove an unrecognized field was applied.

Errors

Application errors use the JSON shape below; framework or proxy rejections can differ. Success codes depend on the endpoint: reads normally return 200, contact upserts return 201 for a new contact or 200 for an update, and many deletes return 204 with no body. Check each endpoint's contract rather than assuming every write returns the same code.

Error shape
{ "error": "Enter a valid email address" }
  • 400 — invalid or malformed request body / validation failed
  • 401 — missing or invalid API key
  • 402 — upgrade required for a paid feature (adds code, feature, and upgrade_to fields)
  • 403 — insufficient permissions or sending restrictions
  • 404 — resource not found (or not in your project)
  • 409 — conflict with the resource's current state
  • 429 — rate limit or sending allowance reached; read the error for the specific limit
  • 500 — internal failure; retry cautiously because a send's outcome may be uncertain

Tracing a sent message

Every message Email Bump sends — API, SMTP, campaign, or flow — carries two headers that say where it came from, so a forwarded email, one sitting in a spam folder, or a bounce report a recipient sends you can be tied back to a project without a database lookup:

Headers on every send
X-EmailBump-Project: 7a10f0f2-3b44-4c19-9c6e-2f81a6d4e0aa
X-EmailBump-Stream: transactional

X-EmailBump-Stream is transactional, marketing (campaigns), or automation (flows). The project id is the same value you'd pass as X-Project-Id, so it drops straight into an API call or a support ticket.

Inside Email Bump the same ids ride along as provider message tags — project, stream, and the campaign, flow, step, or A/B variation responsible — which is what the reports join on. To follow one message: Developers → Transactional has every API and SMTP send with its provider message id, rendered content, and event timeline; Developers → API logs has the attributable request that caused an API submission. SMTP logs also shows gateway attempts that failed before a message was created.

Project resources

One page per thing you can act on, all reachable with a project key.

Account resources

The provisioning half, for standing an account up. Needs an all-access key.

A typical agent onboarding: create a workspace and read its starter project id, add a sending domain and publish the DNS records, mint a project key, then use the project API with that key.

bash
TOKEN=ebk_all_access_key
BASE=https://emailbump.com/api/v1

# 1. A workspace — the response includes its starter project
PROJ=$(curl -s -X POST $BASE/workspaces -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" -d '{"name":"Acme Agency"}' | jq -r .project.id)

# 2. A sending domain (returns the DNS records to publish)
curl -s -X POST $BASE/projects/$PROJ/domains -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" -d '{"domain":"mail.acme.com"}'

# 3. A project key for the application to send with
KEY=$(curl -s -X POST $BASE/projects/$PROJ/api-keys -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" -d '{"name":"server"}' | jq -r .key)

# 4. From here it's the project API — a welcome flow, with the new key
curl -s -X POST $BASE/flows -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" -d '{
    "name": "Welcome",
    "trigger": { "type": "contact_created" },
    "steps": [{ "type": "send", "subject": "Welcome!", "html": "<p>Thanks for joining.</p>" }]
  }'