Skip to main content

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).

Webhook Configuration

Webhooks must be configured through the Integrator Portal.

List Available Events

curl -X GET "https://api.corpxapi.com/v1/webhooks/events" \
-H "Authorization: Bearer YOUR_TOKEN"

Response:

{
"eventTypes": [
"pix.in.completed",
"pix.out.completed",
"pix.out.failed",
"qrcode.paid",
"pix.refund.completed",
"pix.refund.failed",
"pix.med.opened",
"pix.med.updated",
"transfer.internal.in",
"transfer.internal.out",
"account.balance_updated",
"account.lock_created",
"account.lock_released",
"accreditation.pf.created",
"accreditation.pj.created",
"edi.batch",
"ted.payment"
]
}

Receiving Flow

  1. An event occurs on our platform.
  2. We send a POST request to the registered URLs.
  3. Your application must process the notification and return a 2xx status.

Configuration Flexibility

Our webhook infrastructure supports various delivery methods:

  • Grouping: You can receive multiple event types (e.g., pix.in.completed and pix.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

MethodauthType valueDescription
HMAC SignatureHMACSigns each request body with your secret using HMAC-SHA256. The signature is sent in the X-Signature header. Recommended.
API KeyAPI_KEYSends a static API key in a configurable HTTP header (default: X-API-Key).
Basic AuthBASICSends username:password in the standard Authorization: Basic ... header.
Bearer TokenBEARERSends a token in the Authorization: Bearer ... header.
NoneNONENo 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.

caution

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://api.corpxapi.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:

HeaderDescription
x-hookdeck-event-idDelivery event ID (useful for debugging and support requests).
x-hookdeck-request-idOriginal request ID.
x-hookdeck-attempt-countDelivery 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.223
  • 34.138.161.100
  • 35.231.250.193
  • 35.196.71.29
  • 34.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 event will be marked as failed. You can request a manual resend using the Replay endpoint in our API.

Resending Events (Replay)

If you need to resend an event (due to processing failure or lost notification), use the /v1/webhooks/replay endpoint.

Request Example:

curl -X POST "https://api.corpxapi.com/v1/webhooks/replay" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "X-Tenant-Id: your-tenant-id" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"eventId": "evt_abc_123",
"tenantId": "your-tenant-id"
}'

The API will return a 202 Accepted status and the event will be queued for a new delivery attempt.

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",
"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:

FieldDescription
transactionIdTransaction 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.
coreIdUUID 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 / endToEndIdCentral Bank PIX E2E ID (PIX events only).
statusTransaction 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:

StatusMeaningEvents
SUCCESSOperation completed successfully; balance debited/credited.pix.in.completed, pix.out.completed, pix.refund.completed, qrcode.paid, transfer.internal.in, transfer.internal.out, fee.charged, fee.refunded
FAILEDOperation failed. Balance was not moved (or was returned). Check data.error for details.pix.out.failed, pix.refund.failed
REVERSEDA 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:

StatusMeaning
OPENDispute opened by the claimant, awaiting response.
PENDING_DECISIONResponse submitted; awaiting decision by the central bank / regulator.
ACCEPTEDDispute accepted — funds returned (fully or partially).
REJECTEDDispute rejected — funds stay with the payee.
CANCELEDDispute canceled by the claimant or regulator.

Events: pix.med.opened, pix.med.updated.

status guarantee

status 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

All date/time fields returned by the API and webhooks follow ISO 8601 / RFC 3339 in UTC with the Z suffix. This applies to the envelope (occurredAt) and to every field inside data (receivedAt, completedAt, initiatedAt, chargedAt, openedAt, etc.) — as well as createdAt/updatedAt fields returned by REST endpoints (/statement, /balance, /payments, etc.).

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.
  • currency: Currency (e.g., "BRL").
  • payer: Sender details (name, document, bank, branch, account).
  • payee: Receiver details (name, document, bank, branch, account).
  • 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",
"accountId": "acc_123456",
"data": {
"endToEnd": "E0000000020251229211433912",
"amount": 150.50,
"currency": "BRL",
"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 (static or dynamic).
  • identifier: txid (identifier) of the QR Code.
  • qrcodeId: Internal QR Code ID.
  • amount: Amount paid.
  • currency: Currency.
  • 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",
"accountId": "acc_123456",
"data": {
"endToEnd": "E0000000020251229211500000",
"type": "dynamic",
"identifier": "txid-qr-123",
"qrcodeId": "qr_abc123",
"amount": 250.00,
"currency": "BRL",
"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",
"accountId": "acc_123456",
"data": {
"endToEnd": "E9999999920251229211433900",
"key": {
"type": "CPF",
"key": "12345678900"
},
"identifier": "transfer-001",
"amount": 50.00,
"currency": "BRL",
"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.

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",
"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,
"currency": "BRL",
"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)

Removed in v1.28.0 (2026-04-17)

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)

Removed in v1.28.0 (2026-04-17)

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",
"accountId": "acc_123456",
"data": {
"originalEndToEnd": "E0000000020251229211433912",
"refundEndToEnd": "D0000000020251229211453900",
"identifier": "refund-999",
"amount": 150.50,
"currency": "BRL",
"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",
"accountId": "acc_123456",
"data": {
"originalEndToEnd": "E0000000020251229211433912",
"error": "Original transaction has already been refunded",
"identifier": "refund-998",
"amount": 150.50,
"currency": "BRL",
"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:

  • amount: Fee amount charged.
  • currency: Currency.
  • feeServiceType: Type of operation that triggered the fee (e.g., PIX_OUT, PIX_IN).
  • description: Fee description.
  • chargedAt: Timestamp of the charge.

Full Example:

{
"id": "evt_fee_123",
"type": "fee.charged",
"occurredAt": "2026-02-14T20:30:43.000Z",
"schemaVersion": "1.0",
"environment": "production",
"accountId": "acc_123456",
"data": {
"amount": 2.99,
"currency": "BRL",
"feeServiceType": "PIX_OUT",
"description": "Tarifa Bancária",
"chargedAt": "2026-02-14T20:30:43.000Z"
}
}

10. pix.med.opened

Sent when a new MED (Special Return Mechanism) dispute is opened against a transaction.

data Structure:

  • endToEndId: Transaction ID in dispute.
  • identifier: Associated identifier.
  • qrcodeId: QR Code ID (if applicable).
  • amount: Disputed amount.
  • currency: Currency.
  • claimant: Details of the person who filed the claim (name, document, bank, branch, account).
  • openedAt: MED opening date.
  • deadlineAt: Response deadline date.

Full Example:

{
"id": "evt_med_123",
"type": "pix.med.opened",
"occurredAt": "2025-12-29T21:14:33.912Z",
"schemaVersion": "1.0",
"environment": "production",
"accountId": "acc_123456",
"data": {
"endToEndId": "E0000000020251229211433912",
"identifier": "tx-med-001",
"qrcodeId": "qr_abc123",
"amount": 150.50,
"currency": "BRL",
"claimant": {
"name": "John Smith",
"document": "12345678900",
"bankCode": "001",
"branch": "0001",
"accountNumber": "12345-6"
},
"openedAt": "2025-12-29T21:14:33.900Z",
"deadlineAt": "2026-01-05T21:14:33.900Z"
}
}

10. pix.med.updated

Sent when a MED (Special Return Mechanism) dispute is updated (status changed, new information, resolution).

data Structure:

  • endToEndId: Transaction ID in dispute.
  • identifier: Associated identifier.
  • qrcodeId: QR Code ID (if applicable).
  • amount: Disputed amount.
  • currency: Currency.
  • status: Current MED status (OPEN, CLOSED, AGREED, DISAGREED).
  • result: Dispute result (if resolved).
  • claimant: Details of the person who filed the claim.
  • openedAt: MED opening date.
  • updatedAt: Last update date.

Full Example:

{
"id": "evt_med_456",
"type": "pix.med.updated",
"occurredAt": "2025-12-30T15:20:00.000Z",
"schemaVersion": "1.0",
"environment": "production",
"accountId": "acc_123456",
"data": {
"endToEndId": "E0000000020251229211433912",
"identifier": "tx-med-001",
"qrcodeId": "qr_abc123",
"amount": 150.50,
"currency": "BRL",
"status": "CLOSED",
"result": "AGREED",
"claimant": {
"name": "John Smith",
"document": "12345678900",
"bankCode": "001",
"branch": "0001",
"accountNumber": "12345-6"
},
"openedAt": "2025-12-29T21:14:33.900Z",
"updatedAt": "2025-12-30T15:20:00.000Z"
}
}

Best Practices

  • Idempotency: Your application should be prepared to receive the same webhook more than once. Use the envelope id to avoid duplicate processing.
  • Quick Response: Return a 200 OK status as soon as you receive the webhook and process the business logic asynchronously to avoid timeouts.
  • Timestamp Validation: Check that the occurredAt is not too old (we recommend a 5-minute tolerance) to prevent replay attacks.