The Node.js, Python, and Go examples convert the response into one DM send row.
Store message_id, user_id, account, send_status, optional media_id,
media_ids, and source_endpoint. For media DMs, keep media_ids as the
one-item request array and set media_id to the uploaded media ID that you
passed as that single item.
Upload media first with Upload Media, then pass the returned mediaId as the only media_ids item.
curl -X POST https://xquik.com/api/v1/x/dm/44196397 \ -H "x-api-key: xq_YOUR_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{ "account": "myxaccount", "text": "Here is the requested image.", "media_ids": ["1893726451023847424"] }' | jq
media_ids must contain exactly one uploaded media ID. Empty arrays, multiple IDs, and reply_to_message_id return 400 invalid_input.
Generated SDKs can expose reply_to_message_id while the REST route rejects it.
Leave it unset. Pass exactly one uploaded media ID in media_ids, then store
the returned messageId with that media ID.
After the media DM send returns 200 OK, store the returned messageId with the uploaded media ID and recipient user ID so support logs, CRM records, queues, or agent memory can reconcile the attachment with the sent message.
Keep media_ids as a one-item array in the request, keep media_id as the original POST /x/media result, and use message_id as the external DM identifier after the send succeeds.
Use POST /x/dm/{userId} when a support, sales, community, CRM, or agent workflow needs to send an auditable direct message from a connected X account. If you only have a username, look up the recipient first with GET /x/users/{id}. If the workflow needs conversation context, read the latest messages with GET /x/dm/{userId}/history before sending.
messageId
Store messageId as the external message ID for support logs, CRM records, queues, or agent memory.
success
Mark the send job complete after a 200 OK response.
userId
Keep the recipient X user ID from the path with the send job.
account
Store the connected X account that sent the DM.
text
Store the exact message text sent. Add your own sent_at timestamp when downstream systems need it.
media_ids[0]
Store the uploaded media ID when the DM includes one attachment from POST /x/media.
This endpoint costs 10 credits per send. Uploading media first with POST /x/media is a separate 10-credit call. Do not retry 422 x_dm_not_allowed unchanged; use another permitted connected account or ask the recipient to allow messages.
Optional one-item array containing an uploaded media ID. Upload media first with Upload Media, then pass the returned mediaId as the only array item. Empty arrays and multiple IDs are rejected.
{ "error": "invalid_input", "message": "Missing or invalid text or account" }
The request body is missing required fields, includes zero or more than one media_ids item, includes an unsupported reply_to_message_id, or contains invalid values.
{ "error": "unauthenticated", "message": "Missing or invalid API key" }
Missing or invalid API key.
{ "error": "no_subscription", "message": "No active subscription" }
An active subscription is required. Subscribe from the dashboard.
Insufficient credits for this operation. Top up your credit balance from the dashboard.
{ "error": "account_needs_reauth" }
The connected X account failed a recent login attempt and needs to be reconnected. Reconnect it from the dashboard.
{ "error": "account_restricted" }
The connected X account is locked, suspended, recovering, or temporarily restricted. Resolve the restriction on X before retrying.
{ "error": "x_dm_not_allowed", "message": "Cannot send a DM to this user. They may not accept messages from this account." }
X rejected the write. Possible codes: x_dm_not_allowed, x_content_too_long, x_duplicate_action, x_account_suspended, x_account_protected, x_target_not_found, x_account_feature_required, x_rejected. If you receive x_dm_not_allowed, the recipient may not accept messages from this connected account; do not retry unchanged. See error handling for details.
The request hit an Xquik tier limit, or X throttled the write. Possible codes: rate_limit_exceeded, x_rate_limited, or x_daily_limit. Respect the Retry-After header when present.
A transient write service issue occurred. Safe to retry with exponential backoff.
{ "error": "x_write_failed", "message": "Failed to complete the action" }
The write action failed. Retry after a short delay.
Related:Direct Message Workflow for
lookup, history sync, messageId storage, and media handoff; Get User
to look up a user’s ID before messaging; Get DM History
to sync participant-scoped messages; Upload Media
to create the one mediaId allowed in media_ids.