# Comece aqui

> Chaves de API, URL base, Idempotency-Key com tabela única de repetição, tempo de resposta e formato das respostas.

Fonte: https://app.thronecorporate.com/docs/comecar

## URL base

Todas as chamadas usam HTTPS e esta base:

```text
https://app.thronecorporate.com/api/v1
```

O endereço `https://api.thronecorporate.com/api/v1` responde igual. O caminho sempre começa com `/api/v1`: sem o `/api`, a resposta é 404.

## Autenticação

Toda chamada leva o header `Authorization` com a chave pública e a secreta separadas por dois-pontos:

```http
Authorization: Bearer pk_live_xxxxxxxx:sk_live_xxxxxxxx
```

Envie exatamente assim, em texto. Não codifique em base64.

Gere as chaves no painel, em [Chaves de API](https://app.thronecorporate.com/painel/api-keys). A chave secreta aparece uma vez só, na criação. Guarde no seu servidor e nunca coloque no navegador, no aplicativo ou num repositório de código.

Teste a chave com uma chamada simples:

```bash cURL
curl https://app.thronecorporate.com/api/v1/balance \
  -H "Authorization: Bearer $THRONE_PK:$THRONE_SK" \
  -H "Accept: application/json"
```
```php PHP
$ch = curl_init('https://app.thronecorporate.com/api/v1/balance');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 20,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('THRONE_PK') . ':' . getenv('THRONE_SK'),
        'Accept: application/json',
    ],
]);
$saldo = json_decode(curl_exec($ch), true);
```
```js Node.js
const res = await fetch('https://app.thronecorporate.com/api/v1/balance', {
  headers: {
    Authorization: `Bearer ${process.env.THRONE_PK}:${process.env.THRONE_SK}`,
    Accept: 'application/json',
  },
  signal: AbortSignal.timeout(20000),
});
const saldo = await res.json();
```
```python Python
import os, requests

r = requests.get(
    "https://app.thronecorporate.com/api/v1/balance",
    headers={
        "Authorization": f"Bearer {os.environ['THRONE_PK']}:{os.environ['THRONE_SK']}",
        "Accept": "application/json",
    },
    timeout=20,
)
saldo = r.json()
```

Se a autenticação falhar, a resposta é sempre `401` com `{"error": "unauthorized", "detail": "..."}`. O campo `detail` diz o motivo:

| `detail` | O que aconteceu |
|---|---|
| `credentials_required` | O header `Authorization` não veio ou não começa com `Bearer ` (por exemplo, veio como `Basic ...`). |
| `malformed_credentials` | O valor depois de `Bearer` não está no formato `pk:sk` (por exemplo, veio em base64). |
| `invalid_credentials` | A chave não existe, foi revogada ou a conta está bloqueada. |
| `ip_not_allowed` | A chave tem lista de IPs e o IP da chamada não está nela. |

## Permissões da chave

Cada chave tem permissões (escopos). Sem a permissão certa, a resposta é `403` com `error: "scope_required"` e o campo `required_scope`.

| Permissão | Libera | Vem ligada numa chave nova |
|---|---|---|
| `transactions:write` | Criar cobranças Pix e checkouts de cartão | Sim |
| `transactions:read` | Consultar transações | Sim |
| `balance:read` | Consultar saldo | Sim |
| `withdrawals:read` | Consultar saques | Sim |
| `withdrawals:write` | Pedir saque | Não. Ligue no painel e cadastre os IPs de saque (veja abaixo) |
| `payment_links:write` e `payment_links:read` | Criar e consultar links de pagamento | Não. Ligue no painel |

### As duas listas de IP da chave

| Lista | Vale para | Formato | Sem o IP na lista |
|---|---|---|---|
| **IPs da chave** | Todas as chamadas da chave | IP exato | `401 ip_not_allowed` |
| **IPs de saque** | Só `POST /withdrawals` | IP exato ou faixa CIDR | `403 withdrawal_ip_not_authorized` |

A lista de IPs da chave é opcional, mas uma chave de produção com a permissão de saque exige as duas listas. Por isso, use **uma chave só para saques**, rodando de um servidor com IP fixo, e outra para cobranças.

## Não existe ambiente de testes separado

> [!WARNING]
> Hoje as chaves `pk_test_` funcionam como as de produção. Um checkout de cartão cobra o cartão de verdade e um saque transfere dinheiro de verdade. Teste com valores baixos e com o seu próprio cartão ou chave Pix.

O roteiro completo, com os erros que dá para provocar sem custo, está em [Como testar](/docs/testar).

## Idempotency-Key

Toda chamada que cria algo exige o header `Idempotency-Key`: `POST /transactions`, `POST /elements/checkout`, `POST /global/checkout`, `POST /withdrawals` e `POST /payment-links`. Sem ele, a resposta é `400 idempotency_key_required`.

```http
Idempotency-Key: 6f1c2b8e-3a4d-4e5f-9a7b-1c2d3e4f5a6b
```

Regras:

- De 8 a 128 caracteres; fora disso, `400 idempotency_key_required`. Use um UUID v4 novo para cada cobrança, checkout ou saque. Não use número de pedido nem sequência, que são curtos e previsíveis.
- A `Idempotency-Key` vale para a sua conta inteira por 24 horas, em todos os endpoints.
- Gere a chave **antes** de chamar e guarde junto do seu pedido. É ela que permite repetir sem duplicar.
- Mesma `Idempotency-Key` com corpo diferente: `422 idempotency_key_mismatch`, enquanto a chave valer. A ordem dos campos no JSON não importa.

### Como repetir uma chamada com segurança

Esta é a regra única de repetição da API. Ela vale para todo `POST` que exige `Idempotency-Key` e mostra o que acontece quando você repete a chamada com a **mesma** chave e o **mesmo** corpo.

| Resposta da primeira chamada | Repetir com a mesma chave e o mesmo corpo devolve | Pode usar uma chave nova? |
|---|---|---|
| `2xx` (criou) | A resposta original, com o header `Idempotent-Replay: true`. Nada é criado de novo | **Não.** Uma chave nova cria outra cobrança, outro checkout ou outro saque |
| `4xx` (erro de campo ou de regra, inclusive `403`, `422` e `429`) | `409 idempotency_key_race` | Sim. Nada foi criado. Corrija o que for preciso e repita com uma chave nova |
| `502` ou `503`, com o JSON da API (campo `error`), sem `retry_same_key` | `409 idempotency_key_processing`, por até 24 horas. Em `POST /elements/checkout` e no Pix de `POST /transactions`, a chamada roda de novo, como se fosse a primeira | Sim. Nada foi criado. No cartão, siga o campo `retryable` e o header `Retry-After` da resposta |
| `502 acquirer_error` com `"retry_same_key": true` (Pix de `POST /transactions`) | A chamada roda de novo e leva à adquirente a mesma chave da primeira tentativa. Com outro corpo, `422 idempotency_key_mismatch` | **Não.** A cobrança pode ter sido criada, e uma chave nova pode gerar a segunda. Repita com a mesma chave e o mesmo corpo |
| `500` (erro inesperado) | `409 idempotency_key_processing`, por até 24 horas. Em `POST /elements/checkout`, a chamada roda de novo quando nada foi criado | **Só depois de conferir no painel se algo foi criado.** Em `POST /elements/checkout`, repita antes a mesma chamada, igual: se ela rodar de novo, nada tinha sido criado; se vier `409 idempotency_key_processing` por mais de 2 minutos, a venda pode ter sido criada |
| Ainda em andamento | `409 idempotency_key_processing` | **Não.** Espere alguns segundos e repita igual |
| Nenhuma (falha de conexão, timeout do seu lado ou resposta sem o JSON da API, como uma página de erro `502` ou `504` do servidor) | Uma das linhas acima, conforme o que aconteceu no servidor | **Não.** Repita com a mesma chave até ter uma resposta. Veja o caso abaixo |

As respostas `400 idempotency_key_required` e `401 unauthorized` saem antes da chave ser registrada: depois de corrigir, a mesma chave pode ser usada.

**Timeout do seu lado.** Repita com a mesma chave e o mesmo corpo. Se a primeira chamada já terminou, você recebe a resposta dela. Se a resposta for `409 idempotency_key_processing`, espere alguns segundos e repita de novo. Se o `409 idempotency_key_processing` continuar por mais de 2 minutos, a primeira chamada terminou com erro de servidor: confira no painel se algo foi criado antes de seguir com uma chave nova.

**`409 idempotency_key_in_use`.** Essa `Idempotency-Key` não pode ser usada pela sua conta. Gere outra.

## Tempo de resposta e timeouts

Configure um timeout no seu cliente HTTP. Ele precisa ser maior que o tempo que a API leva para responder, senão você desiste de uma chamada que deu certo.

| Chamada | Timeout recomendado |
|---|---|
| `POST /elements/checkout` e `POST /elements/checkout/br` (cartão) | 60 segundos |
| Todas as outras | 20 segundos |

A criação do checkout de cartão fala com a adquirente e costuma responder em poucos segundos. Em horário de pico ela pode demorar mais, por isso o valor maior. Se mesmo assim o timeout estourar, repita com a **mesma** `Idempotency-Key`, como na tabela acima. Os limites e o que fazer com `429` e `503` estão em [Limites e tempos](/docs/limites).

## Limites de requisição

| O quê | Limite |
|---|---|
| Chamadas com chave válida | Sem limite geral |
| Criação de cobrança (`POST /transactions`) | 10 por segundo por conta |
| Criação de checkout de cartão (`POST /elements/checkout`) | Sem limite por conta. Quando a adquirente está sem capacidade, a resposta é `503 temporarily_unavailable` com `Retry-After` |
| Pedido de saque (`POST /withdrawals`) | 1 por minuto por conta |
| Chamadas sem credencial válida | 10 por segundo por IP |

Passou do limite, a resposta é `429`. Espere o tempo do header `Retry-After` (em segundos) e tente de novo com uma `Idempotency-Key` nova. Ignore os headers `X-RateLimit-*` das respostas: eles não refletem os limites acima.

## Formato das respostas

- Corpo sempre em JSON, com `Content-Type: application/json`. Envie também `Accept: application/json` em toda chamada.
- Valores em **centavos inteiros** (`amount_cents`): R$ 49,90 é `4990`. No cartão, na menor unidade da moeda enviada.
- Datas em ISO 8601. As da cobrança, do checkout e do saque vêm em UTC (`2026-10-03T14:20:00Z`); `payment_data.expires_at` do Pix e as datas dos links de pagamento vêm com fuso (por exemplo, `-03:00`). Use um leitor de ISO 8601 que aceite os dois formatos.
- IDs de transação e de saque são UUID em letras minúsculas. Use o id exatamente como veio.
- Campos novos podem aparecer nas respostas e nos webhooks, sempre como acréscimo. Ignore os campos que você não conhece. As mudanças ficam no [Changelog](/docs/changelog).

## Formato dos erros

Erro de campo (validação) volta `422` com a lista por campo:

```json
{
  "message": "O campo amount cents é obrigatório.",
  "errors": { "amount_cents": ["O campo amount cents é obrigatório."] }
}
```

Erro de regra volta com um código fixo em `error` e um texto em `message`:

```json
{ "error": "invalid_request", "message": "Valor mínimo de cobrança: R$ 1,00." }
```

Alguns erros trazem campos extras para você decidir sem ler o texto. Por exemplo, uma indisponibilidade temporária no cartão:

```json
{ "error": "temporarily_unavailable", "message": "A adquirente está temporariamente indisponível. Tente em instantes.", "retryable": true }
```

Trate os erros pelo campo `error` (fixo) e mostre o `message` só como apoio, porque o texto pode mudar. A lista completa, com o que fazer em cada caso, está em [Erros](/docs/errors).

## Próximo passo

- Vai receber por Pix: [Cobrar com Pix](/docs/pix).
- Vai receber cartão: [Cartão: como funciona](/docs/cartao).
- Precisa saber quando a venda foi paga: [Webhooks](/docs/webhooks).
- Antes de vender: [Checklist de verificação](/docs/checklist).
