Pular para o conteúdo principal

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/fullapi2/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/* (exceto GET /v1/me e health). Deve bater com o tenant da conta quando accountId está 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/replayPolling do extrato em janela curta
GET /v1/integrator/webhooksUse GET /v1/webhooks
GET /v1/integrator/webhooks/{id}/deliveriesSem substituto — fale com suporte se depende
POST /v1/integrator/events/replaySem substituto
POST /v1/integrator/events/replay/batchSem substituto
POST /v1/accounts/{id}/pix/med/{medId}/sendUse /decide (mas MED está em 503 hoje)
POST /v1/accounts/{id}/pix/med/{medId}/responseUse /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 da direction da 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 — o partyToDTO da v2 não devolve chaves com string vazia.
  • counterParty (objeto extra com name + document da contraparte) é mantido para facilitar lookup direto sem precisar inspecionar direction.
  • 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 objeto payer / payee com 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, inclui payer + payment com endToEnd.

Se sua integração dependia de polling agressivo do statement, prefira webhooks:

  • pix.in.received para PIX recebido
  • pix.out.confirmed para PIX out concluído
  • boleto.paid / boleto.failed
  • qrcode.paid

Os mesmos efeitos valem para os outros endpoints que liam do mesmo cache:

  • GET /v1/accounts/{id}/pix/transactions
  • GET /v1/accounts/{id}/pix/payments
  • GET /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:

RotaModoO que acontece na resposta HTTP
POST .../pix/outsyncAguarda o workflow até ~25s. Se concluir a tempo: 200 com status final (APPROVED, FAILED422, etc.) e endToEndId quando houver. Se estourar o tempo: 202 com status: PENDING + warning — aí use lookup/webhook.
POST .../pix/out/qr-codesyncIgual ao /pix/out (sync, ~25s).
POST .../pix/out/bank-accountsyncIgual.
POST .../pix/out/refundsyncIgual.
POST .../pix/out/asyncasync202 imediato com {paymentId, workflowId, idempotencyKey, identifier} — resultado final via webhook ou GET .../pix/payments/lookup?identifier=....
POST .../pix/out/bank-account/asyncasync202 imediato (mesma semântica do /async).
POST .../pix/out/bigpix (+ bank-account/bigpix)async202 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íliaStatusComo continuar enxergando os dados
fee.* (fee.charged, fee.refunded, etc.)suspensoTarifas 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.)suspensoMódulo MED inteiro está offline (ver §4.2).
edi.* (eventos de arquivos CNAB/EDI)suspensoOs 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 de CORPX_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)

Breaking de parser

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/dynamic
  • POST /v1/accounts/{id}/pix/qr-code/static
  • GET /v1/accounts/{id}/pix/qr-code/lookup (e o alias deprecated GET /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.txidtxidinalterado
data.payloademvmesma 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

EndpointPara que serve
GET /v1/meIdentidade do client_id autenticado (debug útil)
GET /v1/transactions/{id}/timelineTimeline detalhada de uma transação (sem accountId no path)
GET /v1/accounts/{id}/pix/out/bigpix/{batchId}Status agregado de lote BigPix
GET /healthAlias 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.
EndpointStatus v2Observação
GET /v1/healthmantidoTambém aceita GET /health
GET /v1/accreditations/pfmantido
POST /v1/accreditations/pfmantidoBody com tier simplified para CNPJ pode requerer Agreement
PUT /v1/accreditations/pfmantido
GET /v1/accreditations/pjmantido
POST /v1/accreditations/pjmantidoIdem
PUT /v1/accreditations/pjmantido
GET /v1/accounts/{id}/balancemantido
GET /v1/accounts/{id}/statementalteradoLive (sem cache); ver §4.1
GET /v1/accounts/{id}/transactions/timelinemantido
GET /v1/transactions/{id}/timelineadicionadoNovo
POST /v1/accounts/{id}/locked-balance/lockremovido (2026-05)V2 não tem mais hold local. /balance.locked reflete só blocked MT.
POST /v1/accounts/{id}/locked-balance/unlockremovido (2026-05)idem
GET /v1/accounts/{id}/pix/limitsmantido
PATCH /v1/accounts/{id}/pix/limitsmantido
POST /v1/accounts/{id}/pix/outmantidoSync (~25s): 200/422 se concluir; 202+warning se timeout. §4.4
POST /v1/accounts/{id}/pix/out/asyncmantidoAsync: 202 imediato. §4.4
POST /v1/accounts/{id}/pix/out/bigpixmantidoSingle body; chunking server-side; 202 imediato (async). §4.4
GET /v1/accounts/{id}/pix/out/bigpix/{batchId}adicionadoStatus agregado do lote
POST /v1/accounts/{id}/pix/out/bank-accountmantidoSync (~25s). §4.4
POST /v1/accounts/{id}/pix/out/bank-account/asyncmantidoAsync: 202 imediato. §4.4
POST /v1/accounts/{id}/pix/out/bank-account/bigpixmantidoAsync: 202 imediato (lote). §4.4
POST /v1/accounts/{id}/pix/out/qr-codemantidoSync (~25s), igual /pix/out. §4.4
POST /v1/accounts/{id}/pix/out/qr-code/decodemantido
POST /v1/accounts/{id}/pix/out/qrcode (alias sem hífen)deprecatedSunset 2026-11-21. Use /pix/out/qr-code
POST /v1/accounts/{id}/pix/out/refundmantidoSync (~25s). §4.4
GET /v1/accounts/{id}/pix/transactionsalteradoLive
GET /v1/accounts/{id}/pix/paymentsalteradoLive
GET /v1/accounts/{id}/pix/payments/{paymentId}deprecatedSunset 2026-11-21. Use /pix/payments/lookup?identifier=
GET /v1/accounts/{id}/pix/payments/lookupmantido
GET /v1/accounts/{id}/payments (alias)alteradoLive
GET /v1/accounts/{id}/payments/{paymentId} (alias)deprecatedSunset 2026-11-21. Use /pix/payments/lookup?identifier=
POST /v1/accounts/{id}/boleto/previewalterado503 → 200 (boleto ativo na v2)
POST /v1/accounts/{id}/boleto/payalterado503 → 200
GET /v1/accounts/{id}/boleto/payments/{paymentId}alterado503 → 200
POST /v1/accounts/{id}/pix/qr-code/staticbody-diffBreaking: sem envelope; data.payloademv; sem data.location. §4.9
POST /v1/accounts/{id}/pix/qr-code/dynamicbody-diffBreaking: sem envelope; data.payloademv; sem data.location. §4.9
GET /v1/accounts/{id}/pix/qr-code (sem /lookup)deprecatedSunset 2026-11-21. Use /pix/qr-code/lookup?identifier=. Resposta também body-diff (§4.9)
GET /v1/accounts/{id}/pix/qr-code/lookupbody-diffBreaking: mesmo shape achatado que no POST. §4.9
DELETE /v1/accounts/{id}/pix/qr-codebody-diffBreaking: resposta achatada. §4.9
GET /v1/accounts/{id}/pix/qr-codesmantidoLista paginada — não é o mesmo contrato do POST dynamic (§4.9)
GET /v1/accounts/{id}/pix/qr-codes/statsmantido
GET /v1/accounts/{id}/pix/keysmantido
POST /v1/accounts/{id}/pix/keysmantido
DELETE /v1/accounts/{id}/pix/keys/{pixKey}mantido
GET /v1/accounts/{id}/pix/key/{pixKey}mantidoDICT lookup
GET /v1/accounts/{id}/pix/medalterado503 stub; ver §4.2
POST /v1/accounts/{id}/pix/med/{medId}/answeralterado503 stub
POST /v1/accounts/{id}/pix/med/{medId}/decidealterado503 stub
POST /v1/accounts/{id}/pix/med/{medId}/evidence/upload-urlalterado503 stub
POST /v1/accounts/{id}/pix/med/{medId}/evidence/addalterado503 stub
POST /v1/accounts/{id}/pix/med/{medId}/evidence/downloadalterado503 stub
POST /v1/accounts/{id}/pix/med/{medId}/sendremovidoUse /decide (em 503 hoje)
POST /v1/accounts/{id}/pix/med/{id}/responseremovidoUse /answer (em 503 hoje)
POST /v1/accounts/{id}/transfers/internalmantido
POST /v1/accounts/{id}/transfers/internal/by-bank-accountmantidoAceita extras opcionais (holderDocument, holderName); ver §4.8
POST /v1/accounts/{id}/transfers/internal/by-documentmantido
GET /v1/accounts/{id}/transfers/internal/lookup/{document}mantido
POST /v1/accounts/{id}/exportsmantido
GET /v1/accounts/{id}/exportsmantido
GET /v1/accounts/{id}/exports/{exportId}/downloadmantido
GET /v1/meadicionadoDebug do client_id
GET /v1/webhooksmantido
POST /v1/webhooksmantido
PUT /v1/webhooks/{subscriptionId}mantido
DELETE /v1/webhooks/{subscriptionId}mantido
GET /v1/webhooks/eventsmantidoCatálogo
POST /v1/webhooks/replayremovidoRemovido
GET /v1/integrator/webhooksremovidoUse GET /v1/webhooks
GET /v1/integrator/webhooks/{id}/deliveriesremovidoSem substituto
POST /v1/integrator/events/replayremovidoSem substituto
POST /v1/integrator/events/replay/batchremovidoSem substituto
GET /v1/security/ip-allowlistmantido
PUT /v1/security/ip-allowlistmantido
GET /v1/security/ip-allowlist/auditmantido

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.