SendsetsDocs

Endpoints

The supported SendSets agent-native REST contract.

All customer API paths use /v1 and bearer authentication. API keys begin with ssk_. Run creation is asynchronous and returns 202 Accepted with the durable state.

Runs

MethodPathScope
POST/runsWRITE_RUNS; launch intent also needs EXECUTE_RUNS
GET/runsREAD_RUNS
GET/runs/:idREAD_RUNS
POST/runs/:id/cancelWRITE_RUNS

POST /runs requires Idempotency-Key. Its body accepts an existing campaign or campaign metadata, auto/explicit/tag sender selection, inline leads, contact IDs, segment IDs, ordered steps and variants, a daily limit, schedule, stop_on_reply, opt-out state, and draft or launch intent.

An accepted launch moves through queued, preflighting, and then one of awaiting_approval, launching, active, failed, or denied. Later states are paused, completed, and cancelled. GET /runs/:id includes its append-only event stream.

Campaign variant dimensions

MethodPathScope
GET/campaigns/:id/ab-variantsREAD_CAMPAIGNS
POST/campaigns/:id/ab-variantsWRITE_CAMPAIGNS
PATCH, DELETE/campaigns/:id/ab-variants/:variantIdWRITE_CAMPAIGNS

Variant create and update bodies accept optional text fields offer, angle, and cta, alongside the existing copy, weights, and metadata. An omitted field keeps its default on create or its current value on update; an empty string clears a label. Inline variants in campaign creation accept these fields too. Variant responses include the three labels plus parent_variant_id, generation, changed_dimension, generator_model, and prompt_version. Lineage fields are reserved for generated descendants and cannot be set through these create or update requests.

A contact's step assignment freezes the selected arm, template revision, and labels before dispatch. Weight or copy edits affect only steps that have not been assigned. Retries keep the stored selection. Campaign copies preserve variant labels but start with fresh assignments and send history. See A/B variants.

Inbox-agent approval retries

POST /unibox/agent-drafts/:id/approve requires Write Unibox for API keys and Access Unibox for workspace members. The draft ID is the operation's natural idempotency key. Approval and send-task creation commit together. A retry with the same body, or an omitted body, returns the original task_id, scheduled_at, and send_mode. An omitted body keeps the previously approved edit. A changed body or a discarded draft returns 409 with machine-readable code conflict and a request_id. Approval queues a reply; it does not confirm delivery.

A queue-registration error can occur after this transaction commits. Retrying recovers the same task. The backend also repairs missing queue registrations on startup and every minute. Retries after task pruning do not recreate the send. Historical approvals without recorded task ownership and imported approvals cannot be requeued through this endpoint.

Draft list, count, and detail endpoints expose only completed, charged drafts. In-flight generation and billing recovery are internal states. They cannot be approved or discarded through the draft endpoints.

Reply routing and text model

GET /organization/current and PATCH /organization/current expose two additive workspace settings under the existing Manage settings permission:

  • reply_agent_categories: an array containing any of positive, question, objection, or referral, without duplicates. The default is ["positive"]. An empty array disables reply generation. Other values return 400 bad_request.
  • reply_agent_model: an OpenRouter text-model ID of at most 200 bytes, or an empty string to use the instance default. Jev decision models are rejected. This model writes and validates the draft while the reply agent settings below use model_source: platform; Jev only classifies.

Omitting either field preserves its value. Settings changes are audited and refresh teammates' workspace settings through the existing realtime audit event. Only selected categories reach the LLM. Failed or unclear classifications do not produce a draft or drafting charge. Automatic sending rechecks the category selection at dispatch. These settings do not enable drafting or automatic sending.

Inbox-agent draft categories

Inbox-agent draft responses include reply_category, reply_category_confidence (0 to 1), and reply_category_source (empty, header, lexicon, or model). Categories are positive, question, objection, referral, rejection, automated_reply, unsubscribe, spam, and unclear. These fields are independent of the existing campaign intent_class and confidence; older drafts default to unclear with zero confidence and an empty source. A model source identifies Jev classification when OpenRouter Jev or direct TypeSafe is configured. Unavailable or invalid model classification does not create a draft. Only eligible human replies in the workspace's selected categories reach the reply model. See Inbox agent.

Inbox-agent factual support

Draft responses also include a grounding object:

  • status: unverified for historical drafts, review_required for missing or insufficient support, or model_supported for a model assessment meeting the workspace response and classification confidence thresholds at generation time.
  • reason: empty, no_product_sources, validation_unavailable, invalid_validation, unsupported_claim, low_response_confidence, knowledge_unavailable, or low_classification_confidence.
  • confidence: the factual-check model's confidence from 0 to 1; zero when no valid assessment is available. This is separate from category confidence.
  • sources: internal references with id and sha256, plus content for exact evidence in newly generated drafts and optional label. Workspace IDs are workspace:product_description, workspace:reply_agent_offer, and workspace:reply_agent_signup_url. Retrieved excerpts use knowledge:<version UUID>:<position>. Historical references may omit content.
  • claim_sources: the IDs cited for supported claims. This is empty when validation does not establish support.

The check applies to the generated draft body, including its final signup link. Human edits do not update the assessment. It is advisory and may be incorrect; draft mode requires human approval, while automatic mode requires the explicit grant and send-time checks described below. Validation failures retain a reviewable draft rather than authorizing a send. Historical rows default to unverified, zero confidence, an empty reason, and empty source arrays.

Reply Agent workspace settings

The existing JWT workspace endpoints GET /organization/current and PATCH /organization/current expose reply_agent_signup_url and reply_agent_offer alongside inbox_agent_enabled. PATCH requires the Manage settings organization permission. Both new fields are optional strings: omission keeps the current value, and an empty string clears it. Setting these fields repeatedly to the same values is naturally safe to retry.

The signup URL must be an absolute HTTPS URL without embedded credentials or whitespace, at most 2,048 bytes. The offer supports up to 2,000 characters. Invalid settings return 400 with the existing bad_request code and request_id. Newly generated positive drafts include the exact configured signup URL. These settings do not authorize automatic sending.

The same endpoints expose numeric reply_agent_classification_confidence (default 0.95) and reply_agent_response_confidence (default 0.90). PATCH accepts finite fractions greater than 0 and at most 1; omission preserves values. Invalid values return 400 with bad_request. Thresholds apply to new drafts, including recovered generation; existing drafts are not reassessed. Low response confidence flags factual support for review. If factual support passes but category confidence is too low, the draft receives low_classification_confidence. Human edits do not update confidence or factual support. Negative product claims require explicit evidence, so absent documentation cannot establish that a feature is unsupported.

The workspace endpoints also expose reply_agent_mode (draft, the default, or automatic), reply_agent_thread_cap (default 3, integer 1–100), and reply_agent_daily_cap (default 100, integer 1–10000). Omission preserves values. Invalid settings return 400 with bad_request and request_id. Changing mode alone grants no sending authority. Automatic candidates are new drafts generated in automatic mode under an existing grant. Queued replies recheck the current grant, settings, recipient safety, mailbox/worker health, subscription, budgets, and manual replies immediately before dispatch.

Human-created automatic reply grant

These routes are JWT-only. They do not accept API keys or OAuth credentials.

MethodPathOrganization permissionBehavior
GET/organization/current/reply-agent/grantManage settingsReturns { "data": grant }, or data: null
PUT/organization/current/reply-agent/grantManage settings and Access UniboxCreates or replaces the workspace's grant
DELETE/organization/current/reply-agent/grantManage settingsRevokes the grant; returns data: null

PUT requires mailbox_ids (1–100 unique workspace mailbox UUIDs), max_per_day (integer 1–10000), and expires_at (a future timestamp within one year). The response includes id, organization_id, those three fields, created_by, and created_at. Repeating the same complete PUT body preserves the grant ID; changing any authority field creates a new ID and holds replies already queued under the previous grant. A delayed PUT retry after a newer replacement restores the older authority, following replacement semantics. PUT and DELETE are naturally safe to retry; no Idempotency-Key is needed. Invalid input or foreign mailbox IDs return 400 with bad_request and request_id. A missing permission returns 403. Grant changes publish the organization audit event.

Held drafts expose automatic_reason, a machine-readable reason such as grant_required, grant_changed, policy_held, automatic_disabled, confidence_requires_review, assessment_requires_review, thread_cap, workspace_daily_cap, mailbox_daily_cap, inbound_requires_review, recipient_mailbox_or_thread_changed, workspace_suspended, entitlement_required, credits_exhausted, usage_budget_reached, or policy_unavailable. Held replies use the normal human approval endpoint; automatic retries never create another task for the same action.

Reply agent activity summary

GET /unibox/agent-drafts/summary?days=7 requires Read Unibox for API keys and Access Unibox for workspace members. days is an integer from 1 to 90 (default 7); anything else returns 400 with bad_request and request_id. It returns { "data": summary } with, for drafts created in the window, drafted, sent (approved, by a person or automatically), sent_automatically, discarded, interested (category positive), median_minutes_to_send, categories (category and count) and daily (date, drafted, sent, one row per UTC day). pending and held are the drafts waiting for review right now, whatever the window. recent lists the 12 most recently changed drafts with id, thread_id, to_addr, subject, reply_category, status, automatic, automatic_reason, created_at and updated_at; it carries no message or draft body. last_activity_at is the latest draft change, or null. It is read-only.

Reply classifier labels

These routes are JWT-only. They do not accept API keys or OAuth credentials.

MethodPathOrganization permissionBehavior
GET/organization/current/reply-agent/labelsAccess UniboxReturns { "data": [rule] }, an empty array when none are set
PUT/organization/current/reply-agent/labelsManage settingsReplaces every rule with { "rules": [rule] }

A rule is { "label", "description", "phrases" }. label is 1 to 40 lowercase letters, digits or hyphens, unique in the workspace; description is optional, at most 200 characters; phrases holds 1 to 20 phrases of 2 to 80 characters, stored lowercased with whitespace collapsed and duplicates dropped. At most 12 rules. Invalid input returns 400 with bad_request and request_id. PUT is a full replace, so retries are naturally safe and no Idempotency-Key is needed. It publishes the organization audit event. An inbound message containing a phrase as whole words gets the label on its conversation; see your own labels.

Reply agent model, classifier and test chat

These session routes are JWT-only. API keys and OAuth tokens reach the same settings through the reply agent API.

MethodPathOrganization permissionBehavior
GET/organization/current/reply-agent/settingsManage settingsReturns { "data": settings }, with defaults when never saved
PUT/organization/current/reply-agent/settingsManage settingsReplaces the model source, endpoint, model, classifier and instructions
POST/organization/current/reply-agent/openrouterManage settingsExchanges an OpenRouter PKCE code and code_verifier for a key and stores it
DELETE/organization/current/reply-agent/keyManage settingsForgets the workspace key and returns to the instance model
POST/organization/current/reply-agent/testManage settingsAnswers a test conversation with the saved settings; stores and sends nothing

settings holds model_source (platform, openrouter or custom), base_url, model, api_key_set, api_key_hint (last four characters), classifier (calibrated, rules or llm), instructions (at most 4,000 characters), updated_at, and three instance facts: platform_model_available, platform_model and calibrated_classifier_available. The key itself is never returned.

PUT takes the same fields plus an optional api_key. Omitting api_key keeps the stored key only while model_source and base_url are unchanged; any other change forgets it, so a key is never sent to a host it was not saved for. platform always forgets the key. openrouter needs a key (stored or in the body) and defaults model to openai/gpt-4o-mini. custom needs base_url (HTTPS, publicly routable, no credentials, query or fragment) and model. Repeating the same body leaves the same state, and DELETE is a no-op when no key is stored, so neither needs an Idempotency-Key. Each write publishes the organization audit event without the key.

The test body is { "subject": "...", "messages": [{ "role": "prospect" | "agent", "content": "..." }] } with 1 to 20 turns of at most 8,000 characters, ending with a prospect turn. The response holds category, category_confidence, category_source, drafted, skip_reason (declined_by_rules, declined_by_classifier or category_not_enabled), reply, grounding, automatic_eligible, model_source, model, tokens_used and credits_charged (5 on the instance model when a reply is drafted, otherwise 0). Errors: 400 for invalid input or no configured model, 402 with insufficient_credits, 403 without the paid entitlement, 429 at the usage cap, 503 when the model fails (the message includes the provider's error). Every error carries code and request_id. The test endpoint takes no Idempotency-Key because it changes nothing but the credit balance: each call that drafts a reply on the instance model is charged, so retry it only when you want another answer. The OpenRouter exchange is naturally safe to retry, because OpenRouter accepts each code once and a replayed code changes nothing.

Product knowledge endpoints

JWT workspace sessions with Manage settings can manage up to 20 active text or Markdown sources:

MethodPathBehavior
GET/organization/current/reply-knowledge/sourcesReturns all active sources as { data: [version, ...] }, ordered by creation time and ID; this bounded configuration list is unpaginated
PUT/organization/current/reply-knowledge/sources/:sourceIDCreates or replaces the source with { title, content }; the caller supplies a stable UUID
DELETE/organization/current/reply-knowledge/sources/:sourceIDDeactivates only that source, retaining history and evidence
GET/organization/current/reply-knowledgeReturns { data: version } or { data: null }
POST/organization/current/reply-knowledge/upload-previewExtracts a multipart file into { data: { title, content } } without saving
PUT/organization/current/reply-knowledgeReplaces the active document with { title, content }
DELETE/organization/current/reply-knowledgeClears the active selection, preserving version history and existing draft evidence

A version contains id, source_id, organization_id, title, content, sha256, and created_at. Title is required, trimmed, and limited to 200 characters. Content is required, trimmed, valid UTF-8 without NUL, and limited to 65,536 bytes. Invalid bodies return 400 with code: bad_request and request_id. Save failures leave that source’s previous document active. Invalid source UUIDs and saves exceeding 20 active sources return 400 with code: bad_request. Existing-source replacements remain allowed at the cap. PUT is naturally idempotent: identical normalized title and content within the same source reuse the immutable version; DELETE retries are harmless. Concurrent successful replacements publish complete versions, with the last committed replacement active. Retrying an older PUT after a later save selects that older content again, as with other replacement operations.

The original GET/PUT/DELETE endpoints continue to read, replace, and clear only the legacy source (00000000-0000-0000-0000-000000000000), preserving their response shapes. Clearing it does not clear other sources. Source-specific PUT and DELETE are naturally retry-safe and need no Idempotency-Key. Reuse the same source UUID across retries; using a new UUID creates an independent source.

Upload preview accepts one .txt, .md, .markdown, or .pdf file. Text must be valid UTF-8, optionally with a BOM, and fit within 64 KiB. PDFs must fit within 5 MiB and yield at most 64 KiB of text within ten seconds. Encrypted PDFs, scanned PDFs without text, unsupported extensions, malformed files, and excessive output return 400 with code: bad_request and request_id. A missing PDF extraction runtime returns 503 with code: service_unavailable. The title defaults to the filename without its extension and must meet the same title limit. Preview is read-only and naturally safe to retry; it needs no Idempotency-Key. Review its text, then PUT it to publish a version. Original files are not retained.

These session endpoints are JWT-only; API keys and OAuth tokens use the reply agent API. They do not enable sending or change existing workspace response shapes. Changes publish the organization audit event so teammates' workspace queries refresh live. See Inbox agent for retrieval limits.

Reply agent over the API

API keys and OAuth tokens with AI_AGENT, and dashboard members with Manage settings, configure the reply agent under /reply-agent. Each route runs the same handler and validation as its session counterpart. The automatic-sending grant and the OpenRouter key exchange stay session-only: a credential can switch the agent on and set its mode, but no automatic reply is sent until a person creates a grant in the dashboard.

MethodPathAPI permissionOrganization permissionBehavior
GET/reply-agent/configAI_AGENTManage settingsReturns { "data": config }
PATCH/reply-agent/configAI_AGENTManage settingsChanges only the fields sent; returns the updated config
GET/reply-agent/settingsAI_AGENTManage settingsSame as GET /organization/current/reply-agent/settings
PUT/reply-agent/settingsAI_AGENTManage settingsSame as PUT /organization/current/reply-agent/settings
POST/reply-agent/testAI_AGENTManage settingsTest chat; stores and sends nothing, and charges AI credits on the SendSets model
GET/reply-agent/labelsREAD_UNIBOXAccess UniboxYour classifier labels
PUT/reply-agent/labelsAI_AGENTManage settingsReplaces every label rule
GET, PUT, DELETE/reply-agent/knowledgeAI_AGENTManage settingsThe legacy knowledge source, as above
GET/reply-agent/knowledge/sourcesAI_AGENTManage settingsEvery active knowledge source
PUT, DELETE/reply-agent/knowledge/sources/:sourceIDAI_AGENTManage settingsCreate, replace or deactivate one source
POST/reply-agent/knowledge/upload-previewAI_AGENTManage settingsExtracts a file into { title, content } without saving

config holds inbox_agent_enabled, reply_agent_signup_url, reply_agent_offer, reply_agent_classification_confidence, reply_agent_response_confidence, reply_agent_mode, reply_agent_thread_cap, reply_agent_daily_cap, reply_agent_categories, reply_agent_model and product_description. PATCH accepts any of them with the limits described in Reply Agent workspace settings; omitted fields are unchanged, an empty string clears a text field, and invalid values return 400 with code: bad_request and request_id. PATCH and the PUT routes set state to the values sent, so a retry is naturally safe and needs no Idempotency-Key. Every change publishes the organization audit event.

Agent policies and approvals

MethodPathCaller
GET/agent-policies/effectiveAPI key, OAuth, or JWT
GET, POST/agent-policiesJWT owner/settings manager only
PUT, DELETE/agent-policies/:idJWT owner/settings manager only
GET/approvalsJWT send manager only
POST/approvals/:id/decisionJWT send manager only

Policies bind to one API key or OAuth grant. Capabilities are campaign.launch, email.compose, inbox.reply and mailbox.provision; the last is autonomous only with a max_monthly_mailbox_spend_cents the order's monthly total fits under. Autonomous grants require explicit mailbox IDs, independent capabilities, recipient/run and daily limits, action limits, and an expiry. Approval records expire after 30 minutes. Approving a run causes preflight and all hard safety gates to run again immediately before the campaign starts.

Direct sends

MethodPathScopeCapability
POST/emails/:id/sendSEND_DIRECT_EMAILemail.compose
POST/unibox/composeSEND_DIRECT_EMAILemail.compose
POST/unibox/replySEND_INBOX_REPLYinbox.reply

API-key and OAuth callers receive an executed or awaiting_approval result. JWT browser sessions are already human-authorized. Suppressions, organization limits, mailbox holds/authentication/health, and deliverability rules remain authoritative regardless of policy.

App connections and product events

MethodPathScope
GET, POST/app-connectionsINTEGRATIONS
GET, PATCH, DELETE/app-connections/:idINTEGRATIONS
POST/app-connections/:id/rotate-secretINTEGRATIONS
POST/app-connections/:id/testINTEGRATIONS
POST/eventsWRITE_EVENTS; requires Idempotency-Key
POST/events/testWRITE_EVENTS; stores nothing
GET/eventsREAD_CAMPAIGNS
GET/events/schemaREAD_ANALYTICS
GET/events/:idREAD_CAMPAIGNS
POST/identities/linkWRITE_EVENTS

An app connection is the base URL and signing secret an app_action step calls. POST /app-connections/:id/test sends a signed app.ping and answers 200 with a checklist (dns, tls, connection, authentication, status, latency_ms, error) whether or not the app accepted it. POST /events records a product event for a contact or, with an identity object, for a person who may not be a contact yet, resumes every wait_for_event step open for it, and evaluates goals; the 202 names each resumed step under matched_runs.

Goals, identities, and acquisition reports

MethodPathScope
GET/goals, /goals/:idREAD_ANALYTICS
POST/goalsMANAGE_ACQUISITION
PATCH, DELETE/goals/:idMANAGE_ACQUISITION
POST/goals/preview, /goals/:id/replays/previewREAD_ANALYTICS; write nothing
POST/goals/:id/replaysMANAGE_ACQUISITION
GET/goals/:id/replays, /goals/:id/replays/:replayIdREAD_ANALYTICS
GET/people, /people/:idREAD_CONTACTS
DELETE/people/:idMANAGE_ACQUISITION
GET/identity-conflictsREAD_CONTACTS
POST/identity-conflicts/:id/resolveMANAGE_ACQUISITION
GET/acquisition/summary, /acquisition/breakdownREAD_ANALYTICS
POST/acquisition/event-trends, /acquisition/funnelsREAD_ANALYTICS; write nothing
GET/acquisition/prospects, /acquisition/prospects/:contactId/timelineREAD_ANALYTICS and READ_CONTACTS
PUT, DELETE/acquisition/campaign-spend/:campaignIdMANAGE_ACQUISITION
GET/acquisition/attribution/settings, /acquisition/attribution/summary, /acquisition/attribution/breakdownREAD_ANALYTICS
PUT/acquisition/attribution/settingsMANAGE_ACQUISITION
GET/acquisition/attributions, /acquisition/attributions/:personId, /acquisition/prospects/:contactId/attributionREAD_ANALYTICS and READ_CONTACTS
PUT, DELETE/acquisition/attributions/:personId/correctionMANAGE_ACQUISITION

JWT callers need view_analytics for reads, view_contacts as well for prospects and people, and manage_settings for anything that changes goals, identities, spend, or attribution. Attribution settings and correction writes are replacement operations, naturally safe to retry. PUT spend and POST /identities/link land on the same state when repeated, so they need no Idempotency-Key; the other writes accept one. Details are in Goals and acquisition.

Lead database

MethodPathScope
POST/lead-database/people, /lead-database/companies, /lead-database/coverage, /lead-database/facets, /lead-database/exportREAD_CONTACTS

JWT callers need view_contacts. These are read-only POSTs, so conditions travel as JSON and retries are naturally safe. Details are in Lead database.

Outbound insights

MethodPathScope
GET/analytics/insights/overview, /analytics/insights/breakdown, /analytics/insights/infrastructureREAD_ANALYTICS

JWT callers need view_analytics. All three are read-only. Details are in Analytics and audit.

Mailboxes

MethodPathScope
GET/emailsREAD_EMAILS
POST/emailsWRITE_EMAILS (SMTP/IMAP only)
GET/emails/:idREAD_EMAILS
PATCH, DELETE/emails/:idWRITE_EMAILS
POST/emails/:id/testWRITE_EMAILS
GET/emails/:id/warmupREAD_EMAILS
POST/emails/:id/warmup/start, /pause, /resume, /stopWRITE_EMAILS
POST/emails/onboarding/oauth/cli-startWRITE_EMAILS
GET/emails/onboarding/oauth/session/:sessionWRITE_EMAILS

POST /emails connects an SMTP/IMAP mailbox after a worker has validated the credential; Google and Microsoft mailboxes go through cli-start, which returns a consent URL and a session to poll. POST /emails/:id/test dials the stored credential again and answers 200 with a checklist even when it fails. GET /emails/:id carries the campaigns the mailbox sends for and its recent errors beside the mailbox itself.

Managed mailboxes

MethodPathScope
GET/mailboxes/provision/providersREAD_EMAILS
GET/domainsREAD_EMAILS
POST/domains, /domains/:name/verify, /domains/:name/connect, /domains/:name/connection/verifyMANAGE_MAILBOX_PROVISIONING
POST/domains/quoteMANAGE_MAILBOX_PROVISIONING
POST/domains/orderMANAGE_MAILBOX_PROVISIONING; requires Idempotency-Key
GET/mailboxes/domains/renewalsREAD_EMAILS
POST/mailboxes/provision/quoteMANAGE_MAILBOX_PROVISIONING
POST/mailboxes/provisionMANAGE_MAILBOX_PROVISIONING; requires Idempotency-Key
GET/mailbox-provisioning, /mailbox-provisioning/:idREAD_EMAILS
POST/mailbox-provisioning/:id/cancelMANAGE_MAILBOX_PROVISIONING

renewals lists every managed domain's renewal cycle: when it renews, what was charged, and whether anything needs a person. Renewal is automatic and priced at the provider's current renewal cost plus processing, read fresh each cycle.

providers lists the live Google and Microsoft mailbox prices and the Azure tenant price. A zero or missing Google or Microsoft cost uses $3 per mailbox per month. Failed pricing calls return 503 pricing_unavailable.

A quote prices an order from the upstream provider's live costs and checks every domain; it is valid for fifteen minutes, after which those prices are no longer guaranteed. Pass domain for one domain, or domains for several: the mailboxes are spread across them as evenly as possible, and each domain is priced on its own because registration cost varies by TLD. Provisioning turns a quote into an order once the caller may spend: a session directly, a credential through an agent policy that grants mailbox.provision with a max_monthly_mailbox_spend_cents the order fits under, or a human approval (the 202 awaiting_approval shape, re-submitted with the same quote_id). The order then waits on a Stripe checkout link (awaiting_payment) and moves through paid, queued, provisioning, configuring and ready, or provisioning_failed when a paid provider purchase needs support. The order shows planned mailbox addresses before adoption. Domain-only registration uses payment Checkout; recurring mailbox and Azure orders use subscription Checkout. Managed purchases are offered on SendSets Cloud only; a self-hosted instance answers 503 mailbox_provisioning_unavailable.

Campaign checks

MethodPathScope
POST/campaigns/:id/validateREAD_CAMPAIGNS
POST/campaigns/:id/preflightSEND_CAMPAIGNS
POST/campaigns/:id/testSEND_CAMPAIGNS (sends real mail)
GET/campaigns/:id/test-runs, /campaigns/:id/test-runs/:runIdREAD_CAMPAIGNS
POST/campaigns/:id/test-runs/:runId/cancelSEND_CAMPAIGNS
GET/system/readinessany credential

validate is the read-only twin of preflight: the same checks plus the structural checks on the step graph, answered as problems with a fix each, without storing a report. /system/readiness is what sendsets doctor reads: the workflow sidecar, live workers, the scheduler, and whether inbound events are accepted.

Other supported surfaces

The default contract also mounts authentication, campaigns and sequences, contacts/leads and segments, mailboxes and warmup, inbox, analytics and deliverability, suppressions, realtime/audit events, API keys/OAuth, and webhooks. The generated OpenAPI document is the field-level reference.

CRM, forms, automations, advisor, templates, integrations, and internal AI engines remain compiled for later reuse but return 404 by default. Operators can mount them explicitly with SENDSETS_ENABLE_EXTENDED_SURFACES=true; they are outside this supported contract.

Agent protocols

MethodPathCaller
POST/mcpAPI key or OAuth; each tool gated by its own scope. See MCP server
POST/a2aAPI key or OAuth; each tool gated by its own scope. See A2A agent
GET/.well-known/agent-card.jsonPublic, unversioned. The A2A Agent Card

Customer Stripe recording preview

POST /api/v1/integrations/inbound/stripe/:orgID/:mode is a public provider webhook, authenticated by Stripe-Signature over the raw request body, with mode equal to test or live. It requires an existing Stripe connection configured through the extended integrations surface. The endpoint accepts snapshot events from the account itself, rejects Connect events and mismatched modes, and caps bodies at 1 MiB. It does not accept SendSets API keys as webhook authentication.

A 200 response contains recorded: true and duplicate: true|false only after a durable encrypted receipt is committed. Stripe's event ID provides delivery idempotency; Idempotency-Key is not used. Conflicting reuse returns 409; invalid signatures, modes, or envelopes return 400; unavailable connections return 404; encryption or persistence failures return 500 and should be retried. This preview does not yet normalize payments or change acquisition reports. Setup reuses the existing integration routes, with Manage settings for JWT callers, the Integrations permission for API keys, and the paid-plan gate for connect. See Stripe setup.

B2B database

The shared prospect catalog is separate from the workspace lead database.

MethodPathPermission
GET/b2b-database/metadataREAD_CONTACTS
POST/b2b-database/people, /b2b-database/companiesREAD_CONTACTS
POST/b2b-database/importREAD_CONTACTS and WRITE_CONTACTS

Search filters, cursor pagination, imports, and errors.

On this page