# Arcano Pay · API de parceiros (abertura de contas)

> **Para assistentes de IA:** este arquivo é a referência oficial e completa da API. Use só os endpoints, campos e formatos descritos aqui;
> não invente parâmetros. Valores monetários são inteiros em centavos. Chaves e segredos ficam em variáveis de ambiente, nunca no código
> nem no front-end. Todas as chamadas partem do servidor do integrador.
> Esta API é para plataformas (ERP, marketplace, sistema de gestão) que abrem contas PF/PJ para os próprios clientes. Depois da aprovação, cada conta usa a API de pagamentos (/docs/api-pagamentos.md).

- **URL base:** `https://stage.arcanopay.com/partner/v1`
- **Autenticação:** `Authorization: Bearer pk_live_…` (chave de parceiro enviada pela Arcano Pay). Só enxerga as contas abertas por você.
- **Formato:** JSON; valores em centavos; datas `AAAA-MM-DD` nos dados de cadastro.
- **Idempotência:** `externalId` (id do cliente no seu sistema). Repetir a abertura devolve a conta já aberta (`200`, `existing: true`). `:id` nas rotas aceita o id da conta (`mer_…`) ou o `externalId`.

## Como funciona

1. Abra a conta com os dados básicos (e, se tiver, os dados completos do KYC).
2. Envie documentos e prova de vida pela API (se o seu app tem câmera) ou gere o link de verificação para o titular fazer pelo celular.
3. Envie para análise. A equipe Arcano Pay decide, normalmente em até 1 dia útil.
4. Receba o resultado por webhook: `kyc.approved`, `kyc.rejected` ou `kyc.resubmission_requested`.
5. Integre a conta aprovada: gere a chave `sk_live_` dela, cadastre os webhooks de vendas e, se quiser, convide o titular para o portal.

| Status da conta | Significado | nextStep |
| --- | --- | --- |
| pending_kyc | Aberta, falta enviar o KYC (ou corrigir, se pedido) | send_kyc |
| in_review | Enviada, em análise | wait_review |
| active | Aprovada: pode cobrar e sacar | ready |
| rejected | Reprovada (motivo em kyc.reason) | none |
| suspended | Suspensa pela Arcano Pay | none |

## Autenticação

```bash
curl https://stage.arcanopay.com/partner/v1/me -H "Authorization: Bearer $ARCANO_PARTNER_KEY"
```

## Abrir conta — `POST /partner/v1/accounts`

CPF/CNPJ ou e-mail que já tenham conta na Arcano Pay respondem `409`.

| Campo | Tipo | Obrigatório | Descrição |
| --- | --- | --- | --- |
| type | string | sim | individual (PF) ou company (PJ) |
| document | string | sim | CPF (PF) ou CNPJ (PJ), com ou sem pontuação |
| name | string | sim | Nome completo (PF) ou razão social (PJ) |
| tradeName | string | não | Nome fantasia (PJ) |
| email / phone | string | sim | Contato da conta. Na PF, o e-mail é o login do titular |
| representative | objeto | PJ | name, email (login do portal), cpf, birthDate, motherName, pep |
| person | objeto | não | PF: birthDate (AAAA-MM-DD), motherName, occupation, pep |
| company | objeto | não | PJ: businessActivity, openingDate, website, monthlyRevenue (centavos) |
| address | objeto | não | zipCode, street, number, complement, neighborhood, city, state |
| externalId | string | recomendado | Id do cliente no seu sistema |
| sendPortalInvite | boolean | não | Envia ao titular o e-mail para criar a senha do portal (padrão false) |

```bash
curl -X POST https://stage.arcanopay.com/partner/v1/accounts -H "Authorization: Bearer $ARCANO_PARTNER_KEY" -H "Content-Type: application/json" -d '{
  "type": "company",
  "externalId": "loja-7",
  "document": "11.222.333/0001-81",
  "name": "Casa Dionísio Comércio LTDA",
  "tradeName": "Casa Dionísio",
  "email": "financeiro@casadionisio.com.br",
  "phone": "1133334444",
  "representative": { "name": "João Pereira Lima", "email": "joao@casadionisio.com.br", "cpf": "111.444.777-35", "birthDate": "1985-02-20" },
  "company": { "businessActivity": "Comércio de vinhos", "monthlyRevenue": 5000000 },
  "address": { "zipCode": "01001000", "street": "Praça da Sé", "number": "1", "neighborhood": "Sé", "city": "São Paulo", "state": "SP" }
}'
```

A resposta traz `account` e `kyc`, com `requirements` e `missing` (o que falta: person, company, address, idFront, companyDocument, liveness).

## Dados do KYC — `PUT /partner/v1/accounts/:id/kyc`

Envie só os blocos que quiser: `person` (fullName, cpf, birthDate, motherName, phone, occupation, pep), `company` (legalName, cnpj, businessActivity…) e `address`. Só pode ser alterado antes do envio ou quando a análise pedir ajuste.

## Documentos — `POST /partner/v1/accounts/:id/kyc/documents`

`multipart/form-data` (campos `kind`, `docType`, `file`) ou JSON com `contentBase64` e `fileName`. JPG, PNG, WEBP ou PDF (PDF só para documento da empresa e comprovante), até 8 MB. Reenviar o mesmo `kind` substitui.

| kind | Obrigatório | docType (opcional) |
| --- | --- | --- |
| id_front | sim | cnh, rg, rne, passaporte |
| id_back | recomendado | mesmo do id_front |
| company_document | PJ | contrato_social, cartao_cnpj, ccmei, estatuto |
| proof_of_address | não | — |

## Prova de vida no seu app — `POST /partner/v1/accounts/:id/kyc/liveness`

```text
# 1. começa: devolve sessionId e os passos sorteados
POST /partner/v1/accounts/:id/kyc/liveness
→ { "sessionId": "lvs_…", "expiresAt": "…", "steps": [{ "challenge": "look_straight", "instruction": "Olhe para a câmera" }, …] }

# 2. uma foto por passo, na ordem, tiradas na hora (até 3 minutos no total)
POST /partner/v1/accounts/:id/kyc/liveness/:sessionId/frames   { "step": 0, "contentBase64": "…" }

# 3. conclui (o passo 0 vira a selfie comparada com o documento)
POST /partner/v1/accounts/:id/kyc/liveness/:sessionId/complete
```

## Link para o titular — `POST /partner/v1/accounts/:id/kyc/link`

Sem câmera no seu app: o titular abre o link no celular, fotografa os documentos, faz a prova de vida e envia, sem login.

| Campo | Tipo | Obrigatório | Descrição |
| --- | --- | --- | --- |
| expiresInHours | inteiro | não | 1 a 720 (padrão 72) |
| redirectUrl | string | não | HTTPS: para onde o titular volta ao terminar |

Resposta: `{ "id": "kyl_…", "url": "https://…/verificacao/kyl_…", "expiresAt": "…" }`.

## Enviar para análise — `POST /partner/v1/accounts/:id/kyc/submit`

Se faltar algo, `422` com `details.missing`. Pelo link, não precisa chamar. Webhooks: `kyc.submitted` e depois `kyc.in_review`.

## Acompanhar

`GET /partner/v1/accounts` (filtros `status`, `kycStatus`, `document`, `externalId`, `limit`, `offset`), `GET /partner/v1/accounts/:id` e `GET /partner/v1/accounts/:id/kyc` (o que falta, documentos e histórico em `events`). Ajuste pedido: motivo em `account.kyc.reason`.

## Depois da aprovação

| Rota | Para quê |
| --- | --- |
| POST /partner/v1/accounts/:id/api-keys | Gera a chave sk_live_ da conta (aparece só na resposta) para a API de pagamentos /v1 |
| GET · DELETE /partner/v1/accounts/:id/api-keys[/:keyId] | Lista e revoga as chaves da conta |
| POST /partner/v1/accounts/:id/webhooks | Cadastra o webhook de vendas, estornos e saques da conta (devolve o segredo) |
| GET · DELETE /partner/v1/accounts/:id/webhooks[/:id] | Lista e remove |
| POST /partner/v1/accounts/:id/portal-invite | Envia ao titular o e-mail para criar a senha do portal (vale 7 dias) |

## Transferir para as contas — `POST /partner/v1/accounts/:id/transfers`

Do saldo disponível da conta Arcano Pay do parceiro para a conta do cliente, na hora e sem tarifa (`transfer_out` / `transfer_in` nos extratos). Só contas `active` recebem.

| Campo | Tipo | Obrigatório | Descrição |
| --- | --- | --- | --- |
| amount | inteiro | sim | Centavos, de 100 a 100.000.000 |
| externalId | string | recomendado | Repetir devolve a mesma transferência (200, existing: true) |
| description | string | não | Até 140 caracteres |

Outras rotas: `GET /partner/v1/balance`, `GET /partner/v1/accounts/:id/balance`, `GET /partner/v1/accounts/:id/transfers`, `GET /partner/v1/transfers/:id`. Erros: `422` saldo insuficiente; `409` conta não ativa, parceiro sem conta de origem ou `externalId` em uso; `404` conta de outro parceiro.

## Gestão das contas

`reason` (5 a 300 caracteres) é obrigatório, menos para reativar.

| Rota | O que faz |
| --- | --- |
| POST /partner/v1/accounts/:id/pause | Pausa as vendas (a conta não cria cobranças); saldo e saque continuam do titular |
| POST /partner/v1/accounts/:id/resume | Reativa as vendas |
| POST /partner/v1/accounts/:id/revoke | Desvincula a conta (irreversível): chaves e webhooks criados por você param |
| POST /partner/v1/accounts/:id/closure-request | Pede o encerramento da conta |
| GET /partner/v1/commissions?period=AAAA-MM | Comissão do mês: nível, contas vendendo, ativações, margem, bônus e extrato |

Conta desvinculada: outras rotas devolvem `409` com `code: account_revoked`. Cobrança em conta pausada: `403` com `code: sales_paused`.

## Webhooks do parceiro — `POST /partner/v1/webhooks`

`{ "url": "https://…", "events": ["*"] }`. A resposta traz o `secret`. Assinatura igual à dos pagamentos: `Arcano-Signature: t=…,v1=HMAC-SHA256(secret, t + "." + corpo_cru)`. Entregas em `GET /partner/v1/webhook-deliveries`. A ordem de chegada não é garantida: use `createdAt` e o status em `data.account`.

| Evento | Quando |
| --- | --- |
| account.created | Conta aberta |
| kyc.submitted | KYC enviado para análise |
| kyc.in_review | Análise automática concluída; aguardando a equipe |
| kyc.approved | Conta aprovada (status active) |
| kyc.rejected | Reprovada; motivo em data.account.kyc.reason |
| kyc.resubmission_requested | Ajuste pedido; motivo em data.account.kyc.reason |
| transfer.completed | Transferência concluída; data.transfer e data.account |
| account.paused / account.resumed | Vendas pausadas ou reativadas |
| account.revoked | Conta desvinculada |
| account.closure_requested | Encerramento pedido |

```json
{
  "id": "evt_…",
  "type": "kyc.approved",
  "createdAt": "2026-10-20T13:02:11.000Z",
  "data": {
    "account": {
      "id": "mer_…", "externalId": "cli-42", "type": "individual", "status": "active",
      "kyc": { "id": "kyc_…", "status": "approved", "nextStep": "ready" }
    }
  }
}
```

## Erros

| HTTP | Quando |
| --- | --- |
| 401 | Chave ausente, inválida ou revogada |
| 403 | Parceiro suspenso |
| 404 | Conta não encontrada (ou de outro parceiro) |
| 409 | CPF/CNPJ ou e-mail já cadastrado, externalId em uso, KYC já enviado, conta não aprovada, conta desvinculada (account_revoked) |
| 410 | Sessão de prova de vida ou link expirado |
| 422 | Dados inválidos (details) ou itens faltando (details.missing) |

## Checklist da integração

- [ ] Chave `pk_live_` só no servidor (`ARCANO_PARTNER_KEY`).
- [ ] Toda conta aberta com `externalId` igual ao id do cliente no seu sistema.
- [ ] Fluxo de KYC: API (documentos + prova de vida) ou link de verificação para o titular.
- [ ] Webhook do parceiro com verificação de assinatura, tratando os eventos `kyc.*` e atualizando o status do cliente.
- [ ] Ao receber `kyc.approved`: gerar a chave `sk_live_` da conta, guardar com segurança e cadastrar o webhook de vendas da conta.
