REST API
For developers not using Node.js. Call the API directly with any HTTP client.
Base URL
https://checkout.vonpay.com
Authentication
All merchant-facing endpoints require a Bearer token:
Authorization: Bearer vp_sk_live_xxx
Endpoints
Create Session
curl -X POST https://checkout.vonpay.com/v1/sessions \
-H "Authorization: Bearer vp_sk_live_xxx" \
-H "Content-Type: application/json" \
-H "Von-Pay-Version: 2026-04-14" \
-H "Idempotency-Key: unique_key_123" \
-d '{
"amount": 1499,
"currency": "USD",
"country": "US",
"successUrl": "https://mystore.com/confirm",
"lineItems": [{"name": "Widget", "quantity": 1, "unitAmount": 1499}]
}'
Response (201):
{
"id": "vp_cs_live_k7x9m2n4p3",
"checkoutUrl": "https://checkout.vonpay.com/checkout?session=vp_cs_live_k7x9m2n4p3",
"expiresAt": "2026-03-31T15:30:00.000Z"
}
Get Session Status
curl https://checkout.vonpay.com/v1/sessions/vp_cs_live_k7x9m2n4p3 \
-H "Authorization: Bearer vp_sk_live_xxx" \
-H "Von-Pay-Version: 2026-04-14"
Health Check
curl https://checkout.vonpay.com/api/health
No authentication required.
The full server-side resource set —
POST /v1/payment_intents(+/capture,/void),POST /v1/refunds,POST /v1/tokens, andGET /v1/capabilities— is documented per-resource in API Reference, Payment Intents, Refunds, Tokens, and Capabilities, and in full in the OpenAPI spec. They're all plain HTTP with the same Bearer-token auth shown above.
Webhook Subscriptions
Programmatically manage your webhook endpoints. Secret key only (vp_sk_*) — publishable keys receive 403. Reads are limited to 100/min per key; writes (create / update / delete / rotate / test) to 30/min per key.
| Method | Path | Description |
|---|---|---|
GET | /v1/webhook_subscriptions | List subscriptions (cursor pagination) |
POST | /v1/webhook_subscriptions | Create a subscription |
GET | /v1/webhook_subscriptions/{id} | Retrieve a subscription |
PATCH | /v1/webhook_subscriptions/{id} | Update a subscription |
DELETE | /v1/webhook_subscriptions/{id} | Delete a subscription |
POST | /v1/webhook_subscriptions/{id}/rotate_signing_secret | Rotate the signing secret |
POST | /v1/webhook_subscriptions/{id}/send_test_event | Send a signed test event |
GET | /v1/webhook_events/{id} | Retrieve a stored processor event, by its Events-view id |
This management API is camelCase end to end (enabledEvents, signingSecret, lastDeliveryAt); the delivered event payload is snake_case (see below). Cross-merchant or cross-mode access returns an opaque 404 (never 403).
Create a subscription
curl -X POST https://checkout.vonpay.com/v1/webhook_subscriptions \
-H "Authorization: Bearer vp_sk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"url": "https://mystore.com/webhooks/vonpay",
"enabledEvents": ["charge.succeeded", "charge.refunded"],
"description": "Order fulfillment"
}'
Response (201) — includes the signing secret, returned only once:
{
"id": "b6f2c1a0-3e4d-4c8b-9a1f-2d5e7c9b0a13",
"object": "webhook_subscription",
"url": "https://mystore.com/webhooks/vonpay",
"enabledEvents": ["charge.succeeded", "charge.refunded"],
"status": "active",
"description": "Order fulfillment",
"signingSecret": "whsec_3f9a2b...",
"apiVersion": "2026-04-14",
"lastDeliveryAt": null,
"lastSuccessAt": null,
"lastErrorAt": null,
"createdAt": "2026-06-18T12:00:00.000Z"
}
signingSecret (the whsec_… value) is returned only on create and on rotate_signing_secret — never on reads. Store it immediately; if you lose it, rotate to get a new one. You need it to verify the x-vonpay-signature header on every delivery.
enabledEvents must name events from the catalog. A list containing any unrecognised name (session.expired, which does not exist, or a misspelling) is refused with 400 validation_error, even when the other names are valid, and nothing is stored. The same applies to PATCH.
Selectable events
The most commonly used values for enabledEvents. Webhook Events is the complete list — including connected-platform order mirroring, which is not shown here:
| Event | Fires when |
|---|---|
charge.succeeded | A charge is captured |
charge.failed | A charge attempt fails |
charge.refunded | A charge is refunded (full or partial) |
refund.failed | A refund did not complete — no money went back to the buyer. Delivered to every active subscription whether or not you selected it, but not every processor connection emits it, so never make it your only detector for a failed refund; reconcile an unresolved refund from your own ledger. See Refunds |
payment_intent.succeeded | A payment intent reaches succeeded |
payment_intent.failed | A payment intent fails |
payment_intent.cancelled | A payment intent is voided / cancelled |
Two other families are modeled in the SDK's WebhookEvent type so your handler can switch on type forward-compatibly. dispute.* (created / won / lost) is delivered to every active subscription automatically (as is refund.failed, above): you do not list it in enabledEvents, and your endpoint receives it even if it only registered for charge.*. session.succeeded and session.failed are subscribable like the rest; see Session events. Always handle an unrecognized type gracefully: return 200 and log.
Update, rotate, delete
# Pause without deleting (stops deliveries; keeps the config + secret)
curl -X PATCH https://checkout.vonpay.com/v1/webhook_subscriptions/b6f2c1a0-3e4d-4c8b-9a1f-2d5e7c9b0a13 \
-H "Authorization: Bearer vp_sk_live_xxx" -H "Content-Type: application/json" \
-d '{"status": "paused"}'
# Rotate the signing secret (returns a new whsec_ once)
curl -X POST https://checkout.vonpay.com/v1/webhook_subscriptions/b6f2c1a0-3e4d-4c8b-9a1f-2d5e7c9b0a13/rotate_signing_secret \
-H "Authorization: Bearer vp_sk_live_xxx"
# Delete
curl -X DELETE https://checkout.vonpay.com/v1/webhook_subscriptions/b6f2c1a0-3e4d-4c8b-9a1f-2d5e7c9b0a13 \
-H "Authorization: Bearer vp_sk_live_xxx"
PATCH accepts any of url, enabledEvents, description, and status:
status | Behavior |
|---|---|
active | Receives deliveries normally. |
paused | Stops deliveries; config + signing secret retained. Resume by patching back to active. |
disabled | Two states share this value, and only one is recoverable. Your endpoint returned 410 Gone on a delivery: we stop sending and set this status, but the subscription still exists — fix the endpoint and patch status back to active. You called DELETE: the record is soft-deleted, every subsequent call returns 404, and the signing secret is never reissued, so create a fresh endpoint instead. If a patch to active returns 404, it was deleted rather than auto-disabled. You cannot set disabled yourself — PATCH accepts active and paused only. |
Send a test event
curl -X POST https://checkout.vonpay.com/v1/webhook_subscriptions/b6f2c1a0-3e4d-4c8b-9a1f-2d5e7c9b0a13/send_test_event \
-H "Authorization: Bearer vp_sk_live_xxx" -H "Content-Type: application/json" \
-d '{"eventType": "charge.succeeded"}'
The response is synchronous — it reports exactly what your endpoint returned to the signed test delivery:
{
"delivered": true,
"response_status": 200,
"delivery_attempt_id": "vp_wda_test_...",
"signature_preview": "t=1749000000",
"error": null
}
delivered: false with a non-null response_status means your endpoint was reached and returned a non-2xx — useful for confirming your error handling, not a failure of the test itself.
Two failures look alike and need opposite handling. A 400 validation_error means the test was refused for the event type you named (the message is the generic "verify the URL and that every event type is a recognized event"); retrying the same request will not change it, so check the name against the catalog or choose another event type. A 502 webhook_test_delivery_failed means the test could not be delivered this time; retry shortly.
Refund event payload
charge.refunded delivers the following under data (snake_case):
| Field | Type | Notes |
|---|---|---|
vp_tx_id | string | null | The Von transaction id of the refunded charge (vp_tx_*), the same value its charge.succeeded carried and your dashboard shows. Match the refund to the charge on this. |
transaction_id | string | null | The card processor's reference for the refunded charge, not the vp_tx_* settlement id. It correlates events with each other; see the webhook event reference. |
refund_id | string | null | Identifies which refund in a multi-partial sequence (vpr_*) |
amount | number | ⚠️ Meaning varies by processor connection — do not sum it across refunds. On some it is this refund; on others it is the running total refunded so far. It is not the original charge total. |
refund_amount | number | null | This refund only, unambiguously, on every connection — use this one when you need a per-refund figure. null means the connection could not derive it. |
currency | string | ISO-4217, uppercase |
reason | string | null | One of customer_request, duplicate, fraudulent, other |
is_partial | boolean | null | True when amount < original_charge_amount |
original_charge_amount | number | null | Full charge total before refund — compute remaining refundable balance from this |
session_id, payment_intent_id | string | null | Source ids; null on flows where they don't apply |
The subscription's apiVersion (pinned at create time) governs the delivered payload shape; the event envelope itself carries no per-event version field. See Webhook events for every event's payload and Signature verification for the x-vonpay-signature contract.
Rate Limits
| Endpoint | Limit |
|---|---|
POST /v1/sessions | 30/min per API key; per IP, 10/min with a publishable key or 300/min with a secret key |
GET /v1/sessions/:id | 30/min per IP |
POST /api/checkout/init, /api/checkout/complete | 20/min per IP |
POST /api/webhooks/* (inbound provider) | 100/min per IP |
GET /v1/webhook_subscriptions, /v1/webhook_subscriptions/:id, /v1/webhook_events/:id | 100/min per API key |
POST/PATCH/DELETE /v1/webhook_subscriptions/* (create / update / delete / rotate / test) | 30/min per API key |
See Rate Limits for the full bucket list.
Rate-limited responses return 429 with Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers.
Error Format
All errors return JSON with a flat envelope. Every error also carries a selfHeal object — machine-readable retry guidance for SDKs and agents:
{
"error": "Human-readable error message",
"code": "validation_invalid_amount",
"fix": "Amount must be a positive integer in minor units (cents). 1499 = $14.99",
"docs": "https://docs.vonpay.com/integration/create-session#amount-format",
"selfHeal": {
"retryable": false,
"nextAction": "fix_request",
"llmHint": "Machine-readable guidance for SDKs and agents."
}
}
Every response includes an X-Request-Id header for debugging. See Error Codes for the full envelope and the selfHeal contract.
OpenAPI Spec
The full API specification is available at checkout.vonpay.com/openapi.yaml. Import it into Postman, Insomnia, or any OpenAPI-compatible tool.