API reference
Manage leads, campaigns, suppressions and replies from your own code, Zapier, Make or n8n.
Basics
Base URL https://www.inboxed.si/api/v1. Send a workspace API key as Authorization: Bearer ibx_…. Keys are created by owners and admins in Settings → API, can expire, and can be rotated (the old key keeps working for 24 hours).
Rate limits. 600 requests a minute per key. Every answer has RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset (seconds); a 429 adds Retry-After. Lead writes are also limited to 120 requests per 10 minutes per workspace, because new leads are verified.
Errors are JSON with a readable error and a stable code: {"error": "Campaign not found.", "code": "not_found"}. Validation errors add details.
Pagination. List endpoints take limit (default 50, at most 100) and return {"data": [...], "next_cursor": "…"}. Pass cursor=next_cursor for the next page; it’s null on the last one.
Idempotency. Send Idempotency-Key: <any unique string> on a POST. Retrying with the same key within 24 hours returns the first answer (with Idempotent-Replayed: true) instead of doing it twice. Reusing a key for a different request is a 422.
IP allowlist. If the workspace owner set one, requests from other addresses get 403 ip_not_allowed.
Scopes
- read
- Campaigns, stats, lists, leads, mailboxes, conversations, suppressions, wallet, webhook subscriptions.
- leads:write
- Create and update lists and leads (verified on add).
- campaigns:write
- Pause campaigns and remove leads from them.
- campaigns:launch
- Add leads to running campaigns and resume them. These send (and charge for) more emails. Manager role.
- inbox:write
- Write replies in the team inbox. They wait for approval unless you ask to send and the key’s creator can approve.
- suppressions:write
- Add and remove addresses and domains on the suppress list.
- admin
- Everything above, plus erasing a person’s data.
Every write scope includes read. Spending scopes (campaigns:launch, admin) also check that the person who created the key still has that role. Keys made before scopes existed have read, campaigns:write, campaigns:launch, inbox:write: exactly what they could do before.
Campaigns
GET/campaignsList campaignsread
Parameters
- limit query
- integer · Items per page, 1 to 100.
- cursor query
- string · The next_cursor from the previous page.
- status query
- "draft" | "setting_up" | "active" | "paused" | "completed" | "stopped"
curl -X GET "https://www.inboxed.si/api/v1/campaigns" \
-H "Authorization: Bearer $INBOXED_API_KEY"GET/campaigns/{id}Get a campaignread
Parameters
- id path
- string
curl -X GET "https://www.inboxed.si/api/v1/campaigns/00000000-0000-0000-0000-000000000000" \
-H "Authorization: Bearer $INBOXED_API_KEY"GET/campaigns/{id}/statsCampaign statsread
Results and how many enrolled leads are in each state. Rates are fractions (0.05 = 5%); opens are estimates.
Parameters
- id path
- string
curl -X GET "https://www.inboxed.si/api/v1/campaigns/00000000-0000-0000-0000-000000000000/stats" \
-H "Authorization: Bearer $INBOXED_API_KEY"POST/campaigns/{id}/pausePause a campaigncampaigns:write
Free. Unsent emails aren’t charged.
Parameters
- id path
- string
curl -X POST "https://www.inboxed.si/api/v1/campaigns/00000000-0000-0000-0000-000000000000/pause" \
-H "Authorization: Bearer $INBOXED_API_KEY" \
-H "Idempotency-Key: $(uuidgen)"POST/campaigns/{id}/resumeResume a campaigncampaigns:launch
Starts sending (and charging) again. Needs wallet balance, the campaigns:launch scope, and a key creator who is still a manager.
Parameters
- id path
- string
curl -X POST "https://www.inboxed.si/api/v1/campaigns/00000000-0000-0000-0000-000000000000/resume" \
-H "Authorization: Bearer $INBOXED_API_KEY" \
-H "Idempotency-Key: $(uuidgen)"POST/campaigns/{id}/leadsAdd leads to a running campaigncampaigns:launch
Copies existing leads (leadIds) or creates new ones (leads, verified on add) in the campaign’s list and enrols them. Up to 1000 per request. This sends more emails, so it needs campaigns:launch.
Parameters
- id path
- string
Body
- leadIds
- string[] · Existing leads (any list) to copy into the campaign's list.
- leads
- object[] · New leads to add to the campaign's list (verified on add).
curl -X POST "https://www.inboxed.si/api/v1/campaigns/00000000-0000-0000-0000-000000000000/leads" \
-H "Authorization: Bearer $INBOXED_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"leads":[{"email":"ana@example.com","firstName":"Ana"}],"leadIds":["00000000-0000-0000-0000-000000000000"]}'POST/campaigns/{id}/leads/removeStop leads in a campaigncampaigns:write
Stops leads for good (by leadIds or emails). They get no more emails and are never charged. Leads that replied or bounced are left alone.
Parameters
- id path
- string
Body
- leadIds
- string[]
- emails
- string[]
curl -X POST "https://www.inboxed.si/api/v1/campaigns/00000000-0000-0000-0000-000000000000/leads/remove" \
-H "Authorization: Bearer $INBOXED_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"emails":["ana@example.com"]}'Leads
GET/listsList lead listsread
Parameters
- limit query
- integer · Items per page, 1 to 100.
- cursor query
- string · The next_cursor from the previous page.
curl -X GET "https://www.inboxed.si/api/v1/lists" \
-H "Authorization: Bearer $INBOXED_API_KEY"POST/listsCreate a listleads:write
Body
- name *
- string
curl -X POST "https://www.inboxed.si/api/v1/lists" \
-H "Authorization: Bearer $INBOXED_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"name":"Webinar sign-ups"}'GET/lists/{id}Get a listread
Parameters
- id path
- string
curl -X GET "https://www.inboxed.si/api/v1/lists/00000000-0000-0000-0000-000000000000" \
-H "Authorization: Bearer $INBOXED_API_KEY"PATCH/lists/{id}Rename a listleads:write
Parameters
- id path
- string
Body
- name *
- string
curl -X PATCH "https://www.inboxed.si/api/v1/lists/00000000-0000-0000-0000-000000000000" \
-H "Authorization: Bearer $INBOXED_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Webinar sign-ups (March)"}'DELETE/lists/{id}Delete a list and its leadsleads:write
Refused (409) while a running campaign uses the list.
Parameters
- id path
- string
curl -X DELETE "https://www.inboxed.si/api/v1/lists/00000000-0000-0000-0000-000000000000" \
-H "Authorization: Bearer $INBOXED_API_KEY"GET/lists/{id}/leadsList leads in a listread
Parameters
- id path
- string
- limit query
- integer · Items per page, 1 to 100.
- cursor query
- string · The next_cursor from the previous page.
curl -X GET "https://www.inboxed.si/api/v1/lists/00000000-0000-0000-0000-000000000000/leads" \
-H "Authorization: Bearer $INBOXED_API_KEY"POST/lists/{id}/leadsCreate or update leadsleads:write
One lead object, or {"leads": [...]} with up to 1000. Addresses already in the list are updated with the fields you send; repeats in one request count once. New addresses are verified before they’re stored. A single invalid lead returns 422; in bulk, each item reports its own result.
Parameters
- id path
- string
Body
Either
- email *
- string
- firstName
- string or null
- lastName
- string or null
- company
- string or null
- title
- string or null
- timezone
- string or null · IANA time zone, e.g. Europe/Ljubljana.
- country
- string or null · ISO 3166 alpha-2.
- region
- string or null
- city
- string or null
- companySize
- integer or null
- language
- string or null
- custom
- object · Custom fields by snake_case key, usable as {{key}} in emails.
or
- leads *
- object[]
curl -X POST "https://www.inboxed.si/api/v1/lists/00000000-0000-0000-0000-000000000000/leads" \
-H "Authorization: Bearer $INBOXED_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"leads":[{"email":"ana@example.com","firstName":"Ana","company":"Example Co","country":"SI","custom":{"webinar":"March"}}]}'GET/leads/{id}Get a leadread
Parameters
- id path
- string
curl -X GET "https://www.inboxed.si/api/v1/leads/00000000-0000-0000-0000-000000000000" \
-H "Authorization: Bearer $INBOXED_API_KEY"PATCH/leads/{id}Update a leadleads:write
Send only the fields to change. custom keys are merged.
Parameters
- id path
- string
Body
- firstName
- string or null
- lastName
- string or null
- company
- string or null
- title
- string or null
- timezone
- string or null · IANA time zone, e.g. Europe/Ljubljana.
- country
- string or null · ISO 3166 alpha-2.
- region
- string or null
- city
- string or null
- companySize
- integer or null
- language
- string or null
- custom
- object · Custom fields by snake_case key, usable as {{key}} in emails.
curl -X PATCH "https://www.inboxed.si/api/v1/leads/00000000-0000-0000-0000-000000000000" \
-H "Authorization: Bearer $INBOXED_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title":"Head of Growth","custom":{"plan":"pro"}}'Suppressions
GET/suppressionsList the suppress listread
Parameters
- limit query
- integer
- cursor query
- string
- reason query
- "unsubscribe" | "bounce" | "complaint" | "manual" | "crm"
curl -X GET "https://www.inboxed.si/api/v1/suppressions" \
-H "Authorization: Bearer $INBOXED_API_KEY"POST/suppressionsAdd emails or domainssuppressions:write
Live sequences to these addresses (or domains) stop straight away.
Body
- entries *
- string[] · Emails, or domains (acme.com or @acme.com) to block a whole company.
curl -X POST "https://www.inboxed.si/api/v1/suppressions" \
-H "Authorization: Bearer $INBOXED_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"entries":["someone@example.com","competitor.com"]}'DELETE/suppressions/{entry}Remove an entrysuppressions:write
URL-encode the email or @domain. Unsubscribes and spam complaints can’t be removed (409).
Parameters
- entry path
- string
curl -X DELETE "https://www.inboxed.si/api/v1/suppressions/someone%40example.com" \
-H "Authorization: Bearer $INBOXED_API_KEY"POST/erasureErase a person (GDPR)admin
Deletes everything the workspace holds about this address and keeps a hash so they’re never emailed again. Can’t be undone. Admin scope.
Body
- email *
- string
curl -X POST "https://www.inboxed.si/api/v1/erasure" \
-H "Authorization: Bearer $INBOXED_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"email":"ana@example.com"}'Mailboxes
GET/mailboxesList mailboxesread
curl -X GET "https://www.inboxed.si/api/v1/mailboxes" \
-H "Authorization: Bearer $INBOXED_API_KEY"GET/mailboxes/{id}Mailbox statusread
Connection status, today’s sends, last reply sync and the last DNS check.
Parameters
- id path
- string
curl -X GET "https://www.inboxed.si/api/v1/mailboxes/00000000-0000-0000-0000-000000000000" \
-H "Authorization: Bearer $INBOXED_API_KEY"Inbox
GET/conversationsList conversationsread
Newest first. Filter by status, intent or campaign.
Parameters
- status query
- "open" | "waiting" | "snoozed" | "closed"
- intent query
- string
- campaignId query
- string
- limit query
- integer
- cursor query
- string
curl -X GET "https://www.inboxed.si/api/v1/conversations" \
-H "Authorization: Bearer $INBOXED_API_KEY"GET/conversations/{id}Get a conversation with its messagesread
Parameters
- id path
- string
curl -X GET "https://www.inboxed.si/api/v1/conversations/00000000-0000-0000-0000-000000000000" \
-H "Authorization: Bearer $INBOXED_API_KEY"GET/conversations/{id}/messagesList messagesread
Oldest first; next_cursor is the last message id.
Parameters
- id path
- string
- limit query
- integer · Items per page, 1 to 100.
- cursor query
- string · The next_cursor from the previous page.
curl -X GET "https://www.inboxed.si/api/v1/conversations/00000000-0000-0000-0000-000000000000/messages" \
-H "Authorization: Bearer $INBOXED_API_KEY"POST/conversations/{id}/repliesReply to a conversationinbox:write
Saves a reply in the team inbox. It waits for a manager’s approval, unless you send "send": true and the key’s creator can approve replies (manager or above): then it goes out from the conversation’s mailbox within a minute. Replies are free.
Parameters
- id path
- string
Body
- body *
- string
- send
- boolean
curl -X POST "https://www.inboxed.si/api/v1/conversations/00000000-0000-0000-0000-000000000000/replies" \
-H "Authorization: Bearer $INBOXED_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"body":"Thanks Ana, Tuesday at 10 works. I’ll send an invite.","send":false}'Wallet
GET/walletWallet balanceread
curl -X GET "https://www.inboxed.si/api/v1/wallet" \
-H "Authorization: Bearer $INBOXED_API_KEY"Webhooks
GET/hooksList hook subscriptionsread
curl -X GET "https://www.inboxed.si/api/v1/hooks" \
-H "Authorization: Bearer $INBOXED_API_KEY"POST/hooksSubscribe a URL to an eventread
target_url must be https and resolve to a public address. Subscribing the same URL to the same event again returns the existing subscription.
Body
- event *
- "campaign.completed" | "campaign.paused" | "email.bounced" | "email.clicked" | "email.opened" | "email.sent" | "lead.added" | "lead.hot" | "lead.unsubscribed" | "mailbox.disconnected" | "reply.interested" | "reply.received"
- target_url *
- string
- source
- "zapier" | "make" | "n8n" | "api"
curl -X POST "https://www.inboxed.si/api/v1/hooks" \
-H "Authorization: Bearer $INBOXED_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"event":"reply.interested","target_url":"https://hooks.example.com/inboxed","source":"api"}'DELETE/hooks/{id}Delete a hook subscriptionread
Parameters
- id path
- string
curl -X DELETE "https://www.inboxed.si/api/v1/hooks/00000000-0000-0000-0000-000000000000" \
-H "Authorization: Bearer $INBOXED_API_KEY"GET/hooks/sampleSample eventread
An array with one object shaped exactly like a delivery: recent real data, or a made-up one marked "example": true.
Parameters
- event query
- "campaign.completed" | "campaign.paused" | "email.bounced" | "email.clicked" | "email.opened" | "email.sent" | "lead.added" | "lead.hot" | "lead.unsubscribed" | "mailbox.disconnected" | "reply.interested" | "reply.received"
curl -X GET "https://www.inboxed.si/api/v1/hooks/sample" \
-H "Authorization: Bearer $INBOXED_API_KEY"Webhook events
Subscribe a URL with POST /hooks, or set one workspace webhook URL in Settings → Notifications for every event. Each delivery is a POST of {"event", "sentAt", "data"} with headers X-Inboxed-Event, X-Inboxed-Delivery (unique; ignore repeats) and X-Inboxed-Signature: sha256=<hex HMAC-SHA256 of the raw body>, keyed with the workspace webhook secret.
A 2xx within 10 seconds counts as delivered. Anything else is retried after 1 min, 5 min, 30 min, 2 h and 6 h. Answering 410 Gone removes a subscription.
Verify the signature on the raw body before parsing it:
import { createHmac, timingSafeEqual } from "node:crypto";
// rawBody: the request body exactly as received (a Buffer or string), before JSON parsing.
export function verifyInboxed(rawBody, signatureHeader, secret) {
const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(signatureHeader ?? "");
return a.length === b.length && timingSafeEqual(a, b);
}campaign.completedCampaign finished
A campaign has emailed every lead it will email.
- data.id
- string · Campaign id
- data.name
- string
- data.status
- string · completed
- data.sent
- integer
- data.replies
- integer
- data.positive
- integer
- data.meetings
- integer
- data.bounced
- integer
- data.launchedAt
- string
- data.url
- string · Campaign in the app
{
"event": "campaign.completed",
"sentAt": "2026-01-01T09:00:00.000Z",
"data": {
"id": "00000000-0000-0000-0000-000000000000",
"name": "Example campaign",
"status": "completed",
"sent": 480,
"replies": 21,
"positive": 6,
"meetings": 2,
"bounced": 4,
"launchedAt": "2026-01-01T09:00:00.000Z",
"url": "https://app.example/app/campaigns/00000000-0000-0000-0000-000000000000"
}
}campaign.pausedCampaign paused
A teammate, the API or Inboxed (empty wallet, bounce guard, budget) paused a campaign.
- data.id
- string · Campaign id
- data.name
- string
- data.status
- string · paused
- data.reason
- string · Why it paused
- data.by
- string · user, api or system
- data.sent
- integer
- data.url
- string
{
"event": "campaign.paused",
"sentAt": "2026-01-01T09:00:00.000Z",
"data": {
"id": "00000000-0000-0000-0000-000000000000",
"name": "Example campaign",
"status": "paused",
"sent": 120,
"replies": 4,
"positive": 1,
"meetings": 0,
"bounced": 2,
"launchedAt": "2026-01-01T09:00:00.000Z",
"url": "https://app.example/app/campaigns/00000000-0000-0000-0000-000000000000",
"reason": "Paused through the API.",
"by": "api"
}
}email.bouncedEmail bounced
A send hard-bounced or a bounce report arrived.
- data.email
- string
- data.campaignId
- string
- data.reason
- string · The server’s answer
{
"event": "email.bounced",
"sentAt": "2026-01-01T09:00:00.000Z",
"data": {
"email": "lead@example.com",
"campaignId": "00000000-0000-0000-0000-000000000000",
"reason": "550 5.1.1 User unknown"
}
}email.clickedLink clicked
A lead clicked a tracked link (bot clicks excluded).
- data.email
- string
- data.campaignId
- string
- data.messageId
- string
- data.subject
- string
- data.engagement
- object · kind, at, step, url, client, device
{
"event": "email.clicked",
"sentAt": "2026-01-01T09:00:00.000Z",
"data": {
"email": "lead@example.com",
"campaignId": "00000000-0000-0000-0000-000000000000",
"messageId": "00000000-0000-0000-0000-000000000000",
"subject": "Quick question",
"engagement": {
"kind": "click",
"at": "2026-01-01T09:00:00.000Z",
"step": 1,
"url": "https://example.com/pricing"
}
}
}email.openedEmail opened (estimated)
A tracked email was opened by a person (machine opens excluded). Opens are estimates.
- data.email
- string
- data.campaignId
- string
- data.messageId
- string
- data.subject
- string
- data.engagement
- object · kind, at, step, client, device, estimated
{
"event": "email.opened",
"sentAt": "2026-01-01T09:00:00.000Z",
"data": {
"email": "lead@example.com",
"campaignId": "00000000-0000-0000-0000-000000000000",
"messageId": "00000000-0000-0000-0000-000000000000",
"subject": "Quick question",
"engagement": {
"kind": "open",
"at": "2026-01-01T09:00:00.000Z",
"step": 1,
"estimated": true
}
}
}email.sentEmail sent
A campaign email was accepted by the mail server (and charged).
- data.messageId
- string · Inboxed id of the sent email
- data.campaignId
- string
- data.email
- string · Recipient
- data.step
- integer · Sequence step, from 1
- data.subject
- string
- data.mailbox
- string · Sending address
- data.variant
- string · A/Z version letter
- data.sentAt
- string
{
"event": "email.sent",
"sentAt": "2026-01-01T09:00:00.000Z",
"data": {
"messageId": "00000000-0000-0000-0000-000000000000",
"campaignId": "00000000-0000-0000-0000-000000000000",
"email": "lead@example.com",
"step": 1,
"subject": "Quick question",
"mailbox": "you@yourcompany.com",
"variant": "A",
"sentAt": "2026-01-01T09:00:00.000Z"
}
}lead.addedLead added
A lead was created through the API or added to a running campaign.
- data.leadId
- string
- data.listId
- string
- data.email
- string
- data.firstName
- string
- data.lastName
- string
- data.company
- string
- data.verifyStatus
- string · valid, invalid, risky, catch_all, disposable, role_based or unverified
- data.source
- string · api or campaign
- data.url
- string
{
"event": "lead.added",
"sentAt": "2026-01-01T09:00:00.000Z",
"data": {
"leadId": "00000000-0000-0000-0000-000000000000",
"listId": "00000000-0000-0000-0000-000000000000",
"email": "lead@example.com",
"firstName": "Example",
"lastName": "Lead",
"company": "Example Co",
"verifyStatus": "valid",
"source": "api",
"url": "https://app.example/app/leads/00000000-0000-0000-0000-000000000000"
}
}lead.hotHot lead
A lead opened, clicked or replied enough to count as hot.
- data.email
- string
- data.leadId
- string
- data.campaignId
- string
- data.engagement
- object
- data.detail
- string
{
"event": "lead.hot",
"sentAt": "2026-01-01T09:00:00.000Z",
"data": {
"email": "lead@example.com",
"leadId": "00000000-0000-0000-0000-000000000000",
"campaignId": "00000000-0000-0000-0000-000000000000",
"detail": "Clicked https://example.com/pricing"
}
}lead.unsubscribedLead unsubscribed
A lead unsubscribed by link or by replying.
- data.email
- string
- data.campaignId
- string
- data.via
- string · link, reply or crm
{
"event": "lead.unsubscribed",
"sentAt": "2026-01-01T09:00:00.000Z",
"data": {
"email": "lead@example.com",
"campaignId": "00000000-0000-0000-0000-000000000000",
"via": "link"
}
}mailbox.disconnectedMailbox disconnected
A mailbox can’t send: its sign-in failed or it was disconnected. Sent once per incident.
- data.mailboxId
- string
- data.email
- string
- data.provider
- string · google, microsoft or smtp
- data.reason
- string · The provider’s error
- data.reconnectUrl
- string · Where to reconnect it
{
"event": "mailbox.disconnected",
"sentAt": "2026-01-01T09:00:00.000Z",
"data": {
"mailboxId": "00000000-0000-0000-0000-000000000000",
"email": "you@yourcompany.com",
"provider": "google",
"reason": "535 5.7.8 Username and Password not accepted",
"reconnectUrl": "https://app.example/app/mailboxes/00000000-0000-0000-0000-000000000000"
}
}reply.interestedInterested reply
A reply is tagged Interested or Meeting request.
- data.conversationId
- string
- data.campaignId
- string
- data.lead
- string
- data.email
- string
- data.company
- string
- data.intent
- string
- data.excerpt
- string
- data.url
- string
{
"event": "reply.interested",
"sentAt": "2026-01-01T09:00:00.000Z",
"data": {
"conversationId": "00000000-0000-0000-0000-000000000000",
"campaignId": "00000000-0000-0000-0000-000000000000",
"lead": "Example Lead",
"email": "lead@example.com",
"company": "Example Co",
"intent": "Interested",
"excerpt": "Sounds good. Can we talk next week?",
"url": "https://app.example/app/inbox?c=00000000-0000-0000-0000-000000000000"
}
}reply.receivedReply received
A lead replied (auto-replies such as out-of-office aren’t sent).
- data.conversationId
- string
- data.campaignId
- string
- data.lead
- string
- data.email
- string
- data.company
- string
- data.intent
- string
- data.excerpt
- string
- data.url
- string
{
"event": "reply.received",
"sentAt": "2026-01-01T09:00:00.000Z",
"data": {
"conversationId": "00000000-0000-0000-0000-000000000000",
"campaignId": "00000000-0000-0000-0000-000000000000",
"lead": "Example Lead",
"email": "lead@example.com",
"company": "Example Co",
"intent": "Question",
"excerpt": "How does pricing work?",
"url": "https://app.example/app/inbox?c=00000000-0000-0000-0000-000000000000"
}
}