> ## Documentation Index
> Fetch the complete documentation index at: https://docs.querying.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# 幂等性

> 使用 idempotencyKey 对提交去重。注意单个与批量对重复的处理不同。

`idempotencyKey` 是调用方可选提供的字符串，使提交具备去重能力。用同一个键发送两次，第二次提交会解析到第一次
创建的任务，而不会排入新的工作。

```json theme={null}
{
  "taskType": "GEMINI",
  "idempotencyKey": "job-42:prompt-7:gemini",
  "payload": { "prompt": "best wireless earbuds 2026" }
}
```

省略它就**没有去重**，每次提交都会创建新任务。对一次性查询这是合理的选择；对 cron 或会重试的 worker 可能
重复提交的工作，则是错误的选择。

<Tip>
  好的键应由工作本身而非某次尝试确定地生成。把定义工作单元的标识组合起来，如
  `"{jobId}:{promptId}:{engine}"`，重试时自然会得到同一个键。
</Tip>

## 单个与批量有意不同

这是两个端点唯一的差异之处，也常让人踩坑。

<CodeGroup>
  ```json Single — duplicate is an error theme={null}
  POST /v1/async/task
  → 409 Conflict

  {
    "success": false,
    "error": {
      "code": "RESOURCE_ALREADY_EXISTS",
      "message": "Task already exists.",
      "timestamp": "2026-07-09T04:12:00.000Z"
    }
  }
  ```

  ```json Batch — duplicate is a success theme={null}
  POST /v1/async/task/batch
  → 200 OK

  {
    "success": true,
    "summary": { "total": 1, "succeeded": 1, "failed": 0 },
    "results": [
      {
        "success": true,
        "index": 0,
        "task": { "id": "8f2c1e40-...", "status": "QUEUED", "...": "..." }
      }
    ]
  }
  ```
</CodeGroup>

单任务端点保留显式创建语义：你要求创建任务，而任务已存在，这就是冲突，你应当知道。

批量端点**把重复吸收为成功**。它的调用方通常是重启后重新提交整个作业的 worker，因为一个键已在队列中就把
500 项的批次判为失败，会让幂等键失去意义。你拿到的 `results[i].task` 是*已存在的*任务，因此任务映射依然成立。

## 将结果与提交对应

有两个相互独立的句柄，你两个都需要：

<ResponseField name="results[].index" type="integer">
  与输入数组按位置一一对应。用它把批量响应与你发送的任务对应起来。
</ResponseField>

<ResponseField name="task.idempotencyKey" type="string">
  原样回传。用它对应 *webhook*，webhook 会在几分钟后乱序到达，且不引用你的原始数组。
</ResponseField>

<Note>
  如果没有提供 `idempotencyKey`，该字段会从任务对象中**省略**，而不是返回 `null`。内部任务会以自身 uuid 作为键，
  它天然唯一，因此永远不会去重。
</Note>

## 键的生命周期

永久保留的任务，其键在完成、失败以及 24 小时公开轮询窗口之后仍保持占用。轮询返回 404 并不会释放键。同一次
提交的传输重试请使用同一个键；单独的一次运行请使用新的执行键（例如追加 `:r1` 后缀）。不要求 UUID 格式。
在启用永久保留之前已移入旧历史的记录不会追溯占用键。
