> ## 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 API Errors & X Write Action Status

> Handle Twitter API errors by polling tweet, reply, DM, follow, like, repost, media, profile, and community writes. Check results, billing, and safe retries.

<Panel>
  <Tabs defaultTabIndex={0} sync={false}>
    <Tab title="200" id="response-x-write-get-write-action-status-200">
      ```json theme={null}
      {
        "object": "x_write_action",
        "id": "12345",
        "writeActionId": "12345",
        "action": "like",
        "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-get-write-action-status-202">
      ```json theme={null}
      {
        "object": "x_write_action",
        "id": "12346",
        "writeActionId": "12346",
        "action": "create_tweet",
        "status": "dispatching",
        "terminal": false,
        "retryable": false,
        "safeToRetry": false,
        "statusUrl": "/api/v1/x/write-actions/12346",
        "pollAfterMs": 2000
      }
      ```
    </Tab>

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

    <Tab title="404" id="response-x-write-get-write-action-status-404">
      ```json theme={null}
      {
        "error": "not_found",
        "message": "Resource not found."
      }
      ```
    </Tab>

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

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

Every X write returns a durable `x_write_action` record.
Store its `id` or `writeActionId`.
Poll `statusUrl` whenever `terminal` is `false`.

## Handle Twitter API Errors

Use this endpoint for Twitter API error handling after an Xquik write.
Xquik adds a durable write record beside ordinary HTTP response codes.
An error message does not prove that a write failed.
A timeout can occur after dispatch.
Trust `terminal`, `safeToRetry`, `sendDispatched`, and `nextAction`.
Review X's [response codes and errors](https://docs.x.com/x-api/fundamentals/response-codes-and-errors).
Xquik's write status record uses different fields.

<Note>
  Trust `terminal`, `safeToRetry`, and `nextAction`. Never infer retry safety
  from an HTTP status or error name alone.
</Note>

## Agent Algorithm

1. Generate one unique `Idempotency-Key` for the intended write.
2. Submit the write once.
3. Store `id`, `request.hash`, `account`, `target`, `billing`, and `statusUrl`.
4. When `terminal` is `false`, wait for `Retry-After` or `pollAfterMs`.
5. Poll `statusUrl` until `terminal` is `true`.
6. Record `result` and settled `billing`.
7. Retry only when `safeToRetry` is `true`, using a new key.
8. When `nextAction.type` is `verify_result`, verify the external state first.

Reuse the original `Idempotency-Key` only to replay the same account,
action, target, and payload. The replay returns the same durable action.

## Lifecycle

| Status                 | Terminal | Required behavior                                  |
| ---------------------- | -------- | -------------------------------------------------- |
| `accepted`             | No       | Poll. Dispatch has not completed.                  |
| `dispatching`          | No       | Poll. Do not submit another write.                 |
| `pending_confirmation` | No       | Poll. The write may already exist.                 |
| `success`              | Yes      | Store the result and settled billing.              |
| `failed`               | Yes      | Follow `safeToRetry` and `nextAction`.             |
| `expired`              | Yes      | Verify the result when dispatch may have occurred. |

Active actions return `202`, `Location`, and `Retry-After`. Terminal actions
return `200` from this endpoint.

## Request

<CodeGroup>
  ```bash cURL theme={null}
  curl https://xquik.com/api/v1/x/write-actions/42 \
    -H "x-api-key: xq_YOUR_KEY_HERE" | jq
  ```

  ```javascript Node.js theme={null}
  const response = await fetch(
    "https://xquik.com/api/v1/x/write-actions/42",
    { headers: { "x-api-key": "xq_YOUR_KEY_HERE" } },
  );
  const action = await response.json();

  if (!action.terminal) {
    const delayMs = action.pollAfterMs ?? 2000;
    process.stdout.write(
      JSON.stringify({ delayMs, next: action.statusUrl }) + "\n",
    );
  } else if (action.safeToRetry) {
    process.stdout.write("Ask for approval, then retry with a new key.\n");
  } else {
    process.stdout.write(JSON.stringify(action.result) + "\n");
  }
  ```

  ```python Python theme={null}
  import requests

  response = requests.get(
      "https://xquik.com/api/v1/x/write-actions/42",
      headers={"x-api-key": "xq_YOUR_KEY_HERE"},
  )
  action = response.json()

  if not action["terminal"]:
      print({"delay_ms": action["pollAfterMs"], "next": action["statusUrl"]})
  elif action["safeToRetry"]:
      print("Ask for approval, then retry with a new key.")
  else:
      print(action["result"])
  ```
</CodeGroup>

## Headers

<ParamField header="x-api-key" type="string">
  Send your Xquik API key in this header.
</ParamField>

<ParamField header="Authorization" type="string">
  Send an OAuth 2.1 bearer token instead of `x-api-key`.
</ParamField>

## Path Parameters

<ParamField path="id" type="string" required>
  Durable action ID returned by the original write.
</ParamField>

## Response

<Tabs>
  <Tab title="200 Terminal">
    Polling is complete. Store `result` and settled `billing`. Follow
    `safeToRetry` and `nextAction` before any new attempt.
  </Tab>

  <Tab title="202 Active">
    Poll `statusUrl` after `Retry-After` or `pollAfterMs`. Never submit another
    write while `terminal` is `false`.
  </Tab>

  <Tab title="401 Unauthenticated">
    Fix the authentication credentials. Keep the original write record.
  </Tab>

  <Tab title="404 Not Found">
    Check the action ID and environment. Never resubmit the original write.
  </Tab>

  <Tab title="429 Rate Limited">
    Wait for `Retry-After`. Poll the same action again. Never resend the write.
  </Tab>
</Tabs>

<ResponseField name="object" type="string">Always `x_write_action`.</ResponseField>
<ResponseField name="id" type="string">Durable action ID.</ResponseField>
<ResponseField name="writeActionId" type="string">This field aliases `id`.</ResponseField>
<ResponseField name="action" type="string">Exact write operation.</ResponseField>
<ResponseField name="status" type="string">Current lifecycle status.</ResponseField>
<ResponseField name="terminal" type="boolean">This field is `true` when polling can stop.</ResponseField>
<ResponseField name="retryable" type="boolean">This field is `true` when a later attempt could succeed.</ResponseField>
<ResponseField name="safeToRetry" type="boolean">This field is `true` when a new attempt is safe.</ResponseField>
<ResponseField name="statusUrl" type="string">Relative polling URL.</ResponseField>
<ResponseField name="pollAfterMs" type="number | null">Recommended polling delay.</ResponseField>
<ResponseField name="charged" type="boolean">`true` after billing settles as charged.</ResponseField>
<ResponseField name="chargedCredits" type="string">Settled credits charged.</ResponseField>
<ResponseField name="billing" type="object">Planned and settled billing state.</ResponseField>
<ResponseField name="request" type="object">Stable hash and exact sanitized payload.</ResponseField>
<ResponseField name="account" type="object">Connected account selected for this write.</ResponseField>
<ResponseField name="target" type="object | null">Target type and ID.</ResponseField>
<ResponseField name="targetId" type="string | null">Target ID alias.</ResponseField>
<ResponseField name="result" type="object | null">Confirmed result or desired state.</ResponseField>
<ResponseField name="nextAction" type="object | null">Required poll, retry, or verification step.</ResponseField>
<ResponseField name="requestHash" type="string">Stable request fingerprint.</ResponseField>
<ResponseField name="requestId" type="string">Correlation ID echoed in `X-Request-Id`.</ResponseField>
<ResponseField name="idempotent" type="boolean">`true` when this response replays an action.</ResponseField>
<ResponseField name="error" type="string">Machine-readable error code.</ResponseField>
<ResponseField name="message" type="string">Actionable status or error message.</ResponseField>
<ResponseField name="sendDispatched" type="boolean">This field is `true` after dispatch.</ResponseField>
<ResponseField name="sendDispatchedAt" type="string">ISO 8601 dispatch time.</ResponseField>
<ResponseField name="createdAt" type="string">ISO 8601 creation time.</ResponseField>
<ResponseField name="updatedAt" type="string">ISO 8601 latest update time.</ResponseField>
<ResponseField name="completedAt" type="string">ISO 8601 terminal time.</ResponseField>
<ResponseField name="expiresAt" type="string">Nonterminal resolution deadline.</ResponseField>
<ResponseField name="confirmedAt" type="string">ISO 8601 confirmation time.</ResponseField>
<ResponseField name="confirmationCheckedAt" type="string">ISO 8601 latest confirmation check.</ResponseField>
<ResponseField name="confirmationAttempts" type="number">Confirmation attempt count.</ResponseField>
<ResponseField name="tweetId" type="string">Confirmed tweet ID when available.</ResponseField>
<ResponseField name="messageId" type="string">Confirmed direct message ID when available.</ResponseField>
<ResponseField name="mediaId" type="string">Confirmed media ID when available.</ResponseField>
<ResponseField name="mediaUrl" type="string">Public media URL when available.</ResponseField>
<ResponseField name="communityId" type="string">Confirmed community ID when available.</ResponseField>
<ResponseField name="communityName" type="string">Confirmed community name when available.</ResponseField>
<ResponseField name="resultId" type="string">Result ID alias.</ResponseField>
<ResponseField name="media" type="object">Media details when used.</ResponseField>
<ResponseField name="details" type="object">Structured recovery context.</ResponseField>
<ResponseField name="success" type="boolean">This field is `true` when status equals `success`.</ResponseField>

```json theme={null}
{
  "object": "x_write_action",
  "id": "42",
  "writeActionId": "42",
  "action": "create_tweet",
  "status": "pending_confirmation",
  "terminal": false,
  "retryable": false,
  "safeToRetry": false,
  "statusUrl": "/api/v1/x/write-actions/42",
  "pollAfterMs": 2000,
  "billing": {
    "status": "pending",
    "charged": false,
    "plannedCredits": "30",
    "chargedCredits": "0"
  },
  "request": {
    "hash": "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb",
    "payload": { "text": "Hello" }
  },
  "account": { "id": "8", "username": "agent_account" },
  "target": null,
  "result": null,
  "nextAction": {
    "type": "poll",
    "url": "/api/v1/x/write-actions/42",
    "afterMs": 2000
  },
  "sendDispatched": true,
  "success": false
}
```

## Retry Rules

* `retryable: false`, `safeToRetry: false`: do not retry.
* `retryable: true`, `safeToRetry: false`: verify the result first.
* `safeToRetry: true`: request approval and retry using a new key.
* `terminal: false`: poll, even when the original HTTP response was an error.

Idempotency replay protection remains active for at least 90 days.
