curl -X POST https://sandbox.api.prava.space/v1/sessions \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"user_id": "user_123",
"user_email": "jane@example.com",
"total_amount": "49.99",
"currency": "USD",
"purchase_context": {
"custom": [{
"merchant_details": { "name": "Acme Store", "url": "https://acme.example.com", "country_code_iso2": "US" },
"product_details": [{ "description": "Widget", "unit_price": "24.99", "quantity": 2 }]
}]
},
"integration_type": "full_checkout",
"callback_url": "https://acme.example.com/return"
}'
curl -X POST https://sandbox.api.prava.space/v1/sessions \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"user_id": "user_123",
"user_email": "jane@example.com",
"total_amount": "22.45",
"currency": "USD",
"purchase_context": {
"quote": true,
"quote_id": "qte_9f2c..."
}
}'
import requests
resp = requests.post(
"https://sandbox.api.prava.space/v1/sessions",
headers={"Authorization": "Bearer sk_test_..."},
json={
"user_id": "user_123",
"user_email": "jane@example.com",
"total_amount": "49.99",
"currency": "USD",
"purchase_context": {
"custom": [{
"merchant_details": {"name": "Acme Store", "url": "https://acme.example.com", "country_code_iso2": "US"},
"product_details": [{"description": "Widget", "unit_price": "24.99", "quantity": 2}],
}],
},
"integration_type": "full_checkout",
"callback_url": "https://acme.example.com/return",
},
)
const resp = await fetch("https://sandbox.api.prava.space/v1/sessions", {
method: "POST",
headers: {
Authorization: "Bearer sk_test_...",
"Content-Type": "application/json",
},
body: JSON.stringify({
user_id: "user_123",
user_email: "jane@example.com",
total_amount: "49.99",
currency: "USD",
purchase_context: {
custom: [{
merchant_details: { name: "Acme Store", url: "https://acme.example.com", country_code_iso2: "US" },
product_details: [{ description: "Widget", unit_price: "24.99", quantity: 2 }],
}],
},
integration_type: "full_checkout",
callback_url: "https://acme.example.com/return",
}),
});
curl -X POST https://sandbox.api.prava.space/v1/sessions \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"user_id": "user_123",
"user_email": "jane@example.com",
"total_amount": "40.00",
"currency": "USD",
"purchase_context": {
"custom": [{
"merchant_details": { "name": "Acme Store", "url": "https://acme.example.com", "country_code_iso2": "US" },
"product_details": [{ "description": "Monthly plan", "unit_price": "40.00" }]
}]
},
"mandate_setup": {
"intent": "mandate_setup",
"recurring_frequency": "monthly",
"merchant_scope": "listed",
"max_charges": 12
}
}'
{
"session_id": "ses_9f2c...",
"session_token": "eyJhbGciOi...",
"iframe_url": "https://checkout.prava.space/s/ses_9f2c...",
"order_id": "ord_4a1b...",
"expires_at": "2026-07-26T12:15:00Z"
}
{
"session_id": "ses_7d1a...",
"session_token": "eyJhbGciOi...",
"iframe_url": "https://checkout.prava.space/s/ses_7d1a...",
"order_id": "ord_88c2...",
"expires_at": "2026-07-26T12:15:00Z"
}
{
"error": {
"code": "VAL_2001",
"message": "Validation failed",
"details": { "currency": "Unsupported currency code" }
}
}
{
"error": { "code": "AUTH_1001", "message": "Invalid API key" }
}
Create Session
Create a payment session for a customer and order — standard checkout or mandate setup.
curl -X POST https://sandbox.api.prava.space/v1/sessions \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"user_id": "user_123",
"user_email": "jane@example.com",
"total_amount": "49.99",
"currency": "USD",
"purchase_context": {
"custom": [{
"merchant_details": { "name": "Acme Store", "url": "https://acme.example.com", "country_code_iso2": "US" },
"product_details": [{ "description": "Widget", "unit_price": "24.99", "quantity": 2 }]
}]
},
"integration_type": "full_checkout",
"callback_url": "https://acme.example.com/return"
}'
curl -X POST https://sandbox.api.prava.space/v1/sessions \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"user_id": "user_123",
"user_email": "jane@example.com",
"total_amount": "22.45",
"currency": "USD",
"purchase_context": {
"quote": true,
"quote_id": "qte_9f2c..."
}
}'
import requests
resp = requests.post(
"https://sandbox.api.prava.space/v1/sessions",
headers={"Authorization": "Bearer sk_test_..."},
json={
"user_id": "user_123",
"user_email": "jane@example.com",
"total_amount": "49.99",
"currency": "USD",
"purchase_context": {
"custom": [{
"merchant_details": {"name": "Acme Store", "url": "https://acme.example.com", "country_code_iso2": "US"},
"product_details": [{"description": "Widget", "unit_price": "24.99", "quantity": 2}],
}],
},
"integration_type": "full_checkout",
"callback_url": "https://acme.example.com/return",
},
)
const resp = await fetch("https://sandbox.api.prava.space/v1/sessions", {
method: "POST",
headers: {
Authorization: "Bearer sk_test_...",
"Content-Type": "application/json",
},
body: JSON.stringify({
user_id: "user_123",
user_email: "jane@example.com",
total_amount: "49.99",
currency: "USD",
purchase_context: {
custom: [{
merchant_details: { name: "Acme Store", url: "https://acme.example.com", country_code_iso2: "US" },
product_details: [{ description: "Widget", unit_price: "24.99", quantity: 2 }],
}],
},
integration_type: "full_checkout",
callback_url: "https://acme.example.com/return",
}),
});
curl -X POST https://sandbox.api.prava.space/v1/sessions \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"user_id": "user_123",
"user_email": "jane@example.com",
"total_amount": "40.00",
"currency": "USD",
"purchase_context": {
"custom": [{
"merchant_details": { "name": "Acme Store", "url": "https://acme.example.com", "country_code_iso2": "US" },
"product_details": [{ "description": "Monthly plan", "unit_price": "40.00" }]
}]
},
"mandate_setup": {
"intent": "mandate_setup",
"recurring_frequency": "monthly",
"merchant_scope": "listed",
"max_charges": 12
}
}'
{
"session_id": "ses_9f2c...",
"session_token": "eyJhbGciOi...",
"iframe_url": "https://checkout.prava.space/s/ses_9f2c...",
"order_id": "ord_4a1b...",
"expires_at": "2026-07-26T12:15:00Z"
}
{
"session_id": "ses_7d1a...",
"session_token": "eyJhbGciOi...",
"iframe_url": "https://checkout.prava.space/s/ses_7d1a...",
"order_id": "ord_88c2...",
"expires_at": "2026-07-26T12:15:00Z"
}
{
"error": {
"code": "VAL_2001",
"message": "Validation failed",
"details": { "currency": "Unsupported currency code" }
}
}
{
"error": { "code": "AUTH_1001", "message": "Invalid API key" }
}
session_token + iframe_url for the frontend — embed the iframe via the SDK or open the hosted checkout directly.
POST /v1/sessions · Authenticated with your secret key.
Which flow?
- Standard checkout
- Mandate setup
mandate_setup (or send intent: "checkout"). The session collects a card and charges immediately.mandate_setup block with intent: "mandate_setup". The session is authorize-only: it returns an iframe_url for passkey approval, and completing setup issues no payment credentials. Charge later with Charge a Mandate. There is no separate POST /v1/mandates create endpoint — a mandate is always set up through this endpoint.Mandate setup
Mandate creation is part of the Session APIs — this endpoint is how mandates are created. Send amandate_setup block (see Body) with intent: "mandate_setup" and the session becomes authorize-only: the response carries an iframe_url where the owner approves once with their passkey. Completing setup does not issue payment credentials. Once the mandate is active, charge it with Charge a Mandate within its approved terms — no further passkey needed.
The mandate_setup field reference below covers recurring_frequency, merchant_scope, max_charges, and valid_until; see Mandates for the concept and guardrails.
card to let the cardholder select or enter a card in the collection surface. Send card.card_id to pre-select an already-enrolled card, or card.vault_ref_id (a Skyflow UUID) for the vault-ref flow. Send exactly one — if both are present, card_id wins.
After collection, Get Payment Result returns the card bound to
each transaction as transactions[].card_id.Body
400 VAL_2001."49.99". Need not equal the sum of line items (it may include tax, shipping, and fees). This value becomes the authorized amount cap.country_code_iso2 (the merchant’s location) or the cardholder’s country. A US-issued card can pay an INR-denominated session; the card network settles the cross-currency conversion. Price in whatever currency your product uses — you don’t need to re-denominate.- From a quote — send
quote: trueand the opaquequote_id(qte_…) returned when the quote was created. Merchant and product details come from that quote, so nothing else is needed. - Custom — omit
quote(or sendfalse) and describe the merchant and products yourself incustom.
Show purchase_context properties
Show purchase_context properties
true to build the session from an existing quote. When true, pass quote_id and
nothing else — custom must be omitted. When false or absent, custom is required and
quote_id must be omitted.quote is true. The opaque public quote id returned by quote creation
(for example, qte_…). The quote carries the merchant, products, and locked price.
Create the session before the quote expires; its expiry is separate from the session’s
lifetime. total_amount and currency must match the quote’s final total.
Do not send a merchant checkout-session id.quote is not true. Exactly one entry — multi-merchant sessions are not
yet supported.Show custom[] properties
Show custom[] properties
name renders as the header on the checkout page and is forwarded to
Visa as the merchant of record for the token.Show merchant_details properties
Show merchant_details properties
H&M → HM); must contain at least one usable character.https — the URL is forwarded to Visa."US".full_checkout (hosted redirect) or embedding (mount the iframe via the SDK). Read by the frontend to choose the surface.https, max 2048 chars. Optional for all integration types.Show card properties
Show card properties
enrollmentId from the SDK’s collectPAN(), or a card_id from List Cards). Scoped to this customer + merchant.Show mandate_setup properties
Show mandate_setup properties
mandate_setup (authorize-only, no charge) or checkout. Defaults to mandate_setup when this block is present.one_time, weekly, monthly, or yearly.listed (locked to this merchant) or any (any merchant). Recurring frequencies force listed — sending any with a recurring frequency returns 400 MANDATE_RECURRING_MUST_BE_SCOPED.one_time, which is always clamped to 7 days. Recurring horizons: yearly 5y, monthly 2y, weekly 1y.{ "stage": "payment", "outcome": "success" }; both fields are required and additional
fields are rejected. Custom checkout, production, and mandate-setup sessions cannot use it.
Normal card collection and verification still run. An eligible terminal payment result can
complete the sandbox lifecycle while observed_result retains the actual outcome.
See Sandbox simulation.Response
Notes
- Sessions are short-lived and single-use, tied to a specific merchant, customer, and order.
session_tokenandiframe_urlare used on the frontend: embedded via the SDK, or opened directly for hosted checkout.- Sessions can be revoked before use via
POST /v1/sessions/:id/revoke.
Error responses
| Status | Code | Cause |
|---|---|---|
| 400 | VAL_2001 | Validation error (missing/invalid fields; user_id/user_email missing on a merchant session; quote: true without a quote_id; custom missing when quote is not true). details names the fields. |
| 400 | CARD_NOT_FOUND | Pre-selected card_id does not exist for this customer + merchant |
| 400 | CARD_INACTIVE | Pre-selected card is not active |
| 400 | MANDATE_RECURRING_MUST_BE_SCOPED | Recurring frequency sent with merchant_scope: "any" |
| 400 | SANDBOX_SIMULATION_NOT_ALLOWED | Simulation requested outside sandbox, for a custom checkout, or for mandate setup |
| 401 | AUTH_1001 | Invalid API key |
| 401 | AUTH_1002 | Missing or invalid Authorization header |
| 409 | QUOTE_SESSION_MISMATCH | Session amount or currency differs from the quote’s final total |
| 410 | QUOTE_EXPIRED | The quote expired before session creation |
| 429 | TRIES_EXHAUSTED | Sandbox test-transaction limit reached for this merchant |
| 500 | MERCHANT_LOOKUP_ERROR | Failed to load the merchant account for the key |
| 500 | CONFIG_ERROR | Server-side configuration issue (Visa or Skyflow not configured) |
curl -X POST https://sandbox.api.prava.space/v1/sessions \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"user_id": "user_123",
"user_email": "jane@example.com",
"total_amount": "49.99",
"currency": "USD",
"purchase_context": {
"custom": [{
"merchant_details": { "name": "Acme Store", "url": "https://acme.example.com", "country_code_iso2": "US" },
"product_details": [{ "description": "Widget", "unit_price": "24.99", "quantity": 2 }]
}]
},
"integration_type": "full_checkout",
"callback_url": "https://acme.example.com/return"
}'
curl -X POST https://sandbox.api.prava.space/v1/sessions \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"user_id": "user_123",
"user_email": "jane@example.com",
"total_amount": "22.45",
"currency": "USD",
"purchase_context": {
"quote": true,
"quote_id": "qte_9f2c..."
}
}'
import requests
resp = requests.post(
"https://sandbox.api.prava.space/v1/sessions",
headers={"Authorization": "Bearer sk_test_..."},
json={
"user_id": "user_123",
"user_email": "jane@example.com",
"total_amount": "49.99",
"currency": "USD",
"purchase_context": {
"custom": [{
"merchant_details": {"name": "Acme Store", "url": "https://acme.example.com", "country_code_iso2": "US"},
"product_details": [{"description": "Widget", "unit_price": "24.99", "quantity": 2}],
}],
},
"integration_type": "full_checkout",
"callback_url": "https://acme.example.com/return",
},
)
const resp = await fetch("https://sandbox.api.prava.space/v1/sessions", {
method: "POST",
headers: {
Authorization: "Bearer sk_test_...",
"Content-Type": "application/json",
},
body: JSON.stringify({
user_id: "user_123",
user_email: "jane@example.com",
total_amount: "49.99",
currency: "USD",
purchase_context: {
custom: [{
merchant_details: { name: "Acme Store", url: "https://acme.example.com", country_code_iso2: "US" },
product_details: [{ description: "Widget", unit_price: "24.99", quantity: 2 }],
}],
},
integration_type: "full_checkout",
callback_url: "https://acme.example.com/return",
}),
});
curl -X POST https://sandbox.api.prava.space/v1/sessions \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"user_id": "user_123",
"user_email": "jane@example.com",
"total_amount": "40.00",
"currency": "USD",
"purchase_context": {
"custom": [{
"merchant_details": { "name": "Acme Store", "url": "https://acme.example.com", "country_code_iso2": "US" },
"product_details": [{ "description": "Monthly plan", "unit_price": "40.00" }]
}]
},
"mandate_setup": {
"intent": "mandate_setup",
"recurring_frequency": "monthly",
"merchant_scope": "listed",
"max_charges": 12
}
}'
{
"session_id": "ses_9f2c...",
"session_token": "eyJhbGciOi...",
"iframe_url": "https://checkout.prava.space/s/ses_9f2c...",
"order_id": "ord_4a1b...",
"expires_at": "2026-07-26T12:15:00Z"
}
{
"session_id": "ses_7d1a...",
"session_token": "eyJhbGciOi...",
"iframe_url": "https://checkout.prava.space/s/ses_7d1a...",
"order_id": "ord_88c2...",
"expires_at": "2026-07-26T12:15:00Z"
}
{
"error": {
"code": "VAL_2001",
"message": "Validation failed",
"details": { "currency": "Unsupported currency code" }
}
}
{
"error": { "code": "AUTH_1001", "message": "Invalid API key" }
}
Authorizations
Your secret key: sk_test_* (sandbox) or sk_live_* (production).
Body
Unique identifier for the customer in your system
1 - 255Customer's email address
320Total order amount in decimal format (e.g. "49.99")
1^\d+(\.\d{1,2})?$ISO 4217 currency code — exactly 3 uppercase letters, and one of Prava's supported currencies (e.g. USD, EUR, GBP, INR) - other codes are rejected
3^[A-Z]{3}$What is being bought. Two modes — send exactly one: quote: true + quote_id (session built from an existing quote), or custom (describe the merchant and products yourself).
- Custom checkout
- Quote checkout
Show child attributes
Show child attributes
Customer's phone number
Customer's country — 2 uppercase ISO 3166-1 letters
2^[A-Z]{2}$Your external order reference ID
255Human-readable description of the order
How the card is collected. full_checkout = hosted redirect; embedding = mount the iframe via the SDK.
full_checkout, embedding For hosted checkout: the https URL Prava redirects the cardholder to after they finish. Optional for all integration types.
2048^https://Pre-select a card. Send card_id OR vault_ref_id; if both are sent, card_id wins.
Show child attributes
Show child attributes
Turns the session into mandate setup (authorize-only). Absent = standard checkout.
Show child attributes
Show child attributes
Sandbox-only success simulation for quote-backed payment sessions. Fixed at session creation; production, custom checkout, and mandate setup reject this option. Normal card collection and verification still run. See Testing in Sandbox.
Show child attributes
Show child attributes
Response
Session created