> ## 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.

# Twitter DM API: delete a direct message by ID

> Delete 1 direct message from a connected X account's side of its conversation. The other person keeps it. Costs 10 credits, only when X deletes the DM.

<Panel>
  <Tabs defaultTabIndex={0} sync={false}>
    <Tab title="200" id="response-x-write-delete-dm-200">
      ```json theme={null}
      {
        "object": "x_write_action",
        "id": "12345",
        "writeActionId": "12345",
        "action": "delete_dm",
        "status": "success",
        "terminal": true,
        "retryable": false,
        "safeToRetry": false,
        "statusUrl": "/api/v1/x/write-actions/12345",
        "pollAfterMs": null
      }
      ```
    </Tab>

    <Tab title="202" id="response-x-write-delete-dm-202">
      ```json theme={null}
      {
        "object": "x_write_action",
        "id": "12346",
        "writeActionId": "12346",
        "action": "delete_dm",
        "status": "dispatching",
        "terminal": false,
        "retryable": false,
        "safeToRetry": false,
        "statusUrl": "/api/v1/x/write-actions/12346",
        "pollAfterMs": 2000
      }
      ```
    </Tab>

    <Tab title="400" id="response-x-write-delete-dm-400">
      ```json theme={null}
      {
        "error": "missing_idempotency_key",
        "message": "Idempotency-Key is required. Generate one unique key for this write.",
        "charged": false,
        "chargedCredits": "0",
        "retryable": false,
        "safeToRetry": true
      }
      ```
    </Tab>

    <Tab title="401" id="response-x-write-delete-dm-401">
      ```json theme={null}
      {
        "error": "unauthenticated",
        "message": "Authentication required. Provide a valid API key or bearer token."
      }
      ```
    </Tab>

    <Tab title="402" id="response-x-write-delete-dm-402">
      ```json theme={null}
      {
        "error": "insufficient_credits",
        "message": "Insufficient credits. Top up or subscribe to continue."
      }
      ```
    </Tab>

    <Tab title="403" id="response-x-write-delete-dm-403">
      ```json theme={null}
      {
        "error": "account_needs_reauth",
        "message": "X account needs re-authentication. Re-add the account."
      }
      ```
    </Tab>

    <Tab title="404" id="response-x-write-delete-dm-404">
      ```json theme={null}
      {
        "error": "account_not_found",
        "message": "X account not found. Connect it first at /dashboard/account?tab=x-accounts."
      }
      ```
    </Tab>

    <Tab title="409" id="response-x-write-delete-dm-409">
      ```json theme={null}
      {
        "error": "idempotency_conflict",
        "message": "Idempotency-Key was already used with a different request.",
        "charged": false,
        "chargedCredits": "0",
        "retryable": false,
        "safeToRetry": true
      }
      ```
    </Tab>

    <Tab title="422" id="response-x-write-delete-dm-422">
      ```json theme={null}
      {
        "error": "x_rejected",
        "message": "X rejected this request. Check what you sent & the account on x.com before you try again."
      }
      ```
    </Tab>

    <Tab title="429" id="response-x-write-delete-dm-429">
      ```json theme={null}
      {
        "error": "rate_limit_exceeded",
        "message": "Too many requests. Try again later.",
        "retryAfter": 60
      }
      ```
    </Tab>

    <Tab title="500" id="response-x-write-delete-dm-500">
      ```json theme={null}
      {
        "error": "x_write_failed",
        "message": "Write action failed unexpectedly. Contact support if this persists."
      }
      ```
    </Tab>

    <Tab title="503" id="response-x-write-delete-dm-503">
      ```json theme={null}
      {
        "error": "write_tracking_unavailable",
        "message": "Write tracking unavailable. Try again.",
        "charged": false,
        "chargedCredits": "0",
        "retryable": true,
        "safeToRetry": true
      }
      ```
    </Tab>
  </Tabs>
</Panel>

<blockquote className="agent-llms-directive">
  For the complete documentation index, see <a href="/llms.txt">llms.txt</a>.
</blockquote>

<Callout icon="coins" color="#5c3327">
  **10 credits per deleted DM** · [Compare plans](https://xquik.com/#pricing)
</Callout>

## Delete a direct message with the Twitter DM API

This route deletes 1 DM from the connected account's side of the conversation.
The other person still sees it.
Xquik charges 10 credits only when X deletes the DM.

Put the other person in the path and the DM ID in `messageId`.
[DM history](/api-reference/x/dm-history) and [Send DM](/api-reference/x-write/send-dm) return DM IDs.
Send your connected account in the `account` query parameter.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X DELETE "https://xquik.com/api/v1/x/dm/44196397/messages/1893726451029384192?account=myxaccount" \
    -H "x-api-key: xq_YOUR_KEY_HERE" \
    -H "Idempotency-Key: dm-delete-1893726451029384192" | jq
  ```

  ```javascript Node.js theme={null}
  const userId = "44196397";
  const messageId = "1893726451029384192";
  const url = new URL(`https://xquik.com/api/v1/x/dm/${userId}/messages/${messageId}`);
  url.searchParams.set("account", "myxaccount");

  const response = await fetch(url, {
    method: "DELETE",
    headers: {
      "x-api-key": "xq_YOUR_KEY_HERE",
      "Idempotency-Key": `dm-delete-${messageId}`,
    },
  });
  const action = await response.json();
  if (!response.ok) {
    throw new Error(action.message);
  }

  process.stdout.write(`${JSON.stringify(action)}\n`);
  ```
</CodeGroup>

## Confirm the delete

After the delete, Xquik reads the account's conversation.
A `200` response means the conversation no longer shows the DM.
Its `result` has `type: "state_change"`, the DM `id` and `state: "deleted"`.

A `202` response means Xquik has not confirmed the delete yet.
Poll `statusUrl` until `terminal` is `true`.
You pay nothing while the delete is pending.

Deleting the same DM again also succeeds.
For a DM older than the first conversation page, Xquik trusts X's answer.

## Handle delete DM errors

* `400 invalid_message_id` means `messageId` is not a DM ID. Copy it from DM history.
* `400 account_required` means the `account` query parameter is missing.
* `404 account_not_found` means the account is not connected to Xquik.
* `422 x_dm_not_deleted` means X still shows the DM. You pay nothing, and `safeToRetry` is `true`.

Every other status follows the write rules below.
See [error handling](/guides/error-handling) for each code.

## Headers

<ParamField header="x-api-key" type="string" required>
  Your API key. OAuth bearer authentication is also supported. Generate a key from the [dashboard](https://xquik.com/dashboard).
</ParamField>

<ParamField header="Idempotency-Key" type="string" required>
  Unique key for this intended delete. Reuse it only for an exact network replay.
</ParamField>

## Path parameters

<ParamField path="userId" type="string" required>
  The other person in the conversation: user ID, username with or without `@`, or URL-encoded profile URL, such as `x.com/nasa`.
  See [path IDs](/api-reference/overview#path-ids).
</ParamField>

<ParamField path="messageId" type="string" required>
  DM ID from DM history. Anything else returns `400 invalid_message_id`.
</ParamField>

## Query parameters

<ParamField query="account" type="string" required>
  X username or account ID of the connected account that sees the DM.
  Without it, the route returns `400 account_required`.
</ParamField>

## Response

## Durable write recovery

<Warning>
  Send one unique `Idempotency-Key` per intended write.
  Replay the same account, target, payload, and media after a lost response.
  Keep the original key for that replay.
</Warning>

1. Store `id`, the nested `hash` in `request`, `billing`, and `statusUrl`.
2. Poll after `Retry-After` or `pollAfterMs` when `terminal` is `false`.
3. Retry only when `safeToRetry` is `true`.
4. Use a new key when `nextAction.requiresNewIdempotencyKey` is `true`.

### 200 terminal or 202 active

* After HTTP `200`, store the result and settled billing.
* After HTTP `202`, poll the same action. Never submit another write.
* After HTTP `400`, fix the named field. Use a new idempotency key.
* After HTTP `401`, fix authentication. Do not retry unchanged.
* After HTTP `402`, fund the account before another write.
* After HTTP `403`, reconnect the account.
* After HTTP `409`, keep the original action. Use a new key for new input.
* After HTTP `422`, fix the rejected request before retrying.
* After HTTP `429`, wait for `Retry-After`. Follow `nextAction`.

See [Get Write Action Status](/api-reference/x-write/get-write-action-status)
for every lifecycle field, terminal state, billing field, and retry rule.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.