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.
https://emailbump.com/api/v1curl 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 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.
{
"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 -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:
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 resourcesThe 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 resourcesAn 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
/v1/limitsThe 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 https://emailbump.com/api/v1/limits \
-H "Authorization: Bearer ebk_your_key"{
"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.
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": "Enter a valid email address" }400— invalid or malformed request body / validation failed401— missing or invalid API key402— upgrade required for a paid feature (addscode,feature, andupgrade_tofields)403— insufficient permissions or sending restrictions404— resource not found (or not in your project)409— conflict with the resource's current state429— rate limit or sending allowance reached; read the error for the specific limit500— 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:
X-EmailBump-Project: 7a10f0f2-3b44-4c19-9c6e-2f81a6d4e0aa
X-EmailBump-Stream: transactionalX-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.
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>" }]
}'