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 setPOST /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 delivered event record

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

Selectable events

The event types you can pass in enabledEvents:

EventFires when
charge.succeededA charge is captured
charge.failedA charge attempt fails
charge.refundedA charge is refunded (full or partial)
payment_intent.succeededA payment intent reaches succeeded
payment_intent.failedA payment intent fails
payment_intent.cancelledA payment intent is voided / cancelled

Other event types (session.*, dispute.*, application.*, payout.*, merchant.ready_for_payments) are delivered by Von Payments as platform-level events — they are not selectable via enabledEvents (you can't turn them on or off), but your endpoint may still receive them. The SDK's WebhookEvent type models them so your handler can switch on type. Always handle an unrecognized type gracefully.

Update, rotate, delete

# Pause without deleting (stops deliveries; keeps the config + secret)
curl -X PATCH https://checkout.vonpay.com/v1/webhook_subscriptions/wsub_abc123 \
-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/wsub_abc123/rotate_signing_secret \
-H "Authorization: Bearer vp_sk_live_xxx"

# Delete
curl -X DELETE https://checkout.vonpay.com/v1/webhook_subscriptions/wsub_abc123 \
-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.
disabledSystem-suspended (e.g. after sustained failures / auto-pause). Resume by patching to active once your endpoint is healthy.

Send a test event

curl -X POST https://checkout.vonpay.com/v1/webhook_subscriptions/wsub_abc123/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.

Refund event payload

charge.refunded delivers the following under data (snake_case):

FieldTypeNotes
transaction_idstringSettlement-ledger id of the original charge (vp_tx_*)
refund_idstring | nullIdentifies which refund in a multi-partial sequence (vpr_*)
amountnumberRefund amount in minor units — not the original charge total
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/sessions10/min per IP, 30/min per API 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#required-fields",
"selfHeal": {
"retryable": false,
"nextAction": "no_action",
"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.