curl -X POST https://sandbox.api.prava.space/v1/mandates/mdt_123/charge \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{ "amount": "40.00", "reference": "invoice_2026_07" }'
import requests
resp = requests.post(
"https://sandbox.api.prava.space/v1/mandates/mdt_123/charge",
headers={"Authorization": "Bearer sk_test_..."},
json={"amount": "40.00", "reference": "invoice_2026_07"},
)
const resp = await fetch("https://sandbox.api.prava.space/v1/mandates/mdt_123/charge", {
method: "POST",
headers: {
Authorization: "Bearer sk_test_...",
"Content-Type": "application/json",
},
body: JSON.stringify({
amount: "40.00",
reference: "invoice_2026_07",
}),
});
{
"mandateId": "mdt_123",
"instructionId": "ins_1",
"transactionId": "txn_9",
"orderId": "ord_9",
"status": "awaiting_result",
"fetchStatus": "SUCCESS",
"credentials": { "token": "4111111111111111", "dynamicCvv": "123", "expiryMonth": "12", "expiryYear": "2030" },
"deduplicated": false
}
{
"error": { "code": "MANDATE_FORBIDDEN", "message": "Caller does not own this mandate" }
}
{
"error": { "code": "MANDATE_NOT_ACTIVE", "message": "Mandate is not active" }
}
Server-Side API
Charge a Mandate
Mint single-use card credentials against an active mandate — no passkey.
POST
/
v1
/
mandates
/
{id}
/
charge
curl -X POST https://sandbox.api.prava.space/v1/mandates/mdt_123/charge \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{ "amount": "40.00", "reference": "invoice_2026_07" }'
import requests
resp = requests.post(
"https://sandbox.api.prava.space/v1/mandates/mdt_123/charge",
headers={"Authorization": "Bearer sk_test_..."},
json={"amount": "40.00", "reference": "invoice_2026_07"},
)
const resp = await fetch("https://sandbox.api.prava.space/v1/mandates/mdt_123/charge", {
method: "POST",
headers: {
Authorization: "Bearer sk_test_...",
"Content-Type": "application/json",
},
body: JSON.stringify({
amount: "40.00",
reference: "invoice_2026_07",
}),
});
{
"mandateId": "mdt_123",
"instructionId": "ins_1",
"transactionId": "txn_9",
"orderId": "ord_9",
"status": "awaiting_result",
"fetchStatus": "SUCCESS",
"credentials": { "token": "4111111111111111", "dynamicCvv": "123", "expiryMonth": "12", "expiryYear": "2030" },
"deduplicated": false
}
{
"error": { "code": "MANDATE_FORBIDDEN", "message": "Caller does not own this mandate" }
}
{
"error": { "code": "MANDATE_NOT_ACTIVE", "message": "Mandate is not active" }
}
Mint fresh single-use card credentials against an active mandate — no passkey. Merchant callers receive plaintext
credentials; agent callers receive an encrypted_payload (credentials encrypted to the agent’s public key).
POST /v1/mandates/{id}/charge · Authenticated with your secret key (merchants) or Ed25519 signature (agents).
Need to create a mandate first? Mandate creation is part of the Session APIs — create a mandate with a mandate_setup block on Create Session.
Path parameters
string
required
The mandate id (from List Mandates).
Body
string
required
Charge amount as a decimal string with up to 2 decimals, e.g.
"40.00". The per-charge cap is enforced by the card network — an over-cap charge is declined (THRESHOLD_EXCEEDED).string
Idempotency key (max 255). The same mandate id +
reference returns the original charge (deduplicated: true) instead of minting again. Omit it and every call is a distinct charge. A failed charge clears its key, so a retry after failure is not deduplicated.object[]
Optional per-charge product details (exactly one entry, same shape as the
purchase_context.custom entries in Create Session). Omit to reuse the mandate’s setup context. Currency always comes from the mandate, never the charge.Response
string
string
string
Pass this to Report a Mandate Charge.
string
string
awaiting_result or failed.string
SUCCESS or FAILURE.object
Merchant callers:
{ token, dynamicCvv, expiryMonth, expiryYear }.object
Agent callers:
{ ephemeral_public_key, iv, auth_tag, data }, decrypted client-side.string
string
e.g.
THRESHOLD_EXCEEDED on an over-cap decline.boolean
True when a prior charge with the same
reference was returned.Notes
- The mandate must be
active. Charging a non-active mandate returns409 MANDATE_NOT_ACTIVE. - An over-cap charge is a normal outcome:
status: "failed"with anerrorMessagesuch asTHRESHOLD_EXCEEDED. The amount cap is enforced at the card-network level. - A charge outside a
listedmandate’s merchant returns403 MANDATE_MERCHANT_NOT_ALLOWED. - After checkout, settle the outcome with Report a Mandate Charge.
Error responses
| Status | Code | Cause |
|---|---|---|
| 400 | VAL_2001 | Missing or invalid amount |
| 401 | AUTH_REQUIRED | Missing or invalid credentials |
| 403 | MANDATE_FORBIDDEN | Caller does not own this mandate |
| 403 | MANDATE_MERCHANT_NOT_ALLOWED | Listed-scope mandate; the charge names a merchant not on the allow-list |
| 409 | MANDATE_NOT_ACTIVE | Mandate is not active |
| 409 | NO_INSTRUCTION / NO_ORDER | Mandate is missing its instruction/order |
| 500 | NO_TOKEN | Credential minting failed |
curl -X POST https://sandbox.api.prava.space/v1/mandates/mdt_123/charge \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{ "amount": "40.00", "reference": "invoice_2026_07" }'
import requests
resp = requests.post(
"https://sandbox.api.prava.space/v1/mandates/mdt_123/charge",
headers={"Authorization": "Bearer sk_test_..."},
json={"amount": "40.00", "reference": "invoice_2026_07"},
)
const resp = await fetch("https://sandbox.api.prava.space/v1/mandates/mdt_123/charge", {
method: "POST",
headers: {
Authorization: "Bearer sk_test_...",
"Content-Type": "application/json",
},
body: JSON.stringify({
amount: "40.00",
reference: "invoice_2026_07",
}),
});
{
"mandateId": "mdt_123",
"instructionId": "ins_1",
"transactionId": "txn_9",
"orderId": "ord_9",
"status": "awaiting_result",
"fetchStatus": "SUCCESS",
"credentials": { "token": "4111111111111111", "dynamicCvv": "123", "expiryMonth": "12", "expiryYear": "2030" },
"deduplicated": false
}
{
"error": { "code": "MANDATE_FORBIDDEN", "message": "Caller does not own this mandate" }
}
{
"error": { "code": "MANDATE_NOT_ACTIVE", "message": "Mandate is not active" }
}
Authorizations
Your secret key: sk_test_* (sandbox) or sk_live_* (production).
Path Parameters
The mandate id (from List Mandates).
Body
application/json
Charge amount as a decimal string, e.g. "40.00". Must be within the mandate cap.
Pattern:
^\d+(\.\d{1,2})?$Idempotency key. The same mandate id + reference returns the original charge.
Maximum string length:
255Optional per-charge product details — same shape as purchase_context.custom in Create Session.
Required array length:
1 elementShow child attributes
Show child attributes
Response
Charge initiated; single-use credentials issued.
Available options:
awaiting_result, failed Available options:
SUCCESS, FAILURE Present for merchant callers: single-use card credentials.
Show child attributes
Show child attributes
Present for agent callers: credentials encrypted to the agent's public key.
Show child attributes
Show child attributes
e.g. THRESHOLD_EXCEEDED on an over-cap decline.