Skip to content

Endpoint reference

284 endpoints in API version 0.1.0, generated fromapi/openapi.yaml. Your instance serves the exact contract it runs at /v1/openapi.yaml. Download this copy to generate a client or import it into an API tool.

Endpoints marked Public need no credentials. All others take an API token (Authorization: Bearer oft_...) or a session cookie; see Authentication.{ws} is a workspace ID.

System and discovery

get/healthz

Liveness check
Public
Responses: 200 The process is up

get/readyz

Readiness check, including the database
Public
Responses: 200 Ready to serve traffic, 503 A dependency is unavailable

get/v1/meta

Instance version and capabilities
Public
Responses: 200 Instance metadata

get/v1/openapi.yaml

This document
Public
Responses: 200 The OpenAPI document

get/.well-known/outflare.json

Machine-readable discovery for agents and tools
Public
Responses: 200 Discovery document

Setup, auth, workspaces and access

get/v1/setup

Whether first-run setup is still needed
Public
Responses: 200 Setup status

post/v1/setup

Create the first user and workspace
Public

Only works while the instance has no users. Signs the new owner in.

ParameterInTypeDescription
email *bodystring, email
name *bodystring
password *bodystring
workspace_name *bodystring
telemetrybodybooleanfalse turns off anonymous usage stats and update checks from the start. Default true.
Responses: 201 Created; a session cookie is set, 409 Error

post/v1/auth/login

Sign in with email and password
Public

Sets a session cookie. Ten failed attempts for one email lock it for 15 minutes.

ParameterInTypeDescription
email *bodystring
password *bodystring
Responses: 200 Signed in, 401 Error, 429 Error

post/v1/auth/logout

End the current session
Signed in
Responses: 204 Signed out

post/v1/auth/password-reset

Email a password reset link
Public

Sends a one-time link (valid an hour) to <PUBLIC_URL>/reset-password#<token>, through the account email service (see account_email in /v1/meta). The answer is the same whether or not the email has an account. Ten requests for one email, or from one network, in 15 minutes are refused.

ParameterInTypeDescription
email *bodystring
Responses: 202 Sent, 400 Error, 429 Error

post/v1/auth/password-reset/confirm

Choose a new password with a reset link
Public

Sets the password, signs out every session, signs this browser in, and counts the email as confirmed.

ParameterInTypeDescription
token *bodystringThe part of the link after #.
password *bodystring
Responses: 200 Password changed and signed in, 400 Error, 404 Error

post/v1/auth/verify-email

Confirm an email address with its link
Public

The link is <PUBLIC_URL>/verify-email#<token>, valid 48 hours, sent when an account is made with a password.

ParameterInTypeDescription
token *bodystring
Responses: 200 Confirmed, 404 Error

post/v1/me/email-verification

Send the confirmation link again
Signed in

Replaces any earlier link. Answers 200 without sending when the address is already confirmed.

Responses: 200 Already confirmed, 202 Sent, 429 Error, 502 Error

post/v1/auth/passkey/options

Start signing in with a passkey
Public

Returns WebAuthn request options (publicKey) for navigator.credentials.get, with no allowed credentials so the browser offers whichever passkeys it holds for this site. Sets a short-lived outflare_passkey cookie holding the challenge. Passkeys are on when PUBLIC_URL is https (or PASSKEYS=on); passkey is then in login_methods.

Responses: 200 Options for the browser, 404 Error

post/v1/auth/passkey

Finish signing in with a passkey
Public

The body is the browser's assertion (PublicKeyCredential.toJSON()). Sets a session cookie.

Responses: 200 Signed in, 400 Error, 401 Error, 404 Error, 429 Error

get/v1/me/passkeys

Your passkeys
Signed in
Responses: 200 Passkeys that sign you in, 404 Error

post/v1/me/passkeys

Save a new passkey
Signed in

The body is the browser's new credential (PublicKeyCredential.toJSON()), answering /v1/me/passkeys/options.

ParameterInTypeDescription
namequerystringA label, such as "MacBook". Default Passkey.
Responses: 201 Saved, 400 Error

post/v1/me/passkeys/options

Start adding a passkey
Signed in

Returns WebAuthn creation options (publicKey) for navigator.credentials.create, and sets the outflare_passkey challenge cookie. Up to 10 passkeys.

Responses: 200 Options for the browser, 404 Error, 409 Error

patch/v1/me/passkeys/{id}

Rename a passkey
Signed in
ParameterInTypeDescription
id *pathstring, uuid
name *bodystring
Responses: 200 Renamed, 404 Error

delete/v1/me/passkeys/{id}

Remove a passkey
Signed in
ParameterInTypeDescription
id *pathstring, uuid
Responses: 204 Removed. It no longer signs you in, 404 Error

get/v1/auth/google/start

Start "Continue with Google"
Public

Redirects to Google's sign-in screen (scopes openid, email, profile only). Open it in the browser, not with fetch. Needs GOOGLE_OAUTH_CLIENT_ID/SECRET, with <PUBLIC_URL>/v1/auth/google/callback added to the client's redirect URIs. Google then returns to the callback, which signs the person in to the account linked to that Google account, or the account with the same verified email. Nobody gets an account from Google alone: an unknown person is sent back to return with identity (valid 15 minutes), identity_email and identity_name, and the invite page redeems identity instead of a password. Errors come back as login_error.

ParameterInTypeDescription
returnquerystringPath on this site to come back to, such as /invite#<token>. Default /.
Responses: 302 To Google, 404 Error

get/v1/auth/google/callback

Google's return from "Continue with Google"
Public

Called by Google, not by clients. Redirects to the app, or back to return with identity or login_error.

Responses: 302 Back to the app

post/v1/invites/accept

Accept a workspace invite
Public

A new email creates an account (name and password required). An existing account must give its password, or be signed in already. Instead of a password, identity from "Continue with Google" proves the invited email; a new account made that way has no password and signs in with Google.

ParameterInTypeDescription
token *bodystring
namebodystring
passwordbodystring
identitybodystringThe identity parameter from the Google sign-in callback.
Responses: 200 Joined; a session cookie is set, 401 Error, 404 Error

get/v1/me

The signed-in user and their workspaces
Signed in
Responses: 200 Current user

post/v1/workspaces

Create a workspace owned by the signed-in user
Signed in
ParameterInTypeDescription
name *bodystring
Responses: 201 Created

get/v1/workspaces/{ws}

Get a workspace
Scope: workspace.read
ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 The workspace, 404 Error

get/v1/workspaces/{ws}/members

List members
Scope: members.read
ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 Members, oldest first

patch/v1/workspaces/{ws}/members/{user}

Change a member's role
Scope: members.manage.

Nobody can act above their own role; only owners can grant or change owner; the last owner cannot be demoted.

ParameterInTypeDescription
ws *pathstring, uuid
user *pathstring, uuid
role *body"owner" | "admin" | "operator" | "viewer"
Responses: 200 Updated, 403 Error, 409 Error

delete/v1/workspaces/{ws}/members/{user}

Remove a member
Scope: members.manage
ParameterInTypeDescription
ws *pathstring, uuid
user *pathstring, uuid
Responses: 204 Removed, 409 Error

get/v1/workspaces/{ws}/invites

List pending invites
Scope: members.manage
ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 Pending invites, newest first

post/v1/workspaces/{ws}/invites

Invite someone by email
Scope: members.manage.

The token and link are returned once; they expire after 7 days.

ParameterInTypeDescription
ws *pathstring, uuid
email *bodystring, email
role *body"admin" | "operator" | "viewer"
Responses: 201 Created

delete/v1/workspaces/{ws}/invites/{id}

Revoke a pending invite
Scope: members.manage
ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 204 Revoked

get/v1/workspaces/{ws}/api-tokens

List active API tokens
Scope: tokens.manage.

Secrets are never returned after creation.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 Tokens, newest first

post/v1/workspaces/{ws}/api-tokens

Create a scoped API token
Scope: tokens.manage.

A token cannot hold scopes or looser limits than its creator. The secret is returned once.

ParameterInTypeDescription
ws *pathstring, uuid
name *bodystring
scopes *body"workspace.read" | "members.read" | "members.manage" | "tokens.manage" | "audit.read" | "mailboxes.read" | "mailboxes.manage" | "leads.read" | "leads.manage" | "suppressions.manage" | "sequences.read" | "sequences.manage" | "inbox.read" | "inbox.manage" | "integrations.read" | "integrations.manage" | "outcomes.read" | "outcomes.manage" | "projects.read" | "projects.manage" | "infrastructure.read" | "infrastructure.manage" | "infrastructure.approve" | "analytics.read" | "missions.read" | "missions.run" | "missions.approve"[]
expires_in_daysbodyinteger
daily_send_budgetbodyinteger
max_prioritybody"low" | "normal" | "high"
may_take_mailboxesbodyboolean
default_team_idbodystring, uuid
project_idbodystring, uuidBinds the token to one project: it sees and changes only that project's records, and endpoints that are not project-aware refuse it. Fixed for the token's life.
Responses: 201 Created

patch/v1/workspaces/{ws}/api-tokens/{id}

Change a token's sending and allocation limits
Scope: tokens.manage.

A token cannot change its own limits, or grant looser ones than it has. Scopes are fixed; issue a new token to change them.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
daily_send_budgetbodyinteger
max_prioritybody"low" | "normal" | "high"
may_take_mailboxesbodyboolean
default_team_idbodystring, uuid
Responses: 200 The token, 400 Error, 403 Error

delete/v1/workspaces/{ws}/api-tokens/{id}

Revoke an API token
Scope: tokens.manage
ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 204 Revoked

get/v1/workspaces/{ws}/audit-events

Audit log, newest first
Scope: audit.read.

Page with before=next_before.

ParameterInTypeDescription
ws *pathstring, uuid
limitqueryinteger
beforequeryinteger
Responses: 200 Events

get/v1/workspaces/{ws}/stats

Workspace overview
Scope: workspace.read.

Mailbox health, sending and reply counts, in one call. Windows are rolling (last 24 hours, last 7 days).

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 Counts

instance

get/v1/instance/telemetry

Usage stats and update check for this server
Signed in

Self-hosted servers send one anonymous report a day (version, platform, usage counts as ranges, connector ids) and get back the newest version and any security notices. See docs/telemetry.md. Needs a signed-in user.

Responses: 200 Telemetry and update status, 403 Error

patch/v1/instance/telemetry

Turn usage stats on or off
Signed in

Only the person who set up the server can change it. Refused with 409 when the server's environment (OUTFLARE_TELEMETRY, DO_NOT_TRACK) or the build decides.

ParameterInTypeDescription
enabled *bodyboolean
Responses: 200 Updated status, 400 Error, 403 Error, 409 Error

get/v1/instance/telemetry/preview

The exact report this server would send
Signed in
Responses: 200 The report, not sent, 403 Error

agencies

get/v1/agencies

Agencies the signed-in user belongs to
Signed in
Responses: 200 Agencies

post/v1/agencies

Create an agency
Signed in

Signed-in users only. The creator becomes the agency's owner. Pass workspace_ids to bring workspaces you own in as clients.

ParameterInTypeDescription
name *bodystring
workspace_idsbodystring, uuid[]
Responses: 201 Created, 400 Error

get/v1/agencies/{ag}

An agency
Signed in

Agency members, or the agency's own tokens.

ParameterInTypeDescription
ag *pathstring, uuid
Responses: 200 The agency, 404 Error

patch/v1/agencies/{ag}

Rename the agency or change its white label
Signed in

Agency admins. Client members see the brand name, logo and accent colour in place of Outflare.

ParameterInTypeDescription
ag *pathstring, uuid
namebodystring
brand_namebodystring
logo_urlbodystringAn https URL
accent_colorbodystring
support_emailbodystring
Responses: 200 The agency, 400 Error

get/v1/agencies/{ag}/clients

Every client with this week's numbers
Signed in

Agency members or agency tokens. Worst bounce rate first.

ParameterInTypeDescription
ag *pathstring, uuid
Responses: 200 Clients

post/v1/agencies/{ag}/clients

Create a client workspace, or bring in one you own
Signed in

Send name to create a new client workspace (agency admins, or agency tokens with can_create_clients). It has no direct members: agency people reach it through their agency role. Send workspace_id to bring in a workspace you own directly.

ParameterInTypeDescription
ag *pathstring, uuid
namebodystring
workspace_idbodystring, uuid
Responses: 201 The client workspace, 400 Error, 403 Error

delete/v1/agencies/{ag}/clients/{ws}

Take a workspace out of the agency
Signed in

Agency owners. The workspace and its data stay. If nobody owns it directly, you become its owner.

ParameterInTypeDescription
ag *pathstring, uuid
ws *pathstring, uuid
Responses: 204 Removed, 404 Error

get/v1/agencies/{ag}/members

Agency members
Signed in
ParameterInTypeDescription
ag *pathstring, uuid
Responses: 200 Members

post/v1/agencies/{ag}/members

Add someone who already has an account
Signed in

Agency admins. They get this role in every client workspace. Nobody can give a role above their own.

ParameterInTypeDescription
ag *pathstring, uuid
email *bodystring
role *body"owner" | "admin" | "operator" | "viewer"
Responses: 200 Members, 404 Error

patch/v1/agencies/{ag}/members/{user}

Change an agency member's role
Signed in
ParameterInTypeDescription
ag *pathstring, uuid
user *pathstring, uuid
role *body"owner" | "admin" | "operator" | "viewer"
Responses: 200 Members

delete/v1/agencies/{ag}/members/{user}

Remove someone from the agency
Signed in

The last owner can't be removed.

ParameterInTypeDescription
ag *pathstring, uuid
user *pathstring, uuid
Responses: 204 Removed

get/v1/agencies/{ag}/api-tokens

Active agency tokens
Signed in
ParameterInTypeDescription
ag *pathstring, uuid
Responses: 200 Tokens

post/v1/agencies/{ag}/api-tokens

Create a token that works in every client workspace
Signed in

Agency admins. Scopes apply in each client and can't exceed your agency role. The secret is returned once.

ParameterInTypeDescription
ag *pathstring, uuid
name *bodystring
scopes *body"workspace.read" | "members.read" | "members.manage" | "tokens.manage" | "audit.read" | "mailboxes.read" | "mailboxes.manage" | "leads.read" | "leads.manage" | "suppressions.manage" | "sequences.read" | "sequences.manage" | "inbox.read" | "inbox.manage" | "integrations.read" | "integrations.manage" | "outcomes.read" | "outcomes.manage" | "projects.read" | "projects.manage" | "infrastructure.read" | "infrastructure.manage" | "infrastructure.approve" | "analytics.read" | "missions.read" | "missions.run" | "missions.approve"[]
can_create_clientsbodyboolean
expires_in_daysbodyinteger
Responses: 201 Created

delete/v1/agencies/{ag}/api-tokens/{id}

Revoke an agency token
Signed in
ParameterInTypeDescription
ag *pathstring, uuid
id *pathstring, uuid
Responses: 204 Revoked

get/v1/agencies/{ag}/events

Agency log
Signed in

Agency admins. Clients, members, tokens and branding changes, newest first (last 200).

ParameterInTypeDescription
ag *pathstring, uuid
Responses: 200 Events

Mailboxes

get/v1/workspaces/{ws}/mailboxes

List mailboxes
Scope: mailboxes.read.

Credentials are never returned.

ParameterInTypeDescription
ws *pathstring, uuid
statusquery"verifying" | "active" | "paused" | "error"
stagequery"warming" | "ready" | "resting" | "retired"
pool_idquerystring, uuid
domainquerystring
server_idquerystring, uuid
project_idquerystringA project's id, or none for records shared by the workspace. A project token always sees only its project.
Responses: 200 Mailboxes, ordered by email

post/v1/workspaces/{ws}/mailboxes

Add or update one mailbox
Scope: mailboxes.manage.

Upserts by email. Google Workspace and Microsoft 365 are detected from MX records; other providers need the host and port fields. The mailbox starts as verifying while a job signs in over SMTP and IMAP.

ParameterInTypeDescription
ws *pathstring, uuid
email *bodystring
display_namebodystring
providerbody"google" | "microsoft" | "other"
passwordbodystringApp password used for both SMTP and IMAP.
smtp_hostbodystring
smtp_portbodyinteger
smtp_usernamebodystring
smtp_passwordbodystring
imap_hostbodystring
imap_portbodyinteger
imap_usernamebodystring
imap_passwordbodystring
poolbodystringPool name. Created if missing.
daily_limitbodyinteger
Responses: 200 Updated an existing mailbox, 201 Added, 400 Error

post/v1/workspaces/{ws}/mailboxes/import

Bulk import mailboxes from CSV
Scope: mailboxes.manage. · Body: text/csv

Send the CSV file as the body (up to 5 MB and 5000 rows). Required columns: email and password (or smtp_password). Optional: name (or first_name and last_name), provider, smtp_host, smtp_port, smtp_username, smtp_password, imap_host, imap_port, imap_username, imap_password, pool and daily_limit. Common reseller and sequencer headers such as Email Address and App Password are recognised. Rows are upserted by email; bad rows are reported by line and the rest still import.

ParameterInTypeDescription
ws *pathstring, uuid
poolquerystringPool name for rows that do not name one. Created if missing.
Responses: 200 Import summary, 400 Error, 413 Error, 415 Error

get/v1/workspaces/{ws}/mailboxes/{id}

Get a mailbox
Scope: mailboxes.read
ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 The mailbox, 404 Error

patch/v1/workspaces/{ws}/mailboxes/{id}

Change a mailbox's name, limits, pool, or pause and resume it
Scope: mailboxes.manage.

Set status to paused to stop sending, or to active to resume; resuming re-verifies the connection first. Set pool_id to null to remove the mailbox from its pool.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
display_namebodystring
daily_limitbodyinteger
min_gap_secondsbodyinteger
pool_idbodystring, uuid
statusbody"paused" | "active"
warmupbodyobjectUnset fields are left as they are.
Responses: 200 The updated mailbox, 400 Error, 404 Error

delete/v1/workspaces/{ws}/mailboxes/{id}

Remove a mailbox and its stored credentials
Scope: mailboxes.manage
ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 204 Removed, 404 Error

post/v1/workspaces/{ws}/mailboxes/{id}/verify

Re-check a mailbox's SMTP and IMAP sign-in and its domain's DNS
Scope: mailboxes.manage.

Runs in the background; poll the mailbox for the result.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 202 Queued, 404 Error

get/v1/workspaces/{ws}/mailboxes/{id}/warmup

A mailbox's warm-up results for each of the last 14 days
Scope: mailboxes.read.

Days are UTC, oldest first.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 Daily results, 404 Error

post/v1/workspaces/{ws}/mailboxes/warmup

Turn warm-up on or off, or change its settings, for many mailboxes
Scope: mailboxes.manage.

Applies to the mailboxes in mailbox_ids, or to every mailbox in pool_id, or to every mailbox in the workspace when neither is given. Warming mailboxes email each other, so turn it on for at least two, ideally on different domains. Turning warm-up on starts the ramp from its first day: 2 emails a day, then 2 more each day up to daily_target.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 How many mailboxes changed, 400 Error

get/v1/workspaces/{ws}/mailbox-pools

List mailbox pools
Scope: mailboxes.read
ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 Pools

post/v1/workspaces/{ws}/mailbox-pools

Create a mailbox pool
Scope: mailboxes.manage
ParameterInTypeDescription
ws *pathstring, uuid
name *bodystring
Responses: 201 Created, 409 Error

delete/v1/workspaces/{ws}/mailbox-pools/{id}

Delete a pool. Its mailboxes stay, without a pool.
Scope: mailboxes.manage
ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 204 Deleted, 404 Error

get/v1/workspaces/{ws}/oauth-connections

List admin OAuth connections
Scope: mailboxes.read.

Secrets are never returned.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 Connections

post/v1/workspaces/{ws}/oauth-connections

Add a Google service account or a Microsoft Entra app
Scope: mailboxes.manage.

Admin-level access to every mailbox in an organisation, so mailboxes can be added without passwords. - Google: a service account key file with domain-wide delegation for the scope https://mail.google.com/. - Microsoft: an Entra app with the Exchange Online application permissions IMAP.AccessAsApp and SMTP.SendAsApp. The secret is checked by getting a token before it is saved.

ParameterInTypeDescription
ws *pathstring, uuid
provider *body"google" | "microsoft"
namebodystringDefaults to the service account's email or the tenant id.
service_accountbodyGoogle: the key file, as a JSON object or as its text.
tenant_idbodystringMicrosoft: directory (tenant) id.
client_idbodystringMicrosoft: application (client) id.
client_secretbodystringMicrosoft: client secret value.
Responses: 201 Created, 400 Error

delete/v1/workspaces/{ws}/oauth-connections/{id}

Remove an OAuth connection
Scope: mailboxes.manage.

Refused with 409 while mailboxes use it.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 204 Removed, 404 Error, 409 Error

post/v1/workspaces/{ws}/oauth-connections/{id}/mailboxes

Add mailboxes that sign in through a connection
Scope: mailboxes.manage.

Up to 5000 at once. No passwords: each mailbox signs in to SMTP and IMAP with XOAUTH2 using a token from the connection. Mailboxes that already exist are switched to the connection. Each is verified in the background.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
mailboxes *bodyobject[]
poolbodystringPool name. Created if missing.
daily_limitbodyinteger
Responses: 200 Result. Errors refer to positions in the list, from 1, 400 Error, 404 Error

get/v1/workspaces/{ws}/oauth/{provider}/authorize

Connect one mailbox through the provider's consent screen
Scope: mailboxes.manage

, signed-in users only (not API tokens). Open this in the browser: it redirects to Google or Microsoft, and after approval back to /v1/oauth/callback, which adds the mailbox and redirects to the web app with ?connected=<email> or ?oauth_error=<reason>. Needs the server's OAuth app to be configured; see oauth_providers in /v1/meta.

ParameterInTypeDescription
ws *pathstring, uuid
provider *path"google" | "microsoft"
poolquerystringPool for the connected mailbox.
Responses: 302 Redirect to the consent screen, 400 Error, 403 Error

get/v1/oauth/callback

Where the provider returns after the consent screen
Public

Not called directly. Checks the signed state and that the same user is signed in.

ParameterInTypeDescription
state *querystring
codequerystring
errorquerystring
Responses: 302 Redirect to the web app with the outcome

get/v1/workspaces/{ws}/integrations/{id}/mailboxes

List the mailboxes held at a supplier
Scope: mailboxes.read.

Every mailbox the connected supplier account holds, imported or not, as of the last sync (every 15 minutes, and at once on the supplier's webhooks). The first call for an integration connected before syncing existed starts its first sync; synced is false until it finishes.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuidA mailbox supplier integration, such as Cheap Inboxes.
import_statequery"available" | "queued" | "waiting" | "app_password" | "imported" | "failed"
statusquery"provisioning" | "active" | "cancelling" | "cancelled" | "failed"Status at the supplier.
qquerystringPart of the email address.
limitqueryinteger
offsetqueryinteger
Responses: 200 Mailboxes and counts, 404 Error, 422 Error

post/v1/workspaces/{ws}/integrations/{id}/mailboxes/sync

Refresh the supplier's mailboxes now
Scope: mailboxes.manage.

Queues a full sync. Mailboxes the supplier cancelled are paused in Outflare.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 202 Queued, 404 Error

post/v1/workspaces/{ws}/integrations/{id}/mailboxes/import

Import mailboxes from a supplier
Scope: mailboxes.manage.

Reads each mailbox's password from the supplier and adds it like a CSV import, with SMTP, IMAP and DNS checks in the background. Google mailboxes first get an app password from the supplier (Google refuses plain passwords), at the pace the supplier allows: Cheap Inboxes makes 20 an hour, so a large Google import takes hours; follow import_state in the list. Mailboxes still being set up wait and import when they turn active. Listed mailboxes are imported again even if they were before, which refreshes their passwords.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
allbodybooleanEvery mailbox not imported yet.
external_idsbodystring[]
emailsbodystring[]
Responses: 202 Queued, 400 Error, 404 Error

get/v1/workspaces/{ws}/emailbison-compat/calls

List calls made to the EmailBison-compatible API
Scope: mailboxes.read.

Outflare answers a subset of the EmailBison API at /emailbison/{ws}/api/... so a mailbox supplier that pushes to a self-hosted EmailBison (Cheap Inboxes does) can push into Outflare. Authenticate with an Outflare API token as the EmailBison API key; a write needs mailboxes.manage. Supported: GET users, GET sender-emails (page, per_page, search), GET sender-emails/{id or email}, POST sender-emails/imap-smtp, POST sender-emails/bulk, PATCH sender-emails/{id}, DELETE sender-emails/{id} (pauses the mailbox), PATCH warmup/sender-emails/enable|disable. Every call is logged here, newest first, with field names but never values, so any endpoint a client needs that Outflare does not answer yet shows up as supported: false. The latest 1,000 per workspace are kept.

ParameterInTypeDescription
ws *pathstring, uuid
limitqueryinteger
Responses: 200 Calls, newest first, 401 Error, 403 Error

get/v1/workspaces/{ws}/integrations/{id}/mailbox-sync

How a supplier's mailboxes are synced
Scope: mailboxes.read.
ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 Settings, 404 Error

patch/v1/workspaces/{ws}/integrations/{id}/mailbox-sync

Change how a supplier's mailboxes are synced
Scope: mailboxes.manage.

Turning on auto_import queues every active mailbox not imported yet, and imports new ones as they appear.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
auto_importbodyboolean
app_passwordsbodybooleanAsk the supplier for Google app passwords before importing Google mailboxes. On by default.
daily_limitbodyintegerDaily cap for imported mailboxes. Null uses the supplier's cap.
Responses: 200 Settings, 400 Error, 404 Error

post/v1/integrations/{id}/webhook

Where a supplier posts its events
Public

Not called directly. Registered at the supplier when the engine's PUBLIC_URL is https. The body's HMAC-SHA256 signature, keyed with the secret the supplier issued, is checked; repeated deliveries are ignored.

ParameterInTypeDescription
id *pathstring, uuid
Responses: 200 Accepted, 401 Error, 404 Error

get/v1/workspaces/{ws}/mailbox-health

How every sending mailbox is doing
Scope: mailboxes.read.

Graded from every signal: sign-in and inbox sync errors (explained, e.g. suspended account or revoked consent), send failures, bounce rate, warm-up inbox placement, the domain's SPF, DKIM, DMARC and blocklists, and new mailboxes not warming up. Each signal says what to do. Worst first. Computed on read.

ParameterInTypeDescription
ws *pathstring, uuid
levelquery"ok" | "warning" | "critical" | "paused"
mailbox_idquerystringOne or more ids
Responses: 200 Health by mailbox, 400 Error

get/v1/workspaces/{ws}/mailbox-health/settings

Automatic pausing settings
Scope: mailboxes.read.
ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 The settings

put/v1/workspaces/{ws}/mailbox-health/settings

Change automatic pausing
Scope: mailboxes.manage.

Every field is optional.

ParameterInTypeDescription
ws *pathstring, uuid
auto_pausebodyboolean
pause_bounce_ratebodynumber
min_sendsbodyinteger
Responses: 200 The settings, 400 Error

Sending domains

get/v1/workspaces/{ws}/sending-domains

List sending domains with health, settings and sending stats
Scope: mailboxes.read.

Every domain your mailboxes send from, plus domains added by hand. Each carries its DNS (MX, SPF, DKIM, DMARC) and blocklist results, the issues to fix, its settings, mailbox counts and sends, replies and bounces over rolling windows.

ParameterInTypeDescription
ws *pathstring, uuid
project_idquerystringA project's id, or none for records shared by the workspace. A project token always sees only its project.
Responses: 200 Domains, sorted by name

post/v1/workspaces/{ws}/sending-domains

Add a domain to track before it has mailboxes
Scope: mailboxes.manage.

Accepts acme.com, @acme.com, a URL or an address on the domain. Its DNS check runs in the background; poll the domain until checked_at is set. Domains of connected mailboxes are added on their own.

ParameterInTypeDescription
ws *pathstring, uuid
domain *bodystring
Responses: 201 Added, 400 Error, 409 Error

post/v1/workspaces/{ws}/sending-domains/check

Re-check DNS and blocklists for every domain
Scope: mailboxes.manage.

Runs in the background. Every domain is also re-checked on its own once a day.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 202 Queued

get/v1/workspaces/{ws}/sending-domains/{domain}

Get a domain, with daily sends, replies and bounces for 14 days
Scope: mailboxes.read.

List the domain's mailboxes with GET /mailboxes?domain=.

ParameterInTypeDescription
ws *pathstring, uuid
domain *pathstring
Responses: 200 The domain, 404 Error

patch/v1/workspaces/{ws}/sending-domains/{domain}

Pause or resume a domain, cap its daily sends, or set limits on all its mailboxes
Scope: mailboxes.manage.

status: paused stops sequence and test sends from every mailbox on the domain (replies written in the inbox still go); active resumes. daily_limit caps sends from all of the domain's mailboxes together in any 24 hours; null removes the cap. mailbox_daily_limit and mailbox_min_gap_seconds set those settings on every mailbox on the domain at once.

ParameterInTypeDescription
ws *pathstring, uuid
domain *pathstring
statusbody"active" | "paused"
daily_limitbodyinteger
notesbodystring
mailbox_daily_limitbodyinteger
mailbox_min_gap_secondsbodyinteger
Responses: 200 The updated domain, 400 Error, 404 Error

delete/v1/workspaces/{ws}/sending-domains/{domain}

Stop tracking a domain that has no mailboxes
Scope: mailboxes.manage.

Returns 409 while mailboxes on the domain are connected.

ParameterInTypeDescription
ws *pathstring, uuid
domain *pathstring
Responses: 204 Removed, 404 Error, 409 Error

post/v1/workspaces/{ws}/sending-domains/{domain}/check

Re-check a domain's DNS and blocklists now
Scope: mailboxes.manage.

Runs in the background; poll the domain for a new checked_at. Use it after changing DNS records.

ParameterInTypeDescription
ws *pathstring, uuid
domain *pathstring
Responses: 202 Queued, 404 Error

servers

get/v1/workspaces/{ws}/sending-servers

List sending servers with IPs, blocklist results, settings and sending stats
Scope: mailboxes.read.

A sending server is the SMTP host a group of mailboxes signs in to, and the IP addresses they share: a cold-email SMTP vendor's server (Mailreef, Infraforge) or your own mail server. Mailboxes outside Google and Microsoft join one on their own when they are connected, grouped by smtp_host. Each server carries its IPs, their blocklist results, the issues to fix, a shared daily limit and pause, mailbox counts and sends over rolling windows.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 Servers, sorted by host

post/v1/workspaces/{ws}/sending-servers/check

Re-check IPs and blocklists for every server
Scope: mailboxes.manage.

Runs in the background. Every server is also re-checked on its own once a day.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 202 Queued

get/v1/workspaces/{ws}/sending-servers/{id}

Get a sending server
Scope: mailboxes.read.

List its mailboxes with GET /mailboxes?server_id=.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 The server, 404 Error

patch/v1/workspaces/{ws}/sending-servers/{id}

Pause or resume a server, cap its daily sends, or set its sending IPs
Scope: mailboxes.manage.

status: paused stops sequence and test sends from every mailbox on the server (replies written in the inbox still go); active resumes, including after an automatic blocklist pause. daily_limit caps sends from all its mailboxes together in any 24 hours; null removes the cap. pause_on_listing (on by default) pauses the server when a check finds one of its IPs on a blocklist, and resumes it when every IP is clear again; a pause by a person is never lifted automatically. ips sets the sending addresses by hand, for servers that accept mail on one address and send from others; null goes back to the host's DNS records. Changing ips queues a new check.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
namebodystring
statusbody"active" | "paused"
daily_limitbodyinteger
notesbodystring
pause_on_listingbodyboolean
ipsbodystring[]
Responses: 200 The updated server, 400 Error, 404 Error

delete/v1/workspaces/{ws}/sending-servers/{id}

Remove a server that no mailbox uses
Scope: mailboxes.manage.

Returns 409 while mailboxes still sign in to it.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 204 Removed, 404 Error, 409 Error

post/v1/workspaces/{ws}/sending-servers/{id}/check

Re-check a server's IPs and blocklists now
Scope: mailboxes.manage.

Runs in the background; poll the server for a new checked_at. Use it after a delisting request.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 202 Queued, 404 Error

tracking

get/v1/workspaces/{ws}/tracking-domains

List tracking domains
Scope: mailboxes.read.

Optional. Once a domain is verified, the workspace's unsubscribe links and any click-tracked links use it instead of this server's address. target is what the CNAME must point to.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 Tracking domains

post/v1/workspaces/{ws}/tracking-domains

Add a tracking domain
Scope: mailboxes.manage.

Add a CNAME record from the domain to target first or after; the DNS is checked straight away and whenever you call check.

ParameterInTypeDescription
ws *pathstring, uuid
domain *bodystring
Responses: 201 The domain, with the result of the first check, 400 Error, 409 Error

delete/v1/workspaces/{ws}/tracking-domains/{id}

Remove a tracking domain
Scope: mailboxes.manage.

Links already sent keep working only while the domain still points here.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 204 Removed, 404 Error

post/v1/workspaces/{ws}/tracking-domains/{id}/check

Check a tracking domain's DNS now
Scope: mailboxes.manage.
ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 The domain, with the result of the check, 404 Error

Leads

get/v1/workspaces/{ws}/leads

List and search leads, ordered by email
Scope: leads.read
ParameterInTypeDescription
ws *pathstring, uuid
list_idquerystring, uuid
qquerystringMatches email, name or company.
suppressedqueryboolean
verificationquery"deliverable" | "undeliverable" | "risky" | "unknown" | "unverified"Latest verification result, or unverified for never checked.
limitqueryinteger
afterquerystringnext_after from the previous page.
Responses: 200 Leads

post/v1/workspaces/{ws}/leads

Add a lead, or update the lead with this email
Scope: leads.manage.

Empty fields keep stored values; custom fields are merged.

ParameterInTypeDescription
ws *pathstring, uuid
email *bodystring
first_namebodystring
last_namebodystring
companybodystring
titlebodystring
phonebodystring
linkedin_urlbodystring
websitebodystring
custombodyobject
list_idbodystring, uuid
Responses: 200 Updated, 201 Created, 400 Error

post/v1/workspaces/{ws}/leads/import

Bulk import leads from CSV
Scope: leads.manage. · Body: text/csv

Send the CSV file as the body (up to 50 MB and 100,000 rows). Only an email column is required. Standard columns are recognised under common export names (first name, last name, full name, company, title, phone, LinkedIn URL, website); every other column is kept as a custom field under its snake_case header, for use in templates. Leads are upserted by email: a row for an existing lead fills in non-empty fields and merges custom fields. Rows repeating an email within the file are merged. Suppressed people are imported but counted, and are never emailed.

ParameterInTypeDescription
ws *pathstring, uuid
listquerystringAdd every imported lead to this list, created if missing.
Responses: 200 Import summary, 400 Error, 413 Error, 415 Error

get/v1/workspaces/{ws}/leads/{id}

Get a lead
Scope: leads.read
ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 The lead, 404 Error

patch/v1/workspaces/{ws}/leads/{id}

Change a lead's fields
Scope: leads.manage.

In custom, a null value removes that field.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
first_namebodystring
last_namebodystring
companybodystring
titlebodystring
phonebodystring
linkedin_urlbodystring
websitebodystring
custombodyobject
Responses: 200 The updated lead, 404 Error

delete/v1/workspaces/{ws}/leads/{id}

Delete a lead
Scope: leads.manage.

With suppress=true the address is also added to the suppression list (reason do_not_contact), so an erasure request is honoured even if the person is imported again later.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
suppressqueryboolean
Responses: 204 Deleted, 404 Error

get/v1/workspaces/{ws}/leads/{id}/sources

Where a lead's data came from
Scope: leads.read.

The providers that supplied this lead, their id for the person, and the list run that added it.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 Sources, 404 Error

Lead lists and data providers

get/v1/workspaces/{ws}/lead-lists

List lead lists, with each list's latest run
Scope: leads.read.

A project token sees only its project's lists.

ParameterInTypeDescription
ws *pathstring, uuid
project_idquerystringA project's id, or none for lists shared by the whole workspace.
kindquery"manual" | "search"
Responses: 200 Lists

post/v1/workspaces/{ws}/lead-lists

Create a lead list, optionally built from a data provider search
Scope: leads.manage.

A list with search_integration_id and filters is a search list that can be built and refreshed from that provider. Set build to true to queue the first build in the same call. A project token's lists always belong to its project.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 201 Created, 400 Error, 409 Error

post/v1/workspaces/{ws}/lead-lists/preview

Preview a search before building a list from it
Scope: leads.manage.

Queues a run that fetches one page (10 people) from the provider and saves nothing. Poll GET /list-runs/{run} for the sample and the total number of matches. No emails are revealed; some providers (People Data Labs) charge for search results.

ParameterInTypeDescription
ws *pathstring, uuid
search_integration_id *bodystring, uuid
filters *bodyobjectA people search in shared terms. Each field lists alternatives (any may match); fields combine (all must match). GET /data-providers says which filters each provider understands.
project_idbodystring, uuidScore the sample against this project's ICP.
Responses: 202 Queued, 400 Error

get/v1/workspaces/{ws}/lead-lists/{id}

Get a lead list and its latest run
Scope: leads.read
ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 The list, 404 Error

patch/v1/workspaces/{ws}/lead-lists/{id}

Change a list's settings
Scope: leads.manage.

Absent fields keep their value; filters are replaced as a whole.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
namebodystring
descriptionbodystring
project_idbodystring, uuidNull shares the list with the whole workspace.
search_integration_idbodystring, uuidThe connected provider that searches. Null makes a manual list.
filtersbodyobjectA people search in shared terms. Each field lists alternatives (any may match); fields combine (all must match). GET /data-providers says which filters each provider understands.
enrich_integration_idsbodystring, uuid[]Providers that find missing emails, tried in order until one answers. Empty uses the search provider if it can.
max_leadsbodyintegerMost new leads one run adds. Default 500.
credit_capbodynumberMost provider credits one run spends. Null for no cap.
verifybodybooleanVerify new addresses and leave out ones the workspace's verification settings block. Default true.
verified_emails_onlybodybooleanOnly add addresses the provider itself verified. Default false.
skip_contacted_daysbodyintegerLeave out people any sequence emailed in this many days. 0 turns it off. Default 90.
skip_in_sequencesbodybooleanLeave out people active in another sequence. Default true.
min_scorebodyintegerLeave out people whose ICP fit is below this, before spending credits. Default 0.
refresh_every_daysbodyintegerRefresh on a schedule. Null refreshes only when asked.
Responses: 200 The list, 400 Error, 404 Error

delete/v1/workspaces/{ws}/lead-lists/{id}

Delete a list. Its leads stay.
Scope: leads.manage
ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 204 Deleted, 404 Error

get/v1/workspaces/{ws}/lead-lists/{id}/members

List a list's leads with their ICP fit scores
Scope: leads.read.

Sorted by score, best first, unless sort says otherwise.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
sortquery"score" | "added" | "email"
qquerystringSearch email, name, company and title.
min_scorequeryinteger
limitqueryinteger
offsetqueryinteger
Responses: 200 Members, 404 Error

post/v1/workspaces/{ws}/lead-lists/{id}/leads

Add leads to a list
Scope: leads.manage.

Ids of leads outside the workspace are ignored.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
lead_ids *bodystring, uuid[]
Responses: 200 How many were added, 404 Error

delete/v1/workspaces/{ws}/lead-lists/{id}/leads/{lead}

Remove a lead from a list
Scope: leads.manage
ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
lead *pathstring, uuid
Responses: 204 Removed, 404 Error

get/v1/workspaces/{ws}/lead-lists/{id}/runs

List a list's runs, newest first
Scope: leads.read
ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 Runs, 404 Error

post/v1/workspaces/{ws}/lead-lists/{id}/runs

Build, refresh or enrich a list
Scope: leads.manage.

Spends provider credits. build and refresh search the provider, find missing emails through the waterfall and add new leads; enrich fills in missing fields on the list's leads. One run at a time per list (409 while one is queued or running). max_leads and credit_cap override the list's limits for this run only.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
kindbody"build" | "refresh" | "enrich"
max_leadsbodyinteger
credit_capbodynumber
Responses: 202 Queued, 400 Error, 404 Error, 409 Error

get/v1/workspaces/{ws}/list-runs/{run}

Get a run's progress or result
Scope: leads.read
ParameterInTypeDescription
ws *pathstring, uuid
run *pathstring, uuid
Responses: 200 The run, 404 Error

post/v1/workspaces/{ws}/list-runs/{run}/cancel

Stop a queued or running run. Leads already added stay.
Scope: leads.manage
ParameterInTypeDescription
ws *pathstring, uuid
run *pathstring, uuid
Responses: 200 The run, 404 Error, 409 Error

get/v1/workspaces/{ws}/data-providers

Data providers that can search for people, find emails or enrich
Scope: leads.read.

Every data connector, available or planned, with the workspace's connected accounts for each and the filters its search understands. Connect one with POST /integrations.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 Providers and the shared filter vocabulary

Suppression

get/v1/workspaces/{ws}/suppressions

List the suppression list, ordered by value
Scope: leads.read
ParameterInTypeDescription
ws *pathstring, uuid
kindquery"email" | "domain"
reasonquery"unsubscribed" | "bounced" | "complained" | "manual" | "do_not_contact"
qquerystring
limitqueryinteger
afterquerystringnext_after from the previous page.
Responses: 200 Entries

post/v1/workspaces/{ws}/suppressions

Add emails or domains to the suppression list
Scope: leads.manage.

Each value is an email address or a domain (also accepted as @acme.com or a URL). A suppressed domain blocks every address at it. Existing entries are left as they are.

ParameterInTypeDescription
ws *pathstring, uuid
values *bodystring[]
reasonbody"unsubscribed" | "bounced" | "complained" | "manual" | "do_not_contact"
notebodystring
Responses: 200 Result, 400 Error

delete/v1/workspaces/{ws}/suppressions/{id}

Remove a suppression entry
Scope: suppressions.manage

(owners and admins by default). Audited.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 204 Removed, 404 Error

post/v1/workspaces/{ws}/recipients/check

Ask the send policy whether addresses may be emailed
Scope: leads.read.

Runs the same recipient checks every send goes through, without sending. Use it to filter before enrolling leads.

ParameterInTypeDescription
ws *pathstring, uuid
emails *bodystring[]
Responses: 200 One decision per email, in order

Sequences

post/v1/workspaces/{ws}/mailboxes/{id}/test-send

Send one test email from a mailbox now
Scope: mailboxes.manage.

Goes through the recipient check and the mailbox's status, daily limit and minimum gap (not the sending window) and counts towards the daily limit. Returns 409 when the policy blocks it and 502 when the SMTP server refuses it.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
to *bodystring, email
subjectbodystring
bodybodystring
Responses: 200 Sent, 400 Error, 404 Error, 409 Error, 502 Error

get/v1/workspaces/{ws}/sequences

List sequences (archived ones are left out)
Scope: sequences.read
ParameterInTypeDescription
ws *pathstring, uuid
project_idquerystringA project's id, or none for records shared by the workspace. A project token always sees only its project.
Responses: 200 Sequences, newest first, with steps and enrollment counts

post/v1/workspaces/{ws}/sequences

Create a sequence
Scope: sequences.manage.

A new sequence is a draft: it sends nothing until its status is set to active. Defaults: UTC, Monday to Friday, 09:00 to 17:00, no daily cap on new leads, stop on reply.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 201 The sequence, 400 Error

get/v1/workspaces/{ws}/sequences/{id}

Get a sequence
Scope: sequences.read
ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 The sequence, 404 Error

patch/v1/workspaces/{ws}/sequences/{id}

Change a sequence's settings, or activate, pause or archive it
Scope: sequences.manage.

Activating needs at least one step, and a daily_new_limit or target_daily (or mailboxes assigned by hand) so Outflare knows how many mailboxes to allocate; it allocates them straight away. Pausing holds every enrollment where it is, keeps the mailboxes with leads mid-sequence and frees the rest; activating again carries on.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 The updated sequence, 400 Error, 404 Error

delete/v1/workspaces/{ws}/sequences/{id}

Delete a sequence that is not active
Scope: sequences.manage.

Sent messages are kept in the message log.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 204 Deleted, 400 Error, 404 Error

put/v1/workspaces/{ws}/sequences/{id}/steps

Replace a sequence's steps
Scope: sequences.manage.

The first step needs a subject. A later step with an empty subject replies in the same thread ("Re: ..."). delay_days is the wait after the previous step (at least 1 after the first). Enrollments carry on from their current position.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
steps *bodyobject[]
Responses: 200 The sequence, 400 Error, 404 Error

post/v1/workspaces/{ws}/sequences/{id}/preview

Render every step for one lead
Scope: sequences.read.

Shows the subject and body each step would send, and any variables the lead is missing (a missing variable blocks the real send).

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
lead_id *bodystring, uuid
Responses: 200 Rendered steps, 404 Error

get/v1/workspaces/{ws}/sequences/{id}/enrollments

List the leads in a sequence, ordered by email
Scope: sequences.read
ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
statusquery"active" | "completed" | "stopped" | "unsubscribed" | "bounced" | "replied" | "blocked" | "failed"
limitqueryinteger
afterquerystringnext_after from the previous page.
Responses: 200 Enrollments

post/v1/workspaces/{ws}/sequences/{id}/enrollments

Add leads to a sequence
Scope: sequences.manage.

Send lead_ids or list_id. Suppressed leads are skipped, and so are leads active in another sequence unless skip_active_elsewhere is false. Adding a lead twice does nothing.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
lead_idsbodystring, uuid[]
list_idbodystring, uuid
skip_active_elsewherebodyboolean
Responses: 200 Counts, 400 Error, 404 Error

post/v1/workspaces/{ws}/sequences/{id}/enrollments/{eid}/stop

Stop sending to one lead in a sequence
Scope: sequences.manage
ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
eid *pathstring, uuid
Responses: 204 Stopped, 404 Error

get/v1/workspaces/{ws}/messages

List sent, failed and blocked messages, newest first
Scope: sequences.read.

Every send attempt is recorded, including the ones the policy blocked and why.

ParameterInTypeDescription
ws *pathstring, uuid
sequence_idquerystring, uuid
lead_idquerystring, uuid
mailbox_idquerystring, uuid
enrollment_idquerystring, uuid
statusquery"sending" | "sent" | "failed" | "blocked" | "unknown"
limitqueryinteger
beforequerystring, date-timenext_before from the previous page.
Responses: 200 Messages

get/u/{token}

Unsubscribe page linked from every email
Public

Shows a confirmation button. A GET never unsubscribes, because mail scanners follow links.

ParameterInTypeDescription
token *pathstring
Responses: 200 HTML page, 404 The link is not valid

post/u/{token}

Unsubscribe (the page's button and RFC 8058 one-click)
Public

Adds the address to the suppression list (reason unsubscribed) and stops every active sequence for it in the workspace.

ParameterInTypeDescription
token *pathstring
Responses: 200 HTML confirmation, 404 The link is not valid

Inbox

post/v1/workspaces/{ws}/mailboxes/{id}/sync

Check a mailbox for new mail now
Scope: mailboxes.manage.

Active mailboxes are also synced automatically every two minutes.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 202 Sync queued, 404 Error, 409 Error

get/v1/workspaces/{ws}/inbox

List received mail, newest first
Scope: inbox.read.

By default only replies and auto-replies from leads are listed; pass kind=all to include bounce reports and other mail.

ParameterInTypeDescription
ws *pathstring, uuid
kindquery"reply" | "auto_reply" | "bounce" | "other" | "all"
unreadqueryboolean
mailbox_idquerystring, uuid
lead_idquerystring, uuid
sequence_idquerystring, uuid
statusquery"open" | "snoozed" | "done" | "all"open: not done and not snoozed. Default all.
categoryquery"interested" | "meeting_booked" | "question" | "referral" | "not_now" | "not_interested" | "wrong_person" | "out_of_office" | "unsubscribe" | "none"A category, or none for replies nobody has sorted yet.
assigned_toquerystringme (signed-in person only), none, or a user id.
labelquerystring
qquerystringSearch sender and subject.
limitqueryinteger
beforequerystring, date-timenext_before from the previous page.
Responses: 200 Messages

get/v1/workspaces/{ws}/inbox/{id}

Get a received message and its conversation
Scope: inbox.read.

thread holds everything sent to and received from the lead through that mailbox, oldest first.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 The message and thread, 404 Error

patch/v1/workspaces/{ws}/inbox/{id}

Triage a message
Scope: inbox.manage.

Send only the fields to change. Mark it read, sort it into a category, assign it to a member, snooze it, mark it done or set its labels. Changes other than read are audited.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
readbodyboolean
categorybody"interested" | "meeting_booked" | "question" | "referral" | "not_now" | "not_interested" | "wrong_person" | "out_of_office" | "unsubscribe"
assigned_tobodystring, uuidA workspace member's user id
snoozed_untilbodystring, date-timeMust be in the future; null wakes it now.
donebodyboolean
labelsbodystring[]Replaces the labels. Lower-cased.
Responses: 200 The message, 404 Error

post/v1/workspaces/{ws}/inbox/{id}/reply

Reply from the mailbox the message arrived at, in the same thread
Scope: inbox.manage.

Goes through the recipient check (a suppressed or opted-out address gets 409) and needs the mailbox to be active. It is not held to a sequence's sending window or the mailbox's pacing, but counts towards its daily limit.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
body *bodystring
Responses: 200 Sent, 400 Error, 404 Error, 409 Error, 502 Error

connectors

get/v1/connectors

Every connector this server ships, available or planned
Public

Public, no key needed. Each entry says what the connector is, how it signs in (api_key, oauth2 or none), the fields to paste, and its capabilities. configured is false for OAuth connectors whose app is not set up on this server.

Responses: 200 The catalog, available first, then by category and name

get/v1/workspaces/{ws}/connectors

The catalog, with how many accounts the workspace connected to each
Scope: integrations.read.
ParameterInTypeDescription
ws *pathstring, uuid
categoryquery"email_verification" | "sequencer" | "mailbox_provider" | "dns" | "crm" | "data" | "signals" | "analytics" | "seo" | "calendar" | "registrar" | "warehouse" | "notification" | "model" | "custom"
Responses: 200 The catalog

get/v1/workspaces/{ws}/integrations

List connected accounts
Scope: integrations.read.

Credentials are never returned.

ParameterInTypeDescription
ws *pathstring, uuid
connectorquerystringOnly this connector id.
categoryquery"email_verification" | "sequencer" | "mailbox_provider" | "dns" | "crm" | "data" | "signals" | "analytics" | "seo" | "calendar" | "registrar" | "warehouse" | "notification" | "model" | "custom"
Responses: 200 Integrations, oldest first

post/v1/workspaces/{ws}/integrations

Connect an account with an API key
Scope: integrations.manage.

The credentials are tested against the service before anything is saved, and refused with connector_auth if wrong. OAuth connectors are connected through GET /integrations/oauth/{connector}/authorize instead.

ParameterInTypeDescription
ws *pathstring, uuid
connector *bodystring
namebodystringDefaults to the connector's name, numbered if taken.
credentials *bodyobjectThe connector's fields by name, e.g. {"api_key":"..."}.
Responses: 201 Connected, 400 Error, 409 Error, 422 Error, 429 Error, 502 Error

get/v1/workspaces/{ws}/integrations/{id}

Get a connected account
Scope: integrations.read.
ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 The integration, 404 Error

patch/v1/workspaces/{ws}/integrations/{id}

Rename, replace the API key, or change settings
Scope: integrations.manage.

New credentials are tested before they replace the old ones.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
namebodystring
credentialsbodyobject
configbodyobject
Responses: 200 Updated, 400 Error, 404 Error, 422 Error

delete/v1/workspaces/{ws}/integrations/{id}

Disconnect an account
Scope: integrations.manage.

Deletes the stored credentials. Features using it fall back (verification goes back to the built-in checks).

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 204 Disconnected, 404 Error

post/v1/workspaces/{ws}/integrations/{id}/test

Sign in again with the stored credentials
Scope: integrations.manage.

Reads the account (and credits, when the service reports them); changes nothing at the service and spends no credits.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 The integration with its status and last_error updated, 404 Error

get/v1/workspaces/{ws}/integrations/oauth/{connector}/authorize

Start connecting an OAuth connector
Scope: integrations.manage.

Redirects to the service's sign-in page; afterwards the browser lands on the workspace's Connectors page. With format=json it returns the link instead, for an agent to hand to a person.

ParameterInTypeDescription
ws *pathstring, uuid
connector *pathstring
namequerystring
formatquery"json"
Responses: 200 The sign-in link (format=json), 302 Redirect to the sign-in page, 400 Error, 404 Error

get/v1/integrations/oauth/clients/{file}

OAuth client metadata document
Public

For providers that read the OAuth client from a URL (PostHog). The document's own URL is the client id, so nobody registers an app. {file} is <connector id>.json.

ParameterInTypeDescription
file *pathstring
Responses: 200 The client metadata document, 404 Error

get/v1/integrations/oauth/callback

Where connector sign-in pages return to
Public

Not called directly. Redirects to the Connectors page with integration (the new integration id) or connect_error.

Responses: 302 Redirect to the web app

get/v1/workspaces/{ws}/connect-requests

List connect requests
Scope: integrations.read.

The 100 newest, with status pending, connected, cancelled or expired.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 Connect requests, newest first

post/v1/workspaces/{ws}/connect-requests

Ask a person to connect a service
Scope: integrations.manage.

The agent-first way to connect anything: returns a one-time link to give the person. They open it, without logging in, and paste the key or approve the OAuth sign-in. The secret never passes through the agent. Poll GET /connect-requests/{id} until status is connected.

ParameterInTypeDescription
ws *pathstring, uuid
connector *bodystring
namebodystringName for the integration it creates.
reasonbodystringWhy it is needed; shown to the person on the link's page.
use_for_verificationbodybooleanMake it the workspace's verification service. Default true for verification connectors.
expires_in_minutesbodyintegerDefault 1440 (24 hours).
Responses: 201 Created. `url` is only returned here, 400 Error

get/v1/workspaces/{ws}/connect-requests/{id}

Read a connect request
Scope: integrations.read.

Once status is connected, integration_id is the new integration.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 The request, 404 Error

delete/v1/workspaces/{ws}/connect-requests/{id}

Cancel a pending connect request
Scope: integrations.manage.

The link stops working.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 204 Cancelled, 404 Error

post/v1/connect/lookup

What a connect link is for
Public

Used by the link's page. No login; the token from the link is the authority.

ParameterInTypeDescription
token *bodystring
Responses: 200 The connector and who asked, 404 Error, 410 Error

post/v1/connect/complete

Finish a connect link with an API key
Public

Tests the credentials and saves the integration. The link then stops working.

ParameterInTypeDescription
token *bodystring
credentials *bodyobject
Responses: 201 Connected, 400 Error, 409 Error, 410 Error, 422 Error

post/v1/connect/oauth

Start the OAuth sign-in for a connect link
Public
ParameterInTypeDescription
token *bodystring
Responses: 200 The provider's sign-in URL. Afterwards the browser lands on /connect, 400 Error, 410 Error

analytics

get/v1/workspaces/{ws}/analytics

List connected analytics tools
Scope: analytics.read.

Every connection that reads website or product analytics or a search console (Google Analytics, Search Console, Bing Webmaster Tools, PostHog, Plausible and the rest), with the reports each one answers and its default site. Connect one with POST .../integrations (API key) or the OAuth authorize link, or hand a person a link with POST .../connect-requests. Nothing needs to be connected for the rest of Outflare to work.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 The connections

get/v1/workspaces/{ws}/analytics/summary

Traffic totals from every analytics tool
Scope: analytics.read.

The traffic report's totals from every analytics connection over one range, in one call. A connection that fails (no site picked, revoked key) carries an error instead of failing the answer. Dates are inclusive; the default is the last 28 days.

ParameterInTypeDescription
ws *pathstring, uuid
fromquerystring, dateFirst day, inclusive (YYYY-MM-DD). Default 27 days before to.
toquerystring, dateLast day, inclusive (YYYY-MM-DD). Default today (UTC).
freshquerybooleantrue skips the short cache and asks the provider again.
Responses: 200 One entry per connection, 400 Error

get/v1/workspaces/{ws}/analytics/history

Query stored analytics history
Scope: analytics.read.

Reads what the sync stored, without calling the tools. group is day (one row per day per tool; the default for traffic), tool (totals per tool), key (each page, source, query or event added up across the tools picked; the default for the other reports) or tool_key. Counts are summed, click-through rate and position are weighted by impressions.

ParameterInTypeDescription
ws *pathstring, uuid
reportquery"traffic" | "pages" | "sources" | "search_queries" | "events"
groupquery"day" | "tool" | "key" | "tool_key"
integrationquerystring, uuidOnly this connection.
hostquerystringOnly this website.
fromquerystring, dateFirst day. Default 28 days before to.
toquerystring, dateLast day. Default yesterday (UTC).
limitqueryinteger
Responses: 200 Stored rows, 400 Error

get/v1/workspaces/{ws}/analytics/overview

Every website across every tool
Scope: analytics.read.

Stored traffic totals per website (by domain), with each tool side by side, search clicks added up across search consoles, and every connection's sync status.

ParameterInTypeDescription
ws *pathstring, uuid
fromquerystring, date
toquerystring, date
Responses: 200 The overview, 400 Error

get/v1/workspaces/{ws}/analytics/changes

What changed on each website
Scope: analytics.read.

Reads stored data only, so it is free. For each website (by domain): each tool's metrics over its own latest days stored days against the same number of days before (so a tool that runs behind is not read as a drop), search clicks added up across search consoles, the pages, sources and search queries that rose or fell most, unusual days against the 28 days before, the latest SEO snapshot against the one before (with keyword moves), plain-sentence headlines, and gaps: what is missing and which connector or action fixes it. Units for every metric are given once, in units.

ParameterInTypeDescription
ws *pathstring, uuid
daysqueryintegerPeriod length, 1 to 90. Default 7.
toquerystring, dateLast day to consider. Default yesterday, UTC.
hostquerystringOne website, like example.com.
limitqueryintegerMovers, unusual days and keyword moves per list, 1 to 25. Default 5.
Responses: 200 Changes per website, 400 Error

get/v1/workspaces/{ws}/analytics/catalog

What each website can be asked
Scope: analytics.read.

For each website: the analytics tools, which metrics each has stored per report, the days stored and last sync, the SEO tools with snapshots and budget left, and gaps with the connector or action that fixes each. ask maps common questions to the tool or endpoint that answers them.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 The catalog

get/v1/workspaces/{ws}/analytics/compare

Compare tools for one website, day by day
Scope: analytics.read.

Each tool's stored daily traffic for one domain.

ParameterInTypeDescription
ws *pathstring, uuid
host *querystring
fromquerystring, date
toquerystring, date
Responses: 200 One series per tool, 400 Error

get/v1/workspaces/{ws}/analytics/syncs

What the sync has stored for each connection
Scope: analytics.read.
ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 One entry per analytics connection

post/v1/workspaces/{ws}/analytics/sync

Sync every analytics connection soon
Scope: analytics.read.

Queues a sync of each connected tool. Syncs also run by themselves every six hours (daily for Clarity).

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 202 Queued, 503 Error

post/v1/workspaces/{ws}/analytics/{id}/sync

Sync one analytics connection now
Scope: analytics.read.

The first sync stores 90 days of traffic and 7 days of pages, sources, search queries and events; later ones re-read the last three days. Answers once it is done, with the rows stored and the calls made.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 Done, 400 Error, 404 Error, 422 Error

get/v1/workspaces/{ws}/analytics/{id}/sites

List the sites a connection can read
Scope: analytics.read.

GA4 properties, Search Console properties, PostHog projects, Fathom or Umami sites, and so on. Pass an id as site on reports, or make it the default.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuidThe integration id.
freshquerybooleantrue skips the short cache and asks the provider again.
Responses: 200 The sites, and the current default, 404 Error, 422 Error

put/v1/workspaces/{ws}/analytics/{id}/site

Set a connection's default site
Scope: integrations.manage.

The site must be one the connection can read. An empty site clears the default.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
site *bodystring
domainbodystringThe website's domain, which lines this connection up with other tools in the overview and compare. Set it when the tool does not show one (Google Analytics properties, Vercel projects). An empty string clears it. Stored history moves to the new domain.
Responses: 200 Saved, 400 Error, 404 Error

get/v1/workspaces/{ws}/analytics/{id}/reports/{report}

Read one analytics report
Scope: analytics.read.

The same report from any provider: traffic (totals plus one point per day), pages, sources (referrers), search_queries (Search Console and Bing) and events (events and conversions). A metric a provider does not measure is left out, never reported as zero. Read notes for caveats such as data delay. Answers are kept for 15 minutes (an hour for Mixpanel and Amplitude, six hours for Clarity) to spare the provider's rate limits; fresh=true reads again.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
report *path"traffic" | "pages" | "sources" | "search_queries" | "events"
sitequerystringA site id from .../sites. Default: the connection's default site, or its only one.
fromquerystring, dateFirst day, inclusive (YYYY-MM-DD). Default 27 days before to.
toquerystring, dateLast day, inclusive (YYYY-MM-DD). Default today (UTC).
limitqueryintegerRows in a breakdown report.
freshquerybooleantrue skips the short cache and asks the provider again.
Responses: 200 The report, 400 Error, 404 Error, 422 Error, 429 Error, 502 Error

seo

get/v1/workspaces/{ws}/seo

List SEO connections
Scope: analytics.read.

DataForSEO and Ahrefs connections with their reports, the domains they follow, the country, the weekly snapshot setting and this month's budget and spend. Empty means none is connected; nothing else needs one.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 SEO connections

get/v1/workspaces/{ws}/seo/history

Read stored SEO snapshots
Scope: analytics.read.

Reads what the weekly snapshots stored, without calling or paying the provider. overview gives one row per connection, domain and snapshot day. keywords (and the other lists) gives each connection's latest snapshot, with previous holding the same keyword's numbers from the snapshot before, unless from or to name a range.

ParameterInTypeDescription
ws *pathstring, uuid
reportquery"overview" | "keywords" | "backlinks" | "competitors" | "audit"
hostquerystring
integrationquerystring, uuid
fromquerystring, date
toquerystring, date
limitqueryinteger
Responses: 200 Stored rows, newest first, 400 Error

put/v1/workspaces/{ws}/seo/{id}/settings

Set an SEO connection's domains, country, budget and snapshots
Scope: integrations.manage.

Send only what changes. domains: null follows the domains the analytics tools line up (at most five); monthly_budget: null is the default ($10 for DataForSEO, 10,000 API units for Ahrefs); 0 stops every call.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
domainsbodystring[]
countrybodystringTwo-letter code for keywords and traffic.
monthly_budgetbodynumberPer calendar month (UTC): US dollars for DataForSEO, API units for Ahrefs.
syncbody"weekly" | "off"
Responses: 200 The connection with its new settings, 400 Error, 404 Error

get/v1/workspaces/{ws}/seo/{id}/reports/{report}

Read one SEO report live (costs money)
Scope: analytics.read.

Asks the provider now, which costs money: cost says what this answer cost and budget what is left this month. A call that would go over the connection's monthly budget answers 402 budget_reached without calling. Answers are kept for a day, so asking again is free; fresh=true pays again. Every paid call is recorded in Outcomes spend. Prefer GET .../seo/history for anything the weekly snapshot already has.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
report *path"overview" | "keywords" | "backlinks" | "competitors" | "audit"
targetquerystringA domain. Default: the connection's only domain.
countryquerystring
limitqueryinteger
freshqueryboolean
Responses: 200 The report, 400 Error, 402 Error, 404 Error, 422 Error, 429 Error, 502 Error

post/v1/workspaces/{ws}/seo/{id}/sync

Take an SEO snapshot now
Scope: integrations.manage.

Stores the overview and top 50 keywords of every domain the connection follows, within its budget (this also runs weekly). Costs about $0.04 a domain on DataForSEO or 1,200 API units on Ahrefs.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 What was stored and what it cost, 400 Error, 402 Error, 422 Error, 503 Error

verification

get/v1/workspaces/{ws}/verification-settings

How the workspace verifies addresses
Scope: leads.read.
ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 The settings

put/v1/workspaces/{ws}/verification-settings

Choose the verification service and what to block
Scope: leads.manage.

Every field is optional. integration_id empty or builtin switches to the free built-in checks. Undeliverable addresses are always blocked.

ParameterInTypeDescription
ws *pathstring, uuid
integration_idbodystring
verify_on_importbodyboolean
verify_before_sendbodyboolean
block_riskybodyboolean
block_catch_allbodyboolean
block_unknownbodyboolean
max_age_daysbodyinteger
Responses: 200 Saved, 400 Error

get/v1/workspaces/{ws}/verifications

Verification results, newest first
Scope: leads.read.
ParameterInTypeDescription
ws *pathstring, uuid
statusquery"deliverable" | "undeliverable" | "risky" | "unknown"
emailquerystringOne or more addresses, comma-separated or repeated.
limitqueryinteger
Responses: 200 Results

post/v1/workspaces/{ws}/verifications

Verify addresses, a lead list, or every unverified lead
Scope: leads.manage.

Runs in the background; results appear under GET /verifications. Addresses with a fresh result are skipped unless force is true. Paid services spend credits.

ParameterInTypeDescription
ws *pathstring, uuid
emailsbodystring, email[]
list_idbodystring, uuid
unverifiedbodybooleanEvery lead without a fresh result.
forcebodyboolean
Responses: 202 Queued, 400 Error

get/v1/workspaces/{ws}/verifications/summary

Lead counts by verification status
Scope: leads.read.
ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 Counts

outcomes

get/v1/workspaces/{ws}/outcomes/report

Cost per reply, meeting and deal
Scope: outcomes.read.

Sends, replies, meetings, deals and spend over a period, for the workspace or per project or sequence, with cost per reply, per meeting, per held meeting and per deal. Spend tied to a sequence, project, mailbox or domain is charged to it directly; the rest is shared out by each row's share of emails sent. Usage recorded only as units (credits) is priced with the workspace's spend rates and reported as estimated; usage with no rate is listed under unpriced. Nothing is converted between currencies.

ParameterInTypeDescription
ws *pathstring, uuid
fromquerystringStart of the period, inclusive. A date (2026-10-01) or a time. Default 30 days before to.
toquerystringEnd of the period. A date includes that whole day. Default now.
group_byquery"none" | "project" | "sequence"
project_idquerystring, uuid
sequence_idquerystring, uuid
mission_idquerystring, uuidOnly meetings, deals and spend recorded for this mission.
currencyquerystring
Responses: 200 The report, 400 Error

get/v1/workspaces/{ws}/outcomes/export

Export meetings, deals, spend or the report
Scope: outcomes.read.

Rows for a warehouse or a spreadsheet, as CSV (default) or newline-delimited JSON. Up to 100,000 rows per call; narrow from and to for more. The report dataset takes the report's query parameters and defaults to group_by=sequence.

ParameterInTypeDescription
ws *pathstring, uuid
dataset *query"meetings" | "deals" | "spend" | "report"
formatquery"csv" | "ndjson"
fromquerystringStart of the period, inclusive. A date (2026-10-01) or a time. Default 30 days before to.
toquerystringEnd of the period. A date includes that whole day. Default now.
group_byquery"none" | "project" | "sequence"
currencyquerystring
Responses: 200 The file, 400 Error

get/v1/workspaces/{ws}/outcomes/syncs

Calendar and CRM sync status
Scope: outcomes.read.

Every connected calendar, booking tool and CRM, with when each stream (meetings, deals, push) last ran, what it changed and its last error. Syncs run every 15 minutes.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 Sync status

post/v1/workspaces/{ws}/outcomes/syncs/{id}/run

Sync a calendar or CRM now
Scope: outcomes.manage.

Queues a sync instead of waiting for the next one.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuidThe integration id.
Responses: 202 Queued, 400 Error, 404 Error

get/v1/workspaces/{ws}/meetings

List meetings with leads
Scope: outcomes.read.

Newest first. Page with before=next_before.

ParameterInTypeDescription
ws *pathstring, uuid
fromquerystringStart of the period, inclusive. A date (2026-10-01) or a time. Default 30 days before to.
toquerystringEnd of the period. A date includes that whole day. Default now.
project_idquerystring, uuid
sequence_idquerystring, uuid
mission_idquerystring, uuid
statusquery"booked" | "held" | "no_show" | "canceled"
attributedquerybooleanOnly meetings tied to outreach.
limitqueryinteger
beforequerystring, date-time
Responses: 200 Meetings

post/v1/workspaces/{ws}/meetings

Log a meeting
Scope: outcomes.manage.

For meetings no connected calendar sees. Give lead_id or attendee_email; the meeting is tied to the sequence that last emailed that lead, unless sequence_id or project_id is given (which pins it). Status defaults to held for a meeting more than an hour in the past, otherwise booked.

ParameterInTypeDescription
ws *pathstring, uuid
lead_idbodystring, uuid
attendee_emailbodystring
attendee_namebodystring
sequence_idbodystring, uuid
project_idbodystring, uuid
mission_idbodystring, uuid
titlebodystring
starts_at *bodystring, date-time
ends_atbodystring, date-time
booked_atbodystring, date-timeDefault now. Decides which sequence gets the credit.
statusbody"booked" | "held" | "no_show" | "canceled"
qualifiedbodyboolean
notesbodystring
external_idbodystring
Responses: 201 The meeting, 400 Error

get/v1/workspaces/{ws}/meetings/{id}

Get a meeting
Scope: outcomes.read.
ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 The meeting, 404 Error

patch/v1/workspaces/{ws}/meetings/{id}

Mark a meeting held, no-show or qualified, or fix its attribution
Scope: outcomes.manage.

Setting sequence_id, project_id or unattributed pins the attribution so syncs never move it. A status a person sets (held, no_show) is kept by syncs, unless the source cancels the meeting.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
statusbody"booked" | "held" | "no_show" | "canceled"
qualifiedbodyboolean
notesbodystring
titlebodystring
starts_atbodystring, date-time
sequence_idbodystring, uuid
project_idbodystring, uuid
mission_idbodystringAn id
unattributedbodybooleanNot from outreach; pins that.
Responses: 200 The meeting, 400 Error, 404 Error

delete/v1/workspaces/{ws}/meetings/{id}

Delete a meeting
Scope: outcomes.manage.

A synced meeting comes back on the next sync; mark it canceled or unattributed instead.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 204 Deleted, 404 Error

get/v1/workspaces/{ws}/deals

List deals
Scope: outcomes.read.

Deals from the CRM, plus any logged by hand. Newest first.

ParameterInTypeDescription
ws *pathstring, uuid
fromquerystringStart of the period, inclusive. A date (2026-10-01) or a time. Default 30 days before to.
toquerystringEnd of the period. A date includes that whole day. Default now.
project_idquerystring, uuid
sequence_idquerystring, uuid
mission_idquerystring, uuid
statequery"open" | "won" | "lost"
attributedqueryboolean
limitqueryinteger
beforequerystring, date-time
Responses: 200 Deals

post/v1/workspaces/{ws}/deals

Log a deal
Scope: outcomes.manage.

For workspaces without a connected CRM. Give meeting_id, lead_id or contact_email to tie it to outreach.

ParameterInTypeDescription
ws *pathstring, uuid
name *bodystring
lead_idbodystring, uuid
contact_emailbodystring
meeting_idbodystring, uuid
sequence_idbodystring, uuid
project_idbodystring, uuid
mission_idbodystring, uuid
stagebodystring
statebody"open" | "won" | "lost"Default open.
amount_centsbodyinteger
currencybodystringDefault USD.
opened_atbodystring, date-time
closed_atbodystring, date-time
external_idbodystring
Responses: 201 The deal, 400 Error

get/v1/workspaces/{ws}/deals/{id}

Get a deal
Scope: outcomes.read.
ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 The deal, 404 Error

patch/v1/workspaces/{ws}/deals/{id}

Change a deal or its attribution
Scope: outcomes.manage.

A deal synced from a CRM only takes attribution changes here; change its name, stage or amount in the CRM.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
namebodystring
stagebodystring
statebody"open" | "won" | "lost"
amount_centsbodyinteger
currencybodystring
closed_atbodystring, date-time
sequence_idbodystring, uuid
project_idbodystring, uuid
mission_idbodystring
unattributedbodyboolean
Responses: 200 The deal, 400 Error, 404 Error

delete/v1/workspaces/{ws}/deals/{id}

Delete a deal
Scope: outcomes.manage.

Removes the copy here only; the CRM is not touched.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 204 Deleted, 404 Error

get/v1/workspaces/{ws}/spend

List the spend ledger
Scope: outcomes.read.

One-off charges, newest first. The ledger records cost; it never bills. Providers are paid directly.

ParameterInTypeDescription
ws *pathstring, uuid
fromquerystringStart of the period, inclusive. A date (2026-10-01) or a time. Default 30 days before to.
toquerystringEnd of the period. A date includes that whole day. Default now.
project_idquerystring, uuid
sequence_idquerystring, uuid
mission_idquerystring, uuid
categoryquery"data" | "verification" | "mailboxes" | "domains" | "sending" | "software" | "people" | "ads" | "other"
providerquerystring
limitqueryinteger
beforequerystring, date-time
Responses: 200 Entries

post/v1/workspaces/{ws}/spend

Record spend
Scope: outcomes.manage.

Give amount_cents when the money is known, or units with a unit_name (e.g. 1200 credit) to have reports price it with the workspace's rates. With a ref already in the ledger nothing is added and the existing entry comes back with 200, so retries never double count. project_id is filled from the sequence, mailbox or domain when left out.

ParameterInTypeDescription
ws *pathstring, uuid
category *body"data" | "verification" | "mailboxes" | "domains" | "sending" | "software" | "people" | "ads" | "other"
providerbodystring
amount_centsbodyinteger
unitsbodynumber
unit_namebodystring
currencybodystringDefault USD.
occurred_atbodystring, date-time
project_idbodystring, uuid
sequence_idbodystring, uuid
mission_idbodystring, uuid
mailbox_idbodystring, uuid
domainbodystringA sending domain.
integration_idbodystring, uuid
refbodystringIdempotency key, e.g. apollo:run:123.
notebodystring
Responses: 200 The ref was recorded before; the existing entry, 201 Recorded, 400 Error

get/v1/workspaces/{ws}/spend/{id}

Get a spend entry
Scope: outcomes.read.
ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 The entry, 404 Error

delete/v1/workspaces/{ws}/spend/{id}

Delete a spend entry
Scope: outcomes.manage.

For entries recorded by mistake. The deletion is in the audit log with the amount.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 204 Deleted, 404 Error

get/v1/workspaces/{ws}/spend-recurring

List recurring costs
Scope: outcomes.read.

Mailbox seats, subscriptions, people. Reports spread each over the days it was active.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 Recurring costs

post/v1/workspaces/{ws}/spend-recurring

Add a recurring cost
Scope: outcomes.manage.
ParameterInTypeDescription
ws *pathstring, uuid
name *bodystring
category *body"data" | "verification" | "mailboxes" | "domains" | "sending" | "software" | "people" | "ads" | "other"
providerbodystring
amount_cents *bodyinteger
currencybodystringDefault USD.
everybody"month" | "year"Default month.
starts_onbodystring, date
ends_onbodystring, date
project_idbodystring, uuid
sequence_idbodystring, uuid
notebodystring
Responses: 201 Added, 400 Error

put/v1/workspaces/{ws}/spend-recurring/{id}

Change a recurring cost
Scope: outcomes.manage.

Replaces every field. To stop a cost, set ends_on; past reports keep it.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
name *bodystring
category *body"data" | "verification" | "mailboxes" | "domains" | "sending" | "software" | "people" | "ads" | "other"
providerbodystring
amount_cents *bodyinteger
currencybodystringDefault USD.
everybody"month" | "year"Default month.
starts_onbodystring, date
ends_onbodystring, date
project_idbodystring, uuid
sequence_idbodystring, uuid
notebodystring
Responses: 200 Updated, 400 Error, 404 Error

delete/v1/workspaces/{ws}/spend-recurring/{id}

Delete a recurring cost
Scope: outcomes.manage.

Removes it from every report, past ones included. Set ends_on instead to keep history.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 204 Deleted, 404 Error

get/v1/workspaces/{ws}/spend-rates

List unit prices
Scope: outcomes.read.

Every rate, plus a zero-priced row for each provider and unit used in the ledger without one, so it is clear what still needs a price.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 Rates

put/v1/workspaces/{ws}/spend-rates

Set what a unit costs
Scope: outcomes.manage.

Providers match without case. unit_price_micros is millionths of the currency unit: a $0.004 credit is 4000.

ParameterInTypeDescription
ws *pathstring, uuid
provider *bodystring
unit_name *bodystring
unit_price_micros *bodyinteger
currencybodystringDefault USD.
Responses: 200 The rate, 400 Error

delete/v1/workspaces/{ws}/spend-rates

Remove a unit price
Scope: outcomes.manage.
ParameterInTypeDescription
ws *pathstring, uuid
provider *querystring
unit_name *querystring
Responses: 204 Removed, 404 Error

missions

get/v1/workspaces/{ws}/missions

List missions
Scope: missions.read.

Waiting for approval first, then running, paused, drafts and ended.

ParameterInTypeDescription
ws *pathstring, uuid
statusquerystringComma-separated statuses.
project_idquerystring, uuid
Responses: 200 Missions

post/v1/workspaces/{ws}/missions

Create a mission
Scope: missions.run.

Starts as a draft: writes made in it are dry runs until a person approves it. A mission created with an API token is run by that token; a person can assign any token with token_id. A token bound to a project creates missions only in that project.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 201 Created, 400 Error, 403 Error

post/v1/workspaces/{ws}/missions/plan

Plan a mission from a goal
Scope: missions.run.

Type a goal in plain words ("book 10 meetings with fintech CFOs in Australia by 30 November") and Claude drafts the mission: a measurable goal, steps, instructions for the agent, limits sized to your mailboxes, and the connectors it needs. The draft is saved as a mission in status draft, with a connect request for each missing connector and any questions for you. Nothing runs until a person approves it. Uses the workspace's Claude connection (connect it under Connectors with your own API key). Without one the call fails with 409 connector_needed. Claude is sent the goal, the project brief and a summary of the workspace, never lead lists or email addresses.

ParameterInTypeDescription
ws *pathstring, uuid
goal *bodystring
project_idbodystring, uuidPlan inside this project, using its brief.
Responses: 201 The draft mission and what the planner proposed, 400 Error, 403 Error, 409 Error, 502 Error

get/v1/workspaces/{ws}/missions/{id}

Check in on a mission
Scope: missions.read.

What an agent reads before it acts: the mission, the project brief (and whether it changed since approval), limits, progress, open connect requests and the latest notes.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 The mission and its context, 404 Error

patch/v1/workspaces/{ws}/missions/{id}

Change a mission
Scope: missions.run.

Once a mission is approved, changing its mode, limits, token or project also needs missions.approve, and an agent cannot change its own. An ended mission cannot change.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
namebodystring
project_idbodystring, uuid
goalbodystringThe goal in words.
goal_metricbody"meetings" | "replies" | "sends" | "leads"
goal_targetbodyinteger
instructionsbodystringGuidance on top of the project brief (Markdown).
planbodystringThe agent's plan (Markdown).
modebody"dry_run" | "approve_writes" | "autonomous"dry_run: writes recorded, never run. approve_writes: each write waits for a person. autonomous: writes run within the limits.
token_idbodystring, uuidThe only token that may act in the mission.
max_sendsbodyinteger
max_spend_centsbodyintegerUS cents
ends_atbodystring, date-timeThe mission ends itself at this time.
Responses: 200 Updated, 400 Error, 403 Error, 409 Error

post/v1/workspaces/{ws}/missions/{id}/submit

Submit the plan for approval
Scope: missions.run.

From draft. Writes stay dry runs until approved.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
planbodystringMarkdown.
Responses: 200 Submitted, 409 Error

post/v1/workspaces/{ws}/missions/{id}/approve

Approve and start a mission
Scope: missions.approve.

A person, or an approver token that is not the mission's own. Records the project brief version it was approved against. runner: builtin hands the mission to Outflare's own agent, which runs it in turns with the workspace's Claude key. It gets a token of its own (operator scopes, never more than yours, never missions.approve) and goes through the same gate as any agent, so the mode still decides whether its writes run, wait for you or are dry runs. A turn runs at once, whenever a person writes to the mission or decides on a held change, and when the agent asks to look again. Answers 409 connector_needed until Claude is connected.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
modebody"dry_run" | "approve_writes" | "autonomous"dry_run: writes recorded, never run. approve_writes: each write waits for a person. autonomous: writes run within the limits.
notebodystring
runnerbody"builtin" | "external"Who carries the mission out. Left out, it stays as it is (external for a new mission).
Responses: 200 Running, 403 Error, 409 Error

post/v1/workspaces/{ws}/missions/{id}/pause

Pause a mission
Scope: missions.run

, so an agent can pause itself. Writes are refused and its campaigns stop sending until it resumes.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
reasonbodystring
Responses: 200 Paused, 409 Error

post/v1/workspaces/{ws}/missions/{id}/resume

Resume a paused mission
Scope: missions.approve

, and not the mission's own agent.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 Running, 403 Error, 409 Error

post/v1/workspaces/{ws}/missions/{id}/stop

Stop a mission now
Scope: missions.run.

Ends it: held writes are canceled, its campaigns are paused, and further calls in it are refused.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
reasonbodystring
Responses: 200 Stopped, 409 Error

post/v1/workspaces/{ws}/missions/{id}/complete

Finish a mission with a report
Scope: missions.run.

The agent's final report, in Markdown. Its campaigns are paused.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
reportbodystring
Responses: 200 Completed, 409 Error

get/v1/workspaces/{ws}/missions/{id}/calls

Every call made in the mission, newest first
Scope: missions.read.

What a write changed is in the audit log under the same request_id.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
writesquerybooleanOnly writes.
beforequeryintegerA call id
limitqueryinteger
Responses: 200 Calls

get/v1/workspaces/{ws}/missions/{id}/actions

Writes the mission held or dry-ran
Scope: missions.read.
ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
statusquerystringComma-separated.
Responses: 200 Actions

get/v1/workspaces/{ws}/missions/{id}/actions/{action}

One held write, with its decision and result
Scope: missions.read.

Agents poll this after a 202.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
action *pathstring, uuid
Responses: 200 The action, 404 Error

post/v1/workspaces/{ws}/missions/{id}/actions/{action}/approve

Approve and run a held write
Scope: missions.approve.

Runs the stored request as the agent that made it, through every normal check, and keeps the response as the result. Works on pending writes, and on dry runs once the mission is running. Never by the agent that made it.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
action *pathstring, uuid
notebodystring
Responses: 200 The action, 403 Error, 409 Error

post/v1/workspaces/{ws}/missions/{id}/actions/{action}/reject

Reject a held write
Scope: missions.approve.

A note is passed to the agent as an instruction.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
action *pathstring, uuid
notebodystring
Responses: 200 Rejected, 409 Error

get/v1/workspaces/{ws}/missions/{id}/updates

Notes between the agent and people
Scope: missions.read.
ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
open_requestsquerybooleanOnly connect requests not yet resolved.
beforequeryinteger
limitqueryinteger
Responses: 200 Updates

post/v1/workspaces/{ws}/missions/{id}/updates

Post a note, question, instruction or connect request
Scope: missions.run.

kind connect_request asks a person to connect a tool the agent needs (they paste the key or sign in with OAuth); give connector as the connector id.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
kindbody"note" | "progress" | "question" | "instruction" | "connect_request"
body *bodystring
connectorbodystring
Responses: 201 Posted, 400 Error, 409 Error

post/v1/workspaces/{ws}/missions/{id}/updates/{update}/resolve

Close a connect request
Scope: missions.run.

The tool was connected, or a person decided not to.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
update *pathinteger
notebodystring
Responses: 204 Resolved, 409 Error

get/v1/workspaces/{ws}/mission-actions

Writes waiting for a person, across missions
Scope: missions.read.

The approval inbox.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 Pending actions

imports

get/v1/workspaces/{ws}/integrations/{id}/import-preview

See what an import from another sequencer would bring over
Scope: integrations.read.

Reads the connected Instantly or Smartlead account live: campaigns with their steps, and sending accounts matched to this workspace's mailboxes by address. Changes nothing.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 The outside account's campaigns and sending accounts, 400 Error, 404 Error, 422 Error, 502 Error

post/v1/workspaces/{ws}/integrations/{id}/imports

Import campaigns, leads and mailboxes from another sequencer
Scope: sequences.manage

, plus leads.manage when importing leads. Runs in the background; read it with GET /imports/{id}. Campaigns become draft sequences, so nothing sends until someone starts them. Leads keep the step they reached and the mailbox that wrote to them; bounced and unsubscribed leads go on the suppression list. Running it again updates the same sequences instead of duplicating them.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
campaign_idsbodystring[]Only these campaigns. Default every campaign.
leadsbodybooleanDefault true.
mailboxesbodybooleanMatch sending accounts. Default true.
Responses: 202 Queued, 400 Error, 403 Error, 404 Error

get/v1/workspaces/{ws}/imports

List imports
Scope: sequences.read.

The 50 newest.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 Imports, newest first

get/v1/workspaces/{ws}/imports/{id}

Read an import and what it did
Scope: sequences.read.
ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 The import, 404 Error

reports

get/v1/workspaces/{ws}/reports

Outreach and deliverability report
Scope: sequences.read.

Sends, leads contacted, new leads and follow-ups, failures, policy blocks, bounces, replies (and replied leads), auto-replies, opt-outs, unsubscribes and warm-up placement, with reply, bounce, opt-out and placement rates. Totals for the period, and rows by day, week, sequence, step, mailbox or domain. Default period is the last 30 days.

ParameterInTypeDescription
ws *pathstring, uuid
fromquerystringA date (2026-10-01) or RFC 3339 time.
toquerystringA date (inclusive) or RFC 3339 time. Default now.
group_byquery"day" | "week" | "sequence" | "step" | "mailbox" | "domain"
timezonequerystringIANA zone for dates and day buckets. Default UTC.
sequence_idquerystring, uuid
mailbox_idquerystring, uuid
domainquerystring
Responses: 200 The report, 400 Error

projects

get/v1/workspaces/{ws}/projects

List projects
Scope: projects.read.

A project is one go-to-market motion: an offer sold to one ideal customer in one market. It holds the brief people and agents work from and owns domains, mailboxes, lead lists and sequences. A project token sees only its own project.

ParameterInTypeDescription
ws *pathstring, uuid
statusquery"active" | "paused" | "archived" | "all"Default lists every project but archived ones.
Responses: 200 Projects by name, archived last

post/v1/workspaces/{ws}/projects

Create a project
Scope: projects.manage

, not available to project tokens. Names are unique in the workspace, ignoring case.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 201 The project, 400 Error

get/v1/workspaces/{ws}/projects/{id}

Get a project
Scope: projects.read.
ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 The project, 404 Error

patch/v1/workspaces/{ws}/projects/{id}

Edit a project's brief, status or daily send limit
Scope: projects.manage.

A change to offer, audience, market, brief or icp bumps brief_version. Send the brief_version you read to make sure you are not overwriting someone else's edit: if the brief has changed since, the update fails with 409 brief_changed. status: paused holds every send from the project's sequences; archived also hides it from the default list.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
namebodystring
statusbody"active" | "paused" | "archived"
offerbodystring
audiencebodystring
marketbodystring
briefbodystring
icpbodyReplaces the whole ICP. Null clears it.
daily_send_limitbodyinteger
brief_versionbodyintegerUpdates only: the version your edit is based on. A brief edit fails with 409 if the brief changed since.
Responses: 200 The project, 400 Error, 404 Error, 409 Error

delete/v1/workspaces/{ws}/projects/{id}

Delete a project
Scope: projects.manage

, not available to project tokens. Its domains, mailboxes, lists and sequences stay and become shared by the workspace. Fails with 409 project_has_tokens while the project has live API tokens; revoke them first, or archive the project instead.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 204 Deleted, 404 Error, 409 Error

get/v1/workspaces/{ws}/projects/{id}/brief

Read a project's brief
Scope: projects.read.

What an agent should read before writing copy, building a list or planning a campaign for the project: the structured fields plus the whole brief as one Markdown document. Keep version and compare it later to know the brief moved.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 The brief, 404 Error

post/v1/workspaces/{ws}/projects/{id}/add

Move domains, mailboxes, lists or sequences into a project
Scope: projects.manage

, plus mailboxes.manage for domains and mailboxes, leads.manage for lists and sequences.manage for sequences. Not available to project tokens. Records in another project move to this one. A domain brings its mailboxes unless with_mailboxes is false. A project's mailboxes only send for that project's sequences, so moving one can take it off a sequence (it finishes follow-ups first).

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
domainsbodystring[]
mailbox_idsbodystring, uuid[]
lead_list_idsbodystring, uuid[]
sequence_idsbodystring, uuid[]
with_mailboxesbodybooleanWhether a domain's mailboxes move with it. Default true.
Responses: 200 How many records moved. Unknown ids and names are ignored, 400 Error, 403 Error, 404 Error

post/v1/workspaces/{ws}/projects/{id}/remove

Make a project's domains, mailboxes, lists or sequences shared again
Signed in

Same scopes as addToProject. Only records in this project change.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
domainsbodystring[]
mailbox_idsbodystring, uuid[]
lead_list_idsbodystring, uuid[]
sequence_idsbodystring, uuid[]
with_mailboxesbodybooleanWhether a domain's mailboxes move with it. Default true.
Responses: 200 How many records moved, 400 Error, 403 Error, 404 Error

allocation

get/v1/workspaces/{ws}/teams

List teams
Scope: members.read.

Teams own sequences and senders. A workspace with no teams shares every mailbox.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 Teams by name

post/v1/workspaces/{ws}/teams

Create a team
Scope: members.manage.

Names are unique in the workspace, ignoring case.

ParameterInTypeDescription
ws *pathstring, uuid
namebodystring
allocation_modebody"suggest" | "automatic"
member_idsbodystring, uuid[]
Responses: 201 The team, 400 Error

patch/v1/workspaces/{ws}/teams/{id}

Rename a team, change its mode or replace its members
Scope: members.manage.

member_ids replaces the whole list when sent.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
namebodystring
allocation_modebody"suggest" | "automatic"
member_idsbodystring, uuid[]
Responses: 200 The team, 400 Error, 404 Error

delete/v1/workspaces/{ws}/teams/{id}

Delete a team
Scope: members.manage.

Its sequences and senders stay, without a team.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 204 Deleted, 404 Error

get/v1/workspaces/{ws}/senders

List senders
Scope: mailboxes.read.

A sender is who mailboxes send as: a person, a persona, or a shared identity. Each mailbox has at most one sender, always confirmed by a person or agent, never guessed.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 Senders by name

post/v1/workspaces/{ws}/senders

Create a sender
Scope: mailboxes.manage.
ParameterInTypeDescription
ws *pathstring, uuid
namebodystring
kindbody"person" | "persona" | "shared"
user_idbodystring, uuid
statusbody"active" | "retired"
team_idsbodystring, uuid[]
Responses: 201 The sender, 400 Error

patch/v1/workspaces/{ws}/senders/{id}

Change a sender, or retire it
Scope: mailboxes.manage.

A retired sender's mailboxes finish their follow-ups and take no new leads.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
namebodystring
kindbody"person" | "persona" | "shared"
user_idbodystring, uuid
statusbody"active" | "retired"
team_idsbodystring, uuid[]
Responses: 200 The sender, 400 Error, 404 Error

delete/v1/workspaces/{ws}/senders/{id}

Delete a sender
Scope: mailboxes.manage.

Its mailboxes become unconfirmed.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 204 Deleted, 404 Error

get/v1/workspaces/{ws}/senders/matches

Suggest senders for unconfirmed mailboxes
Scope: mailboxes.read.

Matches a mailbox to a sender only when its display name is exactly the sender's name. Confirm each with POST /mailboxes/sender.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 Matches, plus how many mailboxes have no confirmed sender

post/v1/workspaces/{ws}/mailboxes/sender

Confirm who mailboxes send as, in bulk
Scope: mailboxes.manage.

A null sender_id clears it.

ParameterInTypeDescription
ws *pathstring, uuid
mailbox_ids *bodystring, uuid[]
sender_id *bodystring, uuid
Responses: 200 How many mailboxes changed, 400 Error

get/v1/workspaces/{ws}/allocation/settings

Get the workspace's allocation mode
Scope: sequences.read.

Teams and sequences can override the mode.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 The mode

patch/v1/workspaces/{ws}/allocation/settings

Set the workspace's allocation mode
Scope: members.manage.
ParameterInTypeDescription
ws *pathstring, uuid
mode *body"suggest" | "automatic"automatic: the allocator assigns mailboxes itself and explains each change. suggest: it proposes changes for a person to apply.
Responses: 200 The mode

get/v1/workspaces/{ws}/allocation/plan

Dry run, showing what the allocator would change now
Scope: sequences.read.

Changes nothing.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 Planned changes and each active sequence's capacity

post/v1/workspaces/{ws}/allocation/run

Plan and apply now
Scope: sequences.manage.

The allocator also runs every minute and after any relevant change. In suggest mode, and for changes that need another team's or the owner's approval, it records suggestions instead.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 What was applied and what was suggested

get/v1/workspaces/{ws}/allocation/suggestions

List suggestions waiting on a person
Scope: sequences.read.
ParameterInTypeDescription
ws *pathstring, uuid
sequence_idquerystring, uuid
Responses: 200 Suggestions, approvals first

post/v1/workspaces/{ws}/allocation/suggestions/{id}/apply

Apply or approve a suggestion
Scope: sequences.manage.

A team_approval needs a member of that team, or a workspace owner or admin. An owner_approval needs a person, not an API token. Fails with 409 if the mailbox changed since.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 The change made, 403 Error, 409 Error

post/v1/workspaces/{ws}/allocation/suggestions/{id}/decline

Decline a suggestion, so it is not proposed again
Scope: sequences.manage.
ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 204 Declined, 404 Error

get/v1/workspaces/{ws}/allocation/changes

Mailbox assignment history, newest first
Scope: sequences.read.
ParameterInTypeDescription
ws *pathstring, uuid
sequence_idquerystring, uuid
mailbox_idquerystring, uuid
limitqueryinteger
Responses: 200 Changes

post/v1/workspaces/{ws}/allocation/changes/{id}/undo

Undo a change
Scope: sequences.manage.

Only the latest change to a mailbox can be undone. Undoing an automatic choice also stops the allocator from making it again.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 The reversing change, 400 Error, 409 Error

get/v1/workspaces/{ws}/allocation/attention

What needs a person
Scope: sequences.read.

Approvals, suggestions to review, shortages, follow-ups whose mailbox is gone and unconfirmed senders.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 Items needing attention

get/v1/workspaces/{ws}/platforms

List connected sequencer accounts
Scope: sequences.read.

Every connected account of an outside sequencer (EmailBison, Instantly, Smartlead) that mailbox allocation reads. Outflare reads each one every 5 minutes; its live campaigns become sequences with links, and its mailboxes become mailboxes with has_login: false unless Outflare already has them.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 The accounts

post/v1/workspaces/{ws}/platforms/{id}/sync

Read a sequencer account now
Scope: sequences.manage.

Reads mailboxes and campaigns now instead of waiting for the next 5-minute sync. Mailboxes added to or removed from a campaign in the platform are accepted as manual changes; attaches Outflare asked for are confirmed once the platform shows them.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuidThe connection (integration) id.
Responses: 200 What the sync found and changed, 400 Error, 404 Error, 502 Error

post/v1/workspaces/{ws}/platforms/{id}/review

Let allocation change a newly connected account
Scope: sequences.manage.

Until this is called, every change for the account's campaigns is only suggested, so you can look at the first plan before anything moves. Runs a plan straight away.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuidThe connection (integration) id.
Responses: 200 The plan that ran, 404 Error

post/v1/workspaces/{ws}/mailboxes/assign

Assign mailboxes to a sequence by hand
Scope: sequences.manage.

A mailbox with leads mid-sequence elsewhere drains first: it finishes those follow-ups, then joins. Locks are honoured; a mailbox locked elsewhere is refused.

ParameterInTypeDescription
ws *pathstring, uuid
mailbox_ids *bodystring, uuid[]
sequence_id *bodystring, uuid
Responses: 200 One result per mailbox, 400 Error

post/v1/workspaces/{ws}/mailboxes/release

Take mailboxes off their sequence
Scope: sequences.manage.

Mailboxes with leads in flight drain first.

ParameterInTypeDescription
ws *pathstring, uuid
mailbox_ids *bodystring, uuid[]
Responses: 200 One result per mailbox

post/v1/workspaces/{ws}/mailboxes/lock

Lock mailboxes, or clear their locks
Scope: mailboxes.manage.

sequence: keep on this sequence (assigns it now). team: only this team's sequences. manual_only: the allocator never touches it. A null kind clears the lock. until is optional.

ParameterInTypeDescription
ws *pathstring, uuid
mailbox_ids *bodystring, uuid[]
kind *body"sequence" | "team" | "manual_only"
sequence_idbodystring, uuid
team_idbodystring, uuid
untilbodystring, date-time
notebodystring
Responses: 200 Mailboxes changed, and any assignments a sequence lock made, 400 Error

get/v1/workspaces/{ws}/sequences/{id}/allocation

A sequence's mailboxes, capacity, suggestions and history
Scope: sequences.read.
ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 The sequence's allocation, 404 Error

post/v1/workspaces/{ws}/sequences/{id}/orphaned-follow-ups

Decide what happens to follow-ups whose mailbox is gone
Scope: sequences.manage.

cancel stops them. handoff sends the next step from another of the sequence's mailboxes as a fresh email, not a reply, since the new sender never wrote before.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
action *body"cancel" | "handoff"
Responses: 200 How many leads changed

infrastructure

get/v1/workspaces/{ws}/infrastructure

Mailboxes by lifecycle stage, the ones not ready, and plans in progress
Scope: infrastructure.read.

Counts of mailboxes in each lifecycle stage (warming, ready, resting, retired), every mailbox that is not ready with the reason and until when, the plans that are not finished, and the suppliers plans can buy from on this server.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 Overview

post/v1/workspaces/{ws}/infrastructure/preview

Draft a plan without saving it
Scope: infrastructure.read.

Takes the same fields as creating a plan and returns the domains, mailboxes, timeline, daily capacity and warnings it would have. Nothing is saved or bought.

ParameterInTypeDescription
ws *pathstring, uuid
namebodystring
project_idbodystring, uuid
supplierbodystringmanual (you buy; the plan waits for you), or a supplier connector such as cheapinboxes. See suppliers on GET /infrastructure.
supplier_integration_idbodystring, uuidThe connected supplier account to buy with.
providerbody"google" | "microsoft"
domain_countbodyinteger
mailboxes_per_domainbodyinteger
brandbodystringDomain names are made from it
personasbodyobject[]
domainsbodystring[]Domain names you chose. The rest are suggested from brand.
ready_bybodystring, date
warmup_daysbodyinteger
mailbox_daily_limitbodyintegerCold emails a day per mailbox once ready.
rest_belowbodyintegerA ready mailbox rests when its warm-up inbox placement falls below this percent.
retire_after_daysbodyintegerRetire a mailbox that rested this long without recovering. Null never does.
notesbodystring
Responses: 200 The draft, 400 Error

post/v1/workspaces/{ws}/infrastructure/move

Move mailboxes to warming, ready, resting or retired
Scope: infrastructure.manage.

Works on any mailbox, including ones you imported yourself. Only ready mailboxes send cold email; warm-up carries on while warming and resting. Retiring pauses the mailbox and turns warm-up off for good; its unfinished follow-ups wait for you to cancel them or hand them to another mailbox. A mailbox that cannot make the move is returned with skipped saying why.

ParameterInTypeDescription
ws *pathstring, uuid
mailbox_ids *bodystring, uuid[]
stage *body"warming" | "ready" | "resting" | "retired"Where a mailbox is in its working life. Only ready mailboxes send cold email. warming: warming up. resting: no cold email for a while, warm-up continues. retired: out of use for good.
reasonbodystringShown on the mailbox and in the audit log.
untilbodystring, date-timeResting: when the rest ends (default 7 days). Warming: the earliest it can be ready (default 14 days).
auto_restbodybooleanRest on its own when warm-up inbox placement drops, and come back when it recovers.
cancel_at_supplierbodybooleanRetiring a mailbox a plan bought also cancels it at the supplier, at the end of the period.
Responses: 200 What happened to each mailbox, 400 Error

get/v1/workspaces/{ws}/infra-plans

List infrastructure plans
Scope: infrastructure.read.

Newest first, without their items.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 200 Plans

post/v1/workspaces/{ws}/infra-plans

Draft a plan for domains and mailboxes
Scope: infrastructure.manage.

"10 domains and 30 mailboxes for this project by the 14th" is domain_count: 10, mailboxes_per_domain: 3, ready_by: "2026-11-14", project_id. Domain names come from brand unless you list them in domains; listing a domain the workspace already has adds mailboxes to it instead of buying it. Mailbox names come from personas. A plan with a supplier is priced in the background; read it again until quote.status is ready. A draft buys nothing until it is approved.

ParameterInTypeDescription
ws *pathstring, uuid
Responses: 201 The draft plan, 400 Error

get/v1/workspaces/{ws}/infra-plans/{id}

Get a plan with every domain and mailbox and where each one is
Scope: infrastructure.read
ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 The plan, 404 Error

patch/v1/workspaces/{ws}/infra-plans/{id}

Change a draft plan
Scope: infrastructure.manage.

Drafts only. Domains already in the plan are kept unless you send domains; ones the supplier said are taken are replaced with new suggestions.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
namebodystring
project_idbodystring, uuid
supplierbodystringmanual (you buy; the plan waits for you), or a supplier connector such as cheapinboxes. See suppliers on GET /infrastructure.
supplier_integration_idbodystring, uuidThe connected supplier account to buy with.
providerbody"google" | "microsoft"
domain_countbodyinteger
mailboxes_per_domainbodyinteger
brandbodystringDomain names are made from it
personasbodyobject[]
domainsbodystring[]Domain names you chose. The rest are suggested from brand.
ready_bybodystring, date
warmup_daysbodyinteger
mailbox_daily_limitbodyintegerCold emails a day per mailbox once ready.
rest_belowbodyintegerA ready mailbox rests when its warm-up inbox placement falls below this percent.
retire_after_daysbodyintegerRetire a mailbox that rested this long without recovering. Null never does.
notesbodystring
Responses: 200 The plan, 400 Error, 409 Error

delete/v1/workspaces/{ws}/infra-plans/{id}

Delete a draft or cancelled plan
Scope: infrastructure.manage.

Domains and mailboxes it built stay.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 204 Deleted, 404 Error, 409 Error

post/v1/workspaces/{ws}/infra-plans/{id}/approve

Approve a plan, which starts buying
Scope: infrastructure.approve

(owners and admins; give it to an agent token only if it may spend). Fails with 409 while the plan has a blocking warning. When the plan has a price, send once_cents and monthly_cents exactly as its quote shows them, so a price that changed is never approved by accident. Customers pay the supplier directly, on their own account.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
once_centsbodyinteger, int64
monthly_centsbodyinteger, int64
Responses: 200 The plan, now building, 409 Error

post/v1/workspaces/{ws}/infra-plans/{id}/cancel

Stop a plan
Scope: infrastructure.manage.

Steps not done yet are cancelled. An order already placed with a supplier is not undone; cancel it in your supplier account if you need to.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 200 The plan, 409 Error

post/v1/workspaces/{ws}/infra-plans/{id}/bought

Tell a plan you bought its domains
Scope: infrastructure.manage.

For plans you buy yourself (supplier: manual). Moves the domains on to the DNS check. A domain also moves on by itself once its MX records point at the plan's provider.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
domainsbodystring[]The domains bought; every domain waiting to be bought when left out.
Responses: 200 How many domains moved on, 409 Error

post/v1/workspaces/{ws}/infra-plans/{id}/refresh

Get a new quote for a draft, or check a building plan now
Scope: infrastructure.manage.

Runs in the background. Building plans are also checked every 5 minutes on their own.

ParameterInTypeDescription
ws *pathstring, uuid
id *pathstring, uuid
Responses: 202 Queued, 404 Error