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

# Payment intents

> Crie pedidos de pagamento com links hospedados a partir do seu próprio ERP ou CMS.

Payment intents são a superfície voltada ao integrador. Você mantém seu próprio CMS de faturas ou ERP e faz POST de um "intent" enxuto descrevendo o que cobrar, quem é o comprador e (opcionalmente) o que renderizar na página de pagamento hospedada. A Caratuva roda o KYC do comprador, coleta o pagamento, transfere internacionalmente, faz câmbio e repassa o BRL via PIX, do começo ao fim.

O endpoint retorna uma `hostedPaymentUrl` que você encaminha ao cliente. Seu cliente entra com magic link no e-mail, completa KYC se ainda não tiver feito, e paga. Você fica sabendo de cada transição via webhooks de saída.

## Quando usar isto vs. invoices

* `POST /v1/payment-intents` — seu CMS é dono da fatura; a Caratuva é o motor de pagamento + compliance + liquidação. Pula a etapa de aprovação do lado do vendedor (a chamada de API é a aprovação).
* `POST /v1/invoices` — o painel da Caratuva é dono da fatura. Vendedores criam, um colega aprova caso sua organização exija aprovação, e a plataforma envia o magic link. Use se você não tem sistema de faturamento próprio.

KYC do comprador é obrigatório nos dois fluxos. Não há caminho para pular o KYC em qualquer superfície de pagamento.

## Pré-requisitos

1. **KYB aprovado.** Sua organização precisa ter completado o KYB e ter uma conta virtual provisionada. Caso contrário, a API retorna `400 KybNotApproved`.
2. **Chave de API.** Uma chave de produção (`pk_live_...`) — veja [Chaves de API](/pt-BR/api-reference/api-keys).
3. **Inscrição de webhook.** Você vai querer uma antes de criar intents em produção — veja [Webhooks](/pt-BR/api-reference/webhooks).

## Criar um payment intent

```bash theme={null}
curl -X POST https://api.caratuva.com/v1/payment-intents \
  -H "X-API-Key: $CARATUVA_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "content-type: application/json" \
  -d '{
    "externalId": "INV-2026-00042",
    "amount": "12500.00",
    "currency": "USD",
    "buyer": {
      "email": "buyer@acme.com",
      "name": "ACME Imports LLC",
      "country": "US"
    },
    "returnUrl": "https://erp.example.com/orders/42/paid",
    "display": {
      "title": "Order INV-2026-00042",
      "lineItems": [
        { "description": "Coffee beans, 500 bags", "quantity": "500", "unitPrice": "25.00", "subtotal": "12500.00" }
      ]
    },
    "metadata": {
      "orderId": "42",
      "salesRep": "ana@example.com"
    },
    "expiresAt": "2026-05-09T23:59:59.000Z"
  }'
```

### Corpo da requisição

| Campo                                                 | Tipo            | Obrigatório | Notas                                                                                                                                                                                                                                                                                         |
| ----------------------------------------------------- | --------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `externalId`                                          | string (1–200)  | sim         | Sua chave primária. Único por `(organization, externalId)`. Re-POSTar o mesmo valor retorna o intent existente — seguro sob retentativa de rede.                                                                                                                                              |
| `amount`                                              | decimal string  | sim         | Precisão de duas casas; passe como string para evitar drift de float (`"12500.00"`, não `12500`). \*\*Mínimo de US$ 10,00** — o comprador paga por transferência bancária, que exige um piso de US$ 10, então um valor menor em USD não pode ser pago e é rejeitado com `AmountBelowMinimum`. |
| `currency`                                            | código ISO 4217 | não         | Default `USD`.                                                                                                                                                                                                                                                                                |
| `buyer.email`                                         | string          | sim         | Endereço real — o comprador recebe um magic link nesse e-mail.                                                                                                                                                                                                                                |
| `buyer.name`, `buyer.country`, `buyer.phone`          | string          | não         | Pré-preenche o formulário de KYC; o comprador pode ajustar.                                                                                                                                                                                                                                   |
| `returnUrl`                                           | URL             | não         | Para onde a página hospedada redireciona após a liquidação.                                                                                                                                                                                                                                   |
| `display.title`, `display.notes`, `display.lineItems` | varia           | não         | Renderizados na página hospedada. Texto livre — a Caratuva não valida códigos NCM ou de origem aqui.                                                                                                                                                                                          |
| `metadata`                                            | objeto          | não         | Volta verbatim em todo webhook. Nunca exibido ao comprador.                                                                                                                                                                                                                                   |
| `expiresAt`                                           | RFC 3339        | não         | O intent vai para `expired` se não for pago até este horário.                                                                                                                                                                                                                                 |

### Idempotência

Envie `Idempotency-Key: <uuid>` em toda retentativa do mesmo create lógico. Replays retornam a resposta original sem reexecutar efeitos colaterais. Duas chaves distintas com o mesmo `externalId` colapsam para o mesmo intent (o `externalId` por organização é único).

### Resposta

```json theme={null}
{
  "id": "ckabc123...",
  "externalId": "INV-2026-00042",
  "status": "awaiting_buyer",
  "amount": "12500.00",
  "currency": "USD",
  "hostedPaymentUrl": "https://pay.caratuva.com/r/cl9xy...publicId",
  "buyer": {
    "id": "ckdef456...",
    "email": "buyer@acme.com",
    "name": "ACME Imports LLC",
    "country": "US",
    "kycStatus": "not_started"
  },
  "returnUrl": "https://erp.example.com/orders/42/paid",
  "metadata": { "orderId": "42", "salesRep": "ana@example.com" },
  "display": { "title": "Order INV-2026-00042", "lineItems": [/* ... */] },
  "expiresAt": "2026-05-09T23:59:59.000Z",
  "createdAt": "2026-05-02T14:00:00.000Z",
  "updatedAt": "2026-05-02T14:00:00.000Z"
}
```

`hostedPaymentUrl` é o único campo de que a maioria dos integradores precisa da resposta. Encaminhe ao cliente pelo seu canal (e-mail, SMS, mensagem no app). A Caratuva **não** envia e-mail automático ao comprador quando um payment intent é criado — isso é responsabilidade do integrador nesta superfície, porque você é dono do relacionamento.

## Jornada do comprador na página hospedada

Quando o comprador clica em `hostedPaymentUrl`, ele:

1. Chega no portal do comprador (`pay.caratuva.com/r/<publicId>`) e vê o seu bloco `display` mais o nome do vendedor.
2. Informa o e-mail; recebe um magic link; entra (sessão gerenciada pela Caratuva).
3. Completa o KYC se o `kycStatus` ainda não for `approved`. A etapa é obrigatória — sem fast-forward. Um comprador que voltou e já está verificado em outro fluxo pula essa etapa.
4. Confirma a cotação de câmbio e completa o pagamento.
5. A Caratuva roda a transferência internacional e o repasse PIX para a conta BRL do vendedor.
6. O comprador é redirecionado para `returnUrl` se definido; caso contrário, vê uma página de confirmação da Caratuva.

Cada transição dispara um webhook para o seu ERP manter o status do pedido em sincronia.

## Máquina de estados

Intents criados via API pulam a aprovação do vendedor (`awaiting_approval` / `approved`) e começam em `awaiting_buyer`. A partir daí:

```
awaiting_buyer
  → buyer_kyc_pending
  → buyer_kyc_approved
  → fx_quoted
  → on_ramp_pending
  → on_chain_confirmed
  → offramp_pending
  → settled
```

Estados terminais de falha: `failed`, `cancelled`, `expired`.

## Ler um intent

```bash theme={null}
# Por id Caratuva
curl https://api.caratuva.com/v1/payment-intents/<id> \
  -H "X-API-Key: $CARATUVA_API_KEY"

# Pelo seu externalId (prefixe com ext_)
curl https://api.caratuva.com/v1/payment-intents/ext_INV-2026-00042 \
  -H "X-API-Key: $CARATUVA_API_KEY"
```

## Cancelar um intent

Válido apenas antes do início do repasse em BRL. Após `settled`, cancele o pedido no seu sistema.

```bash theme={null}
curl -X POST https://api.caratuva.com/v1/payment-intents/<id>/cancel \
  -H "X-API-Key: $CARATUVA_API_KEY" \
  -H "content-type: application/json" \
  -d '{ "reason": "Buyer requested cancellation" }'
```

## Erros

| Status | `error`                  | Causa                                                                                           |
| ------ | ------------------------ | ----------------------------------------------------------------------------------------------- |
| 400    | `KybNotApproved`         | Sua organização não tem conta virtual. Complete o KYB primeiro.                                 |
| 400    | `AmountBelowMinimum`     | O `amount` em USD está abaixo do piso de US\$ 10,00 da transferência bancária. Aumente o valor. |
| 400    | `ValidationError`        | Schema Zod rejeitou o body. O campo `message` lista os caminhos ofensores.                      |
| 401    | `InvalidApiKey`          | Chave malformada, desconhecida ou revogada.                                                     |
| 401    | `InvalidApiKeyFormat`    | Header não está no formato `pk_(test\|live)_<id>.<segredo>`.                                    |
| 409    | `IdempotencyKeyConflict` | A `Idempotency-Key` foi usada antes para uma fatura não-payment-intent. Use uma chave nova.     |
| 429    | `RateLimitExceeded`      | Limite por chave de API em `POST /v1/payment-intents`. Faça backoff e tente.                    |

Todas as respostas de erro compartilham o envelope `{ statusCode, error, message }`.
