Changelog
This document records all notable changes to the CorpX API.
v2.53.0 (2026-08-07) - maxLookupRatio is gone, and the per-window limits now always apply
- The
maxLookupRatiolimit has been removed. It allowedratio × transfers completed in the last 24hkey lookups. It was the same calculation as the 24h window'smaxUsageRatio, at worse resolution: a single window, with no sample floor of its own, relying on a global floor of 50 lookups to avoid locking out an account with no transfers. The windows do the same thing across twelve ranges, from 5 minutes to 30 days. - The field is still accepted and ignored. A policy that still carries it
in the body does not start erroring; the number simply stops deciding. The
ratiodenial reason no longer appears in429messages, and the(bootstrap floor of 50 lookups/24h applied)note is gone too. - The per-window limits now always apply. They used to exist only where
someone had written them into a policy. Now a range with no declared numbers
inherits the fleet baseline:
maxUsageRatio2.2 andmaxFailureRatio0.3 across every window, with a sample floor of 20 up to 30 minutes, 50 from 1h to 12h, and 500 per day of window from 24h on. Those values came out of measuring 14 days of real traffic and blocked no account in that history. - What you need to do. Probably nothing. If your account policy declared a
maxLookupRatiotighter than 2.2 lookups per transfer, your effective cap loosens; if it declared a looser one, the 24h window now decides. The absolute caps (maxLookupsPerDay,maxLookupsPerMinute,maxNotFoundPer5min) are unchanged. Details in Policies and Rules.
v2.51.0 (2026-08-07) - Key lookup limits per time window
- New per-time-window rate caps in the
dictLookuppolicy. On top of the four limits that already existed, each window (5m,15m,30m,1h,3h,6h,12h,24h,3d,7d,14d,30d) can cap two rates: lookups per completed PIX Out (maxUsageRatio) and the fraction of lookups that resolve no key (maxFailureRatio). Details and examples in Policies. - Nothing changes if you configure nothing. Windows are not on by default: they only apply to the account or tenant where they are configured, and behaviour is identical for anyone who leaves them alone.
- The
errorCodeis stilldict_lookup_limit_exceeded. What changes is the message, which now says which window went over, what was measured and what the cap was — for example,120 lookups for 10 successful transfers (12.0 per transfer); max 6.0. If you handle the code, nothing to do; if you show the message to an end user, the text is now more useful. - When more than one window is over, the response cites the shortest one, which is the immediate cause and the first to clear.
- Every window has a sample floor (
minLookups). Below it neither rate can refuse, so a new account with very few lookups is not blocked by a rate computed over three calls.
v2.50.0 (2026-08-07) - The holder comes back to your app at the end of onboarding and portability
- New optional field
callbackUrionPOST /v1/accreditations/pfandPOST /v1/accreditations/pj. When the holder finishes their step — facial capture, BYO acceptance or portability authorization — they are taken back to your app or site, instead of stopping on a completion page of ours. The outcome travels in the URI itself, inoutcome, along withaccreditationId, thepersonIdof whoever completed it and, when the accreditation has more than one person,remainingPeople. Full guide in Returning to your app. - The URI must be registered before its first use. Talk to CorpX support
telling them exactly which URI you are going to use.
https://and your application's scheme (deeplink) are accepted; the comparison is exact, with no wildcard and no prefix matching. An unregistered URI refuses the creation right away, with the new400 invalid_callback_urierror — and not at the end of the journey. - The URI parameters are not proof of anything. They travel in the address bar
and the holder can edit them; they exist so your app can choose which screen to
show. Any decision with an effect still depends on the webhook or on
GET /v1/accreditations/{accreditationId}. Andoutcome=biometry_approvedmeans the biometrics step is over, not that the account is active: activation still arrives inaccreditation.active. - New field
personIdinpersons[], in the creation and the query responses. It is the stable identifier of each person within the accreditation, and it is what appears in the return URI — the CPF does not go into a URI that ends up in browser history and proxy logs. It is an additional field: nothing you already read has changed. - Without
callbackUri, nothing changes in any of the flows.
v2.49.1 (2026-08-07) - account_not_entitled now also applies to statement and daily balance
- Statement and daily balance were still answering
502 partner_unauthorizedin the case v2.49.0 announced as409 account_not_entitled. The error code did go live in that version, but those two queries kept returning the502with the "try again in a few minutes" message — false for this condition, which is permanent until the banking provider enables the service for the account. GET /v1/accounts/{accountId}/statementandGET /v1/accounts/{accountId}/daily-balancenow answer409 account_not_entitledin this scenario, like every other operation in the API. Handle it as a definitive error — contact support with theaccountId— not as a transient outage. Full description in Errors.502 partner_unauthorizedis unchanged and still covers a genuine authentication failure between CorpX and the provider.
v2.49.0 (2026-08-06) - A service the provider has not enabled no longer looks like a temporary failure on our side
- New error code
account_not_entitled(HTTP 409, not retryable). It shows up when the account is fully accredited but the banking provider has not enabled that operation for it — every other operation on the same account keeps working. The condition is permanent until the provider enables the service: retrying does not help. When you get this error, contact support with theaccountId. Full description in Errors. - This case used to surface as
502 partner_unauthorized, which said the opposite. That message claimed CorpX's credential had been rejected and that you should try again in a few minutes — both false here, and it led integrators to retry indefinitely.502 partner_unauthorizedis unchanged and still covers a genuine authentication failure between CorpX and the provider. - If you retry on
502for this route, review it. This scenario now answers409and should be handled as a definitive error (open a support ticket), not as a transient outage.
v2.48.0 (2026-08-06) - A provider refusal is no longer reported as a limit on your account
- New error code
partner_rate_limited(HTTP 429, retryable). When the banking provider turns a call down because too many requests were made, the response is now429 partner_rate_limited. This case used to arrive as422 limit_exceeded, which means "the operation amount exceeded a limit configured on the account" — it sent you to check your account limits when all that had been hit was the call rate. Nothing is wrong with your request in this error: slow down and retry with backoff. Full description in Errors. - A PIX key that does not exist now answers
404 key_not_foundon lookup. OnGET /v1/accounts/{accountId}/pix/key/{pixKey}, a key missing from DICT used to come back as400 partner_error, the generic communication-failure code. It now returns the404 key_not_foundthe reference already documented, which you can handle without parsing the message. If your code detected the missing key from the text inside the400, switch to handling the404. A QR Code lookup for a missing charge is unchanged: still404 not_found. - A provider refusal with no response body no longer surfaces as a
401with an emptymessage. It now comes as502 partner_unauthorized, withmessagefilled in stating that the provider refused the call and returned no body. The401made it look like your credential had been rejected, when the authentication involved is CorpX's own with the provider — sending you to investigate the wrong side.401and403are still always about your credential and your permissions. - A
429on key lookup now has two meanings, told apart byerrorCode.dict_lookup_limit_exceededis your account's lookup quota, and themessagesays which counter triggered — see Policies and Rules.partner_rate_limitedhas nothing to do with your quota: it is the provider throttling CorpX's traffic. If your handling of429on this route currently looks only at the status, read theerrorCodebefore concluding that the account quota ran out.
v2.47.0 (2026-08-06) - The ratio floor drops to 50 and the maxLookupRatio default rises to 4
- The
maxLookupRatiocap gets tighter for anyone who looks up a lot and transfers little. The ratio cap issuccessful transfers in the last 24h × maxLookupRatio, and it never drops below a floor — the safety net that lets a new account resolve the key of its first payment. That floor was 500 lookups/24h and is now 50 lookups/24h, per account. This is a tightening, not a loosening: accounts whose cap was coming from the floor now have a lower cap. - In practice, the ratio goes back to being a limit that decides. With the
floor at 500, and since the most restrictive limit always wins, whoever hit a
limit hit the daily cap —
maxLookupRatiowas almost never the limit actually applied. With the floor at 50, the lookup cap tracks the account's transfer volume again, which is what the ratio is for. - The global
maxLookupRatiodefault rises from 1.2 to 4. The default is the value that applies to an account that does not set this limit in its own policy. While the floor was high it masked the default: the ratio almost never got to decide, which is why 1.2 never got in the way. With the floor at 50 the ratio does decide, and 1.2 would block anyone looking up slightly more than once per transfer — a normal usage pattern for whoever checks the recipient before paying. At 4, the cap matches real usage. If your account hasmaxLookupRatioin its policy, nothing changes for you: the configured value still overrides the default. - What to do. If your integration looks up keys far above the number of
payments it makes, revisit the lookup pattern (the 24h cache does not consume
quota) or ask for a review of your account limits. When it is the floor that
decides, the
429 dict_lookup_limit_exceededmessage says(bootstrap floor of 50 lookups/24h applied). The table with every limit's default is in Policies and Rules, and the floor in The 50 lookups/24h floor on the ratio.
v2.46.0 (2026-08-05) - Every key lookup limit is now measured per account
- PIX key lookup limits are now counted per account, always. All four limits
(
maxLookupsPerDay,maxLookupRatio,maxLookupsPerMinuteandmaxNotFoundPer5min) are measured account by account: one account's lookups never count against another, even when both belong to the same tenant. The account is the unit DICT itself meters, so that is where the limit now applies. - A policy configured on the tenant applies to each account individually. A
maxLookupsPerDay: 50on the tenant policy means "each account may make 50 lookups per day", not "50 per day shared between the accounts" — a tenant policy is a template applied to every account, not a shared bucket. If you sized the number thinking of the tenant's combined usage, revisit it: the effective cap is now higher. An account policy still overrides the tenant one when present. Details in Policies and Rules. - The
maxLookupRatiocap now counts only transfers that actually happened. The ratio denominator now considers only that account's completed transfers (including those refunded afterwards). A payment that failed or was rejected no longer widens the cap — previously, retrying a failing attempt granted more lookup quota with every failure. The floor of 500 lookups per 24h still applies, now per account. - Key lookups performed inside a PIX Out will start consuming quota. When
paying in
KEYmode, the key must be resolved before the money leaves, and that question goes to the same DICT as an explicit lookup. In this release the behaviour is off by default and is controlled by configuration; nothing changes for you right now, and we will announce it before any switch-over. Once active, a PIX Out may answer429 dict_lookup_limit_exceeded— and in that case no payment is created, with nopaymentIdand no transaction, so retrying with the sameIdempotency-Keyremains safe. The 24h cache is shared between both paths, so looking a key up and then paying it costs one lookup, not two; BigPix resolves the key once for the whole batch. See Policies and Rules. company.tradeNamebecame required onPOST /v1/accreditations/pj(recorded here after the release was published). The banking provider requires a trade name for a CNPJ and used to reject the accreditation at the end of the journey, after the biometrics and the review; the rejection now happens at creation, with400 missing_fields. If the company has no registered trade name, repeat the legal name. See Company Account Opening.
v2.45.1 (2026-08-05) - A key that does not exist in DICT is recognised as such again
- Fixed: a PIX key that does not exist was reported as a generic
not_found. On a transfer by key, a non-existent key came back with the generic resource-not-found code, the same one used for any other missing resource. It now returnskey_not_found, the specific code already described in Errors — you can handle "wrong key" without parsing the message. - Key enumeration detection works again. The limit on lookups without a
result within a 5-minute window depended on that classification and, because of
the same defect, never fired. Whoever looks up many non-existent keys in a row
now receives
429 dict_lookup_limit_exceededwith the scan-pattern message. Your account limits are in Policies and Rules.
v2.45.0 (2026-08-05) - Accounts still in accreditation answer 409, not a connectivity error
- New error code
account_not_ready(HTTP 409). When the account exists at CorpX but its accreditation has not been completed yet, every banking operation on it now answers409 account_not_ready. These calls used to fall into502 partner_error, which looked like an API outage when it was really an expected onboarding state. The full list is in Errors. GET /v1/accounts/{accountId}/balanceno longer returns a zero balance in that case. It used to answer200withtotal,availableandlockedset to zero, which is indistinguishable from an empty account. It now returns the same409 account_not_ready. If your code reads a zero balance as "no funds", treat409as "account not operational yet" and follow the accreditation until it reachesACTIVEbefore operating on it.
v2.44.3 (2026-08-05) - Rejected key lookups now consume the daily quota
- Fixed: a rejected PIX key lookup did not count against your quota. When DICT
turned the lookup down — because the key was malformed, or because its own limit
for you had been reached — the call did not enter the daily counter nor the ratio
counter. In practice, a retry loop over a key that always fails ran without ever
hitting a ceiling. Those rejections now consume quota like any other lookup, and
the loop starts receiving
429 dict_lookup_limit_exceededonce the account limit is reached. - Failures on our side still cost you nothing. An unavailable lookup or a timeout stays out of the counter: you do not lose quota over an incident that isn't yours.
- What to do if you now get a
429. Treat the key rejection as final instead of repeating the same lookup. Your account limits are in Policies and Rules.
v2.44.2 (2026-08-05) - QR Code payments without an identifier no longer slip by
- Fixed: a QR Code credit without an identifier produced no webhook at all.
When the payment came through a BR Code carrying no identifier — the typical
case of a static code assembled outside the API, with
***in the txid field — neitherqrcode.paidnorpix.in.completedwas delivered. The money landed in the account and only showed up in the statement. Both events are now delivered normally, with an emptyidentifierandmethodset toSTATIC_QR_CODE. UseendToEndas the deduplication key for these credits, since there is no identifier to tie them to. - New Static QR Code Guide. Covers creation, receiving payments by webhook, reconciliation and cancellation, and explains why you should generate the code through the API instead of assembling the BR Code yourself.
v2.44.1 (2026-08-05) - method on incoming PIX tells you whether it came from a QR Code
- Fixed:
methodarrived empty inpix.in.completed. The field was in the payload but was never filled in, so the webhook alone could not tell you whether the credit came from a QR Code or from a PIX key. It now always carriesSTATIC_QR_CODE,DYNAMIC_QR_CODEorDICT(PIX key or bank details). - The statement now reflects the same method. Previously every incoming PIX
showed up as
DICTinGET /v1/accounts/{accountId}/statement, including the ones paid via QR Code. Entries created before this version were not reprocessed. - Policy rules by method now apply to incoming PIX. A
pixInrule conditioned on the method never matched, because the method reached the evaluation empty.
v2.43.4 (2026-08-03) - Onboarding no longer rejected over a short address complement
- Fixed: onboarding rejected because of the address complement. A complement
shorter than three characters (for example
A) caused the account opening to be rejected at the end of the flow, after biometrics. Since the complement is optional, it is now omitted when too short and the account is created normally. - Field rejections now name the field and the limit. Some validation
rejections arrived as a generic message asking you to review the payload. The
response now carries
errorCodeinvalid_field, the field inpartner.fieldand a message with the expected limit.
v2.43.3 (2026-08-03) - Resending a rejected payment runs again
- Fixed: resending a rejected payment with the same
Idempotency-Keynow runs again. For some rejections — most commonly an account without funds — resending with the same key did not re-run the payment and left the operation stuck inPENDING. The only way out was issuing with a fresh key. Every definitive rejection now allows the resend, reusing the samepaymentId. - The rejection response is now consistent. The
422always carriespaymentId,status,errorCode,errorReasonandcompletedAt, in the same shape regardless of the reason for the rejection. - Nothing changes for
TIMEOUT. When the outcome is indeterminate, resending with the same key still returns the existing state without submitting the payment again — reconcile against the statement before issuing with a new key.
The Idempotency page was rewritten with the behavior of each
scenario (success, rejection, in flight, TIMEOUT, new key) and what each flow
deduplicates on — PIX, internal transfers, boleto and TED follow different rules.
v2.43.1 (2026-07-31) - Inflow credential with no access to balance or statement
readis no longer mandatory. It now covers only the account-wide queries (balance, statement, timeline, entries) and is optional when creating a credential. Previously every credential was issued withread, which blocked the very use case granularity exists for.- Every scope queries its own domain.
qrcode.managealone creates the QR and sees whether it was paid;pix_keys.managelists the keys;exports.createdownloads the file;webhooks.managefollows the deliveries;med.defendqueries the MEDs. The same holds for cashout scopes and the status of their own operations. qrcode.manageplus one account is now a credential that charges by QR code on that account and sees nothing else.- Credentials already issued are unchanged — those created earlier keep the
readthey were given. To restrict, issue a new credential and revoke the old one.
Details in the Authentication Guide.
v2.43.0 (2026-07-31) - Granular scopes on integration credentials
- You choose what each credential can do. When creating a credential in the
panel (API Credentials), a tenant manager picks the scopes:
read,qrcode.manage,pix_keys.manage,webhooks.manage,exports.createandmed.defend. A credential without the route's scope gets403 insufficient_scopenaming the missing scope. - A QR-code credential can see its own QR's payment.
qrcode.managecovers creating, cancelling and reading the QR (GET .../pix/qr-code/lookup), so you can have a charge-only credential with no access to balance or statement. - Per-account restriction. A credential can be limited to specific accounts of the tenant — that is how you give a subsystem access to a single account. No user can issue a credential broader than their own access.
- Money-moving scopes are issued by CorpX (
pix_out.create,refund.create,internal_transfer.create,ted.create,boleto_payment.create,med.decide). Requesting one from the panel returns403 scope_not_self_service. - Existing credentials are unchanged. Current integrations keep full access
(
api2/read api2/write) and need no action.
Details in the Authentication Guide.
v2.42.0 (2026-07-30) - Temporary access suspension for open pendencies
- Two new codes:
tenant_suspendedandtenant_disabled. When there is an open pendency, a tenant's access can be suspended temporarily. With the tenant suspended, reads (GET) keep working — balance, statement, operation status — and writes answer403 tenant_suspended. With the tenant disabled, every route answers403 tenant_disabled. - The credential is not revoked. The same
client_id/client_secretkeeps issuing tokens normally; the refusal happens on the API call. Once the pendency is cleared, access returns on the next call — no new credential, no new token. - Incoming money keeps flowing. Received PIX is still credited and the webhooks for those events are still delivered. Suspension blocks starting new operations, not receiving.
Details in the Authentication Guide.
v2.41.1 (2026-07-30) - A refused internal transfer no longer comes back as pending
- Provider refusals now answer
422.POST /v1/accounts/{accountId}/transfers/internal(and the/by-documentand/by-bank-accountvariants) used to answer202withstatus: "PENDING"when the banking provider refused the operation — the transfer was definitively over, yet the response asked you to wait. Refusals now come back as422withstatus: "FAILED",errorCodeanderrorReason, as the contract already documented.202 PENDINGis now reserved for the indeterminate case (operation still running when the synchronous window ends). insufficient_fundsinstead ofpartner_rejectedwhen balance is the reason. The provider refuses internal transfers for lack of balance with a generic message ("the provider refused the operation"). We now read the available balance at refusal time: when it does not cover the requested amount, the response isinsufficient_funds. Without that evidence the refusal stayspartner_rejected, with the provider's reason in thepartnerblock.- Correct
workflowIdfor internal transfers. Repeating the POST with the sameIdempotency-Keyreturned aworkflowIdcarrying the PIX out prefix. It now carries the operation's real id (internal-out-…). - TED no longer stays in
PROCESSINGforever. The settlement bank's outcome did arrive, but it was delivered to the wrong process and lost — and the TED record was never updated. As a result,GET /transfers/ted/{tedId}and the statement showed "processing" even for TEDs already settled or rejected, and a rejection only surfaced asted.out.failed48h later, with a generic timeout reason instead of the real rejection. The outcome is now applied as soon as it arrives, and the TEDs that were stuck have been reconciled with their real state. - TED:
bankCodeonly accepts the 3-digit Compe code. Sending the 8-digit ISPB in that field is now rejected upfront with400 invalid_bank_code. Before, the request was accepted with202, reached the settlement bank with no destination bank, and the TED stayed stuck inPROCESSING— never settling, never failing. ISPB remains valid for PIX by bank account (bankIspb).
The transfer.internal.out webhook is only delivered when the transfer settles.
For a refusal the outcome lives in the response to the call itself — which is
why the 422 above matters. Do not wait for a webhook to close a refused
internal transfer.
v2.41.0 (2026-07-29) - Partial refunds of received PIX
- Partial refunds are now accepted.
POST /v1/accounts/{accountId}/pix/out/refundnow takes anamountsmaller than the original PIX — previously only the full amount was accepted and anything smaller was rejected with400 partial_refund_not_supported. Above the original, the response is still400 refund_amount_exceeded. - The same PIX can be refunded in installments, up to the original amount.
Each refund needs its own
Idempotency-Keyandidentifier: reusing either makes the API return the previous refund instead of starting a new one. The refundable balance is tracked by the banking partner — an installment beyond it comes back asrefund_amount_exceeded. To check how much has already been refunded, look at the statement: refund entries reference the original transaction's E2E. partial_refund_not_supportedis no longer returned. The code stays documented so integrations that already handle it keep working, but no response uses it anymore.- Richer timeline on refunds. The
pix_out.requestedevent of a refund gained two optional fields:originalAmount(the amount of the PIX being refunded) andpartialRefund(truewhen the refund is partial). mode: "REFUND"only works on the dedicated route.POST /v1/accounts/{accountId}/pix/outand its async variant now return400 invalid_fieldfor that mode in the body — a refund needs context that only/pix/out/refundresolves. Flows relying on it should move to the dedicated route.
v2.40.0 (2026-07-29) - Every rule in the panel is now enforced
- Incoming PIX is now evaluated. The panel's PIX In section got an engine:
payer CPF/CNPJ whitelist and blacklist, same ownership, amount limit, person
type, and allowed/blocked banks. An incoming PIX cannot be refused — the
banking provider credits the account and only then tells us — so the evaluation
happens after the credit, with two possible actions:
ALLOW_AND_NOTIFY(default: credit and warn) andAUTO_REFUND(credit, warn, and automatically refund the full amount).pix.in.completedis still delivered in both cases, before the violation warning. - QR code creation now honors the policy.
canCreateStatic,canCreateDynamic,minAmount, andmaxAmountnow reject the creation with422 policy_deniedin the POST response. A static QR without an amount (open, where the payer chooses how much to pay) is not checked against the amount limits, because there is no amount to compare. - PIX key creation now honors the policy.
canCreateandmaxKeysreject the creation with422 policy_denied. The limit counts the keys the account already has; if the count cannot be taken at that moment, the creation goes through — a failed lookup does not become a rejection. - Refunds now honor the policy.
enabledandmaxAmountin the Refund section reject an API-initiated refund onPOST /pix/out/refundwith422 policy_denied. Regulatory refunds (MED) are unaffected. policy.violationnow covers every domain.phasegained the valuepix_inandactiongainedALLOW_AND_NOTIFYandAUTO_REFUND. On an incoming PIX violation the payload carriestransactionId,endToEnd, andrefundedinstead ofpaymentId, and the payer comes incounterparty. Rules from the new sections are prefixed (pixIn.*,qrCode.*,keys.*,refund.*); PIX out rules remain unprefixed.- Actions that could never be honored were removed from the panel. PIX In no
longer offers
BLOCKorQUARANTINE(and the quarantine days field is gone): there is no way to hold money that has already been credited. Existing configuration with those actions now behaves asALLOW_AND_NOTIFY— it warns instead of promising a block. - Fields and sections with no effect were removed. The PIX Out fields
requireApprovalAbove,chunkSize,duplicateGuardSecs, andsyncRateLimitPerMinare gone from the panel and the docs, along with the MED and IP allowlist sections — IP control lives in your account's network configuration, not here. As a result, the "Sections not enforced" part of the Policies and Rules guide no longer exists: everything the panel offers is enforced.
v2.39.0 (2026-07-29) - Banking provider errors are no longer generic
- A provider refusal now has a code of its own. Previously any partner bank
failure arrived as
partner_error(502) on synchronous calls and asinvalid_payloadfor 4xx cases, without saying what to fix. The reason now comes inerrorCode:limit_exceeded_daily,limit_exceeded_nightly,limit_exceeded_monthly,limit_exceeded_transaction,pix_key_limit_exceeded,recipient_account_not_found,identifier_conflict,balance_reservation_failed,boleto_already_settled,boleto_scheduled,boleto_amount_mismatch,qr_gone,qr_amount_mismatch,invalid_field,key_ownership_mismatch,partner_account_disabled, among others. The full list is in Error Handling and is generated from the catalog in our own code. - New
partnerblock in responses and webhooks. Alongside our code and our message, the response carries the provider's rawpartner.code,partner.messageandpartner.fieldfor diagnosis without opening a ticket. In failure webhooks it comes underdata.partner. It is informational: keep branching onerrorCode. - HTTP status change for limits. An exceeded limit used to surface the
provider's
403, which many clients read as an authentication failure; it is now422. A removed QR Code, previously410, is now409. Provider5xxerrors remain502. - Actionable message instead of the raw one.
errorReasonin webhooks and in the transaction detail now carries our message — including the remaining limit and the field name when the provider reports them — instead of the partner's original text. - Accreditation with a specific reason.
accreditation.failedandGET /v1/accreditations/{id}now distinguishpartner_validation_failed(with the rejected field in the message),partner_unavailable(transient, safe to resend) andexisting_partner_account, instead of collapsing everything into a generic refusal. - Known provider limits validated at POST time.
address.numberover 10 characters,address.cityIbgeCodeinconsistent withaddress.state,descriptionover 140 characters and a branch over 4 digits are now rejected immediately with400 invalid_field, instead of failing at the provider — or burning an accreditation journey after the biometry step. - Published error catalog corrected. The codes the documentation listed and
the API never emitted (
pix_limit_exceeded,pix_qr_expired,pix_qr_not_found,pix_qr_already_paid,pix_qr_cancelled,pix_key_not_found,pix_key_already_exists,insufficient_balance,invalid_key_type,invalid_emv,refund_not_allowed,med_*,missing_idempotency_key,invalid_signature,authz_error) were removed. Insufficient balance at the provider isinsufficient_funds; a key missing from DICT iskey_not_found.
v2.38.0 (2026-07-28) - PIX out rules are now enforced, with webhook warnings
- The PIX out rules configured in the panel are now enforced. Kill switch, whitelist, blacklist, same ownership, operating hours, per-transaction limit, night limit, and recipient type (PF/PJ) now reject the transfer that violates them. Nothing changes for anyone without a configured rule: a rule takes effect the moment it is saved.
- New
422 policy_deniederror. The rejection comes back on the POST with the list of violated rules inviolations, each withruleandmessage. It is neither 403 nor 400: the request is valid and was refused by a rule the tenant itself configured. - Rules that depend on the recipient are evaluated after resolving them. In
key and QR Code modes the recipient's document only exists after the DICT
lookup or the BR Code decode. In those cases the payment is accepted on the
POST and ends as
FAILEDwitherrorCode: policy_denied, withpix.out.failedin the timeline and in the webhook. - New
policy.violationwebhook event. Sent on every violation, withphase(edge,counterparty, ordict_lookup),action,blocked,amount, the violated rules, and the counterparty when known. It also covers PIX key lookups rejected by limit, which previously only returned429. Anyone already subscribed to the event starts receiving it without changing the subscription. - Monitor mode (
NOTIFY_ONLY). The PIX Out section gained theviolationActionfield: withNOTIFY_ONLYthe violation is recorded and reported by webhook, but the payment goes through. It is the recommended rehearsal before promoting the rule toBLOCK. - Honest documentation about what is enforced. The Policies and Rules guide now separates the rules the engine evaluates from the sections that are merely saved in the configuration (PIX in and its actions, QR Code, keys, refund, MED, IP allowlist). We also corrected the claim that violations would show up in the statement: a rejected PIX out never reaches the partner bank, so it does not appear there.
v2.37.3 (2026-07-28) - Fix: QR Code PIX out without an amount
- Paying a QR Code without
amountworks again. When the BR Code already carries the value,amountmay be omitted from the payment. In those cases the payment was never executed and the transaction sat as pending, without ever reaching an outcome. The BR Code value is now used and the payment goes through normally. - Every transaction now ends with an outcome. Failures that happened
before the request reached the settlement bank emitted no
pix.out.failedand no timeline entry, leaving the transaction pending indefinitely. Those cases are now closed withpix.out.failed, includingerrorCodeanderrorReason, both on the webhook and on the timeline. - For these QR Code payments, the
amountpublished on the webhook and on the timeline reflects the value actually charged by the BR Code.
v2.37.2 (2026-07-28) - Fix: ted.in.received is delivered again
- Incoming TED generates a webhook again. Since Jul 22 the settlement bank
started sending TED notifications in a format where the direction is only in
the
Flowfield, and every incoming TED was read as an update to an outgoing TED. As a result noted.in.receivedwas dispatched and the credit did not show up in the API statement (balance and the settlement bank's own statement were always correct). - The event is now classified by the direction the settlement bank reports and
is delivered normally to anyone subscribed to
ted.in.received. - Incoming TEDs between Jul 22 and Jul 28 were reprocessed: the webhooks
arrive with the original payload and the credit's
receivedAt.
v2.37.1 (2026-07-26) - Automatic portability now requires enablement
- The holder authorization journey (
PENDING_CONSENT+consentLink, shipped in 2.37.0) is now enabled per tenant under a specific contract, and is off by default. It transfers control over the money in an account that already exists, so it is no longer automatic for every onboarding integrator. - Without the enablement,
POST /v1/accreditations/pfwith a CPF that already has an account goes back to the previous behaviour: the accreditation ends asFAILEDwitherrorReason=existing_partner_account, and the message instructs the holder to request portability from support, which handles the process. - Nothing changes for tenants that already have portability enabled.
- To enable it, talk to your CorpX account contact.
v2.37.0 (2026-07-25) - PF onboarding for a CPF that already has an account (holder authorization)
POST /v1/accreditations/pfwith a CPF that is already an account holder no longer fails withexisting_partner_account. The accreditation starts asPENDING_CONSENTand the response carriespersons[].consentLink: a page where the account holder themselves authorizes your tenant to operate the account they already have.- On that page the holder sees what they are authorizing (including moving and withdrawing funds), goes through an identity verification (live selfie checked against official records by CPF), and only then sees the account number and current balance before authorizing. The authorization is recorded with date, time, device and the balance that was disclosed.
- Once authorized, you receive
accreditation.activewithsharedAccount: trueand anaccountIdto operate. It is the same account: balance, statement and Pix keys are the ones that already existed. - Shared accounts: whoever already operated the account keeps operating
it. Every tenant authorized by the holder receives the webhooks for each
movement; operations originated outside your API arrive with
external: true. - New events:
accreditation.consent.link.created(authorization link issued) andaccount.shared_access.granted(another tenant started operating an account you already operated). - New
errorReasonvalues onaccreditation.failed:consent_declined,consent_expired,consent_identity_failed,consent_link_failed. POST /v1/accreditations/{id}/cancelandPOST /v1/accreditations/{id}/persons/{cpf}/retrynow acceptPENDING_CONSENT(cancel the journey / reissue the link).- The holder can revoke a granted access through support; from then on the
revoked tenant's calls for that account return
403. - Details in Holder who already has an account.
v2.36.1 (2026-07-25) - pixLimits removed from accreditation
- The
pixLimitsfield is no longer accepted onPOST /v1/accreditations/pfandPOST /v1/accreditations/pj. - At account opening, PIX limits use the conservative defaults applied automatically.
- Any limit change (increase or decrease) still requires a manual request to support.
v2.36.0 (2026-07-25) - PIX limits change API disabled
- The PIX limit reduction API has been disabled
(
PATCH /v1/accounts/{accountId}/pix/limits). - From now on, all limit changes (increase and decrease) require a manual request to support.
GET /v1/accounts/{accountId}/pix/limitsremains available for read-only lookup.
v2.35.0 (2026-07-23) - MED (dispute) webhooks are back
- The
pix.med.openedandpix.med.updatedwebhooks are being emitted again: you get notified when a dispute (MED) is opened against a PIX credited to your account and when it changes status. - The payload carries
medId,originalEndToEnd(the disputed PIX),amount,reasonCode, andstatus(OPEN,PENDING_DECISION,ACCEPTED,REJECTED,CANCELED). See Webhooks. - In the Integrator Portal,
pix.med.opened/pix.med.updatedno longer show as "coming soon" and can be subscribed to. - Disputing via API (
GET /pix/med,answer,decide,evidence/*) remains temporarily at 503 — only the notifications are back.
v2.34.3 (2026-07-23) - CorpX fees on statement without fee-* prefix
- Internal CorpX fee transfers on the statement are now classified as
FEEeven whenidentifierdoes not use thefee-*prefix (cases where the settlement bank only sends the operation UUID). Statement fee filtering and display stay consistent.
v2.34.2 (2026-07-23) - Reliable delivery of TED webhooks
- Webhooks
ted.in.received,ted.out.confirmed, andted.out.failedare now delivered reliably when a TED is credited to your account or when a TED you sent is settled or rejected. - On
ted.in.received,data.payer(name, document, bank, branch, account) is populated when the settlement bank provides those fields. - On
ted.out.failed,data.errorReasonis populated when the settlement bank provides a rejection reason.
v2.34.1 (2026-07-23) - payer/payee on external pix.refund.completed
- Webhooks
pix.refund.completedandpix.refund.failedfor refunds initiated outside the API (data.external: true) now includedata.payeranddata.payee(name, document, bank, branch, account), matching the documented contract and thepix.out.*payload shape. - When available,
data.identifieris also populated.
v2.34.0 (2026-07-23) - Company documents on PJ accreditation
POST /v1/accreditations/pjacceptscompany.legalForm(mei|ltda|sa|cooperative; defaultltda).- New
POST /v1/accreditations/{id}/documents: PDF upload (presigned URL) for kindscompany_articles,company_proof_of_address,cnpj_card,financial_statements,business_license,regulatory_license,other. PENDING_REVIEWafter all biometrics are approved. Required PDFs depend onlegalForm(MEI/LTDA: articles + proof_of_address; SA/cooperative:- cnpj_card + financial_statements); document freshness is checked by the operator.
- Approve with missing docs returns
409 documents_incomplete, unlessforceIncompleteDocuments=true+reason(audited asdocumentsOverride*). - PJ
GETexposeslegalForm,documents,requiredDocumentKinds,missingDocumentKinds. - For
cooperative,partners[]are statutory directors (documented).
v2.33.2 (2026-07-21) - Ambiguous destination guard on internal transfer by document
POST /v1/accounts/{accountId}/transfers/internal/by-document: when the destination document has more than one active account, the request is now rejected with409 multiple_destination_accountsinstead of transferring to an automatically chosen account. Address the transfer via/transfers/internal/by-bank-account(branch + account number) or/transfers/internal(destinationAccountId).
v2.33.1 (2026-07-21) - BYO acceptance link + accreditation webhooks
- Accreditation
GET/POSTin the BYO flow now returnpersons[].acceptanceLink(terms acceptance page URL), in addition tolinkExpiresAt/acceptanceStatus. - Accreditation events are listed in
GET /v1/webhooks/eventsand in the portal (Settings → Webhooks):accreditation.pf.created,accreditation.pj.created,accreditation.biometry.link.created,accreditation.acceptance.link.created,accreditation.updated,accreditation.active,accreditation.failed. accreditation.pf.created/accreditation.pj.createdare now actually emitted on create (async confirmation ofPOST).
v2.33.0 (2026-07-21) - Cancel accreditation + docs: no create dedup
- New endpoint
POST /v1/accreditations/{accreditationId}/cancel: cancels an accreditation still inPENDING_BIOMETRY(before the CPF/CNPJ is bound to the tenant). Ends asFAILEDwitherrorReason=cancelled. - Docs/OpenAPI/Postman now state explicitly that
POST /v1/accreditations/pf|pjdoes not deduplicate: repeating the same document while pending creates another accreditation (201); after biometry, a claimed document returns409 already_accredited.
v2.32.0 (2026-07-19) - Deprecation of sync PIX out via QR Code
POST /v1/accounts/{accountId}/pix/out/qr-code(sync) and the/pix/out/qrcodealias now returnDeprecation,Sunset, andLinkheaders (successor:/pix/out/qr-code/async; sunset planned 2026-11-21).- The canonical EMV (copy-and-paste) payment flow is
POST .../pix/out/qr-code/async(202 + webhooks). - Docs, OpenAPI, and Postman now prioritize the async endpoint.
v2.31.0 (2026-07-19) - Async PIX out via QR Code (copy-and-paste)
- New endpoint
POST /v1/accounts/{accountId}/pix/out/qr-code/async: schedules an EMV (copy-and-paste) payment and returns 202 immediately, matching the key-based PIX out async flow (/pix/out/async). - Final outcome is delivered by webhook (
pix.out.completed,pix.out.failed,pix.out.timeout) or via lookup byidentifier. - The synchronous
POST .../pix/out/qr-codeendpoint remains available with unchanged behavior.
v2.30.0 (2026-07-18) - Onboarding: CPF/CNPJ binds to the tenant only after biometry
POST /v1/accreditations/pf|pjno longer creates an account or reserves the document while status isPENDING_BIOMETRY.accountIdappears only after biometry/acceptance is confirmed (fromBIOMETRY_APPROVEDonward).- Repeating
POSTwith the same CPF/CNPJ before confirmation creates a new accreditation (it does not return the pending one). After the bind, it returns409 already_accredited. - When the document already has an account at the settlement partner,
the accreditation fails with
errorReason=existing_partner_accountand a message guiding the holder to request via support that account control be transferred to your tenant. address.neighborhood(ordistrict) is now required onPOST /v1/accreditations/pf|pj.
v2.29.2 (2026-07-15) - PIX out: communication timeout no longer emits pix.out.failed
- Fixes a false negative: when the submit call to the banking partner hit
a communication timeout (no HTTP response), the API marked the
order as
FAILEDand emittedpix.out.failed— even though the payment may have been processed and settled at the SPI. A latepix.out.completedcould then follow, producing two contradictory terminal events. - Indeterminate outcomes now enter the confirmation window (poll/partner
webhook): if the payment went through, we emit only
pix.out.completed; if it was rejected,pix.out.failedwith the real reason; with no confirmation within 5 minutes,pix.out.timeout. - Contract semantics reinforced in the docs:
pix.out.failedis terminal and definitive (never followed bycompleted); uncertainty is alwayspix.out.timeout.
v2.28.2 (2026-07-10) - pix.out.timeout webhook
- PIX out in
TIMEOUTnow emits the outbound webhookpix.out.timeout(previously only wrote timeline; fanout skipped TIMEOUT). - Deterministic EventID
pix-out-timeout-{paymentId}— does not collide with a latepix.out.completed/failed. - Backoffice: subscribe to
pix.out.timeoutin webhook config. - Docs (cashout / webhooks PT·EN·ZH) aligned.
v2.28.1 (2026-07-10) - PIX refund: full amount only
POST /v1/accounts/{accountId}/pix/out/refundnow rejects partial refunds before calling the banking partner. The current partner does not accept an amount on the refund endpoint and always reversed the full PIX — a smalleramountsilently caused a full refund.- If
amount< original →400 partial_refund_not_supported. - If
amount> original →400 refund_amount_exceeded. amountremains required and must equal the original PIX.- Docs (refund guide, OpenAPI, errors) updated.
v2.27.0 (2026-07-08) - Read-only API credentials
- API credentials can now have a limited scope. A read-only
credential (
api2/read) can only call query endpoints (GET); any write operation (POST/PUT/PATCH/DELETE) returns403 insufficient_scope. - Tenant managers can self-service create read-only credentials in the backoffice panel (API Credentials). Write-capable credentials are still provisioned by CorpX during tenant onboarding.
- Existing credentials are unchanged: they keep
api2/read api2/write. - Note: a missing token still returns
401; an invalid/expired token now returns403(previously401). See the Authentication Guide.
v2.26.0 (2026-07-08) - Per-tenant enablement for accreditation
- Account opening endpoints (
POST /v1/accreditations/pf|pj) now require the accreditation (BaaS) feature to be enabled for your tenant. Without it the API responds403 feature_disabled.GETqueries are not affected. - Contact the CorpX team to enable accreditation for your tenant.
v2.25.0 (2026-07-08) - Self-service account opening with facial biometrics
New accreditation endpoints to open PF (individual) and PJ (company) accounts straight from the API, with facial biometrics. Available to BaaS integrators — see the new Account Onboarding documentation section.
POST /v1/accreditations/pf— opens an individual account. In the default flow CorpX issues a facial capture link (Unico) for the holder; once the biometrics is approved the account opens automatically.POST /v1/accreditations/pj— opens a company account. Each partner receives their own facial link; the account opens after all partners complete and CorpX review approves (PENDING_REVIEWstage, mandatory for PJ).- Bring-your-own biometrics (BYO) — enabled tenants can submit the
facial collected on their own platform (Unico, SERPRO, IDWALL,
SUMSUB, ClearSale, CAF or Valid): register the evidence via
POST /v1/accreditations/biometry-evidenceand reference theevidenceIdin thebiometryobject. The holder confirms on an acceptance page branded with your tenant's identity. - Tracking —
GET /v1/accreditations/pf|pj(document/statusfilters),GET /v1/accreditations/{id}and per-person link retry (POST .../persons/{cpf}/retry). - New webhooks —
accreditation.updated(on every status transition),accreditation.biometry.link.created,accreditation.acceptance.link.created,accreditation.activeandaccreditation.failed. Details in Accreditation webhooks. - Creations are idempotent by tenant + document: repeating the
POSTwith the same CPF/CNPJ returns the existing accreditation (200). - OpenAPI spec and Postman Collection updated with the new endpoints.
v2.24.2 (2026-07-07) - Payer and payee data in webhooks and QR lookup
Backward-compatible payload enrichment — no field was removed, no URL changed.
pix.out.completed/pix.out.failed/pix.refund.*webhooks: the payload now includes thepayer(your account) andpayee(recipient, with resolved name and document) objects for PIX sent through the API — in all modes (key, QR code and bank data). Before, these objects only appeared on transactions originated outside the API.- QR code lookup (
GET /v1/accounts/{accountId}/pix/qr-codeand the/lookupalias): when the QR isPAID, the response now includes thepayerobject with the payer's name and document, as already documented. Before, the field could be missing even for a paid QR. - No action required: integrations already parsing
payer/payeewill start receiving them populated; existing fields did not change.
v2.24.1 (2026-07-07) - Statement: per-row status for unsettled transactions
Important change for reconciliation. The statement now also includes cancelled and under-analysis transactions, with each record's individual status.
- Status per statement row.
GET /v1/accounts/{accountId}/statement,GET /v1/accounts/{accountId}/pix/transactionsand the CSV/PDF exports reflect each record's state:COMPLETED(settled),PROCESSING(registered, not yet settled),PENDING_APPROVAL(under analysis) andFAILED(cancelled — no balance movement). - Recommended action: if your reconciliation sums statement rows, filter
by
status = COMPLETED(plusREVERSEDfor refunds).FAILEDandPROCESSINGrows must not enter balance calculations. - The status vocabulary is the same one already documented — no new field was added.
v2.24.0 (2026-07-07) - PIX out: instant rejections and risk analysis
Behavioural, backward-compatible change to the PIX out flow — no field was added or removed, no URL changed.
- Faster rejections on the synchronous flow. When the settlement bank
refuses the transfer right away (anti-fraud or insufficient settlement
funds),
POST /v1/accounts/{accountId}/pix/outnow responds422 FAILEDimmediately, witherrorCode(partner_rejectedorinsufficient_funds) anderrorReasonfilled in — previously the response was202 PENDINGand the failure only arrived later via webhook or lookup. - Risk analysis surfaced as
PENDING_APPROVAL. Transfers held for risk-desk analysis now show statusPENDING_APPROVALon payment lookups (previously they showed asPROCESSING). The final outcome keeps being delivered by thepix.out.completed/pix.out.failedwebhooks. - Extended confirmation window during analysis. Payments in
PENDING_APPROVALwait for the outcome for up to ~30 minutes before markingTIMEOUT(previously: 5 minutes) — fewer falseTIMEOUTs with late confirmations arriving afterwards. endToEndIdonTIMEOUT. In rareTIMEOUTcases with no confirmation from the settlement bank,endToEndIdmay be unavailable on the payment lookup. Keep theidentifier/paymentIdfor reconciliation — those always exist.
v2.22.0 (2026-07-02) - TED gets its own canonical path
Purely additive and backward-compatible reorganisation — nothing breaks.
- New canonical path for TED status:
GET /v1/accounts/{accountId}/ted/{tedId}. The previous pathGET /v1/accounts/{accountId}/transfers/ted/{tedId}still works (same payload) but is now deprecated and will be removed in a future major. - Swagger and Postman reorganised: the TED endpoints (
POST /ted/outand the status query) now live in a dedicated TED section, separate from internal transfers. The previously named Transfers section is now Internal Transfers and groups only thePOST /transfers/internal*+GET /transfers/internal/lookup/*routes.
Recommended migration: whoever hits GET /transfers/ted/{tedId} for status can move to
GET /ted/{tedId} with no other change — payload is identical.
v2.21.0 (2026-07-01) - Boleto ID normalization
Boleto payments now expose a single canonical id, aligned with the other rails (PIX, TED). Additive, backward-compatible change — no field removed.
paymentIdeverywhere.POST /v1/accounts/{accountId}/boleto/payand the status endpointGET /v1/accounts/{accountId}/boleto/payments/{id}now returnpaymentId(bol_<uuid>) — the same value already delivered by theboleto.paid/boleto.failedwebhooks (aspaymentIdandtransactionId). TheboletoIdfield remains available as an alias (same value).- Status accepts both ids. The status path now accepts either the canonical
paymentId(bol_…) or thepartnerId(operationReferenceId, legacy), and echoes both in the response. Integrators no longer need to keep apaymentId ↔ partnerIdmap.
v2.20.1 (2026-06-15) - fee.charged and fee.refunded for external fees
Fee webhooks are now emitted when the fee is issued outside the API
(by the billing service or the settlement provider). Previously these
cases were delivered as transfer.internal.out (charge) or
transfer.internal.in (refund), which contradicted the contract —
they now follow the official typing:
fee.charged— emitted when a fee is debited from the client account to the fee counterparty (e.g.Corp X Brasil LTDAorMT Instituição de Pagamento SA). IncludesfeeServiceType(PIX/TED/BOLETO/INTERNAL_TRANSFER/MONTHLY/OTHER) andoriginalRef/transactionRefreferencing the operation that triggered the charge.fee.refunded— emitted when the settlement provider refunds a previously charged fee.
Fees triggered via API (rare today) still don't generate these events; they are exclusive to externally originated movements.
The shape follows the contract documented in Webhooks → 9. fee.charged / 9.1. fee.refunded.
v2.20.0 (2026-06-12) - DICT lookup rate limits and 24h cache
DICT lookups (GET /v1/accounts/{accountId}/pix/key/{pixKey}) now honor
the limits configured in the tenant or account policy:
maxLookupsPerDay— absolute cap per calendar day (BRT).0or unset = unlimited.maxLookupRatio— relative cap:ratio × pix_outs_24h. Example: ratio3.0with 100 PIX outs in the last 24h allows 300 lookups in the same window.
When both are configured, the most restrictive applies. Exceeding
either returns 429 dict_lookup_limit_exceeded with the current
counter in message.
The 24h cache is now effectively active: repeated lookups of the
same key within 24h return immediately with cached: true and do
not count toward the limit — the policy is meant to protect DICT
against abuse, not penalize repeat queries. Send noCache=true to
force a fresh lookup.
Limits are configured in the Backoffice (Policies → DICT Lookup). Tenants without a policy remain unrestricted (unchanged behavior).
v2.19.1 (2026-06-11) - Fix: spurious pix.refund.received webhook on issued refunds
Fixed: when issuing a refund (POST /v1/accounts/{accountId}/pix/out/refund),
your webhook endpoint could receive a spurious pix.refund.received event — as
if you had received a refund from a third party, when in fact you were the
one who issued it.
Corrected behavior:
- Refund issued by you via the API: you receive only the normal refund flow
confirmation (
pix.refund.completed/pix.refund.failed). Nopix.refund.receivedis generated. - Refund issued outside the API (e.g. settlement provider channels): you
receive
pix.refund.completed/pix.refund.failedwithexternal: true. - Refund received from a third party (counterparty returned a PIX you sent):
pix.refund.receivedcontinues to be sent normally.
v2.19.0 (2026-06-10) - R$ 15,000 per-transaction PIX limit removed; BigPix deprecated
The R$ 15,000 per-transaction PIX limit has been removed by the settlement
provider. POST /v1/accounts/{accountId}/pix/out (and the /async,
/bank-account, /qr-code variants) now accept any amount in a single
transaction — no need to split into multiple PIX transfers.
As a result, BigPix is deprecated:
POST /v1/accounts/{accountId}/pix/out/bigpixPOST /v1/accounts/{accountId}/pix/out/bank-account/bigpixGET /v1/accounts/{accountId}/pix/out/bigpix/{batchId}
These endpoints remain functional for backward compatibility, but
responses now include the Deprecation: true header. We recommend migrating
to POST /pix/out (or /pix/out/bank-account), which has the same request
shape — just send the total value in the amount field. The final removal
date will be announced in this changelog in advance.
Your account's operational limits (daily, nighttime, per counterparty)
still apply — see GET /v1/accounts/{accountId}/limits.
v2.18.3 (2026-06-09) - Webhooks for external internal transfers
Internal transfers between accounts at the same bank (MT Bank) originated
outside the API — such as through the mobile app or partner console — now
trigger transfer.internal.in (for the receiving account) or
transfer.internal.out (for the sending account) webhooks.
This complements existing behavior: internal transfers initiated via
POST /v1/accounts/{accountId}/transfers/internal already fired webhooks for
both ends. Now, movements you didn't initiate through the API will also notify
your webhook endpoint, with the external: true field in the payload
indicating the origin was external.
v2.18.2 (2026-06-07) - More precise refund error
PIX refund (POST /v1/accounts/{accountId}/pix/out/refund): when the
original transaction cannot be refunded (it is not Confirmed — already
refunded/reversed, rejected, or still processing), the rejection now returns
errorCode: conflict with the settlement bank's reason in
errorReason/message (e.g. Transação com status diferente de 'Confirmado'.).
Previously this case returned errorCode: invalid_payload with the
errorReason containing the partner's raw JSON. The code now correctly
reflects a state conflict (not an invalid payload) and the reason comes
through clean.
Additionally, the statement exact lookup (by endToEndId/identifier on
GET /v1/accounts/{accountId}/statement) now queries the settlement bank
directly and may include the optional initiation, errorMessage, and
fee fields on the item, plus full payer/payee/counterParty.
v2.18.1 (2026-06-07) - Balance is read-only (no balance locking on our side)
Contract clarification: the API does not handle balance locking.
GET /v1/accounts/{accountId}/balance only displays the amount blocked
by the settlement bank in the locked field (a mirror of MT's block).
There is no balance lock/unlock endpoint and no "local hold".
The balance response no longer includes the locks field (list of local
locks) — it was never populated and has been removed from the spec. The
total, locked, available, currency, and updatedAt fields are
unchanged.
v2.18.0 (2026-06-06) - Daily balances, richer counterparty, and new statement filters
New endpoint GET /v1/accounts/{accountId}/daily-balance: returns the
account's daily balance series — opening balances (total / blocked /
available at the start of each day) plus the consolidated daily flows (PIX
in/out, TED in/out, internal transfers, boleto payments, stablecoin
mint/burn, and fees). Dates are calendar days in Brasília time (BRT).
Maximum window of 30 days. Only closings from 2026-06-04 onward are
returned (earlier partner data was unreliable and is omitted; earlier data
is omitted) — the response includes cutoffDate and timezone.
Statement (GET /v1/accounts/{accountId}/statement) — new fields
(additive, backward-compatible):
transactionType: codeC(credit/incoming) orD(debit/outgoing), mirroringdirection.counterPartynow includes the counterparty's bank details when the partner provides them:bankName,bankIspb,bankCode,branch,account,accountType,pixKey(previously onlynameanddocument).
Statement — filtering is now 100% server-side:
- The
operationfilter (PIX, TED, BOLETO, INTERNAL_TRANSFER) is applied at the partner, with exacttotalElements/totalPages. Combine it withorder(asc/desc) andstartDate/endDate. - The
statusandoperation=FEEfilters were removed: they only applied to the current page (post-pagination), producing inconsistent counts. ThefilteredCountresponse field was also removed. To filter by status or fee, process the returneditemson your side.
v2.17.0 (2026-06-05) - identifier length limit raised to 38 characters
The identifier field sent by integrators in PIX out, QR codes
(static/dynamic), and internal transfers now accepts up to 38
characters (previously: 32). The allowed charset ([A-Za-z0-9._-])
and the reserved fee- prefix remain unchanged.
This is a fully backward-compatible change: existing identifiers up to 32 characters keep working without any client code change. Only the cap was relaxed.
Why: aligns the per-payment limit with the BigPix base identifier limit (already 38 chars). Integrators using both flows no longer need separate id-generation schemes.
v2.16.0 (2026-06-02) - Legacy statement (pre-cutover)
New endpoint GET /v1/accounts/{accountId}/statement/legacy to query
history imported from the previous core (Finaya/Stark), stored in
legacy_transactions.
Contract: same shape as GET /v1/accounts/{accountId}/statement
(page, size, startDate, endDate, status, operation). Response
has source: legacy and header X-Source: legacy. Each item includes
ispb (30306294 Finaya, 20018183 Stark) and source: legacy.
No change to the live statement (GET .../statement) — still MT Bank
real-time only.
Unified legacy + live bulk view: async CSV export
(POST /v1/accounts/{accountId}/exports).
v2.15.0 (2026-05-30) - Statement CSV export restored
Statement CSV export is back on the live MT Bank model plus legacy
history (legacy_transactions).
Endpoints:
POST /v1/accounts/{accountId}/exports— queue async job (202)GET /v1/accounts/{accountId}/exports— list jobsGET /v1/accounts/{accountId}/exports/{exportId}/download— presigned URL
CSV: statement-aligned columns with an ispb column to tell
Finaya/Stark (legacy) from MT Bank (live). No post-transaction balance
column. Live fetches are chunked in 31-day windows.
v2.14.0 (2026-05-30) - Bank account details lookup
New endpoint GET /v1/accounts/{accountId}/bank-account to retrieve
the bank code (COMPE), branch, and account number for the credentialed account.
Response (200):
bankCode,bankIspb,bankName,branch,accountNumberholderDocument,holderName,accountId,status
Useful for deposit instructions or reconciliation without relying on PIX keys. Accounts still in accreditation return 422 with a clear message.
v2.13.0 (2026-05-25) - Richer QR Code decode
POST /v1/accounts/{accountId}/pix/out/qr-code/decode now exposes
all the data the partner already returns from the QR capture step
— previously most of it lived only in internal logs. The change is
fully backward compatible: every existing field is preserved and
keeps its name and shape; new fields are additive.
New optional fields (populated when the partner returns them):
originalAmount— face value of the QR before discount/interest/ penalty (may differ fromamountfordynamic-due-date).qrCodeType(static|dynamic-immediate|dynamic-due-date) andqrCodeTypeId(numeric) — promoted to top level (they used to live insideExtra).allowChange— now actually populated from the partner (AllowPayerChangeValue) ondynamic-immediate. Previously documented but never set.payeeTradeName— fantasy / trade name of a legal entity (typical fordynamic-due-dateinvoices).bankIspb/bankBranch/bankAccount/accountType— beneficiary bank account details.accountTypeis canonicalised toCHECKING|SAVINGS|PAYMENT|SALARY(upper snake-case for unknown variants).discount,deduction,interest,penalty— charge components fordynamic-due-date. Relationship:amount = originalAmount - discount - deduction + interest + penalty. Previously onlydiscountwas documented (and was always0).dueDateandpaymentDeadline— promoted to top level (they remain insideExtrafor backward compatibility).
Compatibility: key, amount, payeeName, payeeDocument,
identifier, decodeId, description keep their names and shapes.
Existing integrations continue to work unchanged.
v2.12.1 (2026-05-24) - PIX status PENDING_APPROVAL
New canonical status returned by PIX endpoints (GET /v1/accounts/{accountId}/pix/payments/lookup, GET /v1/accounts/{accountId}/payments/{paymentId}, statement, and webhook
envelopes):
PENDING_APPROVAL— the partner accepted the PIX OUT request but is holding it for internal approval (multi-step authorisation, 2FA, anti-fraud review). There is noendToEndIdyet, and funds have not moved. At some point the partner will release (transition toPENDING→COMPLETED) or reject (transition toFAILED).
Why the new status: previously, this scenario leaked the partner's
raw value (AWAITING-AUTHORIZATION) through the public API. Starting
this release, we never propagate raw partner values — every status
is canonicalised. Anything outside the known vocabulary maps to
UNKNOWN plus a structured warning on our side so we add the mapping
in the next release.
No impact for ordinary PIX OUT integrations (typical flow still
goes PROCESSING → COMPLETED/FAILED). Integrations that monitor
intermediate states must treat PENDING_APPROVAL as non-terminal
(keep polling or wait for the next webhook).
pix.out.completed / pix.out.failed webhooks still only fire on
terminal states — PENDING_APPROVAL does not trigger an
outbound webhook.
v2.12.0 (2026-05-23) - TED available (send + receive)
Major release: TED (Transferência Eletrônica Disponível BACEN) is now a public API product. Covers interbank send, async status query, and receiving webhooks. Settlement bank: MT Instituição de Pagamentos (Compe 681 / ISPB 50871921). See the TED guide.
New endpoints
POST /v1/accounts/{accountId}/ted/out— sends TED (async, 202)GET /v1/accounts/{accountId}/transfers/ted/{tedId}— query status (PROCESSING→COMPLETED|FAILED)
New webhooks
ted.out.requested— TED registered at the settlement bank (initial status)ted.out.confirmed— settlement confirmed (terminal — success)ted.out.failed— rejection/expiration/48h timeout (terminal — failure)ted.in.received— TED received in your account
Window and timing
- Business days 06:30–17:00 → settlement same business day (~30min after send)
- Outside the window → scheduled for D+1; status stays
PROCESSINGuntil settled - Server runs defensive polling every 1 minute for up to 48 hours — guarantees convergence even if settlement bank webhook fails
Deprecation
ted.payment (v1 legacy catalog) becomes a deprecated alias of ted.out.confirmed. Both are dispatched together during v2.x lifecycle; ted.payment will be removed in v3.0 (date TBA).
- New integrators: subscribe only to
ted.out.confirmed - Legacy integrators: can keep
ted.paymentuntil v3.0; migration is unsubscribe + subscribe toted.out.confirmed(identical payloads)
v2.11.1 (2026-05-22) - Fix: duplicate webhooks for tenants with multiple subscriptions
Bug fix that directly affects integrators with more than one active
subscription for the same eventType within the same tenant.
Previously, each matching subscription generated a separate POST to
egress (Hookdeck). Because the Hookdeck filter matches tenantId + type in the body, every one of those POSTs got delivered to ALL
destinations, multiplying deliveries: a tenant with N subscriptions
to the same event received N² deliveries instead of N.
Starting from this release, every event generates one single POST to egress; Hookdeck fans it out to the N destinations (1 delivery per subscription, no duplication).
No payload or contract change — only the delivery count is fixed.
Client-side dedup by envelope id (good practice) is still safe.
v2.11.0 (2026-05-22) - Webhook envelope back to the v1 shape
The egress webhook payload (delivered to the URL registered via
POST /v1/webhooks) is back to the same envelope documented in v1.
Customers that migrated from v1 → v2 without changing their webhook
parser now receive the fields exactly where they used to expect them.
Envelope (all events):
| Field | Before (v2.10) | Now (v2.11) |
|---|---|---|
schemaVersion | missing | "1.0" |
environment | missing | "production" | "sandbox" |
accountId (root) | only inside data.accountId | added at root (still in data.accountId) |
tenantId (root) | already present | kept |
data field changes per event (to match v1):
pix.in.completed:endToEndIdrenamed toendToEnd; addedreceivedAt.pix.out.completed/pix.out.failed:statusis now"SUCCESS"/"FAILED"(used to be"COMPLETED"); addedkey: { type, key }when the PIX was by key; addedcompletedAt; addederroralias forerrorReasonon failures.pix.refund.received:refundEndToEndId→refundEndToEnd;originalEndToEndId→originalEndToEnd; addedreceivedAt.qrcode.paid:endToEndId→endToEnd;paidAmount→amount;payerName/payerDocumentgrouped intopayer: { name, document }; addedqrcodeId(= txid),type(static|dynamic),receivedAt,status: "SUCCESS".qrcode.cancelled: addedstatus: "CANCELLED"andtype.qrcode.expired: addedstatus: "EXPIRED"andtype.pix.med.opened/pix.med.updated: names fixed topix.med.*(the backend was emittingmed.opened/med.decided, which were not in the public catalog — effectively integrators never received these events).originalEndToEndId→originalEndToEnd; addedstatus(OPEN|PENDING_DECISION|ACCEPTED|REJECTED|CANCELED).transfer.internal.in/transfer.internal.out:endToEndId→endToEnd.boleto.paid/boleto.failed:statusis now"SUCCESS"/"FAILED"; addederroralias on failures.
Removed: the currency field is no longer emitted nor documented —
all monetary values are in BRL by contract and use the dot decimal
separator (150.50). The currency column never reached production in
v2 for most events; this closes the last gap.
Compatibility
No aliases are emitted for the old names. If you used data.endToEndId
on PIX in or data.paidAmount on QR paid, you must rename to
data.endToEnd and data.amount. See the up-to-date examples in
Webhooks.
v2.10.0 (2026-05-22) - Refund reason aligned with partner bank
POST /v1/accounts/{accountId}/pix/out/refund (and POST /pix/out
with mode=REFUND) now accepts a closed set of kebab-case
slugs on the reason field. The legacy values (USER_REQUESTED,
FRAUD, BANK_ERROR, CASHIER_ERROR, CUSTOMER_REQUEST) are no
longer accepted — payloads using those values receive HTTP 400
with a clear message listing the valid slugs.
Accepted slugs are forwarded verbatim to the partner bank (MT Bank), with no translation:
| Slug | When to use |
|---|---|
user-requested | End customer requested the refund |
transaction-error | Generic transaction error |
unauthorized-transaction | Transaction not authorized |
fraud | Confirmed/suspected fraud |
trade-disagreement | Commercial dispute |
withdrawal-purchase | PIX Saque/Troco |
contractual-divergence | Contractual disagreement |
operational-error | Operational/processing error |
duplicate-payment | Duplicate payment |
Why this change: the v2 refund endpoint was returning HTTP 422 on every request because the legacy vocabulary did not match what the partner bank accepts. This version aligns public API and partner 1:1, avoiding opaque mappings.
v2.9.0 (2026-05-21) - Counterparty document on statement
Statement items (GET /v1/accounts/{accountId}/statement) now include
the counterparty document (CPF/CNPJ) in addition to the name:
recipientName: counterparty name (already existed).recipientDocument: new field — CPF (11 digits) or CNPJ (14 digits), unmasked (digits-only).counterParty: new object aggregating{ name, document }. Omitted when both fields are empty.
For transactions with direction: "IN", the counterparty is the
payer; for direction: "OUT", the recipient. All fields are
backward-compatible — integrators that don't need the document don't
have to change anything.
v2.0 (2026-05-21) - New CorpX platform
v2 is a full rewrite of the platform on faster, more predictable infrastructure, with direct integration to the new settlement bank. Mostly backward compatible with v1.
Everything you need to migrate in one place:
- Quick migration guide (2 min)
- Full reference (endpoint-by-endpoint audit)
Auth
- New base URL:
https://tenant.api.corpx.com/v1(washttps://api.corpxapi.com/v1) - New Token URL:
https://auth.api.corpx.com/oauth2/token - New
client_id+client_secretper integrator (delivered via secure channel) - Scopes:
api/full→api2/read api2/write Idempotency-Keyis now optional (auto-generated if missing)X-Tenant-Idis required on all/v1/*requests (exceptGET /v1/meand health); must match the account tenant whenaccountIdis in the path
Statement consolidated into a single endpoint
GET /v1/accounts/{accountId}/statementis now always live — queries the settlement bank in real time (no local cache).- Derived endpoints that used to read from the same cache
(
/pix/transactions,/pix/payments,/paymentsalias) are also live now. - The previous v1 API and backoffice remain online in read-only mode for 30 days after cutover for historical lookup.
/transactions/real-statement,/transactions/legacy-statementand/payments/{id}/legacydo not exist on v2 — use/statement(current data) or the v1 read-only API (history).
Deprecated endpoints (still work until 2026-11-21)
4 paths that existed on v1 and weren't kept in the canonical v2 shape come back as deprecated aliases. They keep responding normally, but attach 3 warning headers to the response:
Deprecation: true
Sunset: Sat, 21 Nov 2026 00:00:00 GMT
Link: </v1/accounts/.../new-path>; rel="successor-version"
These headers follow RFC 8594 (Deprecation) and RFC 9745 (Sunset). Planned sunset: 2026-11-21 — after that date they start returning 410 Gone. They no longer appear in the OpenAPI or the Postman collection.
GET /v1/accounts/{id}/pix/payments/{paymentId}→ use/pix/payments/lookup?identifier=GET /v1/accounts/{id}/payments/{paymentId}(alias without/pix/) → use/pix/payments/lookup?identifier=GET /v1/accounts/{id}/pix/qr-code(no/lookup) → use/pix/qr-code/lookup?identifier=POST /v1/accounts/{id}/pix/out/qrcode(no hyphen) → use/pix/out/qr-code
Removed endpoints (return 404 on v2)
POST /v1/webhooks/replay→ no replacementGET /v1/integrator/webhooks→ useGET /v1/webhooksGET /v1/integrator/webhooks/{id}/deliveries→ no replacementPOST /v1/integrator/events/replay+/batch→ no replacementPOST .../pix/med/{medId}/send+/response→ use/decide+/answer
GET /v1/health still exists on v2 (and now has an additional
GET /health alias for external health checks).
New endpoints
GET /v1/me(debug for the authenticatedclient_id)GET /v1/transactions/{id}/timeline(noaccountIdin the path)GET /v1/accounts/{id}/pix/out/bigpix/{batchId}(aggregated BigPix batch status)GET /health(alias for/v1/healthwithout the prefix)- Boleto family (
POST .../boleto/preview|pay,GET .../boleto/payments/{id}) — was 503 on v1, now active
Behavior changes (same path, new shape or semantics)
- Statement and derived endpoints: cache → live; no derived
tariff_ref;X-Source: liveheader; max window per query 31 days. - Internal transfer
by-bank-account: accepts optionalholderDocument+holderName(non-breaking). - Locked balance
unlock: v2 only validateslockId; extra fields are ignored (non-breaking). feefield on the transaction object removed temporarily — manual reconciliation viaidentifier=fee-{slug}-{operationReferenceId}in the statement. Returns with no payload change in a future release.
MED and webhooks temporarily suspended
/v1/accounts/{id}/pix/med/*endpoints return HTTP 503 during the migration. Open disputes follow BACEN's natural lifecycle — only the API integration is paused.- Suspended webhooks:
fee.*,pix.med.*,edi.*. Subscriptions to these events can stay active — once they're back they resume automatically. Fees still show up in the statement as separate lines (withidentifier=fee-{slug}-{ref}).
Compat (no change to your code)
- BigPix (
POST .../pix/out/bigpixand.../bank-account/bigpix): body stays single ({amount, key, ...}); chunking remains server-side, same as v1. - PIX out variants (
/pix/out/async,/bank-account/async,/bank-account/bigpix): preserved. /v1/accounts/{accountId}/transfers/internal(all 3 modes): path and body preserved./v1/accounts/{id}/pix/qr-code/staticbody:valuestays as on v1./v1/accounts/{id}/pix/out/qr-code/decode: path preserved./v1/accounts/{id}/pix/keys(GET + POST + DELETE): kept./v1/accounts/{id}/pix/key/{pixKey}(DICT lookup): kept.
Support
Contact: api@corpx.com during the tenant cutover — reply within 1h in business hours.
v1.30.3 (2026-04-30) - Duplicate webhooks removed (pix.refund.completed, pix.out.failed, qrcode.paid)
Fixes
-
Duplicate webhooks eliminated: in some scenarios the platform emitted two webhooks for the same event — one following the documented format and a second one with a legacy format that was never described in the documentation. The behavior is now corrected: each event generates exactly one notification, always in the documented format.
Events affected:
pix.refund.completed(sent when a PIX refund completes)pix.out.failed(sent when an outbound PIX fails)qrcode.paid(sent when a QR code is paid)
The legacy format was never present in docs/webhooks — it was a parallel emission from an internal routine that escaped the documentation. Integrations that relied only on documented fields will not notice any difference besides the absence of the duplicate notification.
-
data.statusfield inpix.refund.completednow always returnsSUCCESS, aligned with the official status vocabulary. Previously, in some cases it leakedREFUNDED(the status of the original PIX transaction).
Fields and formats that disappear (only for integrations coupled to the undocumented legacy format)
| Legacy field / format | Replaced by (documented format) |
|---|---|
id: refund_completed_{paymentId}_{ts} | id as a sha256-hash string |
id: pix_out_failed_{paymentId}_{ts} | id as a sha256-hash string |
id: qr_paid_{txid}_{ts} | id as a sha256-hash string |
data.status: COMPLETED (on pix.refund.completed) | data.status: SUCCESS |
data.status: REFUNDED (on pix.refund.completed) | data.status: SUCCESS |
data.refundId (on pix.refund.completed) | use data.transactionId (already documented) |
data.failedAt (on pix.out.failed) | use occurredAt from the envelope |
data.paymentId (on pix.out.failed) | use data.transactionId (already documented) |
payer.account / payee.account | payer.accountNumber / payee.accountNumber |
If your integration consumed any of these legacy-only fields, update it to use the documented fields before adopting this version.
Recommended deduplication keys
Always deduplicate webhooks on your side using a strong key from the payload:
- For
pix.refund.completed:data.refundEndToEnd(BACEN D-code, globally unique). - For
pix.out.failed:data.endToEnd(BACEN E-code) ordata.identifier. - For
qrcode.paid:data.endToEndordata.qrcodeId.
These fields are unique per transaction and present on every event.
v1.30.2 (2026-04-30) - Webhook timestamps standardized to UTC
Fixes
- Date fields inside
dataare now always in UTC with theZsuffix across every webhook. Previously some fields (receivedAt,completedAt,initiatedAt,chargedAt,openedAt) were forwarded without a timezone indicator, which led strict ISO 8601 parsers to interpret them as the consumer's local time — producing up to 3-hour drifts on BRT machines. - Standard consolidated and documented at Webhooks → Date and time format.
- Documentation already showed
Z(UTC) in all examples; now every emitted payload matches the standard.
v1.30.1 (2026-04-29) - qrcode.paid webhook kept (not deprecated)
Deprecation reverted
qrcode.paidremains officially supported. The deprecation notices that appeared in the Webhooks Guide (in PT, EN and ZH) were removed. The event is still valid and stable; no migration is required.- The equivalent event
pix.in.completedwithmethod: QR_CODE_DYNAMICorQR_CODE_STATICremains available as an alternative (and continues to be dispatched in parallel) — pick whichever best fits your integration.
v1.30.0 (2026-04-23) - Internal transfers: error mapping + identifier in webhook
Fixes
- Banking partner
account_disabled(TX00012) error mapping: when the source account is disabled at the banking partner, the API now returns HTTP 422 witherrorCode: partner_account_disabled. Previously this case was mapped to HTTP 502 (Bad Gateway), misleading integrators into treating it as infrastructure failure rather than a business rule. The change applies to all three internal transfer endpoints (/transfers/internal,/transfers/internal/by-document,/transfers/internal/by-bank-account). - Unified banking partner error classification: HTTP errors from the core banking service (used for internal transfers and account lookups) now go through the same classifier already used by the other endpoints. Specific codes such as
insufficient_balance,daily_limit_exceeded,account_disablednow return 422 instead of 502.
Improvements
identifieranddescriptionnow returned in thetransfer.internal.outwebhook: the integrator-supplied identifier and description from the originalPOST /transfers/internal*request are now propagated to the webhook sent to the payer account, allowing reconciliation without consulting the statement.- Custom payer description replaces the default
Transferência Internatext in the statement (GET /statement) when provided in the request. coreIdnow included in every ledger-entry webhook: the UUID of the banking partner's core ledger line (same value returned in the statement'scoreIdfield) is now propagated inpix.in.completed,pix.out.completed,pix.out.failed,pix.refund.*,qrcode.paid,transfer.internal.*,fee.chargedandfee.refunded. Allows reconciliation across webhook ↔ statement ↔ partner exports through a single stable key.- Standardized
statusacross every ledger-entry webhook:data.statusis now guaranteed on all PIX, QR, internal transfer, refund and fee events. Standardized vocabulary:SUCCESS(operation completed),FAILED(operation failed, withdata.error),REVERSED(previously completed entry was reversed). MED events (pix.med.*) keep their own vocabulary (OPEN,PENDING_DECISION,ACCEPTED,REJECTED,CANCELED). Full table in docs/webhooks.
New errorCode values
| Code | HTTP | Meaning |
|---|---|---|
partner_account_disabled | 422 | The source account is disabled at the banking partner (reactivation required) |
v1.29.0 (2026-04-18) - Lookup internal transfer recipient by document
New Features
- Recipient preview for internal transfers: New endpoint
GET /v1/accounts/{accountId}/transfers/internal/lookup/{document}lets you preview the holder name before executing an internal transfer by CPF/CNPJ. Useful for showing a confirmation step to the end user. - The name is returned with a privacy mask: the first name is kept in full; remaining surnames are reduced to their first letter followed by
***. Connectors like "de", "da", "dos" are preserved.- Example:
"JOAO DA SILVA PEREIRA"→"JOAO da S*** P***"
- Example:
- The endpoint also returns the
accountStatusat the banking partner.
Recommended flow
- User enters the recipient's CPF/CNPJ
- Integrator calls
GET /transfers/internal/lookup/{document}and showsmaskedNamefor confirmation - User confirms
- Integrator calls
POST /transfers/internal/by-documentto execute the transfer
v1.28.0 (2026-04-17) - Deprecated webhooks removed
Breaking Changes
payment.sentandpayment.refundedwebhooks disabled: These events were deprecated in v1.15.0 (2026-02-20) and have now been permanently disabled.payment.sent→ usepix.out.completed(includesmethodfield andpaymentobject)payment.refunded→ usepix.refund.completed(includesoriginalEndToEnd,refundEndToEndand party details)
- If your integration still subscribes to these events, migrate immediately to the replacements listed above.
v1.27.0 (2026-04-14) - PIX Out by bank account
New Features
- PIX Out by bank account: New endpoints to send PIX transfers using the recipient's bank details (ISPB, branch, account number) instead of a PIX key:
POST /v1/accounts/{accountId}/pix/out/bank-account— Synchronous transfer by bank account.POST /v1/accounts/{accountId}/pix/out/bank-account/async— Asynchronous (queued) transfer.POST /v1/accounts/{accountId}/pix/out/bank-account/bigpix— Large transfer with automatic chunking.
- Required fields:
bankIspb(bank ISPB code),accountNumber,accountBranch,documentNumber(recipient CPF/CNPJ), andname. - The
accountTypefield acceptsCHECKING_ACCOUNT,SAVINGS_ACCOUNT, orPAYMENT(default:CHECKING_ACCOUNT).
Notes
- Webhooks, fees, and reconciliation are the same as PIX Out by key — transactions appear as
PIX_OUTin the statement. - The async endpoint uses the same FIFO queue as PIX Out by key — same ordering and idempotency guarantees.
- BigPix works the same: splits into R$ 15,000 chunks (configurable via policy), fee charged once per operation.
v1.26.1 (2026-04-13) - Stability fixes
Bug Fixes
- Static QR Code: Fixed an issue where the amount encoded in the static QR code could differ from the requested value in some scenarios.
- Webhook
pix.in.completed: Fixed a scenario where theidentifierfield was missing from the webhook payload for QR code payments, even when the identifier was available.
v1.26.0 (2026-04-09) - Boleto payment
New Features
- Boleto payment: New endpoints to look up and pay bank slips, utility bills, and taxes:
POST /v1/accounts/{accountId}/boleto/preview— Look up boleto data by barcode or "linha digitável". Returns beneficiary, bank, original amount, interest, fine, discount, and total updated amount.POST /v1/accounts/{accountId}/boleto/pay— Submit boleto payment. ReturnspaymentIdwith statusPROCESSING.GET /v1/accounts/{accountId}/boleto/payments/{paymentId}— Check boleto payment status. Status progresses fromPROCESSINGtoCOMPLETEDorFAILED.
- New transaction type
BOLETO_PAYMENT: Boleto payments appear in the statement as outgoing transactions (direction: OUT,transactionType: BOLETO_PAYMENT). - Feature flag
boleto_payment: The functionality is controlled by a feature flag intenant_configs. Must be enabled by admin for each tenant that wants to use it.
Notes
- Bank slips (47 digits), utility bills (48 digits), and taxes are automatically classified by type (
boleto,utility,tax). - Payment is asynchronous — after
POST /boleto/pay, the integrator should pollGET /boleto/payments/{paymentId}to track status. - Boleto cancellation and scheduled payments will be implemented in a future version.
v1.25.1 (2026-04-02) - Origin transaction reference in fee.charged and refund improvements
Improvements
- Enriched
fee.chargedwebhook: The payload now includesoriginTransactionType(type of the transaction that triggered the fee, e.g.PIX_IN,PIX_OUT) andoriginTransactionRef(E2E ID of the original transaction). Also includes the fulldescriptionwith calculation details. pix.in.completedwebhook with identifier for dynamic QR codes: Fixed a scenario where the QR codeidentifierwas not included in the webhook when the payment confirmation arrived before the charge notification.- More resilient refunds: Fixed a scenario where the refund was successfully processed by the banking partner but the API returned a temporary error. The API now detects the confirmation via webhook and returns the D-code correctly.
v1.25.0 (2026-03-26) - payerDescription field, fee webhooks, and refund fixes
New Features
payerDescriptionfield in statement and webhooks: TheGET /v1/accounts/{accountId}/statementendpoint and thepix.in.completed,pix.out.completed,qrcode.paid, andtransfer.internal.*webhooks now return an optionalpayerDescriptionfield. It contains the message the payer typed when sending the PIX (when available). Thedescriptionfield continues with the standardized descriptive format ("PIX Received - Payer Name").fee.chargedandfee.refundedwebhooks: Banking fees now generatefee.charged(when charged) andfee.refunded(when reversed) webhooks. To receive them, add these event types to your webhook subscription.
Improvements
- Richer PIX IN webhooks:
pix.in.completedwebhooks now always contain complete data:reconciliationId,payerDescription, payer details, and PIX key info. Previously, in some scenarios the webhook could arrive with partial data.
Bug Fixes
- Refund webhooks (PIX refund): Fixed a scenario where the
pix.refund.completedwebhook was not delivered to the integrator when the refund confirmation arrived shortly after creation. - PIX webhooks for internal transfers (CORPX-CORPX): Fixed the event type for transfers between CORPX accounts. Previously, the recipient could receive
pix.out.completedinstead ofpix.in.completed. reconciliationIdin static QR codes: Fixed a scenario wherereconciliationIdwas not included in thepix.in.completedwebhook for static QR code payments.- Statement status filter: Fixed the
statusfilter on the statement endpoint that in some scenarios did not return results correctly.
v1.24.1 (2026-03-18) - Error semantics improvements (generic 502 reduction)
Improvements
- We standardized partner-originated errors to semantic HTTP statuses across
pix/keys,pix/key/{pixKey}(DICT),pix/limits,pix/out/qr-code,pix/out/qr-code/decode,pix/med,transfers/internal,accreditations, andaccounts/{accountId}/balance. - Error payload format remains stable:
{ "errorCode": "...", "message": "..." }. - OpenAPI and Postman were updated to reflect the new error formats/statuses.
Changed error mapping (from -> to)
| Error / scenario | Before | Now |
|---|---|---|
Sync Pix Out timeout (POST /pix/out) | 504 Gateway Timeout | 207 Multi-Status (partner_timeout) |
pix_key_not_found in sync Pix Out (POST /pix/out) | 502 Bad Gateway | 422 Unprocessable Entity |
Generic partner_error for business/validation failures (PIX/Transfers/MED/Accreditations) | 502 Bad Gateway | 422 Unprocessable Entity |
pix_key_lookup_failed (classified DICT/partner failure) | 502 Bad Gateway | 422 Unprocessable Entity |
partner_auth_failed / partner_signature_rejected | 502 Bad Gateway | 503 Service Unavailable |
partner_internal_error / partner_recipient_unavailable | 502 Bad Gateway | 503 Service Unavailable |
partner_timeout (upstream timeout endpoints) | 502 Bad Gateway | 504 Gateway Timeout |
account_not_found from partner-integrated flows | 502 Bad Gateway | 404 Not Found |
pix_key_not_found in DICT lookup (GET /pix/key/{pixKey}) | 502 Bad Gateway | 404 Not Found |
| Unclassified fallback | 502 Bad Gateway | 502 Bad Gateway (kept as fallback) |
v1.24.0 (2026-03-18) - Sync Pix Out: per-account rate limit and timeout 207
Breaking Changes
- Sync Pix Out timeout status changed:
POST /v1/accounts/{accountId}/pix/outnow returns HTTP 207 (partner_timeout) on partner timeout, instead of HTTP 504. - Expected handling remains the same: the transfer may have been executed, so check statement/payment status before retrying.
Improvements
- Per-account rate limit for sync Pix Out: a per-account limit is now enforced (current default
100 req/min, customizable by policy). - Next days: the default for synchronous requests will be adjusted to
10 req/min. - When exceeded, API returns
429 rate_limit_exceededwith contextual fields (currentRateLimit,scope) and guidance to use async flow. - New async cashout endpoint:
POST /v1/accounts/{accountId}/pix/out/asyncschedules payment processing and returns immediate202with apaymentIdfor tracking. - BigPix is now async-only:
POST /v1/accounts/{accountId}/pix/out/bigpixnow schedules processing through the async flow (same behavior as/async) and no longer executes synchronously.
v1.23.0 (2026-03-17) - PIX QR Code Decode
New features
- PIX QR Code decode/consult: New endpoint
POST /v1/accounts/{accountId}/pix/out/qr-code/decodedecodes a PIX QR Code EMV string and returns the beneficiary details without executing payment. Returns: name, document, amount, PIX key, bank and account type of the recipient. Useful for displaying a confirmation screen to the user before paying.
v1.22.0 (2026-03-07) - Internal transfer by document and by bank account
New features
-
Internal transfer by document (CPF/CNPJ): New endpoint
POST /v1/accounts/{accountId}/transfers/internal/by-documentallows creating internal transfers by identifying the destination account via the holder's CPF or CNPJ. Works for any account in the banking ecosystem, not just accounts registered in the API. Required fields:document,value,description. -
Internal transfer by bank account: New endpoint
POST /v1/accounts/{accountId}/transfers/internal/by-bank-accountallows creating internal transfers by identifying the destination account via branch and account number (branch+accountNumber). Only works for accounts registered in the API (same tenant). To transfer to external accounts, use the/by-documentendpoint.
v1.20.0 (2026-03-01) - Account ownership validation
Improvements
- Strengthened account ownership validation: The API now verifies that the
accountIdin the request belongs to the authenticated tenant. Integrators accessing only their own accounts are not affected. This improvement enhances security against unauthorized cross-account access.
v1.19.1 (2026-02-26) - BigPix improvement
Improvements
- BigPix (
POST /v1/accounts/{accountId}/pix/out/bigpix): improved chunk processing robustness for better consistency under high-volume scenarios.
v1.19.0 (2026-02-26) - Transaction timeline
New features
- Transaction timeline: New endpoint
GET /v1/accounts/{accountId}/transactions/timelinereturns the timeline of events for a single transaction (byendToEndIdoridentifier). Includes: BACEN confirmations (short summary text), webhooks sent to the client (with full payload for "View full payload"), fees, refunds, and QR Code lifecycle when applicable. Exactly one query parameter is required:endToEndIdoridentifier. No changes to existing endpoints or response formats.
v1.18.0 (2026-02-24) - Performance optimizations and independent fee transactions
Improvements
- Fees as independent transactions: Fees charged by the API now appear as separate lines in the statement, linked to the original transaction. QR Code and payment lookups now include fee details (
fees,totalFees). - Performance optimizations: Parallel batch processing for reconciliation, support for higher transaction volumes.
- Automatic fee retry: The self-healing process now includes retry of fees that failed on initial charge.
Bug Fixes
- Fixed MED dispute rate calculation that was double-counting transactions and disputes.
- Fixed fee charging that could silently fail under high concurrency.
v1.17.0 (2026-02-22) - Automatic reconciliation and transaction status fix
Improvements
- Automatic reconciliation (Self-Healing): Expired QR Codes are now automatically marked as expired. Paid QR Codes and pending payments are periodically reconciled with the statement, ensuring consistency even during transient communication failures.
- Transaction status fix: PIX transactions that have reached a terminal status (SUCCESS, FAILED, REVERSED) can no longer be overwritten by delayed webhooks, eliminating rare cases where a completed transaction could temporarily appear as pending.
v1.16.2 (2026-02-21) - Enriched QR Code and Payment lookups
Improvements
- QR Code lookup (
GET /v1/accounts/{accountId}/pix/qr-code/lookup) and Payment lookup (GET /v1/accounts/{accountId}/pix/payments/lookup,GET /v1/accounts/{accountId}/pix/payments/{paymentId}) now return an enrichedtransactionobject when the transaction is reconciled. - The
transactionobject includes: detailed status, full payer/payee info, balance after transaction, fee details, dispute (MED) info, policy violations, refund references, and error reason (when applicable).
v1.16.1 (2026-02-21) - Partner response fixes
Bug Fixes
- Fixed HTTP 502 error when deleting a PIX key (
DELETE /v1/accounts/{accountId}/pix/keys/{pixKey}). The operation completed successfully, but the API incorrectly returned an error response. - Fixed HTTP 502 error when requesting a refund (
POST /v1/accounts/{accountId}/pix/refund). The refund was processed successfully, but the API incorrectly returned an error response. - General improvements to partner response handling for all PIX operations.
v1.16.0 (2026-02-20) - Lookup Endpoints and Payment Pagination
New Features
Lookup Endpoints (single-item search)
- New
GET /v1/accounts/{accountId}/pix/qr-code/lookupfor QR Code search by?identifier=X,?qrcodeId=X, or?endToEndId=X. - New
GET /v1/accounts/{accountId}/pix/payments/lookupfor Payment search by?paymentId=X,?identifier=X, or?endToEndId=X. - Only one parameter allowed at a time.
Payment List Pagination
GET /v1/accounts/{accountId}/paymentsnow supports?page=0&size=50.- Response includes:
totalElements,totalPages,page,size,hasNext,hasPrevious.
QR Code List Pagination
GET /v1/accounts/{accountId}/pix/qr-codesnow supports?page=0&size=50&status=PAID.- Response includes:
totalElements,totalPages,page,size,hasNext,hasPrevious.
Route Standardization
- New alias
POST /v1/accounts/{accountId}/pix/out/qr-code(hyphenated, recommended).
Deprecated
GET /v1/accounts/{accountId}/payments→ use/v1/accounts/{accountId}/pix/paymentsGET /v1/accounts/{accountId}/payments/{paymentId}→ use/v1/accounts/{accountId}/pix/payments/{paymentId}POST /v1/accounts/{accountId}/pix/out/qrcode(no hyphen) → use/pix/out/qr-code- Old routes still work but will be removed in a future version.
v1.15.0 (2026-02-20) - Webhook method field and refund reconciliation
New Features
method field in PIX webhooks
pix.in.completed,pix.out.completedandpix.out.failedwebhooks now include amethodfield indicating how the transfer was made.- PIX IN:
PIX_KEY,QR_CODE_STATIC,QR_CODE_DYNAMIC,REFUND_RECEIVED - PIX OUT:
PAYMENT,REFUND_PIX_KEY,REFUND_QR_STATIC,REFUND_QR_DYNAMIC
Contextual objects in webhooks
- PIX IN via QR Code includes
qrCodeobject withtxid,identifier,type,value. - PIX OUT via API includes
paymentobject withpaymentId,identifier,reconciled.
Refund reconciliation with QR Codes
- Refunds of PIX received via QR Code now update the QR Code record with refund details.
- QR Code query endpoint returns full refund history in
refunds[].
Deprecated
qrcode.paid: usepix.in.completedwithmethod: QR_CODE_*instead.payment.sent: usepix.out.completedwithmethod: PAYMENTinstead.payment.refunded: usepix.out.completedwithmethod: REFUND_*instead.
v1.14.0 (2026-01-16) - BigPix: High-value transfers
New Features
BigPix Endpoint (POST /v1/accounts/{accountId}/pix/out/bigpix)
- New endpoint for PIX transfers above the individual limit (R$15,000 by default).
- Automatically splits the amount into multiple smaller transfers, executed in parallel.
- Each chunk includes identification in the format
PIX X/Y - Total R$... - message [id]. - If
identifieris provided, each chunk receivesidentifier__X/Y. - Consolidated response with status
FULL(all ok),PARTIAL(some failed) orFAILED(none executed). - Includes retry count per chunk and individual details (E2E ID, status, error).
- Chunk size configurable per tenant via
pixOut.chunkSizepolicy.
v1.13.2 (2026-01-16) - Webhook delivery improvements
Improvements
- New webhook subscriptions now have redundant headers removed before delivery to the recipient.
v1.13.1 (2026-02-19) - Remove qrCodeFormat and Payment Approval Flow
Breaking Changes
qrCodeFormat field removed from QR Code endpoints
- The
qrCodeFormatfield has been removed from dynamic and static QR Code creation endpoints. - The API now always returns QR Codes in EMV format (copy-and-paste). IMAGE format is no longer supported.
- Requests that still send
qrCodeFormatwill not receive an error, but the field will be ignored.
New Features
Payment Approval Flow
- When the
requireApprovalAbovepolicy is configured, PIX Out payments above the threshold now create a payment intent with statusPENDING_APPROVAL. - Administrators can approve or reject pending payments from the admin panel.
- On approval, the payment is automatically executed. On rejection, the reason is recorded.
v1.13.0 (2026-02-18) - Disputes (MED), Policy Enforcement and Contestation
New Features
Dispute Management (MED)
- New MED dispute management system with automatic reconciliation of MEDs with transactions via E2E ID.
- MED data (status, deadline, claimant, reason) now appears linked to the transaction in the statement.
- MED rate monitoring: daily statistics of MED/PIX-In ratio per tenant.
- New endpoints:
GET /v1/backoffice/tenants/{tenantId}/med-stats- Daily MED statistics.POST /v1/accounts/{accountId}/pix/med/{medId}/answer- MED contestation with response and evidence.POST /v1/accounts/{accountId}/pix/med/{medId}/evidence/upload-url- Evidence file upload.
PIX In Policy Enforcement
- PIX In rules are now enforced in real-time (previously only logged violations).
AUTO_REFUNDaction now initiates automatic refund of the violating transaction.- Policy violations appear in the statement with rule details, action, and message.
New Policy Rules
- PIX In: Bank code blacklist, same ownership restriction.
- PIX Out: Whitelist is now evaluated before blacklist (whitelist overrides all other rules).
- MED: Auto-block balance for MEDs above a threshold, MED/PIX-In rate limit with configurable actions.
Improvements
- Documentation: new Policies and Rules guide with all rules and evaluation order.
- Documentation: new Disputes (MED) guide with lifecycle, contestation, and monitoring.
v1.12.1 (2026-02-18) - Required Refund Reason and Pagination Fix
New Features
- New endpoint
GET /v1/accounts/{accountId}/pix/key/{pixKey}for DICT key lookup with 24h cache and configurable rate limiting.
Breaking Changes
reason field now required on refund endpoint
- The
POST /v1/accounts/{accountId}/pix/out/refundendpoint now requires thereasonfield with one of:USER_REQUESTED,FRAUD,BANK_ERROR,CASHIER_ERROR,CUSTOMER_REQUEST. - Requests without
reasonor with invalid values will return HTTP 400.
Improvements
X-API-Versionheader included in all API responses for version debugging.
Fixes
- Fixed: statement pagination returned blank pages after page 10 (more than 1000 transactions).
v1.12.0 (2026-02-18) - Policy Engine
New Features
Policy Engine
- New configurable policy system per tenant and per account, with hierarchy (default -> tenant -> account).
- Available rules:
- PIX Out: per-transaction limits, night limits, allowed person types (PF/PJ), CPF/CNPJ whitelist/blacklist, operating hours.
- PIX In: amount limits, auto-quarantine, configurable violation actions.
- QR Code: type-specific permissions (dynamic/static), amount limits.
- PIX Keys: max keys per account.
- Refund: enable/disable and amount limits.
- Operating Hours: time window restrictions with overnight support.
v1.11.1 (2026-02-15) - Statement Fixes
Fixes
- Fixed: statement pagination (
GET /v1/accounts/{accountId}/statement) was not working correctly.pageandsizeparameters now return the requested page. - Fixed: standardized statement descriptions (e.g., "Transferência Pix", "Devolução Pix", "Tarifa Bancária", "Pagamento de QR Code").
- Fixed: statement ordering when fees and transactions occurred at the same time.
v1.11.0 (2026-02-15) - Banking Fees in Statement and fee.charged Webhook
New Features
Fees in Statement
- Banking fees charged per transaction now appear in the statement (
GET /v1/accounts/{accountId}/statement) with typeFEE. - Each fee entry includes a
feeServiceTypefield indicating which operation triggered the charge (e.g.,PIX_OUT,PIX_IN).
fee.charged Webhook Event
- New event type available for subscription:
fee.charged. - Sent in real-time when a banking fee is charged to the account.
- Payload includes:
amount,currency,feeServiceType,description,chargedAt.
v1.10.0 (2026-02-13) - Reconciliation ID, PIX Reversal Support
New Features
Reconciliation ID
- All webhooks sent to integrators (
qrcode.paid,pix.in.completed,pix.out.completed,pix.out.failed,pix.refund.completed,pix.refund.failed) now include areconciliationIdfield when available. - The
reconciliationIdfield is also returned in the statement endpoint (GET /v1/accounts/{accountId}/statement), enabling cross-referencing with bank statements.
PIX Reversal Support
- The API now processes PIX reversals initiated by the recipient. When a PIX Out recipient returns the funds, the transaction is automatically recorded in the statement and linked to the original payment.
- The original payment status is updated to
REFUNDEDwhen the full amount is returned.
Fixes
- Fixed:
qrcode.paidwebhook now includes completepayerandpayeedata (name, document, bank, branch, account), matchingpix.in.completed. - Fixed: monetary values in synced statement entries were incorrect (duplicate division).
v1.9.0 (2026-02-13) - Payment Lifecycle, QR Code & Webhook Fixes
New Features
Complete Payment Lifecycle (PIX Out)
- Payments (PIX Out) now have a complete lifecycle with reconciliation tracking.
- The
GET /v1/accounts/{accountId}/paymentsresponse now includes:reconciled: whether the payment was confirmed by the banking partner.reconciledAt: confirmation timestamp.transactionId: links the payment to its confirmed transaction in the statement.
- Payment status flow:
CREATED→PENDING→COMPLETED(orFAILED).
Full Payer/Payee Data in Webhooks
pix.in.completedandqrcode.paidwebhooks now include complete payer and payee details (name, document, bank, branch, account, PIX key).
Event Type Validation
- When creating or updating a webhook subscription, event types are validated against the official list. Invalid or legacy event types are rejected with HTTP 400.
Bug Fixes
- Fixed: PIX Out response was missing
endToEndIdandcurrencyfields. - Fixed: Static QR Code creation failed due to partner API format change.
- Fixed: removed unused
clientIdfield from static QR Code request.
v1.8.0 (2026-02-11) - Enriched Statement & Webhook Signature Fix
New Features
Statement with Full Transaction Details
- The statement endpoint (
GET /v1/accounts/{accountId}/statement) now returns complete transaction information:transactionType: Transaction type (PIX_IN, PIX_OUT, QR_CODE_PAYMENT, PIX_REFUND).method: Payment method (KEY, STATIC_QR_CODE, DYNAMIC_QR_CODE, MANUAL).status: Current status (SUCCESS, FAILED, PENDING, REFUNDED).direction: Direction (IN or OUT).identifier: Integrator-provided identifier.payer/payee: Full payer and payee details (name, document, bank, branch, account, PIX key).originalEndToEnd/refundEndToEndIds: Linking between refunds and original transactions.qrcodeId/qrcodeIdentifier: QR Code reconciliation data.
Bug Fixes
- Fixed: webhook HMAC signature was not applied correctly in some subscription update scenarios.
v1.7.0 (2026-02-11) - Webhook Format, Configurable Authentication & HMAC Signing
Breaking Changes
Webhook Payload Format
- The webhook body now follows the documented envelope format directly:
{ id, type, occurredAt, schemaVersion, environment, accountId, data }. - Redundant fields have been removed. The payload now contains only the documented data.
- Added
tenantIdandeventTypeat root level for easier routing.
Webhook Signature
- The signature is now sent in the
X-Signatureheader asbase64(HMAC_SHA256(secret, raw_body)). - The previous format (
hex(HMAC_SHA256(secret, timestamp + "." + body))withX-Timestampheader) has been deprecated. - The
X-Timestamp,X-Webhook-Event, andX-Webhook-IDheaders are no longer sent.
New Features
Configurable Authentication per Subscription
- Integrators can now choose the authentication method for each webhook subscription:
- HMAC (recommended): HMAC-SHA256 signature in the
X-Signatureheader. - API_KEY: Static key in a configurable header (default:
X-API-Key). - BASIC: HTTP Basic authentication (
Authorization: Basic ...). - BEARER: Bearer token (
Authorization: Bearer ...). - NONE: No authentication (not recommended for production).
- HMAC (recommended): HMAC-SHA256 signature in the
- Configured via the Integrator Portal under Webhooks > Edit Subscription.
Improvements
- Webhook documentation fully rewritten with signature verification examples in Node.js, Python, and Go.
- Portuguese documentation locale fully translated.
Bug Fixes
- Fixed: webhook authentication configuration was not being applied correctly to deliveries in some cases.
v1.6.0 (2026-02-06) - Currency Standardization, Statement Sync & Amount Validation
New Features
Improvements
Currency Standardization
- All monetary values in the API are now clearly documented as BRL (REAIS) with at most 2 decimal places (centavos precision).
- Example:
150.75represents R$150,75. - Values with more than 2 decimal places are now rejected with HTTP 400 and a clear error message.
- Updated all OpenAPI field descriptions with the BRL standard.
- Fixed QR Code Static creation to accept values in REAIS (previously required centavos).
Enhanced Transaction Details
- Added
transactionTypefield to transaction responses (PIX_IN, PIX_OUT, QR_CODE_PAYMENT, PIX_REFUND). - Added
pixKeyfield to payer/payee details. - Fixed QR Code Static/Dynamic classification in statement.
v1.5.0 (2026-02-05) - URL Standardization, Payments API & QR Code Fix
Breaking Changes
URL Standardization
All endpoints that previously used accountId as a query parameter now use it as a path parameter under /v1/accounts/{accountId}/.... This provides consistent, RESTful URL patterns across the entire API.
Migration guide:
| Old URL | New URL |
|---|---|
GET /v1/pix/transactions?accountId=X | GET /v1/accounts/{accountId}/pix/transactions |
GET /v1/payments?accountId=X | GET /v1/accounts/{accountId}/payments |
GET /v1/payments/{id}?accountId=X | GET /v1/accounts/{accountId}/payments/{id} |
GET /v1/pix/qr-codes?accountId=X | GET /v1/accounts/{accountId}/pix/qr-codes |
GET /v1/pix/qr-codes/stats?accountId=X | GET /v1/accounts/{accountId}/pix/qr-codes/stats |
POST /v1/pix/qr-code/accounts/{id}/dynamic | POST /v1/accounts/{accountId}/pix/qr-code/dynamic |
POST /v1/pix/qr-code/accounts/{id}/static | POST /v1/accounts/{accountId}/pix/qr-code/static |
GET /v1/pix/qr-code/accounts/{id} | GET /v1/accounts/{accountId}/pix/qr-code |
DELETE /v1/pix/qr-code/accounts/{id} | DELETE /v1/accounts/{accountId}/pix/qr-code |
GET /v1/pix/keys/accounts/{id} | GET /v1/accounts/{accountId}/pix/keys |
POST /v1/pix/keys/accounts/{id} | POST /v1/accounts/{accountId}/pix/keys |
DELETE /v1/pix/keys/{key}/accounts/{id} | DELETE /v1/accounts/{accountId}/pix/keys/{key} |
GET /v1/pix/med | GET /v1/accounts/{accountId}/pix/med |
POST /v1/pix/med/{id}/response | POST /v1/accounts/{accountId}/pix/med/{id}/response |
Note: Legacy URLs continue to work for backward compatibility but are deprecated and will be removed in a future version.
New Features
Payments API
GET /v1/accounts/{accountId}/payments: List all PIX Out payment intents.GET /v1/accounts/{accountId}/payments/{paymentId}: Retrieve payment details.
Bug Fixes
QR Code Reconciliation
- Fixed a race condition where QR code payments were not linked to their transactions when the charge webhook arrived before the PIX webhook.
v1.4.0 (2026-01-16) - Webhook Event Type Standardization & Documentation
New Features
Webhook Event Type Improvements
- New event
pix.med.updated: Notification when a MED/infraction status is updated. - Improved PIX IN/OUT differentiation: Clearer distinction between incoming and outgoing PIX events.
Standardized Event Types
The following event types are now consistent across all systems:
pix.in.completed- Incoming PIX completedpix.out.completed- Outgoing PIX completedpix.out.failed- Outgoing PIX failedqrcode.paid- QR Code was paidpix.refund.completed- Refund completedpix.refund.failed- Refund failedpix.med.opened- MED/Infraction openedpix.med.updated- MED/Infraction updated
Documentation
- Full English translation: OpenAPI specification and Postman collection are now fully in English.
- Updated event type documentation: All webhook documentation updated with standardized event types.
- Consistency improvements: Event types are now consistent across frontend, API handlers, and documentation.
Breaking Changes
- Legacy event types (
pix.received,pix.cash_in,pix.cash_out) are deprecated in favor of the new standardized types. - Existing webhook subscriptions using old event types will continue to work but should be migrated.
v1.3.0 (2026-01-29) - QR Code Statistics and Charge Webhooks
New Features
QR Code Statistics
- New endpoint
GET /v1/pix/qr-codes/stats: Returns aggregated QR Code statistics for the last 24 hours.- Number of QR Codes generated, paid, and refunded
- Total values (generated, paid, refunded)
- Conversion rate (paid/generated)
- Hourly aggregated data for trend analysis
Charge Webhooks (COB)
- QR Code payment notifications: When a dynamic QR Code is paid, the system automatically updates the QR Code status to
PAIDand notifies via webhook. qrcode.paidevent: Webhook sent when a QR Code is confirmed as paid by BACEN.- Automatic webhook registration: When creating a PIX key, the charge webhook is automatically registered to receive payment notifications.
Bug Fixes
- Fixed balance display values.
- Fixed transaction values in the statement.
Documentation
- Added QR Code statistics endpoint to OpenAPI specification.
- Updated Postman collection with new statistics endpoint.
v1.2.0 (2026-01-27) - Integrator Portal and Webhooks
New Features
- Integrator Portal: Documentation added with portal link and capabilities.
Changes
- Webhooks via Portal: Webhook configuration now occurs exclusively through the Integrator Portal.
- Adjusted documentation: Removed references to public webhook management endpoints; only
POST /webhooks/replayandGET /v1/webhooks/eventsretained.
v1.1.0 (2026-01-27) - Webhook Management via API
New Features
Self-Service Webhooks
- Webhook CRUD via API: You can now create, list, update, and delete webhook configurations directly via API without contacting support.
- Multiple Authentication Types: Support for HMAC, API_KEY, BASIC, BEARER, and IP_WHITELIST for webhook authentication.
- Event Type Filter: Configure which event types each webhook should receive.
- Available Events Endpoint: New
GET /v1/webhooks/eventsendpoint to list all supported event types.
New Webhook Events
pix.pending: New event sent when a Pix is pending confirmation (useful for real-time payment tracking).pix.qrcode.cash_in: Specific event for QR Code payments via charge.
Security
- IP Restriction for Webhooks: Webhook endpoints now support source IP restriction.
Bug Fixes
- Improved webhook documentation with more detailed examples.
- Fixed payload examples in webhook events.
v1.0.0 (2025-12-29) - Initial Release (CorpX API)
This is the first official version of the unified public documentation for the CorpX API, focused on external integrators and banking partners.
Main Features
Pix and Payments
- Pix Out: Instant transfers using Pix keys in a single step.
- QR Codes: Dynamic and static QR Code generation, with support for capture, status query, and cancellation.
- Refunds: Refund flow for received Pix.
- Pix MED: Full support for the Special Return Mechanism for infraction and dispute management.
Account Management
- Balance Query: Detailed view of total, available, and locked balance.
- Statement: Paginated transaction and account movement history.
- Internal Transfers: P2P resource movement between accounts on the same platform.
Webhooks and Notifications
- Outbound Notifications: Automatic event reception (Pix received, Pix sent, MED updates).
- Security: HMAC-SHA256 signature on all notifications for origin guarantee.
- Retries: Automatic retry system with exponential backoff.
- Replay Self-Service: Endpoints to request notification resend (Individual or Batch) via API.
Security and Idempotency
- Authentication: OAuth2 via Identity Provider (
client_credentialsandauthorization_codeflows). - Idempotency: Single execution guarantee for mutable operations via
Idempotency-Keyheader. - Transaction Signing: All Pix transfer operations are protected by RSA-SHA512 signatures.
Multi-language Support
- Documentation available in Portuguese, English, and Simplified Chinese.
Developer Experience
- OpenAPI 3.0: Complete specification available for download and interactive Swagger UI.
- Postman Collection: Ready-to-use collection for testing in Sandbox environment.
- Integrator Guide: Detailed documentation on headers, errors, and best practices.