Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.xquik.com/llms.txt

Use this file to discover all available pages before exploring further.

Use this workflow when a support, sales, community, or agent system needs to read direct message history and send direct messages from a connected X account. Xquik reads a participant-scoped DM conversation with GET /x/dm/{userId}/history, sends text DMs with POST /x/dm/{userId}, and can attach one uploaded media item with media_ids.

When to use this workflow

NeedUse
Convert a username to a recipient IDGET /x/users/{id}
Read direct message historyGET /x/dm/{userId}/history with account
Send a text direct messagePOST /x/dm/{userId} with account and text
Send a direct message with mediaUpload media first, then pass one mediaId in media_ids
Avoid bad retriesRetry only 429 and 503; fix 400, 402, 403, and 422 first

Data you get

EndpointReturned data
GET /x/users/{id}Recipient id, username, name, optional canDm, and profile fields
GET /x/dm/{userId}/historymessages, has_next_page, and next_cursor
POST /x/dm/{userId}messageId and success
POST /x/mediamediaId, mediaUrl, and success

Step 1: Look up the recipient user ID

POST /x/dm/{userId} requires the numeric X user ID in the path. If you only have a username, call GET /x/users/{id} first.
curl https://xquik.com/api/v1/x/users/xquikcom \
  -H "x-api-key: xq_YOUR_KEY_HERE" | jq
{
  "id": "987654321",
  "username": "xquikcom",
  "name": "Xquik",
  "canDm": true
}
When canDm is returned, use it as a preflight hint. If the field is omitted, rely on the DM write response.

Step 2: Read direct message history

Use the sender account as the account query parameter. The connected account must be a participant in the conversation, because direct messages are private and user-scoped.
curl -G https://xquik.com/api/v1/x/dm/987654321/history \
  --data-urlencode "account=myxhandle" \
  -H "x-api-key: xq_YOUR_KEY_HERE" | jq
{
  "messages": [
    {
      "id": "1893726451029384191",
      "text": "Can you send the setup link?",
      "senderId": "987654321",
      "receiverId": "123456789",
      "createdAt": "2026-02-24T10:00:00.000Z"
    }
  ],
  "has_next_page": true,
  "next_cursor": "1893726451029384190"
}
For older messages, pass the previous next_cursor as cursor. Store each message id, text, senderId, receiverId, createdAt, and optional mediaUrl in your support ticket, CRM note, or JSON export.

Sync and retry rules

RuleWhy it matters
Treat next_cursor as opaquePass it back as cursor for the next page. Do not decode it or build your own cursor.
Store every message idUse the ID to dedupe support tickets, CRM notes, warehouse rows, or JSON exports.
Use a participant accountGET /x/dm/{userId}/history returns 400 account_required without account and 403 dm_not_permitted when the connected account is not in the conversation.
Avoid legacy pagination in new codeUse cursor; keep maxId only for older integrations that already depend on it.
Retry only transient failuresRetry 429 and 503; do not retry 403 dm_not_permitted with the same non-participant account.

Step 3: Send a text direct message

Use a connected account as the sender. The account value can be the connected account username or account ID.
curl -X POST https://xquik.com/api/v1/x/dm/987654321 \
  -H "x-api-key: xq_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{
    "account": "myxhandle",
    "text": "Thanks for reaching out. Here is the next step."
  }' | jq
{
  "messageId": "1893726451029384192",
  "success": true
}

Step 4: Send a direct message with media

Upload media first, then send exactly one uploaded media ID in media_ids.
curl -X POST https://xquik.com/api/v1/x/media \
  -H "x-api-key: xq_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{
    "account": "myxhandle",
    "url": "https://example.com/support-image.png"
  }' | jq
curl -X POST https://xquik.com/api/v1/x/dm/987654321 \
  -H "x-api-key: xq_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{
    "account": "myxhandle",
    "text": "Here is the requested image.",
    "media_ids": ["1893726451023847424"]
  }' | jq
DMs accept one uploaded media ID. Do not pass multiple IDs, an empty array, or reply_to_message_id.

Costs

ActionCost
Look up recipient with GET /x/users/{id}1 credit per call
Read DM history with GET /x/dm/{userId}/history1 credit per message returned
Upload media with POST /x/media10 credits per upload call
Send DM with POST /x/dm/{userId}10 credits per call

Error handling

FailureWhat to do
400 invalid_inputCheck account, text, userId, and one-item media_ids.
400 account_requiredPass the connected sender handle as account when reading DM history.
402 no_subscription or 402 insufficient_creditsSubscribe or top up credits before retrying.
403 account_needs_reauthReconnect the sender account from the dashboard.
403 dm_not_permittedUse a connected account that participates in the conversation, or reconnect the account.
422 x_rejected or related write codesCheck the recipient, account state, and message content before retrying.
429 or 503Retry with exponential backoff and respect Retry-After when present.

Handoff checklist

HandoffRequired detail
SenderConnected X account username or ID in account.
RecipientNumeric user ID in POST /x/dm/{userId}.
HistoryFor DM history exports, store messages, has_next_page, and next_cursor; pass cursor to fetch older messages.
TextRequired text, up to 10,000 characters.
MediaOptional one-item media_ids array containing a mediaId from POST /x/media.
ResponseStore messageId for audit trails or support logs.
Last modified on May 9, 2026