Skip to main content

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, and GET /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.

MethodPathDescription
GET/v1/webhook_subscriptionsList subscriptions (cursor pagination)
POST/v1/webhook_subscriptionsCreate 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_secretRotate the signing secret
POST/v1/webhook_subscriptions/{id}/send_test_eventSend 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"
}
Save the signing secret now

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:

EventFires when
charge.succeededA charge is captured
charge.failedA charge attempt fails
charge.refundedA charge is refunded (full or partial)
refund.failedA 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.succeededA payment intent reaches succeeded
payment_intent.failedA payment intent fails
payment_intent.cancelledA 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:

statusBehavior
activeReceives deliveries normally.
pausedStops deliveries; config + signing secret retained. Resume by patching back to active.
disabledTwo 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):

FieldTypeNotes
vp_tx_idstring | nullThe 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_idstring | nullThe 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_idstring | nullIdentifies which refund in a multi-partial sequence (vpr_*)
amountnumber⚠️ 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_amountnumber | nullThis refund only, unambiguously, on every connection — use this one when you need a per-refund figure. null means the connection could not derive it.
currencystringISO-4217, uppercase
reasonstring | nullOne of customer_request, duplicate, fraudulent, other
is_partialboolean | nullTrue when amount < original_charge_amount
original_charge_amountnumber | nullFull charge total before refund — compute remaining refundable balance from this
session_id, payment_intent_idstring | nullSource 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​

EndpointLimit
POST /v1/sessions30/min per API key; per IP, 10/min with a publishable key or 300/min with a secret key
GET /v1/sessions/:id30/min per IP
POST /api/checkout/init, /api/checkout/complete20/min per IP
POST /api/webhooks/* (inbound provider)100/min per IP
GET /v1/webhook_subscriptions, /v1/webhook_subscriptions/:id, /v1/webhook_events/:id100/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.