The canonical specification includes reusable schemas for campaigns, target lists, lead outcomes, connected accounts, settings, cursors, and the shared error envelope.
Error
object · Required: code, message
| Property | Type | Required | Rules |
|---|
code | string | Yes | — |
message | string | Yes | — |
Settings
object · Required: timezone, blacklist
| Property | Type | Required | Rules |
|---|
timezone | string | Yes | — |
blacklist | string | Yes | Newline-delimited X usernames. |
SettingsUpdate
object
- At least 1 property must be provided.
| Property | Type | Required | Rules |
|---|
timezone | string | No | — |
blacklist | string | No | — |
Campaign
object · Required: id, name, status, createdAt, targetListIds, accountIds, sequence, messagesSent, contactedCount
| Property | Type | Required | Rules |
|---|
id | string | Yes | — |
name | string | Yes | — |
description | string | No | — |
status | string | Yes | Allowed: draft, active, paused, completed |
statusReason | string | null | No | Why the campaign is in its current status, e.g. user_paused, completed_all_leads. |
statusChangedAt | string | null | No | — |
createdAt | string | Yes | — |
targetListIds | array<string> | Yes | — |
accountIds | array<string> | Yes | X user ids of the sending accounts. |
sequence | array<SequenceStep> | Yes | — |
workMins | integer | No | Minutes per day the campaign sends. |
perDay | integer | No | Minimum: 0 · Maximum: 500 · Daily send target across all accounts. Ignored when volumeMode is managed. |
sendDays | array<string> | No | — |
volumeMode | string | No | Allowed: manual, managed · managed lets Xsender pace sends within each account’s safe limit. |
messagesSent | integer | Yes | Successful sends so far, all time. |
contactedCount | integer | Yes | Leads the campaign has claimed or messaged. |
nextSendAt | string | null | No | Next planned send, null when nothing is scheduled. |
confirmationRequired | boolean | No | true until the current content revision has been activated with confirmed: true. |
CampaignUpdateInput
object
- At least 1 property must be provided.
| Property | Type | Required | Rules |
|---|
name | string | No | — |
description | string | No | — |
workMins | array<unspecified> | No | Minimum items: 2 · Maximum items: 2 |
perDay | integer | No | Minimum: 0 · Maximum: 500 |
sendDays | array<string> | No | — |
targetListIds | array<string> | No | — |
accountIds | array<string> | No | — |
sequence | array<object> | No | — |
CampaignCreateInput
CampaignUpdateInput + object · Required: name
- At least 1 property must be provided.
| Property | Type | Required | Rules |
|---|
name | string | Yes | — |
description | string | No | — |
workMins | array<unspecified> | No | Minimum items: 2 · Maximum items: 2 |
perDay | integer | No | Minimum: 0 · Maximum: 500 |
sendDays | array<string> | No | — |
targetListIds | array<string> | No | — |
accountIds | array<string> | No | — |
sequence | array<object> | No | — |
TargetList
object · Required: id, name, count, variableSchema, createdAt
| Property | Type | Required | Rules |
|---|
id | string | Yes | — |
name | string | Yes | — |
count | integer | Yes | Usable leads on the list. |
handles | array<string> | No | Lead usernames. Present on single-list reads and writes; use /leads to page through variables. |
variableSchema | array<TargetVariableField> | Yes | — |
createdAt | string | Yes | — |
TargetVariableField
object · Required: key, label
| Property | Type | Required | Rules |
|---|
key | string | Yes | Pattern: ^[A-Za-z][A-Za-z0-9]*$ |
label | string | Yes | Maximum length: 200 |
TargetListUpdateInput
object
- At least 1 property must be provided.
- Requires at least one of: `name` or `handles` or `leads`.
- When `variableSchema` is provided, also requires `leads`.
- When any `leads[].variables` has at least 1 property, also requires `variableSchema` (Minimum items: 1).
| Property | Type | Required | Rules |
|---|
name | string | No | — |
handles | array<string> | No | — |
leads | array<Lead> | No | — |
variableSchema | array<TargetVariableField> | No | — |
TargetListCreateInput
TargetListUpdateInput + object · Required: name
- At least 1 property must be provided.
- When `variableSchema` is provided, also requires `leads`.
- When any `leads[].variables` has at least 1 property, also requires `variableSchema` (Minimum items: 1).
- Requires at least one of: `handles` or `leads`.
| Property | Type | Required | Rules |
|---|
name | string | Yes | — |
handles | array<string> | No | — |
leads | array<Lead> | No | — |
variableSchema | array<TargetVariableField> | No | — |
Lead
object · Required: username, variables
| Property | Type | Required | Rules |
|---|
username | string | Yes | — |
variables | object | Yes | — |
AccountPauseInput
object · Required: paused
| Property | Type | Required | Rules |
|---|
paused | boolean | Yes | — |
ConnectedAccount
object · Required: user_id, username, installation_id, online, paused, busy, login_state, safety_cooldown_until
| Property | Type | Required | Rules |
|---|
user_id | string | Yes | — |
username | string | null | Yes | — |
installation_id | string | Yes | — |
online | boolean | Yes | true while the extension for this account has checked in during the last two minutes. |
paused | boolean | Yes | — |
busy | boolean | Yes | true while a send is in progress on this account. |
login_state | string | Yes | logged_in, logged_out or unknown as last reported by the extension. |
safety_cooldown_until | string | null | Yes | Set while X asked this account to slow down; sends resume after it. |
ConnectedAccountPage
object · Required: data, next_cursor
| Property | Type | Required | Rules |
|---|
data | array<ConnectedAccount> | Yes | — |
next_cursor | string | null | Yes | — |
CampaignPage
object · Required: data, next_cursor
| Property | Type | Required | Rules |
|---|
data | array<Campaign> | Yes | — |
next_cursor | string | null | Yes | — |
TargetListPage
object · Required: data, next_cursor
| Property | Type | Required | Rules |
|---|
data | array<TargetList> | Yes | — |
next_cursor | string | null | Yes | — |
LeadPage
object · Required: data, next_cursor
| Property | Type | Required | Rules |
|---|
data | array<Lead> | Yes | — |
next_cursor | string | null | Yes | — |
SequenceVariant
object · Required: text
| Property | Type | Required | Rules |
|---|
text | string | Yes | Message text. Placeholders like {{company}} are filled from the lead’s custom fields. |
SequenceStep
object · Required: label, variants
| Property | Type | Required | Rules |
|---|
id | string | No | — |
label | string | Yes | "First message" or "Follow-up N"; assigned by position. |
delay | object | No | Follow-up wait after the previous step (follow-ups only). |
variants | array<SequenceVariant> | Yes | A/B variants for this step; one is picked per lead. |
LeadOutcome
object · Required: username, outcome, at, message_index, variant_index, attempts
| Property | Type | Required | Rules |
|---|
username | string | Yes | — |
outcome | string | Yes | Allowed: sent, skipped, rate_limited, blocked, account_locked, failed, expired, auth_required, cancelled · sent is the only success. skipped means X reported the lead cannot be messaged. |
error_code | string | null | No | Machine-readable reason for a non-sent outcome. |
at | string | Yes | When the attempt finished. |
account_id | string | null | No | X user id of the sending account (matches ConnectedAccount.user_id). |
message_index | integer | Yes | 0 for the first message, 1 for the first follow-up, and so on. |
variant_index | integer | Yes | — |
attempts | integer | Yes | Minimum: 1 |
LeadOutcomePage
object · Required: data, next_cursor
| Property | Type | Required | Rules |
|---|
data | array<LeadOutcome> | Yes | — |
next_cursor | string | null | Yes | — |
TargetListLeadsAppendInput
object
- At least 1 property must be provided.
- Requires at least one of: `handles` or `leads`.
- When `variableSchema` is provided, also requires `leads`.
| Property | Type | Required | Rules |
|---|
handles | array<string> | No | — |
leads | array<Lead> | No | — |
variableSchema | array<TargetVariableField> | No | Declares the variable keys used in leads[].variables. New keys are added to the list’s existing schema. |
TargetListLeadsAppendResult
object · Required: target_list, added, skipped_duplicates, skipped_contacted
| Property | Type | Required | Rules |
|---|
target_list | TargetList | null | Yes | The list after the change; null when nothing was added. |
added | integer | Yes | — |
skipped_duplicates | integer | Yes | Usernames already on the list. |
skipped_contacted | integer | Yes | Usernames this workspace has already messaged from any campaign. |
MetricsRange
object · Required: all_time, from, to
| Property | Type | Required | Rules |
|---|
all_time | boolean | Yes | — |
from | string | null | Yes | Start of the window the totals cover, or null for all-time. |
to | string | null | Yes | End of the window the totals cover, or null for open-ended. |
MetricsMessaging
object · Required: sent, skipped, rate_limited, blocked, account_locked, auth_required, failed, expired, cancelled, terminal, deliverable, unique_recipients, retry_attempts
| Property | Type | Required | Rules |
|---|
sent | integer | Yes | Successful sends (the only success). |
skipped | integer | Yes | — |
rate_limited | integer | Yes | — |
blocked | integer | Yes | — |
account_locked | integer | Yes | — |
auth_required | integer | Yes | — |
failed | integer | Yes | — |
expired | integer | Yes | — |
cancelled | integer | Yes | — |
terminal | integer | Yes | Attempts that reached a final outcome (sent plus every failure kind). |
deliverable | integer | Yes | Terminal attempts excluding cancelled ones. |
unique_recipients | integer | Yes | — |
retry_attempts | integer | Yes | — |
MetricsEngagement
object · Required: contacted, replied
| Property | Type | Required | Rules |
|---|
contacted | integer | Yes | Distinct recipients messaged in the window. |
replied | integer | Yes | Contacted recipients who replied. Requires reply tracking. |
MetricsDailyPoint
object · Required: day, sent, failed, skipped, total
| Property | Type | Required | Rules |
|---|
day | string | Yes | Calendar day (YYYY-MM-DD) in the workspace timezone. |
sent | integer | Yes | — |
failed | integer | Yes | — |
skipped | integer | Yes | — |
total | integer | Yes | — |
MetricsCampaign
object · Required: campaign_id, sent, skipped, rate_limited, blocked, failed, attempted, contacted, replies
| Property | Type | Required | Rules |
|---|
campaign_id | string | Yes | — |
sent | integer | Yes | — |
skipped | integer | Yes | — |
rate_limited | integer | Yes | — |
blocked | integer | Yes | — |
failed | integer | Yes | — |
attempted | integer | Yes | — |
contacted | integer | Yes | — |
replies | integer | Yes | — |
MetricsAccount
object · Required: account_id, sent, failed, risk_stops, contacted, replied
| Property | Type | Required | Rules |
|---|
account_id | string | Yes | X user id of the sending account (matches ConnectedAccount.user_id). |
sent | integer | Yes | — |
failed | integer | Yes | — |
risk_stops | integer | Yes | Sends stopped for safety (blocks, rate limits, locks). |
contacted | integer | Yes | — |
replied | integer | Yes | — |
MetricsVariant
object · Required: campaign_id, message_index, variant_index, sent, replies
| Property | Type | Required | Rules |
|---|
campaign_id | string | Yes | — |
message_index | integer | Yes | 0 for the first message, 1 for the first follow-up, and so on. |
variant_index | integer | Yes | — |
sent | integer | Yes | — |
replies | integer | Yes | — |
MetricsBlocker
object · Required: code, count
| Property | Type | Required | Rules |
|---|
code | string | Yes | Machine-readable reason sends did not complete. |
count | integer | Yes | — |
Metrics
object · Required: range, messaging, engagement, daily, campaigns, accounts, variants, blockers, pace
| Property | Type | Required | Rules |
|---|
range | MetricsRange | Yes | — |
messaging | MetricsMessaging | Yes | — |
engagement | MetricsEngagement | Yes | — |
daily | array<MetricsDailyPoint> | Yes | Per-day send activity, oldest first. |
campaigns | array<MetricsCampaign> | Yes | Per-campaign outcome totals. |
accounts | array<MetricsAccount> | Yes | Per-sending-account outcome totals. |
variants | array<MetricsVariant> | Yes | Per-message, per-variant sends and replies for A/B comparison. |
blockers | array<MetricsBlocker> | Yes | The reasons, with counts, that sends did not complete. |
pace | array<MetricsCampaignPace> | Yes | How long each campaign's leads last: the numbers behind the dashboard's Leads left card. Not bound to the range: remaining_leads counts against everything the campaign has ever contacted, and the pace fields always cover the last seven days. Days of leads left ≈ remaining_leads ÷ (contacts_last_7_days ÷ send_days_last_7_days). |
OutcomeEvent
object · Required: username, outcome, at, account_id, message_index, variant_index, attempts, campaign_id
| Property | Type | Required | Rules |
|---|
username | string | Yes | — |
outcome | string | Yes | Allowed: sent, skipped, rate_limited, blocked, account_locked, failed, expired, auth_required, cancelled · sent is the only success. skipped means X reported the lead cannot be messaged. |
error_code | string | null | No | Machine-readable reason for a non-sent outcome. |
at | string | Yes | When the attempt finished. |
account_id | string | null | Yes | X user id of the sending account (matches ConnectedAccount.user_id). |
message_index | integer | Yes | 0 for the first message, 1 for the first follow-up, and so on. |
variant_index | integer | Yes | — |
attempts | integer | Yes | Minimum: 1 |
campaign_id | string | null | Yes | The campaign this send belongs to (matches Campaign.id). |
OutcomeEventPage
object · Required: data, next_cursor
| Property | Type | Required | Rules |
|---|
data | array<OutcomeEvent> | Yes | — |
next_cursor | string | null | Yes | Opaque cursor for the next page. Pass it back as cursor to resume exactly after the last event; null when caught up. |
WebhookEndpoint
object · Required: id, url_masked, event_types, status, verified_at, enabled_at, disabled_at, backfill_started_at, backfill_completed_at, last_tested_at, last_test_http_status, created_at, updated_at
| Property | Type | Required | Rules |
|---|
id | string | Yes | — |
url_masked | string | Yes | The endpoint URL with its path hidden. The full URL is never returned. |
event_types | array<string> | Yes | The events this endpoint delivers. |
status | string | Yes | Allowed: pending, backfilling, enabled, disabled |
verified_at | string | null | Yes | When a test last succeeded against the current URL. |
enabled_at | string | null | Yes | — |
disabled_at | string | null | Yes | — |
backfill_started_at | string | null | Yes | — |
backfill_completed_at | string | null | Yes | — |
last_tested_at | string | null | Yes | — |
last_test_http_status | integer | null | Yes | HTTP status the endpoint returned to the last test. |
created_at | string | Yes | — |
updated_at | string | Yes | — |
WebhookEndpointState
object · Required: available, endpoint
| Property | Type | Required | Rules |
|---|
available | boolean | Yes | — |
endpoint | WebhookEndpoint | null | Yes | — |
signing_secret | string | No | Returned once by create and rotate-secret. Store it: it signs deliveries and is never shown again. |
enabled | boolean | No | — |
backfilled_count | integer | No | — |
pending_count | integer | No | — |
enabled_at | string | null | No | — |
WebhookDelivery
object · Required: id, event_type, status, occurred_at, attempt_count, next_attempt_at, last_attempt_at, delivered_at, failed_at, last_http_status, last_error_code, created_at
| Property | Type | Required | Rules |
|---|
id | string | Yes | — |
event_type | string | Yes | — |
status | string | Yes | Allowed: pending, processing, delivered, failed |
occurred_at | string | Yes | — |
attempt_count | integer | Yes | — |
next_attempt_at | string | null | Yes | — |
last_attempt_at | string | null | Yes | — |
delivered_at | string | null | Yes | — |
failed_at | string | null | Yes | — |
last_http_status | integer | null | Yes | — |
last_error_code | string | null | Yes | — |
created_at | string | Yes | — |
WebhookDeliveryList
object · Required: deliveries
| Property | Type | Required | Rules |
|---|
deliveries | array<WebhookDelivery> | Yes | — |
WebhookTestResult
object · Required: ok, http_status, endpoint
| Property | Type | Required | Rules |
|---|
ok | boolean | Yes | — |
http_status | integer | null | Yes | HTTP status the endpoint returned to the test request. |
endpoint | WebhookEndpoint | Yes | — |
WebhookOk
object · Required: ok
| Property | Type | Required | Rules |
|---|
ok | boolean | Yes | — |
WebhookRetryResult
object · Required: ok, id
| Property | Type | Required | Rules |
|---|
ok | boolean | Yes | — |
id | string | Yes | — |
WebhookEndpointCreateInput
object · Required: url
| Property | Type | Required | Rules |
|---|
url | string | Yes | A public HTTPS endpoint on port 443. |
event_types | array<string> | No | Which events to deliver. Defaults to ["message.sent"]. |
WebhookEndpointUpdateInput
object
| Property | Type | Required | Rules |
|---|
url | string | No | — |
event_types | array<string> | No | — |
enabled | boolean | No | Only false is accepted, to disable delivery. |
InboxSession
object · Required: url, workspace, base_url, token, expires_at, access
| Property | Type | Required | Rules |
|---|
url | string | Yes | Origin of the inbox service for this environment. |
workspace | string | Yes | Your workspace id, the same one the token is bound to. |
base_url | string | Yes | Prefix for every inbox endpoint: {url}/w/{workspace}/api. |
token | string | Yes | Bearer token for the inbox service. Valid for one hour; never store it longer than that. |
expires_at | string | Yes | — |
access | string | Yes | Allowed: read, write · write when the key holds inbox:write, otherwise read. |
MetricsCampaignPace
object · Required: campaign_id, target_leads, remaining_leads, contacts_last_7_days, send_days_last_7_days
| Property | Type | Required | Rules |
|---|
campaign_id | string | Yes | — |
target_leads | integer | Yes | Leads on the campaign's target lists. |
remaining_leads | integer | Yes | Leads the campaign has not contacted yet. |
contacts_last_7_days | integer | Yes | Leads first contacted in the last seven days. |
send_days_last_7_days | integer | Yes | How many of the last seven days were send days for this campaign (its send-day schedule, from the day it was created). |