Pular para o conteúdo principal
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.
  3. Inscrição de webhook. Você vai querer uma antes de criar intents em produção — veja Webhooks.

Criar um payment intent

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

CampoTipoObrigatórioNotas
externalIdstring (1–200)simSua chave primária. Único por (organization, externalId). Re-POSTar o mesmo valor retorna o intent existente — seguro sob retentativa de rede.
amountdecimal stringsimPrecisão de duas casas; passe como string para evitar drift de float ("12500.00", não 12500). **Mínimo de US10,00ocompradorpagaportransfere^nciabancaˊria,queexigeumpisodeUS 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.
currencycódigo ISO 4217nãoDefault USD.
buyer.emailstringsimEndereço real — o comprador recebe um magic link nesse e-mail.
buyer.name, buyer.country, buyer.phonestringnãoPré-preenche o formulário de KYC; o comprador pode ajustar.
returnUrlURLnãoPara onde a página hospedada redireciona após a liquidação.
display.title, display.notes, display.lineItemsvarianãoRenderizados na página hospedada. Texto livre — a Caratuva não valida códigos NCM ou de origem aqui.
metadataobjetonãoVolta verbatim em todo webhook. Nunca exibido ao comprador.
expiresAtRFC 3339nãoO 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

{
  "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

# 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.
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

StatuserrorCausa
400KybNotApprovedSua organização não tem conta virtual. Complete o KYB primeiro.
400AmountBelowMinimumO amount em USD está abaixo do piso de US$ 10,00 da transferência bancária. Aumente o valor.
400ValidationErrorSchema Zod rejeitou o body. O campo message lista os caminhos ofensores.
401InvalidApiKeyChave malformada, desconhecida ou revogada.
401InvalidApiKeyFormatHeader não está no formato pk_(test|live)_<id>.<segredo>.
409IdempotencyKeyConflictA Idempotency-Key foi usada antes para uma fatura não-payment-intent. Use uma chave nova.
429RateLimitExceededLimite 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 }.