Webhooks
Webhooks are notifications sent by our API to your registered URL whenever a relevant event occurs (e.g., a PIX is received or a payment is completed).
While we migrate from Finaya to MT Bank as the settlement provider
(May/2026), the following events are not being emitted yet. They
remain listed under GET /v1/webhooks/events to preserve the contract,
so you can subscribe to them now — deliveries will start automatically
once the corresponding module is back:
- Fees (tariffs):
fee.charged,fee.refunded— the backend does not yet materialize MT tariff charges as a standalone event; for now the tariff appears in the statement as a separateINTERNAL_TRANSFERline (descriptionfee-{slug}-{ref}). - EDI:
edi.batch— EDI batch processing will be reintroduced in a later phase.
No specific ETA. Notices will be published in the changelog when each
module returns. In the backoffice (Settings → Webhooks) the checkboxes
for these events are flagged as em breve ("coming soon") and are
disabled — you cannot subscribe until the backend starts emitting them.
Webhook Configuration
Webhooks must be configured through the Integrator Portal.
List Available Events
curl -X GET "https://tenant.api.corpx.com/v1/webhooks/events" \
-H "Authorization: Bearer YOUR_TOKEN"
Response (array of { event, description }; illustrative excerpt):
[
{ "event": "pix.in.completed", "description": "PIX received successfully" },
{ "event": "pix.out.completed", "description": "PIX sent successfully" },
{ "event": "accreditation.pf.created", "description": "PF accreditation created (async confirmation of POST)" },
{ "event": "accreditation.pj.created", "description": "PJ accreditation created (async confirmation of POST)" },
{ "event": "accreditation.biometry.link.created", "description": "Facial capture link issued (Unico flow; per person)" },
{ "event": "accreditation.acceptance.link.created", "description": "Terms acceptance link issued (BYO flow; per person)" },
{ "event": "accreditation.consent.link.created", "description": "Authorization link issued — CPF already has an account (per person)" },
{ "event": "accreditation.updated", "description": "Accreditation status transition" },
{ "event": "accreditation.active", "description": "Account ready to operate — accountId available" },
{ "event": "accreditation.failed", "description": "Accreditation ended without opening an account" },
{ "event": "account.shared_access.granted", "description": "Another tenant started operating an account you already operated" },
{ "event": "policy.violation", "description": "A policy rule was violated — operation rejected (BLOCK), merely reported (NOTIFY_ONLY / ALLOW_AND_NOTIFY), or incoming PIX refunded (AUTO_REFUND)" }
]
The full list also includes PIX out/refund/MED, QR Code, boleto, TED, internal transfers, and fees. Onboarding events are documented in Onboarding Webhooks.
Receiving Flow
- An event occurs on our platform.
- We send a
POSTrequest to the registered URLs. - Your application must process the notification and return a
2xxstatus.
Configuration Flexibility
Our webhook infrastructure supports various delivery methods:
- Grouping: You can receive multiple event types (e.g.,
pix.in.completedandpix.out.completed) at the same URL. - Segregation: You can configure different URLs for each event type.
- Redundancy: We can send the same event to multiple independent URLs simultaneously.
Security (Destination Authentication)
When creating or updating a webhook subscription, you can choose how our delivery infrastructure authenticates requests to your endpoint. The authentication method is configured per subscription via the API or dashboard.
Available Authentication Methods
| Method | authType value | Description |
|---|---|---|
| HMAC Signature | HMAC | Signs each request body with your secret using HMAC-SHA256. The signature is sent in the X-Signature header. Recommended. |
| API Key | API_KEY | Sends a static API key in a configurable HTTP header (default: X-API-Key). |
| Basic Auth | BASIC | Sends username:password in the standard Authorization: Basic ... header. |
| Bearer Token | BEARER | Sends a token in the Authorization: Bearer ... header. |
| None | NONE | No authentication. Not recommended for production. |
You configure the authentication method when creating or updating a webhook subscription in the Integrator Portal. Go to Webhooks > Edit Subscription and select your preferred authentication method from the dropdown. You will be asked to provide the required credentials (e.g., your HMAC secret, API key, or username/password) depending on the method chosen.
HMAC Signature Verification
When authType is set to HMAC, each request includes an X-Signature header containing a Base64-encoded HMAC-SHA256 hash of the raw request body, computed using your secret as the key.
Formula:
expected = base64(HMAC_SHA256(your_secret, raw_request_body))
Compare the computed value with the X-Signature header. If they match, the request is authentic.
Always use the raw request body bytes for verification, not a re-parsed/re-serialized version. Re-serializing JSON may change field order or whitespace, which will invalidate the signature.
Verification Examples
Node.js:
const crypto = require("crypto");
function verifySignature(secret, rawBody, signatureHeader) {
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("base64");
return expected === signatureHeader;
}
// In your Express handler:
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
const signature = req.headers["x-signature"];
if (!verifySignature(WEBHOOK_SECRET, req.body, signature)) {
return res.status(403).send("Invalid signature");
}
const event = JSON.parse(req.body);
// Process event...
res.sendStatus(200);
});
Python:
import hmac, hashlib, base64
def verify_signature(secret: str, raw_body: bytes, signature_header: str) -> bool:
expected = base64.b64encode(
hmac.new(secret.encode(), raw_body, hashlib.sha256).digest()
).decode()
return hmac.compare_digest(expected, signature_header)
Go:
func verifySignature(secret string, rawBody []byte, signatureHeader string) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(rawBody)
expected := base64.StdEncoding.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(expected), []byte(signatureHeader))
}
API Key Authentication
When authType is set to API_KEY, we send your configured API key in an HTTP header on every request. The default header name is X-API-Key, but you can customize it via authConfig.header.
# Creating a subscription with API Key auth:
curl -X POST "https://tenant.api.corpx.com/v1/webhooks" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "X-Tenant-Id: your-tenant-id" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/webhook",
"authType": "API_KEY",
"authConfig": {
"key": "your-api-key-value",
"header": "X-API-Key"
},
"eventTypes": ["pix.in.completed"]
}'
Your server should validate the header value matches the expected key.
Basic Auth
When authType is set to BASIC, we send the Authorization: Basic <base64(username:password)> header on every request.
{
"authType": "BASIC",
"authConfig": {
"username": "your-username",
"password": "your-password"
}
}
Bearer Token
When authType is set to BEARER, we send the Authorization: Bearer <token> header on every request.
{
"authType": "BEARER",
"authConfig": {
"token": "your-bearer-token"
}
}
Informational Headers
In addition to the authentication headers above, our delivery infrastructure adds the following informational headers to every request:
| Header | Description |
|---|---|
x-hookdeck-event-id | Delivery event ID (useful for debugging and support requests). |
x-hookdeck-request-id | Original request ID. |
x-hookdeck-attempt-count | Delivery attempt number (1 for the first attempt). |
These headers are informational and do not need to be validated.
IP Whitelist
To increase the security of your integration, we recommend that your destination server validates the source IP address of incoming requests. Only accept notifications from our infrastructure's official IPs:
34.138.140.22334.138.161.10035.231.250.19335.196.71.2934.138.56.192
We suggest adding these addresses to a whitelist in your firewall or web server.
Retries
If your application returns an error (status other than 2xx) or a timeout occurs, our system will attempt to resend the notification following an exponential backoff strategy:
- Attempts: Up to 6 times.
- Intervals: Progressively increasing.
After exhausting all attempts, the delivery is marked as failed. You can request a manual retry of that specific delivery.
Retrying a Delivery
To resend a failed delivery, use the per-delivery retry endpoint, passing the
subscriptionId and deliveryId (both visible in the subscription's delivery
list):
Request Example:
curl -X POST "https://tenant.api.corpx.com/v1/webhooks/{subscriptionId}/deliveries/{deliveryId}/retry" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "X-Tenant-Id: your-tenant-id"
The API queues a new delivery attempt for that deliveryId.
Notification Format
All notifications follow a standard envelope format. The specific content of each event resides in the data object.
Standard Envelope
{
"id": "evt_123456789",
"type": "pix.in.completed",
"occurredAt": "2025-12-29T21:14:33.912Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": { }
}
Common fields inside data
A handful of fields appear in every event type that represents a ledger entry (PIX IN/OUT, refund, QR paid, internal transfer, fee). They are the recommended way to correlate webhooks with other API endpoints:
| Field | Description |
|---|---|
transactionId | Transaction identifier in the format used by our API. Matches what's returned by GET /v1/accounts/{accountId}/statement and by the synchronous response of the endpoint that originated the transaction. Use it to fetch the transaction on our APIs. |
coreId | UUID of the ledger line at the banking partner's core. Same value exposed in the coreId field of the statement; remains stable across webhook and async statement sync. Use it to reconcile with partner exports. |
endToEnd / endToEndId | Central Bank PIX E2E ID (PIX events only). |
status | Transaction state. Standardized vocabulary (see table below). |
The first three fields can coexist in the same payload. If only one is present it means the other isn't relevant for that event type (e.g., internal transfers carry transactionId + coreId but not endToEnd).
data.status values
The status field uses a standardized vocabulary across all events:
| Status | Meaning | Events |
|---|---|---|
SUCCESS | Operation completed successfully; balance debited/credited. | pix.in.completed, pix.out.completed, pix.refund.completed, pix.refund.received, qrcode.paid, boleto.paid, transfer.internal.in, transfer.internal.out, fee.charged, fee.refunded |
FAILED | Operation failed. Balance was not moved (or was returned). Check data.error for details. | pix.out.failed, pix.refund.failed, boleto.failed |
TIMEOUT | Partner call was sent successfully but response timed out. Check the statement before retrying with a new idempotency key. | pix.out.timeout |
EXPIRED | Dynamic QR Code expired without being paid within the configured expiration. | qrcode.expired |
CANCELLED | Dynamic QR Code cancelled by an explicit integrator call (DELETE /qrcodes/{txid}). | qrcode.cancelled |
REVERSED | A previously completed PIX IN that has been reversed (e.g., full chargeback). Appears when late reconciliation overrides an already-delivered PIX IN. | pix.in.completed (rare, post-reconciliation) |
For MED (dispute) events, status uses its own vocabulary:
| Status | Meaning |
|---|---|
OPEN | Dispute opened by the claimant, awaiting response. |
PENDING_DECISION | Response submitted; awaiting decision by the central bank / regulator. |
ACCEPTED | Dispute accepted — funds returned (fully or partially). |
REJECTED | Dispute rejected — funds stay with the payee. |
CANCELED | Dispute canceled by the claimant or regulator. |
Events: pix.med.opened, pix.med.updated.
status guaranteestatus is always present in events that represent a ledger entry. If you receive a webhook missing the field (or with an empty value), it's a purely informational event (e.g., account.balance_updated) — in that case use the type field itself to determine semantics.
Date and time format
Date/time fields use ISO 8601 / RFC 3339 with an explicit offset — read the offset, don't assume. Webhooks (envelope occurredAt and fields inside data such as receivedAt, completedAt, initiatedAt, chargedAt, openedAt) are always UTC (Z). In REST responses, "our-side" fields (createdAt, updatedAt, reconciledAt, fetchedAt, balance) are UTC (Z) too; the transaction time in the statement and lookups — timestamp (/statement, /pix/transactions) and occurredAt (/pix/payments/lookup, boleto, fee.occurredAt) — is in São Paulo time (-03:00).
Examples:
2026-04-29T23:53:55.001Z ← with milliseconds
2026-04-29T23:53:49.328720Z ← with microseconds
2026-04-30T18:04:36Z ← no fractional seconds
We never emit timestamps in Brazilian local time (BRT) nor "naive" timestamps (without timezone indicator). If you ever receive a date field without the Z suffix, treat it as a bug and report it — we will normalize as soon as we identify the source.
Event Types and Contents (data)
1. pix.in.completed
Sent when a PIX is successfully received (inbound) into the account.
data Structure:
endToEnd: Unique transaction ID at the Central Bank.amount: Transaction amount.payer: Sender details (name, document, bank, branch, account).payee: Receiver details (name, document, bank, branch, account).method: How the payer started the PIX —STATIC_QR_CODE,DYNAMIC_QR_CODEorDICT(PIX key or bank details).identifier: When the credit came from a QR Code, this is the txid of the QR that was paid — use it to reconcile with the QR you created.reconciliationId: Reconciliation identifier from the banking partner (when available).receivedAt: Timestamp of receipt.
Full Example:
{
"id": "evt_123456789",
"type": "pix.in.completed",
"occurredAt": "2025-12-29T21:14:33.912Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"endToEnd": "E0000000020251229211433912",
"amount": 150.50,
"method": "STATIC_QR_CODE",
"identifier": "SEV798c4a1f4b2e4d9c8a1b2c3d4",
"reconciliationId": "FAlGf89fN0ol41Wyc5zVQ0k5KD",
"payer": {
"name": "John Smith",
"document": "12345678900",
"bankCode": "001",
"branch": "0001",
"accountNumber": "12345-6"
},
"payee": {
"name": "Test Company",
"document": "12345678000199",
"bankCode": "999",
"branch": "0001",
"accountNumber": "98765-4"
},
"receivedAt": "2025-12-29T21:14:33.900Z"
}
}
2. qrcode.paid
Sent when a QR Code generated by you is paid.
data Structure:
endToEnd: Unique transaction ID at the Central Bank.type: QR Code type (staticordynamic).identifier: txid (identifier) of the QR Code.qrcodeId: Internal QR Code ID.amount: Amount paid.payer: Payer details.payee: Receiver details.reconciliationId: Reconciliation identifier from the banking partner (when available).receivedAt: Timestamp of receipt.
Full Example:
{
"id": "evt_987654321",
"type": "qrcode.paid",
"occurredAt": "2025-12-29T21:15:00.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"endToEnd": "E0000000020251229211500000",
"type": "dynamic",
"identifier": "txid-qr-123",
"qrcodeId": "qr_abc123",
"amount": 250.00,
"reconciliationId": "FAlGf89fN0ol41Wyc5zVQ0k5KD",
"payer": {
"name": "Maria Oliveira",
"document": "98765432100",
"bankCode": "033",
"branch": "0001",
"accountNumber": "54321-0"
},
"payee": {
"name": "Your Company",
"document": "12345678000199",
"bankCode": "999",
"branch": "0001",
"accountNumber": "98765-4"
},
"receivedAt": "2025-12-29T21:14:33.900Z"
}
}
3. pix.out.completed
Sent when an outbound PIX transfer is completed successfully.
data Structure:
endToEnd: End-to-end ID.key: Destination key used.identifier: Transaction identifier.amount: Transferred amount.payer: Your account details.payee: Destination details.reconciliationId: Reconciliation identifier from the banking partner (when available).initiatedAt: When the transfer started.completedAt: When it was confirmed.
Full Example:
{
"id": "evt_out_123",
"type": "pix.out.completed",
"occurredAt": "2025-12-29T21:14:53.900Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"endToEnd": "E9999999920251229211433900",
"key": {
"type": "CPF",
"key": "12345678900"
},
"identifier": "transfer-001",
"amount": 50.00,
"reconciliationId": "FAlGf89fN0ol41Wyc5zVQ0k5KD",
"payer": {
"name": "Your Company",
"document": "12345678000199",
"bankCode": "999",
"branch": "0001",
"accountNumber": "98765-4"
},
"payee": {
"name": "John Smith",
"document": "12345678900",
"bankCode": "001",
"branch": "0001",
"accountNumber": "12345-6"
},
"initiatedAt": "2025-12-29T21:14:33.900Z",
"completedAt": "2025-12-29T21:14:53.900Z"
}
}
4. pix.out.failed
Sent when an outbound PIX transfer fails definitively — the banking partner rejected the operation (insufficient funds, invalid key, anti-fraud, BACEN rejection etc.) and no balance was moved (or it was returned).
pix.out.failed is terminal: once received, no later pix.out.completed will arrive for the same order. Uncertainty scenarios (e.g. communication timeout with the partner, outcome unconfirmed) emit pix.out.timeout — never pix.out.failed.
data Structure:
endToEnd: End-to-end ID (if available).error: Error description/failure reason.key: Destination key.identifier: Transaction identifier.amount: Amount.payer: Your account details.payee: Destination details.reconciliationId: Reconciliation identifier from the banking partner (when available).initiatedAt: Start timestamp.
Full Example:
{
"id": "evt_out_err_123",
"type": "pix.out.failed",
"occurredAt": "2025-12-29T21:14:35.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"endToEnd": "E9999999920251229211433900",
"error": "Insufficient balance in destination account or invalid key",
"key": {
"type": "EMAIL",
"key": "teste@exemplo.com"
},
"identifier": "transfer-002",
"amount": 1000.00,
"payer": {
"name": "Your Company",
"document": "12345678000199",
"bankCode": "999",
"branch": "0001",
"accountNumber": "98765-4"
},
"payee": {
"name": "Failed Recipient",
"document": "00000000000",
"bankCode": "001",
"branch": "0001",
"accountNumber": "00000-0"
},
"initiatedAt": "2025-12-29T21:14:33.900Z"
}
}
5. payment.sent (removed)
This event was deprecated in v1.15.0 (2026-02-20) and has been permanently disabled in v1.28.0 (2026-04-17). Use pix.out.completed with method: PAYMENT and the payment object in the payload instead.
6. payment.refunded (removed)
This event was deprecated in v1.15.0 (2026-02-20) and has been permanently disabled in v1.28.0 (2026-04-17). Use pix.refund.completed which includes originalEndToEnd, refundEndToEnd and party details.
7. pix.refund.completed
Sent when a PIX refund is completed.
data Structure:
originalEndToEnd: ID of the original transaction being refunded.refundEndToEnd: ID of the new refund PIX.identifier: Refund identifier.amount: Refunded amount.payer: Your account details (the one issuing the refund).payee: Destination details (the one receiving the refund).reconciliationId: Reconciliation identifier from the banking partner (when available).initiatedAt: Start timestamp.completedAt: Completion timestamp.
Full Example:
{
"id": "evt_ref_123",
"type": "pix.refund.completed",
"occurredAt": "2025-12-29T21:14:53.900Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"originalEndToEnd": "E0000000020251229211433912",
"refundEndToEnd": "D0000000020251229211453900",
"identifier": "refund-999",
"amount": 150.50,
"payer": {
"name": "Your Company",
"document": "12345678000199",
"bankCode": "999",
"branch": "0001",
"accountNumber": "98765-4"
},
"payee": {
"name": "John Smith",
"document": "12345678900",
"bankCode": "001",
"branch": "0001",
"accountNumber": "12345-6"
},
"initiatedAt": "2025-12-29T21:14:33.900Z",
"completedAt": "2025-12-29T21:14:53.900Z"
}
}
8. pix.refund.failed
Sent when a refund request fails.
data Structure:
originalEndToEnd: Original transaction ID.error: Failure reason.identifier: Identifier.amount: Amount.payer: Your account details.payee: Destination details.initiatedAt: Start timestamp.
Full Example:
{
"id": "evt_ref_err_123",
"type": "pix.refund.failed",
"occurredAt": "2025-12-29T21:14:35.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"originalEndToEnd": "E0000000020251229211433912",
"error": "Original transaction has already been refunded",
"identifier": "refund-998",
"amount": 150.50,
"payer": {
"name": "Your Company",
"document": "12345678000199",
"bankCode": "999",
"branch": "0001",
"accountNumber": "98765-4"
},
"payee": {
"name": "John Smith",
"document": "12345678900",
"bankCode": "001",
"branch": "0001",
"accountNumber": "12345-6"
},
"initiatedAt": "2025-12-29T21:14:33.900Z"
}
}
9. fee.charged
Sent when a banking fee is charged to the account (e.g., per-transaction PIX fee).
data structure:
transactionId: Deterministic ledger row id (fee-{coreId}).accountId/tenantId: Charged account and tenant.amount: Fee amount, always positive in the account currency.description: Fee description.feeServiceType: Underlying service that triggered the fee (PIX,BOLETO, ...) when the bank reports it.originalRef/transactionRef: Cross reference to the originating movement when available.occurredAt: Timestamp at which the bank booked the fee.status: Always"SUCCESS".kind:"CHARGED".
Full Example:
{
"id": "evt_fee_123",
"type": "fee.charged",
"occurredAt": "2026-02-14T20:30:43.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"transactionId": "fee-9c1f...e2",
"accountId": "acc_123456",
"tenantId": "tenant-acme",
"amount": 2.99,
"description": "Banking Fee",
"feeServiceType": "PIX",
"originalRef": "tx_38a1b2",
"transactionRef": "tx_38a1b2",
"occurredAt": "2026-02-14T20:30:43.000Z",
"status": "SUCCESS",
"kind": "CHARGED"
}
}
9.1. fee.refunded
Sent when the bank reverses a previously charged fee (cashback, adjustment). Same shape as fee.charged, with kind="REFUNDED". Funds are already credited to the account.
{
"id": "evt_fee_456",
"type": "fee.refunded",
"occurredAt": "2026-02-15T13:42:11.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"transactionId": "fee-7c2a...11",
"accountId": "acc_123456",
"tenantId": "tenant-acme",
"amount": 2.99,
"description": "Bank fee refund",
"feeServiceType": "PIX",
"originalRef": "fee-9c1f...e2",
"occurredAt": "2026-02-15T13:42:11.000Z",
"status": "SUCCESS",
"kind": "REFUNDED"
}
}
10. pix.med.opened
Sent when a new MED (Special Return Mechanism) dispute is opened against a transaction credited to one of your accounts. This is a notification — disputing via API (answer/decide/evidence) is not available yet.
data Structure:
medId: Dispute identifier (infraction report).originalEndToEnd:endToEndIdof the disputed PIX transaction.amount: Disputed amount.reasonCode: Reported reason (e.g.scam-or-fraud).claimMessage: Free-text details provided by the claimant (when present).openedAtIso: MED opening date (ISO 8601).status: alwaysOPENon this event.
Full Example:
{
"id": "evt_med_123",
"type": "pix.med.opened",
"occurredAt": "2026-07-21T17:55:59.601Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"medId": "c40a8974-4b4c-47c5-9d4b-81376e9071c6",
"originalEndToEnd": "E0000000020251229211433912",
"amount": 150.50,
"reasonCode": "scam-or-fraud",
"claimMessage": "See contacts provided in FundsRecovery",
"openedAtIso": "2026-07-21T17:55:59.601Z",
"status": "OPEN"
}
}
10. pix.med.updated
Sent when a MED (Special Return Mechanism) dispute changes status (resolution, agreement, cancellation). Also a notification only.
data Structure:
medId: Dispute identifier (same aspix.med.opened).originalEndToEnd:endToEndIdof the disputed PIX transaction.amount: Disputed amount.reasonCode: Reported reason.status: Current MED status — dedicated vocabulary (OPEN,PENDING_DECISION,ACCEPTED,REJECTED,CANCELED; see the status table above).
Full Example:
{
"id": "evt_med_456",
"type": "pix.med.updated",
"occurredAt": "2026-07-24T01:15:03.410Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"medId": "c40a8974-4b4c-47c5-9d4b-81376e9071c6",
"originalEndToEnd": "E0000000020251229211433912",
"amount": 150.50,
"reasonCode": "scam-or-fraud",
"status": "CANCELED"
}
}
11. transfer.internal.in
Sent to the receiving account of an internal transfer.
data structure:
transactionId/accountId/tenantId: Local row identification.direction: Always"IN"for this event.amount: Positive value, in BRL.description: Description supplied by the sender (or default).occurredAt: Bank-side timestamp.status: Always"SUCCESS".counterparty: Other leg of the transfer when known.partnerAccountId: Bank-side account identifier.name/document: When the partner reports them.isLocal:truewhen the sender is also a CorpX account.
{
"id": "evt_int_in_001",
"type": "transfer.internal.in",
"occurredAt": "2026-04-23T14:21:05.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "773107de-139e-48d1-9462-f4e88f251891",
"data": {
"transactionId": "internal-in-9d4a5b7c-1234-4abc-9876-abc123456789",
"accountId": "773107de-139e-48d1-9462-f4e88f251891",
"tenantId": "tenant-acme",
"direction": "IN",
"amount": 500.00,
"description": "Salary payment",
"occurredAt": "2026-04-23T14:21:05.000Z",
"status": "SUCCESS",
"counterparty": {
"partnerAccountId": "a1b2c3d4-...",
"name": "EMPRESA ORIGEM LTDA",
"document": "12345678000199",
"isLocal": false
}
}
}
12. transfer.internal.out
Sent to the sending account. Same shape as transfer.internal.in with direction="OUT". When the integrator triggered the transfer through POST /v1/accounts/{accountId}/transfers/internal, the original identifier is echoed back in data.identifier for reconciliation.
{
"id": "evt_int_out_001",
"type": "transfer.internal.out",
"occurredAt": "2026-04-23T14:21:05.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "a1b2c3d4-aaaa-bbbb-cccc-111122223333",
"data": {
"transactionId": "internal-out-9d4a5b7c-1234-4abc-9876-abc123456789",
"accountId": "a1b2c3d4-aaaa-bbbb-cccc-111122223333",
"tenantId": "tenant-acme",
"direction": "OUT",
"amount": 500.00,
"description": "Salary payment",
"occurredAt": "2026-04-23T14:21:05.000Z",
"status": "SUCCESS",
"counterparty": {
"partnerAccountId": "b2c3d4e5-...",
"name": "MARIA SILVA",
"document": "12345678900",
"isLocal": false
}
}
}
13. pix.out.timeout
Sent when the order outcome is indeterminate at the end of the confirmation window: the partner call was dispatched but the response/final confirmation did not arrive in time (this includes communication timeouts on the submit call itself). Not a definitive failure — a late pix.out.completed or pix.out.failed may still arrive afterwards. Check the statement before retrying with a new idempotency key; retrying with the same key is safe.
{
"id": "evt_pix_out_timeout_001",
"type": "pix.out.timeout",
"occurredAt": "2026-05-04T10:42:11.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"paymentId": "pay_9ccd1869-7593-4feb-9602-e525e818ab8e",
"transactionId": "162982-pix",
"accountId": "acc_123456",
"tenantId": "tenant-acme",
"endToEnd": "E0000000020260504104211",
"amount": 250.00,
"currency": "BRL",
"status": "TIMEOUT",
"errorCode": "confirmation_timeout",
"errorReason": "partner response timed out. The call to send the money was accepted, but the final status confirmation has not arrived yet.",
"error": "partner response timed out. The call to send the money was accepted, but the final status confirmation has not arrived yet.",
"warning": "partner response timed out. The call to send the money was accepted, but the final status confirmation has not arrived yet.",
"identifier": "order-12345"
}
}
14. pix.refund.received
Sent when the receiver of a PIX out returns the funds on their own initiative (without us asking). Different from pix.refund.completed, which confirms a refund we triggered.
data structure:
transactionId: Deterministic ID of the received refund row (pix-refund-received-{refundEndToEnd}).refundEndToEnd: Refund D-code (BACEN).originalEndToEnd: E-code of the original PIX out being reversed.originalIdentifier: Identifier of the original PIX out (echo of the value sent at payment creation), when available.accountId/tenantId: Account that received the refund.amount: Reversed amount.description: Description from the partner.receivedAt: Refund timestamp.payer: Party that returned the funds (counterparty / original PIX out recipient): name, document, bank, branch, account.payee: Party that received the credit (our account / original PIX out payer): name, document, bank, branch, account.status: Always"SUCCESS".
{
"id": "evt_pix_refund_recv_001",
"type": "pix.refund.received",
"occurredAt": "2026-05-04T11:11:43.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"transactionId": "pix-refund-received-D2026050420260504...",
"refundEndToEnd": "D2026050420260504000000001",
"originalEndToEnd": "E0000000020260503100000001",
"originalIdentifier": "client-refund-key-001",
"accountId": "acc_123456",
"tenantId": "tenant-acme",
"amount": 320.00,
"description": "PIX refund",
"receivedAt": "2026-05-04T11:11:43.000Z",
"payer": {
"name": "João Silva",
"document": "12345678901",
"bankCode": "341",
"bankIspb": "60701190",
"branch": "0001",
"accountNumber": "12345-6"
},
"payee": {
"name": "Acme Corp LTDA",
"document": "12345678000199",
"bankCode": "681",
"bankIspb": "50871921",
"branch": "0001",
"accountNumber": "98765-4"
},
"status": "SUCCESS"
}
}
15. qrcode.expired
Sent when a dynamic QR Code expires without being paid within the configured expiration.
{
"id": "evt_qrcode_exp_001",
"type": "qrcode.expired",
"occurredAt": "2026-05-04T12:00:00.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"txid": "qr_abc123",
"accountId": "acc_123456",
"tenantId": "tenant-acme",
"amount": 99.90,
"expiredAt": "2026-05-04T12:00:00.000Z",
"status": "EXPIRED"
}
}
16. qrcode.cancelled
Sent when the integrator explicitly cancels a dynamic QR Code via DELETE /v1/qrcodes/{txid} before payment.
{
"id": "evt_qrcode_cancel_001",
"type": "qrcode.cancelled",
"occurredAt": "2026-05-04T11:45:30.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"txid": "qr_abc123",
"accountId": "acc_123456",
"tenantId": "tenant-acme",
"amount": 99.90,
"cancelledAt": "2026-05-04T11:45:30.000Z",
"status": "CANCELLED"
}
}
17. boleto.paid
Sent when a boleto paid via POST /v1/accounts/{accountId}/boleto/pay is confirmed by the issuing bank.
{
"id": "evt_boleto_paid_001",
"type": "boleto.paid",
"occurredAt": "2026-05-04T15:00:11.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"paymentId": "bol_aabbccdd",
"boletoId": "bol_aabbccdd",
"transactionId": "bol_aabbccdd",
"partnerId": "op-ref-99887766",
"accountId": "acc_123456",
"tenantId": "tenant-acme",
"amount": 1234.50,
"line": "00190.00009 03450.000004 47018.500003 1 88880000123450",
"status": "SUCCESS"
}
}
18. boleto.failed
Sent when the issuer rejects the payment. data.error carries the reason.
{
"id": "evt_boleto_failed_001",
"type": "boleto.failed",
"occurredAt": "2026-05-04T15:00:11.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"paymentId": "bol_eeffgghh",
"boletoId": "bol_eeffgghh",
"transactionId": "bol_eeffgghh",
"partnerId": "op-ref-99887766",
"accountId": "acc_123456",
"tenantId": "tenant-acme",
"amount": 1234.50,
"line": "00190.00009 03450.000004 47018.500003 1 88880000123450",
"status": "FAILED",
"error": {
"code": "boleto_expired",
"message": "Expired boleto — please issue a new bill"
}
}
}
19–22. ted.out.* and ted.in.received
TED events follow the same envelope structure as boleto.*. See the TED guide for complete payload examples (ted.out.requested, ted.out.confirmed, ted.out.failed, ted.in.received). ted.in.received includes data.payer when the settlement bank provides originator details; ted.out.failed includes data.errorReason when a rejection reason is available.
Deprecated alias:
ted.paymentis dispatched alongsideted.out.confirmedwith the same payload, kept only for backward compatibility. Will be removed in v3.0.
23. policy.violation
A policy rule configured for your account or tenant was violated. The event covers every policy section — PIX out, PIX in, QR Code, keys, and refunds — and is sent both when the operation is rejected (action: "BLOCK") and when it only produces a warning (NOTIFY_ONLY in monitor mode, ALLOW_AND_NOTIFY and AUTO_REFUND on incoming PIX). Use blocked to tell them apart without interpreting the action.
{
"id": "evt_policy_violation_001",
"type": "policy.violation",
"occurredAt": "2026-05-23T22:41:10.000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-acme",
"accountId": "acc_123456",
"data": {
"phase": "counterparty",
"action": "BLOCK",
"blocked": true,
"paymentId": "pay_9f2c...",
"accountId": "acc_123456",
"identifier": "ORDER-1234",
"mode": "KEY",
"amount": 5000.00,
"violations": [
{ "rule": "cpfCnpjBlacklist", "message": "recipient document is blacklisted by policy" }
],
"counterparty": { "document": "12345678900", "name": "JOHN DOE" }
}
}
| Field | Description |
|---|---|
phase | edge (rejected in the response, HTTP 422), counterparty (after resolving the recipient, during processing), pix_in (after an incoming PIX was credited), or dict_lookup (key lookup rejected by limit) |
action | BLOCK, NOTIFY_ONLY, ALLOW_AND_NOTIFY, or AUTO_REFUND, as configured in the policy |
blocked | true when the operation was actually rejected. Always false for pix_in |
mode | KEY, BANK_ACCOUNT, QRCODE, or REFUND on PIX out; the operation subtype in the other sections (static/dynamic for QR, the key type, the incoming PIX method) |
violations[].rule | PIX out rules carry no prefix (pixOutDisabled, cpfCnpjBlacklist, sameOwnershipOnly, operatingHours, maxAmount, nightMaxAmount, allowedPersonTypes); the other sections are prefixed (pixIn.*, qrCode.*, keys.*, refund.*) or dictLookup.* |
counterparty | The recipient (PIX out) or the payer (PIX in), when known |
When blocked is true on a PIX out, you also receive the matching pix.out.failed, with errorCode: policy_denied. They are two events with distinct ids: this one is the rule warning, that one is the payment outcome.
In monitor mode (NOTIFY_ONLY) the payment goes through normally and will have its own outcome (pix.out.completed, for example) — this event is only a heads-up that the transaction would have been rejected had the rule been set to BLOCK.
Violation on an incoming PIX (phase: "pix_in")
An incoming PIX cannot be refused: the settlement bank credits the account and only then tells us. The violation always comes after the credit, and the payload swaps paymentId for transactionId and endToEnd, adding refunded:
{
"phase": "pix_in",
"action": "AUTO_REFUND",
"blocked": false,
"refunded": true,
"transactionId": "tx_8a1b...",
"accountId": "acc_123456",
"endToEnd": "E18236120202607291230abcdef1234",
"amount": 5000.00,
"mode": "DICT",
"violations": [
{ "rule": "pixIn.cpfCnpjBlacklist", "message": "payer document is blacklisted by policy" }
],
"counterparty": { "document": "12345678900", "name": "JOHN DOE", "bankCode": "260" }
}
pix.in.completed is delivered either way, and before this event. With refunded: true, the refund then produces the usual PIX out events (pix.out.completed or pix.out.failed).
Rule and configuration details are in the Policies and Rules guide.
Best Practices
- Idempotency: Your application should be prepared to receive the same webhook more than once. Use the envelope
idto avoid duplicate processing. - Quick Response: Return a
200 OKstatus as soon as you receive the webhook and process the business logic asynchronously to avoid timeouts. - Timestamp Validation: Check that the
occurredAtis not too old (we recommend a 5-minute tolerance) to prevent replay attacks.