Migrar da v1 — referência completa
Para o resumo executivo de 2 minutos, veja Migrar da v1 — versão rápida.
Este documento existe para devs auditando a migração, time de ops, e casos edge. Lista as ~75 rotas integrator-facing e como cada uma se comporta na v2.
1. Autenticação
Antes (v1)
TOKEN_URL="https://corpx-{env}.auth.sa-east-1.amazoncognito.com/oauth2/token"
curl -X POST "$TOKEN_URL" \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-d "grant_type=client_credentials&scope=api/full"
Agora (v2)
TOKEN_URL="https://auth.api.corpx.com/oauth2/token"
curl -X POST "$TOKEN_URL" \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-d "grant_type=client_credentials&scope=api2/read api2/write"
Diferenças:
- Token URL muda (passa pelo nosso CloudFront).
scope:api/full→api2/read api2/write.- TTL do JWT: 1h (mesmo da v1).
Idempotency-Key: opcional na v2 (auto-gerado se ausente; recomendado continuar enviando para controle de retry).X-Tenant-Id: obrigatório em todas as requisições/v1/*(excetoGET /v1/mee health). Deve bater com o tenant da conta quandoaccountIdestá no path.
2. Endpoints deprecated (continuam funcionando até 2026-11-21)
4 paths que existiam na v1 e ficaram fora da versão canônica da v2 foram reabilitados como aliases deprecated. Eles continuam respondendo normalmente — apenas anexam 3 headers de aviso na resposta:
Deprecation: true
Sunset: Sat, 21 Nov 2026 00:00:00 GMT
Link: </v1/accounts/.../novo-caminho>; rel="successor-version"
Esses headers seguem RFC 8594 (Deprecation) e RFC 9745 (Sunset). Clientes podem detectá-los automaticamente para alertar a equipe.
Sunset previsto: 2026-11-21. Após essa data eles passam a retornar 410 Gone.
| Rota deprecated (ainda funciona) | Substituto canônico |
|---|---|
GET /v1/accounts/{id}/pix/payments/{paymentId} | GET /v1/accounts/{id}/pix/payments/lookup?identifier=... |
GET /v1/accounts/{id}/payments/{paymentId} (alias sem /pix/) | GET /v1/accounts/{id}/pix/payments/lookup?identifier=... |
GET /v1/accounts/{id}/pix/qr-code (sem /lookup) | GET /v1/accounts/{id}/pix/qr-code/lookup?identifier=... |
POST /v1/accounts/{id}/pix/out/qrcode (sem hífen) | POST /v1/accounts/{id}/pix/out/qr-code |
Essas rotas não aparecem mais no OpenAPI nem na Postman collection — só estão documentadas aqui pra dar tempo de migrar. Quem está começando uma integração nova deve usar diretamente os substitutos.
3. Endpoints removidos (7 rotas — retornam 404)
| Rota v1 (removida) | O que fazer |
|---|---|
POST /v1/webhooks/replay | Polling do extrato em janela curta |
GET /v1/integrator/webhooks | Use GET /v1/webhooks |
GET /v1/integrator/webhooks/{id}/deliveries | Sem substituto — fale com suporte se depende |
POST /v1/integrator/events/replay | Sem substituto |
POST /v1/integrator/events/replay/batch | Sem substituto |
POST /v1/accounts/{id}/pix/med/{medId}/send | Use /decide (mas MED está em 503 hoje) |
POST /v1/accounts/{id}/pix/med/{medId}/response | Use /answer (mas MED está em 503 hoje) |
GET /v1/health continua existindo na v2. Foi adicionado um alias GET /health (sem /v1) para conveniência de health checks externos — você não precisa mudar nada.
4. Endpoints com mudança de comportamento
Mesmo path, mesmo método, mas comportamento ou shape JSON diferente.
4.1 Statement — cache → live (e payer/payee reduzidos)
GET /v1/accounts/{id}/statement
v1: lia de um cache local (DynamoDB) populado por webhooks. Latência ~50ms; enriquecia com tariff_ref e labels traduzidos. As linhas tinham payer.{bankCode,bankIspb,branch,account,pixKey,...} e beneficiary.{...} completos porque eram montados a partir dos webhooks (que trazem todos os campos bancários da contraparte).
v2: chama o liquidante na hora da request. Latência ~500ms-1.5s; sem tariff_ref derivado (a tarifa aparece como linha própria no extrato); header novo X-Source: live. Janela máxima por consulta: 31 dias.
O que mudou no shape de cada linha
A v2 padronizou o objeto da contraparte para name + document + a info do banco do nosso lado (banco liquidante = MT Bank). Os demais campos bancários da contraparte (branch, account, pixKey, etc.) não vêm mais nas linhas do extrato porque o endpoint de extrato do liquidante não os expõe.
v1 (legado, vindo do cache de webhooks):
{
"amount": -100.00,
"direction": "OUT",
"endToEndId": "E...",
"payer": { "name": "...", "document": "...", "bankCode": "681", "bankIspb": "50871921", "branch": "...", "account": "...", "pixKey": "..." },
"beneficiary":{ "name": "...", "document": "...", "bankCode": "001", "bankIspb": "00000000", "branch": "...", "account": "...", "pixKey": "..." }
}
v2 (live, vindo do liquidante):
{
"amount": -100.00,
"direction": "OUT",
"endToEndId": "E...",
"payer": { "name": "MINHA EMPRESA LTDA", "document": "12345678000190", "bankCode": "681", "bankIspb": "50871921", "bankName": "MT Instituição de Pagamentos" },
"payee": { "name": "FORNECEDOR XYZ", "document": "98765432000110" },
"counterParty": { "name": "FORNECEDOR XYZ", "document": "98765432000110" }
}
Notas sobre o shape v2:
- O par
payer/payeeé montado a partir dadirectionda linha:direction=IN:payer= contraparte (quem nos pagou);payee= a nossa conta;direction=OUT:payer= a nossa conta;payee= contraparte (a quem pagamos).
- O lado self (nossa conta) sempre traz
name(titular) +document(CPF/CNPJ) + os identificadores do banco liquidante (bankCode,bankIspb,bankName). - O lado contraparte sempre traz só
name+document. Campos opcionais (bankCode,bankIspb,branch,account,pixKey,accountType,bankName) ficam omitidos quando vazios — opartyToDTOda v2 não devolve chaves com string vazia. counterParty(objeto extra comname+documentda contraparte) é mantido para facilitar lookup direto sem precisar inspecionardirection.- Removido o campo
authorizationCode(a API do liquidante não o expõe no extrato; estava sempre vazio na v1 também quando lido via fallback live).
Onde recuperar o payload bancário completo da contraparte
Se sua integração precisa de branch/account/pixKey/bankCode da contraparte, use uma das alternativas abaixo (todas trazem o payload completo, pois vêm de endpoints específicos de PIX, não do extrato):
- Webhook outbound:
pix.in.completed,pix.out.completed,pix.refund.completed,qrcode.paid— o envelope CorpX inclui o objetopayer/payeecom todos os campos bancários. - Lookup de pagamento individual:
GET /v1/accounts/{id}/pix/payments/lookup?endToEndId=...— retorna o objeto da transação enriquecido (igual ao que vinha no webhook). - QR Code:
GET /v1/accounts/{id}/pix/qr-code/lookup?identifier=...— para QRs pagos, incluipayer+paymentcomendToEnd.
Se sua integração dependia de polling agressivo do statement, prefira webhooks:
pix.in.receivedpara PIX recebidopix.out.confirmedpara PIX out concluídoboleto.paid/boleto.failedqrcode.paid
Os mesmos efeitos valem para os outros endpoints que liam do mesmo cache:
GET /v1/accounts/{id}/pix/transactionsGET /v1/accounts/{id}/pix/paymentsGET /v1/accounts/{id}/payments(alias)
Todos passam a consultar o liquidante em tempo real, sem cache local.
4.2 MED — em 503 durante a migração
Todos os endpoints /v1/accounts/{id}/pix/med/* retornam 503 service_temporarily_unavailable. Reativação prevista para v2.x. Se houver MEDs ativos no momento do cutover, nossa equipe entra em contato individualmente.
Webhooks med.* também estão suspensos durante esta janela — nenhum evento de disputa será emitido. Você não precisa remover a subscription; quando o módulo voltar, os eventos retomam automaticamente.
4.3 Boleto — agora ativo
V1 retornava 503 em todos os endpoints de boleto (POST .../boleto/preview, POST .../boleto/pay, GET .../boleto/payments/{id}). V2 está ativo. Veja o Guia de Cash Out.
4.4 PIX out — sync vs async (não virou “tudo async”)
A v2 preserva a mesma divisão da v1:
| Rota | Modo | O que acontece na resposta HTTP |
|---|---|---|
POST .../pix/out | sync | Aguarda o workflow até ~25s. Se concluir a tempo: 200 com status final (APPROVED, FAILED→422, etc.) e endToEndId quando houver. Se estourar o tempo: 202 com status: PENDING + warning — aí use lookup/webhook. |
POST .../pix/out/qr-code | sync | Igual ao /pix/out (sync, ~25s). |
POST .../pix/out/bank-account | sync | Igual. |
POST .../pix/out/refund | sync | Igual. |
POST .../pix/out/async | async | 202 imediato com {paymentId, workflowId, idempotencyKey, identifier} — resultado final via webhook ou GET .../pix/payments/lookup?identifier=.... |
POST .../pix/out/bank-account/async | async | 202 imediato (mesma semântica do /async). |
POST .../pix/out/bigpix (+ bank-account/bigpix) | async | 202 imediato (lote; chunking continua server-side). |
O que mudou internamente (não obriga migrar de sync → async): a orquestração passou por Temporal, mas quem já chama POST .../pix/out sem /async continua no fluxo síncrono. Só migre para /async se quiser alto volume ou evitar esperar os ~25s na conexão HTTP.
Idempotência: retentar com a mesma Idempotency-Key reaproveita o mesmo paymentId/workflow quando ainda em curso; após FAILED, nova chave pode iniciar nova tentativa (ver Cash Out).
4.5 X-CF-Origin-Verify
V2 só aceita requests vindos do CloudFront. Tentar bater direto no execute-api da AWS retorna 403. Use sempre https://tenant.api.corpx.com/v1.
4.6 Webhooks temporariamente suspensos — fee.*, med.*, edi.*
Durante a janela de migração, 3 famílias de eventos não são emitidas:
| Família | Status | Como continuar enxergando os dados |
|---|---|---|
fee.* (fee.charged, fee.refunded, etc.) | suspenso | Tarifas aparecem como linhas independentes no extrato (GET /v1/accounts/{id}/statement). Procure por type=internal-transfer com identifier=fee-{slug}-{operationReferenceId} para CORPX_FEE_ACCOUNT_ID. |
med.* (med.opened, med.answered, etc.) | suspenso | Módulo MED inteiro está offline (ver §4.2). |
edi.* (eventos de arquivos CNAB/EDI) | suspenso | Os arquivos seguem sendo gerados, mas a notificação por webhook ficou pausada. Use o GET de batches CNAB para acompanhar. |
O que isso significa para você:
- Subscriptions a esses eventos podem ficar registradas — quando voltarem, retomam automaticamente.
- Se sua reconciliação dependia desses eventos, mude temporariamente para polling do extrato (consultas ao liquidante são tempo-real, não há defasagem de cache).
Reativação prevista por etapas:
fee.*— próxima minor após estabilização do refactor no-cache.med.*— junto com a reativação do módulo MED na v2.x.edi.*— a definir; depende da nova pipeline de batches.
4.7 Conciliação automática transação ↔ tarifa — suspensa
v1: o objeto de transação incluía um campo fee: { amount, ... } que pareava cada PIX/boleto com sua tarifa de processamento.
v2: esse campo foi removido temporariamente. A conciliação é feita do lado do cliente cruzando o identifier:
- Transação principal:
identifier = <seu identifier>(ou auto-gerado) - Tarifa correspondente:
identifier = fee-{slug}-{operationReferenceId}na mesma janela temporal, debitada deCORPX_FEE_ACCOUNT_ID
Como reconciliar pelo extrato:
GET /v1/accounts/{accountId}/statement?startDate=2026-05-21&endDate=2026-05-21
Filtrar items onde identifier começa com fee- e cruzar com a transação principal pelo operationReferenceId embutido.
Quando o refactor no-cache estabilizar, o campo fee no objeto de transação volta. Não há mudança de payload prevista para esse retorno — apenas reaparição do campo.
4.8 Internal transfer by-bank-account — campos extras opcionais
POST /v1/accounts/{id}/transfers/internal/by-bank-account
V2 aceita dois campos extras opcionais: holderDocument e holderName. O liquidante atual precisa desses dados; quando o cliente não envia, o backend faz lookup automático. Não é breaking — clientes que continuam mandando só {branch, accountNumber, value, ...} funcionam normalmente.
4.9 QR-code PIX — resposta achatada (breaking no JSON)
Na tabela (§6), estes endpoints aparecem como body-diff, não como mantido nem alterado genérico: o path é o mesmo, mas o JSON de sucesso mudou. Quem faz response.data.payload ou lê data.location quebra até ajustar o parser — inclusive em POST .../pix/qr-code/dynamic.
Afeta os 5 endpoints de QR-code:
POST /v1/accounts/{id}/pix/qr-code/dynamicPOST /v1/accounts/{id}/pix/qr-code/staticGET /v1/accounts/{id}/pix/qr-code/lookup(e o alias deprecatedGET /v1/accounts/{id}/pix/qr-code)DELETE /v1/accounts/{id}/pix/qr-code
v1: envelope tipo Finaya:
{
"statusCode": 200,
"title": "...",
"type": "...",
"message": "...",
"data": {
"txid": "ABC123",
"payload": "00020126...",
"location": "https://qr.mtbank.com.br/cob/...",
"...": "..."
}
}
v2: shape canônico achatado, sem envelope:
{
"txid": "ABC123",
"emv": "00020126...",
"type": "dynamic",
"status": "ACTIVE",
"value": 12.34,
"message": "Cobrança X",
"identifier": "abc123",
"expiresAt": "2026-12-31T23:59:59Z",
"createdAt": "2026-05-21T20:00:00Z"
}
Mapeamento de campos:
| v1 (envelope) | v2 (achatado) | Notas |
|---|---|---|
data.txid | txid | inalterado |
data.payload | emv | mesma string BRcode/EMV |
data.location | (removido) | renderize o QR a partir do emv no client |
statusCode, title, type, message (envelope) | (removidos) | substituídos pelos campos canônicos type, status, value, identifier, expiresAt, createdAt |
Status code HTTP: v2 retorna 201 Created em criação (POST). GET continua 200.
Como migrar (mínimo):
- const emv = response.data.payload;
+ const emv = response.emv;
- const qrUrl = response.data.location;
+ // Sem equivalente nativo. Renderize o EMV com qrcode-svg / qrcode.js,
+ // ou hospede em CDN próprio se quiser uma URL pública.
Se a remoção do location for bloqueante para você, fale com o suporte — podemos avaliar reintroduzir o campo gerando uma URL CDN com o EMV cacheado.
5. Endpoints novos
| Endpoint | Para que serve |
|---|---|
GET /v1/me | Identidade do client_id autenticado (debug útil) |
GET /v1/transactions/{id}/timeline | Timeline detalhada de uma transação (sem accountId no path) |
GET /v1/accounts/{id}/pix/out/bigpix/{batchId} | Status agregado de lote BigPix |
GET /health | Alias do /v1/health (sem o prefixo /v1) |
6. Tabela completa
Status legenda:
- mantido: mesmo path e mesmo JSON de resposta em sucesso. Seu parser continua igual.
- body-diff: mesmo path; JSON de resposta diferente (breaking). Ex.: QR-code sem envelope
{ data: {...} }— ver §4.9. - alterado: mesmo path; comportamento diferente (live, latência, 503→200). O shape JSON de sucesso costuma ser compatível; leia a observação.
- deprecated: ainda funciona até 2026-11-21, com header
Deprecation: true. Migre para o substituto canônico antes do sunset. - removido: rota existia só na v1; retorna 404 na v2.
- adicionado: rota nova; existia só na v2.
| Endpoint | Status v2 | Observação |
|---|---|---|
GET /v1/health | mantido | Também aceita GET /health |
GET /v1/accreditations/pf | mantido | — |
POST /v1/accreditations/pf | mantido | Body com tier simplified para CNPJ pode requerer Agreement |
PUT /v1/accreditations/pf | mantido | — |
GET /v1/accreditations/pj | mantido | — |
POST /v1/accreditations/pj | mantido | Idem |
PUT /v1/accreditations/pj | mantido | — |
GET /v1/accounts/{id}/balance | mantido | — |
GET /v1/accounts/{id}/statement | alterado | Live (sem cache); ver §4.1 |
GET /v1/accounts/{id}/transactions/timeline | mantido | — |
GET /v1/transactions/{id}/timeline | adicionado | Novo |
POST /v1/accounts/{id}/locked-balance/lock | removido (2026-05) | V2 não tem mais hold local. /balance.locked reflete só blocked MT. |
POST /v1/accounts/{id}/locked-balance/unlock | removido (2026-05) | idem |
GET /v1/accounts/{id}/pix/limits | mantido | — |
PATCH /v1/accounts/{id}/pix/limits | mantido | — |
POST /v1/accounts/{id}/pix/out | mantido | Sync (~25s): 200/422 se concluir; 202+warning se timeout. §4.4 |
POST /v1/accounts/{id}/pix/out/async | mantido | Async: 202 imediato. §4.4 |
POST /v1/accounts/{id}/pix/out/bigpix | mantido | Single body; chunking server-side; 202 imediato (async). §4.4 |
GET /v1/accounts/{id}/pix/out/bigpix/{batchId} | adicionado | Status agregado do lote |
POST /v1/accounts/{id}/pix/out/bank-account | mantido | Sync (~25s). §4.4 |
POST /v1/accounts/{id}/pix/out/bank-account/async | mantido | Async: 202 imediato. §4.4 |
POST /v1/accounts/{id}/pix/out/bank-account/bigpix | mantido | Async: 202 imediato (lote). §4.4 |
POST /v1/accounts/{id}/pix/out/qr-code | mantido | Sync (~25s), igual /pix/out. §4.4 |
POST /v1/accounts/{id}/pix/out/qr-code/decode | mantido | — |
POST /v1/accounts/{id}/pix/out/qrcode (alias sem hífen) | deprecated | Sunset 2026-11-21. Use /pix/out/qr-code |
POST /v1/accounts/{id}/pix/out/refund | mantido | Sync (~25s). §4.4 |
GET /v1/accounts/{id}/pix/transactions | alterado | Live |
GET /v1/accounts/{id}/pix/payments | alterado | Live |
GET /v1/accounts/{id}/pix/payments/{paymentId} | deprecated | Sunset 2026-11-21. Use /pix/payments/lookup?identifier= |
GET /v1/accounts/{id}/pix/payments/lookup | mantido | — |
GET /v1/accounts/{id}/payments (alias) | alterado | Live |
GET /v1/accounts/{id}/payments/{paymentId} (alias) | deprecated | Sunset 2026-11-21. Use /pix/payments/lookup?identifier= |
POST /v1/accounts/{id}/boleto/preview | alterado | 503 → 200 (boleto ativo na v2) |
POST /v1/accounts/{id}/boleto/pay | alterado | 503 → 200 |
GET /v1/accounts/{id}/boleto/payments/{paymentId} | alterado | 503 → 200 |
POST /v1/accounts/{id}/pix/qr-code/static | body-diff | Breaking: sem envelope; data.payload→emv; sem data.location. §4.9 |
POST /v1/accounts/{id}/pix/qr-code/dynamic | body-diff | Breaking: sem envelope; data.payload→emv; sem data.location. §4.9 |
GET /v1/accounts/{id}/pix/qr-code (sem /lookup) | deprecated | Sunset 2026-11-21. Use /pix/qr-code/lookup?identifier=. Resposta também body-diff (§4.9) |
GET /v1/accounts/{id}/pix/qr-code/lookup | body-diff | Breaking: mesmo shape achatado que no POST. §4.9 |
DELETE /v1/accounts/{id}/pix/qr-code | body-diff | Breaking: resposta achatada. §4.9 |
GET /v1/accounts/{id}/pix/qr-codes | mantido | Lista paginada — não é o mesmo contrato do POST dynamic (§4.9) |
GET /v1/accounts/{id}/pix/qr-codes/stats | mantido | — |
GET /v1/accounts/{id}/pix/keys | mantido | — |
POST /v1/accounts/{id}/pix/keys | mantido | — |
DELETE /v1/accounts/{id}/pix/keys/{pixKey} | mantido | — |
GET /v1/accounts/{id}/pix/key/{pixKey} | mantido | DICT lookup |
GET /v1/accounts/{id}/pix/med | alterado | 503 stub; ver §4.2 |
POST /v1/accounts/{id}/pix/med/{medId}/answer | alterado | 503 stub |
POST /v1/accounts/{id}/pix/med/{medId}/decide | alterado | 503 stub |
POST /v1/accounts/{id}/pix/med/{medId}/evidence/upload-url | alterado | 503 stub |
POST /v1/accounts/{id}/pix/med/{medId}/evidence/add | alterado | 503 stub |
POST /v1/accounts/{id}/pix/med/{medId}/evidence/download | alterado | 503 stub |
POST /v1/accounts/{id}/pix/med/{medId}/send | removido | Use /decide (em 503 hoje) |
POST /v1/accounts/{id}/pix/med/{id}/response | removido | Use /answer (em 503 hoje) |
POST /v1/accounts/{id}/transfers/internal | mantido | — |
POST /v1/accounts/{id}/transfers/internal/by-bank-account | mantido | Aceita extras opcionais (holderDocument, holderName); ver §4.8 |
POST /v1/accounts/{id}/transfers/internal/by-document | mantido | — |
GET /v1/accounts/{id}/transfers/internal/lookup/{document} | mantido | — |
POST /v1/accounts/{id}/exports | mantido | — |
GET /v1/accounts/{id}/exports | mantido | — |
GET /v1/accounts/{id}/exports/{exportId}/download | mantido | — |
GET /v1/me | adicionado | Debug do client_id |
GET /v1/webhooks | mantido | — |
POST /v1/webhooks | mantido | — |
PUT /v1/webhooks/{subscriptionId} | mantido | — |
DELETE /v1/webhooks/{subscriptionId} | mantido | — |
GET /v1/webhooks/events | mantido | Catálogo |
POST /v1/webhooks/replay | removido | Removido |
GET /v1/integrator/webhooks | removido | Use GET /v1/webhooks |
GET /v1/integrator/webhooks/{id}/deliveries | removido | Sem substituto |
POST /v1/integrator/events/replay | removido | Sem substituto |
POST /v1/integrator/events/replay/batch | removido | Sem substituto |
GET /v1/security/ip-allowlist | mantido | — |
PUT /v1/security/ip-allowlist | mantido | — |
GET /v1/security/ip-allowlist/audit | mantido | — |
Resumo: 46 mantidos, 5 body-diff (QR-code PIX — JSON achatado, §4.9), 12 alterados (statement live, MED stubs, boleto 503→200, etc.), 4 deprecated (aliases, sunset 2026-11-21), 7 removidos (404 na v2), 3 adicionados.
7. Account IDs preservados
Os account_id que você usa em todos os paths e webhooks são os mesmos antes e depois do cutover. Você não precisa atualizar referências armazenadas no seu sistema. Eles permanecem como UUIDs.
O que muda internamente (transparente pra você):
- Identificadores no liquidante são novos — você nunca via esse campo.
- Mecanismo de autenticação com o liquidante mudou — handler interno.
8. FAQ completo
Meu webhook URL precisa mudar? Não.
Meu HMAC secret muda? Não.
Account IDs mudam? Não.
Posso voltar pra v1 se algo der errado? Durante as primeiras 4h após o cutover, sim — temos rollback ensaiado. Após esse período, a infraestrutura antiga é gradualmente desligada.
Por quanto tempo o backoffice antigo fica disponível? 30 dias após o cutover.
Polling de extrato em alta frequência ainda funciona? Funciona, mas tem latência maior (~1s vs ~50ms do cache antigo) e tem janela máxima de 31 dias por consulta. Para mudanças, prefira webhooks.
Existe rate limit novo?
Mantemos os mesmos limites da v1 por tenant. Se você bater limite, retorna 429 Too Many Requests com Retry-After.
O campo fee voltará no objeto de transação?
Sim, na próxima minor após estabilização. Sem mudança de payload — apenas reaparição.
Os webhooks fee.* / med.* / edi.* voltarão?
Sim, em fases: fee.* na próxima minor; med.* junto com a reativação do módulo MED na v2.x; edi.* a definir.
Suporte
Contato: suporte-api@corpx.com — resposta em até 1h em horário comercial.