# Requisições

Uma requisição é uma chamada de um modelo. Ela tem um id (`req_…`), um status, seu custo em créditos de API (1000 = 1 USD) e, quando é bem-sucedida, os arquivos gerados. O mesmo objeto é retornado por todos os endpoints abaixo, para qualquer modelo.

## Encontrar um modelo

O catálogo de todos os 1.496 modelos é um JSON público (não é necessária uma chave):

```bash
curl "https://api.doitong.com/v1/models?category=text-to-video&lab=Google&limit=20"
```

| Parâmetro de consulta | Significado |
|---|---|
| `q` | Palavras que devem aparecer todas no nome, id, fabricante ou tags, por exemplo `kling 3`. |
| `category` | Um id de categoria, por exemplo `text-to-video` ou `upscale`. `GET https://api.doitong.com/v1/categories` lista todas as 21 com suas contagens. |
| `lab` | O fabricante do modelo, por nome ou id, por exemplo `Google` ou `black-forest-labs`. |
| `family` | Um id de `family` de uma linha anterior: cada variante de um modelo (pro, fast, image-to-video, …). |
| `mode` | `exact` (o preço é calculado a partir da entrada), `measured` (a partir da duração ou do tamanho dos arquivos de entrada), `estimate` (um preço estimado por execução) ou `metered` (cobrado pelo uso real, até um máximo por execução). |
| `limit`, `cursor` | Tamanho da página (1 a 100, padrão 50) e o `next_cursor` da página anterior. |

Cada linha tem `id`, `name`, `lab`, `category`, `family`, `summary`, `tags`, `output_kind`, `docs_url` e um objeto `price` (consulte [Preços](https://doitong.com/developers/docs/pricing#price-objects)). `GET https://api.doitong.com/v1/models/{model_id}` adiciona o JSON Schema completo de entrada e saída, os nomes canônicos dos parâmetros, as regras de preço e um exemplo de entrada. Uma `category` ou `mode` desconhecida retorna `400 invalid_parameter`.

## Executar um modelo

```bash
curl -X POST "https://api.doitong.com/v1/run/{model_id}?wait=20" \
  -H "Authorization: Bearer $DOITONG_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1842-cover" \
  -d '{"prompt": "..."}'
```

- O corpo é a entrada do modelo, um objeto JSON. Cada página de modelo lista seus parâmetros.
- A entrada é verificada antes de qualquer cobrança. Uma entrada inválida retorna `422 invalid_input` com o `param` incorreto e não custa nada.
- `?wait=N` (até 20 segundos) mantém a chamada aberta até a requisição terminar ou o tempo se esgotar.
- `?webhook_url=https://…` envia um POST assinado para essa URL quando a requisição termina (consulte [Webhooks](https://doitong.com/developers/docs/webhooks)).
- `?storage=permanent` ou `?storage=temporary` escolhe por quanto tempo os arquivos de saída são mantidos; sem isso, aplica-se a configuração da conta (consulte [Onde os arquivos ficam](#storage)).

A resposta é `202 Accepted` enquanto a requisição ainda está em andamento e `200 OK` quando ela termina dentro do tempo de espera (ou quando é uma repetição, veja abaixo). Os cabeçalhos `Location` e `Doitong-Request-Id` contêm a URL e o id da requisição.

## Status

Ciclo de vida da solicitação: `queued` → `running` → `succeeded` | `failed` | `canceled` (final; falhas e cancelamentos são reembolsados).

| Status | Significado | Final |
|---|---|---|
| `queued` | Aceita e cobrada, aguardando um worker. `queue_position` pode mostrar a posição na fila. | não |
| `running` | O modelo está gerando. | não |
| `succeeded` | Concluída. `outputs` lista os arquivos. | sim |
| `failed` | O modelo ou um limite a interrompeu. `error` informa o motivo. Os créditos de API são reembolsados. | sim |
| `canceled` | Cancelada antes de produzir um resultado. Os créditos de API são reembolsados. | sim |

Uma requisição que ultrapassa seu limite de tempo falha com o código `timeout` e é reembolsada: 15 minutos para imagens, 60 minutos para vídeo, 30 minutos para áudio e todo o restante.

## O objeto de requisição

```json
{
  "id": "req_8k2m4q7x1z9c3v5b6n0p2r4t",
  "object": "request",
  "model": "flux/schnell",
  "status": "succeeded",
  "created_at": "2026-10-20T12:00:00Z",
  "started_at": "2026-10-20T12:00:01Z",
  "completed_at": "2026-10-20T12:00:02Z",
  "queue_position": null,
  "cost": { "credits": 3, "usd": "0.003", "charged": 3, "refunded": 0 },
  "input": { "prompt": "..." },
  "output": { "images": [ { "url": "https://content.doitong.com/dev-api/…/0-output.jpeg", "width": 1024, "height": 768 } ] },
  "outputs": [
    { "type": "image", "url": "https://content.doitong.com/dev-api/…/0-output.jpeg", "content_type": "image/jpeg",
      "width": 1024, "height": 768, "file_size": 183920, "stored": true }
  ],
  "error": null,
  "metrics": { "inference_seconds": 1.2 },
  "storage": { "mode": "permanent", "bytes": 183920, "expires_at": null, "expired": false },
  "urls": {
    "self": "https://api.doitong.com/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t",
    "cancel": "https://api.doitong.com/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t/cancel"
  }
}
```

- `outputs` é igual para todos os modelos: uma entrada por arquivo gerado com `type` (`image`, `video`, `audio`, `file` ou `3d`), uma `url` em nosso armazenamento, `content_type` e, quando conhecidos, `file_size`, `width`, `height`, `duration`.
- `output` é o próprio objeto de resultado do modelo (seus campos são listados em cada página de modelo), com as mesmas URLs.
- `storage` informa como os arquivos são mantidos: `mode` (`permanent` ou `temporary`), `bytes` armazenados, `expires_at` (quando um arquivo temporário é excluído, `null` para arquivos permanentes) e `expired` (`true` quando os arquivos desaparecem).
- `cost.credits` é o preço da chamada em créditos de API (`cost.usd` em USD), `cost.charged` é o que foi debitado do saldo de API e `cost.refunded` é o que voltou. Para um modelo `metered`, `cost.credits` é o máximo retido no início (`cost.held`); quando a execução é bem-sucedida, `cost.settled` informa o valor correspondente ao uso e o restante do máximo fica em `cost.refunded` (consulte [Cobrança por uso](https://doitong.com/developers/docs/pricing#metered)).
- Quando uma requisição falha, `error` é `{ "type": …, "code": …, "message": … }`.

## Ler uma requisição

```bash
curl "https://api.doitong.com/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t?wait=20" \
  -H "Authorization: Bearer $DOITONG_API_KEY"
```

- `?wait=N` (até 20 segundos) faz long polling: a chamada retorna assim que a requisição chega a um estado final ou, após N segundos, com o estado atual. Repetir essa chamada é a maneira mais simples de aguardar um resultado.
- `?logs=1` adiciona as linhas do log de progresso do modelo (as últimas 50) como `logs`.

Você só pode ler requisições da sua própria conta; qualquer outro id retorna `404 request_not_found`.

## Listar requisições

```bash
curl "https://api.doitong.com/v1/requests?limit=20&status=succeeded&model=flux/schnell" \
  -H "Authorization: Bearer $DOITONG_API_KEY"
```

Mais recentes primeiro. `limit` vai de 1 a 100 (padrão 20); `status` e `model` filtram. A resposta é `{ "object": "list", "data": [ … ], "has_more": true, "next_cursor": "req_…" }`; envie `cursor=<next_cursor>` para a próxima página.

## Cancelar uma requisição

```bash
curl -X POST "https://api.doitong.com/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t/cancel" \
  -H "Authorization: Bearer $DOITONG_API_KEY"
```

- Cancelada antes de o modelo começar: o status muda para `canceled`, os créditos de API são reembolsados e você recebe `200`.
- Já em geração: o cancelamento é encaminhado e você recebe `202` com `"cancel_requested": true`. Se o modelo terminar mesmo assim, a requisição será concluída normalmente e cobrada.
- Já finalizada: `409 not_cancelable` (uma requisição que já está `canceled` simplesmente retorna `200`).

## Excluir os arquivos de uma requisição

```bash
curl -X DELETE "https://api.doitong.com/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t/files" \
  -H "Authorization: Bearer $DOITONG_API_KEY"
```

Exclui agora nossas cópias dos arquivos de saída da requisição e interrompe a cobrança de armazenamento. A resposta é o objeto da requisição com `storage.expired: true`; as URLs permanecem no registro, mas deixam de funcionar. Chamá-lo novamente não altera nada (`200`). Uma requisição que ainda está `queued` ou `running` não tem arquivos: `409 request_active`. Requer `models:run`.

## Idempotência: repetir sem pagar duas vezes

As redes falham. Para repetir uma execução com segurança, envie um cabeçalho `Idempotency-Key` (qualquer string de até 255 caracteres, exclusiva por operação, por exemplo seu próprio id do pedido):

- Mesma chave e mesmo corpo de antes: você recebe a requisição original (`200`, cabeçalho `Idempotent-Replayed: true`) e nada é cobrado novamente.
- Mesma chave e corpo após uma execução que falhou antes de chegar ao modelo (`upstream_error` ou `internal_error` no envio, `insufficient_credits`, `budget_exceeded`, `too_many_inflight`): nada foi produzido e nada foi cobrado, então a repetição executa o modelo novamente em vez de retornar a falha.
- Mesma chave com corpo diferente: `409 idempotency_mismatch`.
- As chaves são vinculadas à chave de API que as enviou.

## Onde os arquivos ficam

Cada arquivo de saída é copiado para nosso armazenamento antes de a requisição ser marcada como `succeeded`, portanto as URLs em `outputs` estão em nossos próprios hosts e você pode vinculá-las diretamente. Você escolhe por quanto tempo elas ficam:

| Modo | Duração | Preço |
|---|---|---|
| `permanent` (padrão) | Até você excluí-los | 100 créditos de API ($ 0,10) por GB por mês, cobrados diariamente do saldo de API |
| `temporary` | 7 dias, depois excluídos | Grátis |

- A configuração da conta decide para novas requisições: a chave "Manter arquivos para sempre" no [dashboard](https://doitong.com/developers/dashboard#balance), ou `PATCH https://api.doitong.com/v1/account/storage` com `{"mode": "temporary"}`. `GET https://api.doitong.com/v1/account` a mostra em `storage`, com os bytes mantidos e a estimativa mensal.
- `?storage=permanent|temporary` em uma execução (o argumento `storage` da ferramenta `run` do MCP) substitui essa configuração para essa execução.
- Alterar a configuração nunca afeta arquivos que você já tem; exclua-os com [`DELETE /requests/{id}/files`](#delete-files).
- Quando o saldo de API não pode pagar pelo armazenamento, nada é cobrado e você recebe um e-mail: faça uma recarga em até 30 dias ou os arquivos mantidos se tornarão temporários e serão excluídos 7 dias depois.

No caso raro de nossa cópia falhar, a entrada terá `"stored": false` e a URL será a original, de curta duração: baixe-a em breve.
