Skip to content
Home/API docs

Getting started

Create a key under Settings → Integrations (Scale and Enterprise plans), then send it in the Authorization: Bearer header from your server. Never embed keys in browser code or public repositories. Every request goes to https://api.xsenderapp.com/v1/public.

Authorization: Bearer xsk_live_…

Pagination

Collections return opaque cursors. Use limit from 1–100 and pass next_cursor unchanged.

Errors

Errors use { "code": "…", "message": "…" }. Common statuses are 400, 401, 402, 403, 404, 409, 413, 415, and 429. 429 rate limits always include Retry-After; temporary 409 or 503 responses may include it too.

Rate limits

Each key receives 600 reads and 120 writes per minute.

Scopes

Each key carries read or write access per resource. A write scope also grants reads of the same resource; nothing crosses resources.

Versioning

This is version 1.1.0. Fields are only ever added; nothing documented here is removed or renamed without a new major version.

A typical flow

Create a target list with your leads, create a campaign that uses it, activate it with confirmed: true, then read /campaigns/…/leads or listen to the webhook to see who was messaged.

Webhooks

Add one HTTPS endpoint under Settings → Integrations and Xsender sends a message.sent event for every campaign message that goes out: the lead and their custom fields, the sending account, the campaign and step, and when. Events carry no message text. Turning the webhook on also delivers the last 30 days of sends, oldest first. A webhook.test event is sent from the Settings test button. Reply events are not available yet.

{
  "id": "1f0c6a1e-5b7e-4a7a-9c2d-3f7c1e9a2b10",
  "api_version": "v1",
  "type": "message.sent",
  "created_at": "2026-09-05T12:00:00.000Z",
  "occurred_at": "2026-09-05T11:59:30.000Z",
  "data": {
    "contact": {
      "username": "lead_handle",
      "profile_url": "https://x.com/lead_handle",
      "custom_fields": { "company": "Example Ltd" }
    },
    "sender": {
      "x_user_id": "1234567890",
      "username": "sender_handle",
      "profile_url": "https://x.com/sender_handle"
    },
    "campaign": { "id": "cmp_123", "name": "Founders outreach" },
    "target_source": { "id": "tl_456", "name": "Seed-stage founders" },
    "message": {
      "id": "send_789",
      "direction": "outbound",
      "sent_at": "2026-09-05T11:59:30.000Z",
      "message_index": 0,
      "variant_index": 0,
      "variant_label": "First message",
      "attempts": 1
    }
  }
}

Verify the signature

Read X-Xsender-Timestamp, X-Xsender-Signature, X-Xsender-Event-Id, and X-Xsender-Event-Type. Compute HMAC-SHA256 over the exact bytes in ${timestamp}.${rawBody}, reject timestamps more than five minutes old, compare equal-length signatures in constant time, and deduplicate the event ID in durable storage. Xsender retries with at-least-once semantics.

const express = require('express');
const { createHmac, timingSafeEqual } = require('node:crypto');

const app = express();
const secret = process.env.XSENDER_WEBHOOK_SECRET;

app.post(
  '/webhooks/xsender',
  express.raw({ type: 'application/json' }),
  async (request, response) => {
    const timestamp = request.get('X-Xsender-Timestamp') || '';
    const signature = request.get('X-Xsender-Signature') || '';
    const eventId = request.get('X-Xsender-Event-Id') || '';
    const eventType = request.get('X-Xsender-Event-Type') || '';
    const timestampSeconds = Number(timestamp);

    if (
      !secret ||
      !eventId ||
      !Number.isInteger(timestampSeconds) ||
      Math.abs(Math.floor(Date.now() / 1000) - timestampSeconds) > 300
    ) {
      return response.sendStatus(401);
    }

    const receivedHex = /^v1=([a-f0-9]{64})$/.exec(signature)?.[1];
    if (!receivedHex) return response.sendStatus(401);

    const expected = Buffer.from(
      createHmac('sha256', secret)
        .update(`${timestamp}.`)
        .update(request.body)
        .digest('hex'),
      'hex'
    );
    const received = Buffer.from(receivedHex, 'hex');
    if (
      expected.length !== received.length ||
      !timingSafeEqual(expected, received)
    ) {
      return response.sendStatus(401);
    }

    // Atomically record eventId in durable storage before processing.
    // If it already exists, return 204 without processing it again.
    const event = JSON.parse(request.body.toString('utf8'));
    request.app.emit('xsender.webhook', { eventId, eventType, event });
    return response.sendStatus(204);
  }
);

Campaigns

GET/campaignscampaigns:read

List campaigns

Parameters

NameLocationTypeRequiredDetails
limitqueryintegerNoDefault: 50 · Minimum: 1 · Maximum: 100
cursorquerystringNo

Responses

200Campaign pageCampaignPage
400API errorError
401API errorError
402API errorError
403API errorError
429Per-key rate limit exceeded. Retry-After contains seconds.Error
curl --request GET \
  --url 'https://api.xsenderapp.com/v1/public/campaigns' \
  --header 'Authorization: Bearer $XSENDER_API_KEY'
POST/campaignscampaigns:write

Create a draft campaign

Request body Required

application/json · CampaignCreateInput

  • At least 1 property must be provided.
PropertyTypeRequiredRules
namestringYes
descriptionstringNo
workMinsarray<unspecified>NoMinimum items: 2 · Maximum items: 2
perDayintegerNoMinimum: 0 · Maximum: 500
sendDaysarray<string>No
targetListIdsarray<string>No
accountIdsarray<string>No
sequencearray<object>No

Responses

201CampaignCampaign
400API errorError
401API errorError
402API errorError
403API errorError
413Request body exceeds the 256 KB limit.Error
415Content-Type must be application/json for requests with a body.Error
429Per-key rate limit exceeded. Retry-After contains seconds.Error
curl --request POST \
  --url 'https://api.xsenderapp.com/v1/public/campaigns' \
  --header 'Authorization: Bearer $XSENDER_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{"name":"New campaign","targetListIds":[],"accountIds":[]}'
GET/campaigns/{campaign_id}campaigns:read

Get a campaign

Parameters

NameLocationTypeRequiredDetails
campaign_idpathstringYes

Responses

200CampaignCampaign
401API errorError
402API errorError
403API errorError
404API errorError
429Per-key rate limit exceeded. Retry-After contains seconds.Error
curl --request GET \
  --url 'https://api.xsenderapp.com/v1/public/campaigns/CAMPAIGN_ID' \
  --header 'Authorization: Bearer $XSENDER_API_KEY'
PATCH/campaigns/{campaign_id}campaigns:write

Update a campaign

Parameters

NameLocationTypeRequiredDetails
campaign_idpathstringYes

Request body Required

application/json · CampaignUpdateInput

  • At least 1 property must be provided.
PropertyTypeRequiredRules
namestringNo
descriptionstringNo
workMinsarray<unspecified>NoMinimum items: 2 · Maximum items: 2
perDayintegerNoMinimum: 0 · Maximum: 500
sendDaysarray<string>No
targetListIdsarray<string>No
accountIdsarray<string>No
sequencearray<object>No

Responses

200CampaignCampaign
400API errorError
401API errorError
402API errorError
403API errorError
404API errorError
413Request body exceeds the 256 KB limit.Error
415Content-Type must be application/json for requests with a body.Error
429Per-key rate limit exceeded. Retry-After contains seconds.Error
curl --request PATCH \
  --url 'https://api.xsenderapp.com/v1/public/campaigns/CAMPAIGN_ID' \
  --header 'Authorization: Bearer $XSENDER_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{"name":"Updated campaign name"}'
DELETE/campaigns/{campaign_id}campaigns:write

Delete a campaign

Parameters

NameLocationTypeRequiredDetails
campaign_idpathstringYes

Responses

200Action acceptedobject
401API errorError
402API errorError
403API errorError
404API errorError
429Per-key rate limit exceeded. Retry-After contains seconds.Error
curl --request DELETE \
  --url 'https://api.xsenderapp.com/v1/public/campaigns/CAMPAIGN_ID' \
  --header 'Authorization: Bearer $XSENDER_API_KEY'
POST/campaigns/{campaign_id}/activatecampaigns:write

Activate a confirmed campaign

Parameters

NameLocationTypeRequiredDetails
campaign_idpathstringYes

Request body Optional

application/json · object

PropertyTypeRequiredRules
confirmedbooleanNo

Responses

200CampaignCampaign
400API errorError
401API errorError
402API errorError
403API errorError
404API errorError
409API errorError
413Request body exceeds the 256 KB limit.Error
415Content-Type must be application/json for requests with a body.Error
429Per-key rate limit exceeded. Retry-After contains seconds.Error
curl --request POST \
  --url 'https://api.xsenderapp.com/v1/public/campaigns/CAMPAIGN_ID/activate' \
  --header 'Authorization: Bearer $XSENDER_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{"confirmed":true}'
POST/campaigns/{campaign_id}/pausecampaigns:write

Pause a campaign

Parameters

NameLocationTypeRequiredDetails
campaign_idpathstringYes

Responses

200CampaignCampaign
401API errorError
402API errorError
403API errorError
404API errorError
429Per-key rate limit exceeded. Retry-After contains seconds.Error
curl --request POST \
  --url 'https://api.xsenderapp.com/v1/public/campaigns/CAMPAIGN_ID/pause' \
  --header 'Authorization: Bearer $XSENDER_API_KEY'
GET/campaigns/{campaign_id}/leadscampaigns:read

List who a campaign has messaged

One row per finished send attempt, newest first, from the same ledger the dashboard counts. Use outcome=sent for successful sends only.

Parameters

NameLocationTypeRequiredDetails
campaign_idpathstringYes
outcomequerystringNoOnly outcomes of this kind. · Allowed: sent, skipped, rate_limited, blocked, account_locked, failed, expired, auth_required, cancelled
limitqueryintegerNoDefault: 50 · Minimum: 1 · Maximum: 100
cursorquerystringNo

Responses

200Lead outcome pageLeadOutcomePage
400API errorError
401API errorError
402API errorError
403API errorError
404API errorError
429Per-key rate limit exceeded. Retry-After contains seconds.Error
curl --request GET \
  --url 'https://api.xsenderapp.com/v1/public/campaigns/CAMPAIGN_ID/leads' \
  --header 'Authorization: Bearer $XSENDER_API_KEY'

Target lists

GET/target-liststargets:read

List target lists

Parameters

NameLocationTypeRequiredDetails
limitqueryintegerNoDefault: 50 · Minimum: 1 · Maximum: 100
cursorquerystringNo

Responses

200Target-list pageTargetListPage
400API errorError
401API errorError
402API errorError
403API errorError
429Per-key rate limit exceeded. Retry-After contains seconds.Error
curl --request GET \
  --url 'https://api.xsenderapp.com/v1/public/target-lists' \
  --header 'Authorization: Bearer $XSENDER_API_KEY'
POST/target-liststargets:write

Create a target list

Request body Required

application/json · TargetListCreateInput

  • 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`.
PropertyTypeRequiredRules
namestringYes
handlesarray<string>No
leadsarray<Lead>No
variableSchemaarray<TargetVariableField>No

Responses

201Target listTargetList
400API errorError
401API errorError
402API errorError
403API errorError
413Request body exceeds the 24 MB limit.Error
415Content-Type must be application/json for requests with a body.Error
429Per-key rate limit exceeded. Retry-After contains seconds.Error
curl --request POST \
  --url 'https://api.xsenderapp.com/v1/public/target-lists' \
  --header 'Authorization: Bearer $XSENDER_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{"name":"Prospects","variableSchema":[{"key":"company","label":"Company"}],"leads":[{"username":"example","variables":{"company":"Acme"}}]}'
GET/target-lists/{target_list_id}targets:read

Get a target list

Parameters

NameLocationTypeRequiredDetails
target_list_idpathstringYes

Responses

200Target listTargetList
401API errorError
402API errorError
403API errorError
404API errorError
429Per-key rate limit exceeded. Retry-After contains seconds.Error
curl --request GET \
  --url 'https://api.xsenderapp.com/v1/public/target-lists/TARGET_LIST_ID' \
  --header 'Authorization: Bearer $XSENDER_API_KEY'
PATCH/target-lists/{target_list_id}targets:write

Update a target list

Parameters

NameLocationTypeRequiredDetails
target_list_idpathstringYes

Request body Required

application/json · TargetListUpdateInput

  • 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).
PropertyTypeRequiredRules
namestringNo
handlesarray<string>No
leadsarray<Lead>No
variableSchemaarray<TargetVariableField>No

Responses

200Target listTargetList
400API errorError
401API errorError
402API errorError
403API errorError
404API errorError
413Request body exceeds the 24 MB limit.Error
415Content-Type must be application/json for requests with a body.Error
429Per-key rate limit exceeded. Retry-After contains seconds.Error
curl --request PATCH \
  --url 'https://api.xsenderapp.com/v1/public/target-lists/TARGET_LIST_ID' \
  --header 'Authorization: Bearer $XSENDER_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{"name":"Qualified prospects"}'
DELETE/target-lists/{target_list_id}targets:write

Delete a target list

Parameters

NameLocationTypeRequiredDetails
target_list_idpathstringYes

Responses

200Action acceptedobject
401API errorError
402API errorError
403API errorError
404API errorError
429Per-key rate limit exceeded. Retry-After contains seconds.Error
curl --request DELETE \
  --url 'https://api.xsenderapp.com/v1/public/target-lists/TARGET_LIST_ID' \
  --header 'Authorization: Bearer $XSENDER_API_KEY'
GET/target-lists/{target_list_id}/leadstargets:read

List target-list leads and custom variables

Parameters

NameLocationTypeRequiredDetails
target_list_idpathstringYes
limitqueryintegerNoDefault: 50 · Minimum: 1 · Maximum: 100
cursorquerystringNo

Responses

200Lead pageLeadPage
400API errorError
401API errorError
402API errorError
403API errorError
404API errorError
429Per-key rate limit exceeded. Retry-After contains seconds.Error
curl --request GET \
  --url 'https://api.xsenderapp.com/v1/public/target-lists/TARGET_LIST_ID/leads' \
  --header 'Authorization: Bearer $XSENDER_API_KEY'
POST/target-lists/{target_list_id}/leadstargets:write

Add leads to a target list

Adds new usernames to the end of the list. Usernames already on the list and leads this workspace has already messaged are skipped and counted in the result. Campaigns using the list keep running. Send one append at a time per list: two appends that overlap in time can each miss the other’s leads.

Parameters

NameLocationTypeRequiredDetails
target_list_idpathstringYes

Request body Required

application/json · TargetListLeadsAppendInput

  • At least 1 property must be provided.
  • Requires at least one of: `handles` or `leads`.
  • When `variableSchema` is provided, also requires `leads`.
PropertyTypeRequiredRules
handlesarray<string>No
leadsarray<Lead>No
variableSchemaarray<TargetVariableField>NoDeclares the variable keys used in leads[].variables. New keys are added to the list’s existing schema.

Responses

200Leads appendedTargetListLeadsAppendResult
400API errorError
401API errorError
402API errorError
403API errorError
404API errorError
409API errorError
413Request body exceeds the 24 MB limit.Error
415Content-Type must be application/json for requests with a body.Error
429Per-key rate limit exceeded. Retry-After contains seconds.Error
curl --request POST \
  --url 'https://api.xsenderapp.com/v1/public/target-lists/TARGET_LIST_ID/leads' \
  --header 'Authorization: Bearer $XSENDER_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{"variableSchema":[{"key":"company","label":"Company"}],"leads":[{"username":"new_lead","variables":{"company":"Globex"}}]}'

Accounts

GET/accountsaccounts:read

List connected X accounts

Parameters

NameLocationTypeRequiredDetails
limitqueryintegerNoDefault: 50 · Minimum: 1 · Maximum: 100
cursorquerystringNo

Responses

200Connected X account pageConnectedAccountPage
400API errorError
401API errorError
402API errorError
403API errorError
429Per-key rate limit exceeded. Retry-After contains seconds.Error
curl --request GET \
  --url 'https://api.xsenderapp.com/v1/public/accounts' \
  --header 'Authorization: Bearer $XSENDER_API_KEY'
PATCH/accounts/{account_id}accounts:write

Pause or resume a connected X account

Returns the account with its new pause state, the same shape as the list.

Parameters

NameLocationTypeRequiredDetails
account_idpathstringYes

Request body Required

application/json · AccountPauseInput

PropertyTypeRequiredRules
pausedbooleanYes

Responses

200Connected accountConnectedAccount
400API errorError
401API errorError
402API errorError
403API errorError
404API errorError
413Request body exceeds the 256 KB limit.Error
415Content-Type must be application/json for requests with a body.Error
429Per-key rate limit exceeded. Retry-After contains seconds.Error
curl --request PATCH \
  --url 'https://api.xsenderapp.com/v1/public/accounts/ACCOUNT_ID' \
  --header 'Authorization: Bearer $XSENDER_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{"paused":true}'

Settings

GET/settingssettings:read

Get workspace settings

Responses

200SettingsSettings
401API errorError
402API errorError
403API errorError
429Per-key rate limit exceeded. Retry-After contains seconds.Error
curl --request GET \
  --url 'https://api.xsenderapp.com/v1/public/settings' \
  --header 'Authorization: Bearer $XSENDER_API_KEY'
PUT/settingssettings:write

Update user-editable workspace settings

Request body Required

application/json · SettingsUpdate

  • At least 1 property must be provided.
PropertyTypeRequiredRules
timezonestringNo
blackliststringNo

Responses

200SettingsSettings
400API errorError
401API errorError
402API errorError
403API errorError
413Request body exceeds the 2 MB limit.Error
415Content-Type must be application/json for requests with a body.Error
429Per-key rate limit exceeded. Retry-After contains seconds.Error
curl --request PUT \
  --url 'https://api.xsenderapp.com/v1/public/settings' \
  --header 'Authorization: Bearer $XSENDER_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{"timezone":"Europe/Vilnius"}'

Specification

GET/openapi.json

Download the public OpenAPI document

Responses

200OpenAPI 3.1 document
curl --request GET \
  --url 'https://api.xsenderapp.com/v1/public/openapi.json'

Schemas

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

PropertyTypeRequiredRules
codestringYes
messagestringYes

Settings

object · Required: timezone, blacklist

PropertyTypeRequiredRules
timezonestringYes
blackliststringYesNewline-delimited X usernames.

SettingsUpdate

object

  • At least 1 property must be provided.
PropertyTypeRequiredRules
timezonestringNo
blackliststringNo

Campaign

object · Required: id, name, status, createdAt, targetListIds, accountIds, sequence, messagesSent, contactedCount

PropertyTypeRequiredRules
idstringYes
namestringYes
descriptionstringNo
statusstringYesAllowed: draft, active, paused, completed
statusReasonstring | nullNoWhy the campaign is in its current status, e.g. user_paused, completed_all_leads.
statusChangedAtstring | nullNo
createdAtstringYes
targetListIdsarray<string>Yes
accountIdsarray<string>YesX user ids of the sending accounts.
sequencearray<SequenceStep>Yes
workMinsintegerNoMinutes per day the campaign sends.
perDayintegerNoMinimum: 0 · Maximum: 500 · Daily send target across all accounts. Ignored when volumeMode is managed.
sendDaysarray<string>No
volumeModestringNoAllowed: manual, managed · managed lets Xsender pace sends within each account’s safe limit.
messagesSentintegerYesSuccessful sends so far, all time.
contactedCountintegerYesLeads the campaign has claimed or messaged.
nextSendAtstring | nullNoNext planned send, null when nothing is scheduled.
confirmationRequiredbooleanNotrue until the current content revision has been activated with confirmed: true.

CampaignUpdateInput

object

  • At least 1 property must be provided.
PropertyTypeRequiredRules
namestringNo
descriptionstringNo
workMinsarray<unspecified>NoMinimum items: 2 · Maximum items: 2
perDayintegerNoMinimum: 0 · Maximum: 500
sendDaysarray<string>No
targetListIdsarray<string>No
accountIdsarray<string>No
sequencearray<object>No

CampaignCreateInput

CampaignUpdateInput + object · Required: name

  • At least 1 property must be provided.
PropertyTypeRequiredRules
namestringYes
descriptionstringNo
workMinsarray<unspecified>NoMinimum items: 2 · Maximum items: 2
perDayintegerNoMinimum: 0 · Maximum: 500
sendDaysarray<string>No
targetListIdsarray<string>No
accountIdsarray<string>No
sequencearray<object>No

TargetList

object · Required: id, name, count, variableSchema, createdAt

PropertyTypeRequiredRules
idstringYes
namestringYes
countintegerYesUsable leads on the list.
handlesarray<string>NoLead usernames. Present on single-list reads and writes; use /leads to page through variables.
variableSchemaarray<TargetVariableField>Yes
createdAtstringYes

TargetVariableField

object · Required: key, label

PropertyTypeRequiredRules
keystringYesPattern: ^[A-Za-z][A-Za-z0-9]*$
labelstringYesMaximum 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).
PropertyTypeRequiredRules
namestringNo
handlesarray<string>No
leadsarray<Lead>No
variableSchemaarray<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`.
PropertyTypeRequiredRules
namestringYes
handlesarray<string>No
leadsarray<Lead>No
variableSchemaarray<TargetVariableField>No

Lead

object · Required: username, variables

PropertyTypeRequiredRules
usernamestringYes
variablesobjectYes

AccountPauseInput

object · Required: paused

PropertyTypeRequiredRules
pausedbooleanYes

ConnectedAccount

object · Required: user_id, username, installation_id, online, paused, busy, login_state, safety_cooldown_until

PropertyTypeRequiredRules
user_idstringYes
usernamestring | nullYes
installation_idstringYes
onlinebooleanYestrue while the extension for this account has checked in during the last two minutes.
pausedbooleanYes
busybooleanYestrue while a send is in progress on this account.
login_statestringYeslogged_in, logged_out or unknown as last reported by the extension.
safety_cooldown_untilstring | nullYesSet while X asked this account to slow down; sends resume after it.

ConnectedAccountPage

object · Required: data, next_cursor

PropertyTypeRequiredRules
dataarray<ConnectedAccount>Yes
next_cursorstring | nullYes

CampaignPage

object · Required: data, next_cursor

PropertyTypeRequiredRules
dataarray<Campaign>Yes
next_cursorstring | nullYes

TargetListPage

object · Required: data, next_cursor

PropertyTypeRequiredRules
dataarray<TargetList>Yes
next_cursorstring | nullYes

LeadPage

object · Required: data, next_cursor

PropertyTypeRequiredRules
dataarray<Lead>Yes
next_cursorstring | nullYes

SequenceVariant

object · Required: text

PropertyTypeRequiredRules
textstringYesMessage text. Placeholders like {{company}} are filled from the lead’s custom fields.

SequenceStep

object · Required: label, variants

PropertyTypeRequiredRules
idstringNo
labelstringYes"First message" or "Follow-up N"; assigned by position.
delayobjectNoFollow-up wait after the previous step (follow-ups only).
variantsarray<SequenceVariant>YesA/B variants for this step; one is picked per lead.

LeadOutcome

object · Required: username, outcome, at, message_index, variant_index, attempts

PropertyTypeRequiredRules
usernamestringYes
outcomestringYesAllowed: 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_codestring | nullNoMachine-readable reason for a non-sent outcome.
atstringYesWhen the attempt finished.
account_idstring | nullNoX user id of the sending account (matches ConnectedAccount.user_id).
message_indexintegerYes0 for the first message, 1 for the first follow-up, and so on.
variant_indexintegerYes
attemptsintegerYesMinimum: 1

LeadOutcomePage

object · Required: data, next_cursor

PropertyTypeRequiredRules
dataarray<LeadOutcome>Yes
next_cursorstring | nullYes

TargetListLeadsAppendInput

object

  • At least 1 property must be provided.
  • Requires at least one of: `handles` or `leads`.
  • When `variableSchema` is provided, also requires `leads`.
PropertyTypeRequiredRules
handlesarray<string>No
leadsarray<Lead>No
variableSchemaarray<TargetVariableField>NoDeclares 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

PropertyTypeRequiredRules
target_listTargetList | nullYesThe list after the change; null when nothing was added.
addedintegerYes
skipped_duplicatesintegerYesUsernames already on the list.
skipped_contactedintegerYesUsernames this workspace has already messaged from any campaign.