Skip to main content

Webhooks

ONVO uses webhooks to notify your application when a result is produced while processing a payment intent, subscription, checkout session, or incoming transfer.

To receive them, configure a callback URL in your ONVO account. ONVO sends a POST request to that URL whenever a supported event is processed. The payload includes a type attribute that identifies the event and a data object with the related information.

  1. Expose an HTTPS endpoint.
  2. Register the endpoint in the ONVO Dashboard under developers.
  3. Verify the event origin with the webhook secret.
  4. Process the event idempotently.
  5. Respond with a 2xx status when processing succeeds.

Supported events

EventWhen it happens
payment-intent.succeededA payment intent is processed successfully.
payment-intent.failedA payment intent fails.
payment-intent.deferredA payment intent is waiting for approval, for example in a SINPE flow.
subscription.renewal.succeededA subscription renews successfully.
subscription.renewal.failedA subscription renewal fails.
checkout-session.succeededA checkout session is processed successfully.
mobile-transfer.receivedAn incoming transfer is received on a custom SINPE Movil number.

mobile-transfer.received is an additional notification for merchants with this capability enabled. It does not represent a successful payment by itself and is not sent by default. Contact support to enable it.

Payload format

ONVO sends every webhook as a POST request with Content-Type: application/json. The body always has this structure:

{
"type": "payment-intent.succeeded",
"data": {
"id": "clpiment0001"
}
}
FieldTypeDescription
typestringName of the event received. Use it to decide which logic to run.
dataobjectSnapshot of the object related to the event. Its shape depends on type.

Amounts are sent as integers in the smallest currency unit. For example, 500000 represents ₡5,000.00 in CRC and 5000 represents $50.00 in USD. Dates are sent as ISO 8601 timestamps in UTC. The examples show the most important fields for integrations; depending on the payment method or flow, data can include additional fields or null values.

Payloads by event

payment-intent.succeeded

data contains the updated payment intent. When available, it includes the customer and the list of charges generated for that intent.

{
"type": "payment-intent.succeeded",
"data": {
"id": "clpiment0001",
"accountId": "clacct0001",
"mode": "test",
"amount": 500000,
"baseAmount": 500000,
"baseExchangeRate": 1,
"capturableAmount": 0,
"receivedAmount": 500000,
"currency": "CRC",
"status": "succeeded",
"confirmationAttempts": 1,
"description": "Order #1001",
"paymentMethodId": "clpm0001",
"customerId": "clcus0001",
"metadata": {
"orderId": "order_1001"
},
"customer": {
"id": "clcus0001",
"name": "Ana Mora",
"email": "ana@example.com",
"phone": "+50688888888"
},
"charges": [
{
"id": "clch0001",
"amount": 500000,
"baseAmount": 500000,
"baseExchangeRate": 1,
"refNumber": "123456",
"mode": "test",
"currency": "CRC",
"status": "succeeded",
"failureCode": null,
"failureMessage": null,
"isApproved": true,
"isCaptured": true,
"createdAt": "2026-04-29T16:10:00.000Z",
"updatedAt": "2026-04-29T16:10:01.000Z"
}
],
"lastPaymentError": null,
"createdAt": "2026-04-29T16:09:50.000Z",
"updatedAt": "2026-04-29T16:10:01.000Z"
}
}

payment-intent.failed

data contains the payment intent and an error object with the failure reason.

{
"type": "payment-intent.failed",
"data": {
"id": "clpiment0002",
"accountId": "clacct0001",
"currency": "CRC",
"capturableAmount": 0,
"status": "requires_payment_method",
"metadata": {
"orderId": "order_1002"
},
"customer": {
"id": "clcus0002",
"name": "Luis Vega",
"email": "luis@example.com",
"phone": "+50688887777"
},
"error": {
"id": "clerr0001",
"mode": "test",
"paymentMethodType": "card",
"type": "processing_error",
"code": "declined",
"message": "Transaction declined due to test payment method used",
"createdAt": "2026-04-29T16:12:00.000Z",
"updatedAt": "2026-04-29T16:12:00.000Z"
}
}
}

payment-intent.deferred

data contains the payment intent while it is waiting for external confirmation, for example when ONVO is waiting for a transfer confirmation.

{
"type": "payment-intent.deferred",
"data": {
"id": "clpiment0003",
"accountId": "clacct0001",
"mode": "test",
"amount": 250000,
"currency": "CRC",
"status": "processing",
"paymentMethodId": "clpm0003",
"customerId": "clcus0003",
"metadata": {
"orderId": "order_1003"
},
"createdAt": "2026-04-29T16:13:00.000Z",
"updatedAt": "2026-04-29T16:13:02.000Z"
}
}

subscription.renewal.succeeded

data contains the renewal associated with the charged period. Each renewal is tied to a subscription and to the payment intent created for that period.

{
"type": "subscription.renewal.succeeded",
"data": {
"id": "clinv0001",
"accountId": "clacct0001",
"mode": "test",
"status": "paid",
"currency": "CRC",
"attemptCount": 1,
"attempted": true,
"description": "Monthly plan",
"total": 1200000,
"subTotal": 1200000,
"originalTotal": null,
"periodStart": "2026-04-01T00:00:00.000Z",
"periodEnd": "2026-05-01T00:00:00.000Z",
"paymentIntentId": "clpiment0004",
"subscriptionId": "clsub0001",
"customerId": "clcus0004",
"metadata": {
"plan": "pro"
},
"createdAt": "2026-04-01T00:00:00.000Z",
"updatedAt": "2026-04-01T00:00:05.000Z"
}
}

subscription.renewal.failed

data contains the renewal status, the subscription status, the next retry date, and the error returned while processing the payment intent.

{
"type": "subscription.renewal.failed",
"data": {
"subscriptionId": "clsub0002",
"paymentIntentId": "clpiment0005",
"currency": "CRC",
"lastPaymentAttempt": "2026-04-29T16:20:00.000Z",
"attemptCount": 2,
"invoiceStatus": "open",
"subscriptionStatus": "past_due",
"metadata": {
"accountTier": "pro"
},
"invoiceMetadata": {
"period": "2026-04"
},
"invoicePeriodStart": "2026-04-01T00:00:00.000Z",
"invoicePeriodEnd": "2026-05-01T00:00:00.000Z",
"currentPeriodStart": "2026-04-01T00:00:00.000Z",
"currentPeriodEnd": "2026-05-01T00:00:00.000Z",
"nextPaymentAttempt": "2026-04-30T16:20:00.000Z",
"customer": {
"id": "clcus0005",
"name": "María Solís",
"email": "maria@example.com",
"phone": "+50688886666"
},
"error": {
"id": "clerr0002",
"mode": "test",
"paymentMethodType": "card",
"type": "processing_error",
"code": "declined",
"message": "Transaction declined due to test payment method used",
"createdAt": "2026-04-29T16:20:00.000Z",
"updatedAt": "2026-04-29T16:20:00.000Z"
}
}
}

checkout-session.succeeded

data contains the completed Checkout Session, its line items, and the customer from the associated payment intent.

{
"type": "checkout-session.succeeded",
"data": {
"id": "clcs0001",
"url": "https://checkout.onvopay.com/pay/clcs0001",
"mode": "test",
"status": "complete",
"paymentStatus": "paid",
"paymentMode": "payment",
"amountSubTotal": 350000,
"amountTotal": 350000,
"currency": "CRC",
"successUrl": "https://example.com/success",
"cancelUrl": "https://example.com/cancel",
"paymentIntentId": "clpiment0006",
"customerId": "clcus0006",
"metadata": {
"cartId": "cart_123"
},
"customer": {
"id": "clcus0006",
"name": "Sofía Rojas",
"email": "sofia@example.com",
"phone": "+50688885555"
},
"lineItems": [
{
"name": "Test product",
"description": "Product description",
"currency": "CRC",
"amount": 350000,
"priceId": "clprice0001"
}
],
"createdAt": "2026-04-29T16:25:00.000Z",
"updatedAt": "2026-04-29T16:25:10.000Z"
}
}

mobile-transfer.received

data contains the incoming transfer information. This event does not confirm a payment intent by itself.

{
"type": "mobile-transfer.received",
"data": {
"amount": 1450000,
"currency": "CRC",
"description": "PAGO DE SERVICIOS",
"SINPERefNumber": "2025110312774577852010",
"authorizationDate": "2026-04-29T16:30:00.000Z",
"originId": "01-1393-1919",
"originName": "JUAN PEREZ CASTRO",
"originPhone": "72940567"
}
}

Security

Each webhook has an assigned secret. You can view it in the ONVO Dashboard next to the webhook configuration.

ONVO includes that value in the X-Webhook-Secret header. The backend generates secrets with the webhook_secret_ prefix. Use it to verify that the request comes from ONVO before processing the event.

X-Webhook-Secret: webhook_secret_...

Responses and errors

When an event represents an error, the payload object can include an error attribute. That object can contain:

FieldDescription
messageHuman-readable error description.
codeError code, when available.
typeError type, when available.

Your endpoint should respond with a 2xx status only when the event was received and processed correctly. If you respond with another status, ONVO records the delivery as failed.

Your system should tolerate events received out of order.