# Navigating These Docs Source: https://docs.prava.space/ai How to read these docs as a human, and how to feed them to an AI tool or agent. These docs are built for two kinds of readers: people, and the AI tools people build with. This page covers both. **Two different MCP servers.** The **docs MCP** on this page (`docs.prava.space/mcp`) lets an AI *read these docs*. The **payments MCP** (`mcp.pay.prava.space/mcp`) lets an agent actually *pay*; see [Prava MCP](/mcp/overview). ## Reading as a human Start at the [home page](/) and pick the card that matches you. Each card opens a path you can read top to bottom: | You are | Read in this order | | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Evaluating Prava | [Use Cases](/use-cases) → [How Prava Works](/concepts/how-it-works) → [Compliance](/guides/compliance) | | Building an AI app | [Choosing Your Integration](/choosing-your-integration) → [Quickstart](/quickstart) → [the tutorial](/guides/add-payments-to-your-ai-app) → [API Reference](/api-reference/overview) | | Connecting an agent | [Prava MCP](/mcp/overview) or [Prava CLI](/prava-pay/overview), then that section's pages in order | | An agent owner | [Your Prava Pay Dashboard](/prava-pay/your-wallet). One page; no code. | Stuck on a term? The [Glossary](/concepts/glossary) maps every name across the API, CLI, and dashboard. Stuck on anything else? [FAQ](/developer-faq). ## Reading as an AI tool or agent Three ways, from lightest to deepest: 1. **Paste an index**: [`llms.txt`](https://docs.prava.space/llms.txt) (page index) or [`llms-full.txt`](https://docs.prava.space/llms-full.txt) (entire docs as one file) into any LLM. 2. **Per-page**: every page has a top-right AI menu (copy as Markdown, open in ChatGPT/Claude, view raw Markdown). 3. **Connect the docs MCP** (below): your coding agent gets a live "search the Prava docs" tool and answers from current pages instead of stale training data. And when the agent should *act*, not only read: connect the [payments MCP](/mcp/connect) too. ## Connect the docs MCP server We host a standard **Model Context Protocol (MCP)** server for these docs. MCP is an open protocol, so **any MCP-compatible AI tool** can connect to the same endpoint and gain a "search the Prava docs" capability: Claude Code, Cursor, VS Code / Copilot, Windsurf, Cline, Continue, Goose, Zed, and others. Add this to your client's MCP settings: ```json theme={null} { "mcpServers": { "prava-docs": { "url": "https://docs.prava.space/mcp" } } } ``` ### Quick-add by tool ```bash Claude Code theme={null} claude mcp add --transport http prava-docs https://docs.prava.space/mcp ``` ```text Cursor / Windsurf / VS Code theme={null} MCP settings → "Add server" → paste the URL: https://docs.prava.space/mcp ``` ```text Claude (web / desktop) theme={null} Settings → Connectors → "Add custom connector" → Name: Prava Docs URL: https://docs.prava.space/mcp ``` ### Older / stdio-only clients If a client only supports local (stdio) MCP servers, bridge to the hosted endpoint with `mcp-remote`: ```bash theme={null} npx -y mcp-remote https://docs.prava.space/mcp ``` Once connected, ask your agent things like *"Using the Prava docs, how do I create a payment session?"* and it will pull the answer straight from these pages. ## Feed the docs to any LLM (llms.txt) Prefer to paste context into a model directly? These docs publish machine-readable indexes: | File | What it is | | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------- | | [`https://docs.prava.space/llms.txt`](https://docs.prava.space/llms.txt) | A concise index of every page; good for navigation. | | [`https://docs.prava.space/llms-full.txt`](https://docs.prava.space/llms-full.txt) | The full docs as one text file; paste it in for complete context. | Drop either URL (or its contents) into ChatGPT, Claude, or any LLM to ground it in Prava's documentation. ## Per-page AI actions Every page has an AI menu in the top-right: * **Copy page**: copy the page as clean Markdown. * **Open in ChatGPT / Claude**: start a chat pre-loaded with this page. * **View as Markdown**: see the raw Markdown a model would read. ## Why this matters for Prava Prava is built for AI agents. Making the docs first-class for the tools your developers already use (Cursor, Claude Code, and others) means less guesswork, fewer hallucinated API calls, and a faster path from "what's Prava?" to a working integration. # Create Session Source: https://docs.prava.space/api-reference/create-session api-reference/openapi.json POST /v1/sessions Create a new payment session for a customer and order `user_id` and `user_email` are **required** for merchant (secret-key) requests: they identify the customer this session belongs to. Only **one** `purchase_context` entry is supported per session (multi-merchant is not yet available). ## Notes * Sessions are **short-lived** and **single-use**, tied to a specific merchant, customer, and order. * `effective_until_minutes` in `purchase_context` controls how long the mandate (the spending permission created when the cardholder approves) is valid (default: 15). * `session_token` and `iframe_url` are used on the frontend: embedded via the [SDK](/sdk/overview), or opened directly for [hosted checkout](/sdk/integration-modes). * Set `integration_type: "embedding"` for the SDK flow; omit it (or use `"full_checkout"`) for hosted, and provide a `callback_url`. * Sessions can be revoked via [`POST /v1/sessions/:id/revoke`](/api-reference/revoke-session). ## Error responses | Status | Code | Cause | | ------ | ----------------------- | -------------------------------------------------------------------------- | | 400 | `VAL_2001` | Validation error: missing or invalid fields (details included in response) | | 401 | `AUTH_1001` | Invalid API key | | 401 | `AUTH_1002` | Missing or invalid Authorization header | | 429 | `TRIES_EXHAUSTED` | Sandbox test-transaction limit reached for this merchant | | 500 | `MERCHANT_LOOKUP_ERROR` | Failed to load merchant account for the given key | | 500 | `CONFIG_ERROR` | Server-side configuration issue (Visa or Skyflow not configured) | # Delete Card Source: https://docs.prava.space/api-reference/delete-card api-reference/openapi.json POST /v1/deleteCard Delete a customer's enrolled card and retire its network token ## Notes * `card_id` is the same identifier as the `enrollmentId` returned by the SDK's [`collectPAN`](/sdk/cards/collect-pan) and the `card_id` in [List Cards](/api-reference/list-cards). * Deletion also retires the card's network token (the tokenized stand-in for the card at the card network) where one exists — `network_token_deleted` in the response tells you whether that happened. * If the deleted card was the customer's default, `was_default` is `true`; the customer picks a new default on their next payment. ## Error responses | Status | Code | Cause | Resolution | | ------ | ----------------------- | ------------------------------------ | ------------------------------------------------------------ | | 401 | `AUTH_1001` | Invalid API key | Check your secret key | | 400 | `INVALID_REQUEST` | Missing `customer_id` or `card_id` | Include both fields | | 404 | `CUSTOMER_NOT_FOUND` | No customer for the given identifier | Verify `customer_id` | | 404 | `NOT_FOUND` | Card not found | Verify `card_id` via [List Cards](/api-reference/list-cards) | | 502 | `NETWORK_DELETE_FAILED` | Card-network deletion failed | Retry the request | # Errors Source: https://docs.prava.space/api-reference/errors Every error code the Prava API can return — status, cause, and how to recover. All errors share one envelope: ```json theme={null} { "error": { "code": "ERROR_CODE", "message": "Human-readable error message", "details": {} } } ``` `details` is optional; it's included for validation errors to specify which fields failed. Every response also carries an `X-Response-ID` header; include it when contacting [support@prava.space](mailto:support@prava.space). ## Authentication & validation These can occur on **any** endpoint: | Status | Code | Cause | Recovery | | ------ | ----------------- | ----------------------------------------------------------- | ---------------------------------------------------------------------------------- | | 401 | `AUTH_1001` | Invalid or missing API key | Use `Authorization: Bearer sk_…` with a valid secret key for the right environment | | 401 | `AUTH_1002` | Missing or invalid Authorization header | Include the header exactly as `Bearer sk_…` | | 401 | `AUTH_1003` | Session has expired | Create a new session | | 401 | `AUTH_1004` | Session has been revoked | Create a new session | | 400 | `VAL_2001` | Request failed schema validation | Check `details` for the failing fields | | 400 | `INVALID_REQUEST` | A required field is missing (e.g. `customer_id`, `card_id`) | Include the named field | ## Create Session — `POST /v1/sessions` | Status | Code | Cause | Recovery | | ------ | ----------------------- | ------------------------------------------------- | ---------------------------------------------------------------------- | | 429 | `TRIES_EXHAUSTED` | Your account's session allowance is depleted | Contact [support@prava.space](mailto:support@prava.space) | | 500 | `MERCHANT_LOOKUP_ERROR` | Your merchant account couldn't be resolved | Retry; if persistent, contact support with the `X-Response-ID` | | 500 | `CONFIG_ERROR` | Merchant account is missing payment configuration | Contact support; this is an account-setup issue, not a request problem | | 400 | `CARD_NOT_FOUND` | A pre-selected card doesn't exist | Verify via [List Cards](/api-reference/list-cards) | | 400 | `CARD_INACTIVE` | A pre-selected card is not active | Choose an active card | | 500 | `SESSION_CREATE_ERROR` | Internal failure creating the session | Retry; then support | ## Payment Result — `GET /v1/sessions/{sessionId}/payment-result` | Status | Code | Cause | Recovery | | ------ | ----------- | ---------------------------------------------------- | ---------------------------------------------- | | 404 | `NOT_FOUND` | Session not found, or it belongs to another merchant | Verify the session ID and which key created it | ## Report Status — `POST /v1/sessions/{sessionId}/report-status` | Status | Code | Cause | Recovery | | ------ | -------------------------- | ------------------------------------------------- | ------------------------------------------------------------- | | 404 | `NOT_FOUND` | Session / order / transaction reference not found | Verify the session ID and `txn_ref_id` | | 400 | `INVALID_STATE` | No transaction awaiting a result | It may already be reported, or the cardholder hasn't finished | | 400 | `MANDATE_EXPIRED` | The mandate has expired | Create a new session | | 400 | `PRODUCT_NOT_FOUND` | Product not found by the given ID | Verify `product_id` / `product_ref_id` | | 502 | `VISA_CONFIRMATION_FAILED` | Card-network confirmation failed | Retry; then support | | 500 | `REPORT_STATUS_ERROR` | Internal error | Contact support with the `X-Response-ID` | ## Cards — `GET /v1/listCards` · `POST /v1/deleteCard` | Status | Code | Cause | Recovery | | ------ | ----------------------- | ------------------------------------- | ----------------------------------------------------------------- | | 404 | `CUSTOMER_NOT_FOUND` | No customer for the given identifier | Verify `customer_id` (the `user_id` you used at session creation) | | 404 | `NOT_FOUND` | Card not found (delete) | Verify `card_id` via [List Cards](/api-reference/list-cards) | | 502 | `NETWORK_DELETE_FAILED` | Card-network deletion failed (delete) | Retry | ## Revoke Session — `POST /v1/sessions/{id}/revoke` | Status | Code | Cause | Recovery | | ------ | --------------- | -------------------------------- | -------------------------------------- | | 404 | `NOT_FOUND` | Session not found | Verify the session ID | | 400 | `INVALID_STATE` | Session not in a revocable state | It may already be completed or expired | The **SDK** has its own client-side error codes (`SDK_ALREADY_ACTIVE`, `INVALID_CONFIG`, `IFRAME_LOAD_ERROR`, `SDK_INIT_ERROR`); see [Collect Card Details](/sdk/cards/collect-pan). CLI errors are mapped on the [Prava Pay troubleshooting page](/prava-pay/troubleshooting). # Get Payment Result Source: https://docs.prava.space/api-reference/get-payment-result api-reference/openapi.json GET /v1/sessions/{sessionId}/payment-result Retrieve payment credentials and transaction status for a session This is a **polling endpoint**: after the cardholder completes card entry and passkey approval on Prava's secure surface, poll it to retrieve the payment credentials. ## Notes * The `token` and `dynamic_cvv` fields contain the virtual card credentials your agent uses at checkout. * Prava performs a lazy mandate expiry check on every request — expired mandates are reflected in the status. * After using the credentials at checkout, report the outcome via [Report Status](/api-reference/report-status). ## Error responses | Status | Code | Cause | | ------ | ----------- | ------------------------------------------------------------ | | 401 | `AUTH_1001` | Invalid API key | | 401 | `AUTH_1002` | Missing or invalid Authorization header | | 404 | `NOT_FOUND` | Session not found or doesn't belong to your merchant account | # List Cards Source: https://docs.prava.space/api-reference/list-cards api-reference/openapi.json GET /v1/listCards Retrieve enrolled cards for a customer Cards are scoped by `merchant_id` (derived from your secret key) and the `customer_id` query parameter. Only non-sensitive card metadata is returned — full PANs (the real card numbers) are never exposed. ## Notes * Use `status=all` to include previously deleted cards for historical reference. * The `card_id` returned here is the same as the `enrollmentId` from the SDK's `collectPAN()`. ## Error responses | Status | Code | Cause | | ------ | -------------------- | --------------------------------------------- | | 400 | `INVALID_REQUEST` | Missing `customer_id` query parameter | | 401 | `AUTH_1001` | Invalid API key | | 401 | `AUTH_1002` | Missing or invalid Authorization header | | 404 | `CUSTOMER_NOT_FOUND` | No customer found for the given `customer_id` | # API Reference Source: https://docs.prava.space/api-reference/overview Server-side REST API for Prava Payments *Not sure this is the right path? See [Choosing Your Integration](/choosing-your-integration).* ## Overview The Prava REST API is used for server-side operations: creating sessions, managing cards, retrieving payment results, and reporting status. All server-side requests are authenticated using your **secret key**. Never expose your secret key in client-side code, version control, or logs. Use it only in server-to-server requests. ## Base URLs | Environment | Base URL | Key Prefix | | -------------- | --------------------------------- | ----------- | | **Sandbox** | `https://sandbox.api.prava.space` | `sk_test_*` | | **Production** | `https://api.prava.space` | `sk_live_*` | Use the **sandbox** URL with your `sk_test_*` key during development. Switch to the **production** URL with `sk_live_*` when you're ready to go live. These are the only **API** hosts. [dashboard.prava.space](https://dashboard.prava.space) (developer console) and [pay.prava.space](https://pay.prava.space) (agent-owner wallet) are web UIs; never send API requests to them. ## Authentication All API requests require a Bearer token using your secret key: ```bash theme={null} Authorization: Bearer sk_test_xxx ``` ## Response Format All responses are JSON. Every response includes an `X-Response-ID` header for debugging: ``` X-Response-ID: resp_a1b2c3d4e5f6 ``` Include this ID when contacting support to help trace issues. ## Error Response Format All errors follow a consistent structure: ```json theme={null} { "error": { "code": "ERROR_CODE", "message": "Human-readable error message", "details": {} } } ``` The `details` field is optional and included for validation errors to specify which fields failed. ### Common Error Codes | Status | Code | Description | | ------ | --------------- | ------------------------------------------------ | | 401 | `AUTH_1001` | Invalid API key | | 400 | `VAL_2001` | Validation error (check `details` for specifics) | | 404 | `NOT_FOUND` | Requested resource not found | | 400 | `INVALID_STATE` | Operation not allowed in current state | The full catalog (every code, per endpoint, with recovery steps) is on the [Errors](/api-reference/errors) page. ## The payment journey A complete payment is **three API calls and one non-API step**. Nothing else is required: Your backend pins the order: customer, merchant, amount, line items. Returns `session_token` and `iframe_url`. → [Create Session](/api-reference/create-session) This step happens on Prava's secure surface, not through the API: redirect the cardholder to the `iframe_url` (**hosted**) or mount it in your page with [`collectPAN`](/sdk/cards/collect-pan) (**embedded**). Card entry and passkey approval (a biometric confirmation on the cardholder's device) happen here. Prava handles authentication, mandate registration (the spending permission recorded with the card network), and tokenization (swapping the card number for a secure stand-in) for you. Once the cardholder finishes, this returns the one-time credentials: `token` (virtual card number) and `dynamic_cvv`. Use them at the merchant checkout like a normal card. → [Get Payment Result](/api-reference/get-payment-result) Tell Prava whether the checkout succeeded (`APPROVED`) or failed (`DECLINED`) so transaction records and the card network stay in sync. → [Report Status](/api-reference/report-status) ### Supporting endpoints Cancel a session before it's used Retrieve a customer's enrolled cards # Report Status Source: https://docs.prava.space/api-reference/report-status api-reference/openapi.json POST /v1/sessions/{sessionId}/report-status Report payment execution outcome back to Prava **Always report status** after checkout execution: this updates transaction records and relays the outcome to the card network. Report `DECLINED` if a payment token was used but checkout failed. ## Notes * A mandate is the spending permission created when the cardholder approved the payment. Mandates are currently **one-time**: an `APPROVED` status automatically consumes the mandate (it can't be reused). Recurring mandate frequencies are planned; once available, `APPROVED` will keep a recurring mandate active for future invocations. * `product_statuses` is optional and informational; the overall mandate status follows `txn_status`. ## Error responses | Status | Code | Cause | Resolution | | ------ | -------------------------- | ------------------------------------------------- | ----------------------------------------------- | | 401 | `AUTH_1001` | Invalid API key | Check your secret key | | 401 | `AUTH_1002` | Missing or invalid Authorization header | Include `Authorization: Bearer sk_xxx` | | 404 | `NOT_FOUND` | Session / order / transaction reference not found | Verify the session ID and `txn_ref_id` | | 400 | `INVALID_STATE` | No transaction awaiting payment result | May already be reported, or not yet ready | | 400 | `MANDATE_EXPIRED` | Mandate has expired | Register a new intent | | 400 | `PRODUCT_NOT_FOUND` | Product not found by the given ID | Verify `product_id` or `product_ref_id` | | 502 | `VISA_CONFIRMATION_FAILED` | Card network confirmation failed | Retry or contact support | | 500 | `REPORT_STATUS_ERROR` | Internal error | Contact support with the `X-Response-ID` header | # Revoke Session Source: https://docs.prava.space/api-reference/revoke-session api-reference/openapi.json POST /v1/sessions/{id}/revoke Revoke an active payment session ## Notes * Once revoked, the session token can no longer be used for any SDK operations. * Any in-progress card collection or intent registration (the step that creates the payment's spending permission) within the session will fail. * Revocation is **immediate and irreversible**. ## Error responses | Status | Code | Cause | | ------ | ----------- | ------------------------------------------------------------ | | 401 | `AUTH_1001` | Invalid API key | | 401 | `AUTH_1002` | Missing or invalid Authorization header | | 404 | `NOT_FOUND` | Session not found or doesn't belong to your merchant account | # Test Cards Source: https://docs.prava.space/api-reference/test-cards Sandbox test card numbers and the test OTP, organized by card network. Use these values to exercise the full payment flow in sandbox. They work like real cards but never move money. More networks will appear here as they're supported. **Sandbox only.** These values work only on `sandbox.api.prava.space` / `sandbox.collect.prava.space`. They are declined everywhere else. ## Visa | Card number | CVV | Expiry | | --------------------- | ----- | ------- | | `4622 9431 2313 7789` | `757` | `12/27` | | `4622 9431 2313 7797` | `640` | `12/27` | | `4622 9431 2313 7805` | `304` | `12/27` | ## Test OTP When card verification asks for an OTP (the one-time code your bank would normally text you), enter: ``` 456789 ``` ## Next The full sandbox test run, step by step. Every code you might hit, with recovery steps. # Testing in Sandbox Source: https://docs.prava.space/api-reference/testing How to exercise the full payment flow in sandbox before going live. Sandbox is **self-serve**: sign up at [dashboard.prava.space](https://dashboard.prava.space), create `pk_test_*`/`sk_test_*` keys, and you can create sessions immediately against `https://sandbox.api.prava.space`. ## Health check Confirm connectivity before anything else: ```bash theme={null} curl https://sandbox.api.prava.space/health # → { "status": "ok", "timestamp": "…" } ``` ## Test cards Test card numbers and the test OTP live on their own reference page, organized by card network: [Test Cards](/api-reference/test-cards). ## What behaves differently in sandbox | Area | Sandbox behavior | | -------------- | -------------------------------------------------------------------------------------------------------------------------------------- | | **Charges** | No real money moves; the card network runs in test mode | | **Card entry** | The secure surface is served from `sandbox.collect.prava.space` | | **Passkeys** | Real WebAuthn prompts: you'll use actual Touch ID / Face ID / a security key. Supported: Chrome 80+, Safari 14+, Firefox 80+, Edge 80+ | | **Sessions** | Same 15-minute expiry as production | | **Keys** | Only `sk_test_*` / `pk_test_*` work on the sandbox host | **Prava Pay (CLI) has no separate sandbox host.** The CLI talks to the live API; agent-linked payments use real cards. Sandbox environments apply to the **SDK/API integration path**. ## A full sandbox test run 1. [Create a session](/api-reference/create-session) with your `sk_test_*` key. 2. Open the returned `iframe_url` (hosted) or mount [`collectPAN`](/sdk/cards/collect-pan) (embedded) and enter a [test card](/api-reference/test-cards). 3. Approve with a passkey when prompted. 4. Poll [Get Payment Result](/api-reference/get-payment-result) until `status` is `awaiting_result`; the line items now carry `token` + `dynamic_cvv`. 5. [Report Status](/api-reference/report-status) with `APPROVED` or `DECLINED`. 6. Verify the final state: payment result `status` becomes `completed` (or `failed`). Anything unexpected? Check [Errors](/api-reference/errors). # Authentication & Environments Source: https://docs.prava.space/authentication Learn how to authenticate your requests and use Prava environments. **Two portals, two audiences.** [dashboard.prava.space](https://dashboard.prava.space) is the [**developer console**](/dashboard): sign up, create API keys, manage allowed domains, switch to production. [pay.prava.space](https://pay.prava.space) is the [**Prava Pay dashboard**](/prava-pay/your-wallet) for agent *owners*: approve agent links, enroll cards, set spending controls. A developer integrating the API only needs the console; an agent owner only needs the Prava Pay dashboard. ## Authentication Model Prava uses a **dual-key system** with two distinct authentication modes depending on where the request originates. ### Merchant Secret Key (Server-to-Server) Used for backend operations like creating sessions and listing cards. * Include the key as a Bearer token: `Authorization: Bearer sk_test_xxx` or `Authorization: Bearer sk_live_xxx` * **Never** expose secret keys in client-side code, version control, or logs. * Rotate keys immediately if compromised. ### Publishable Key (Client-Side) Used to initialize the SDK in the browser. * Passed during SDK initialization: `new PravaSDK({ publishableKey: 'pk_live_xxx' })` * Safe to include in frontend code — scoped to client-side operations only. | Key Type | Prefix | Usage | Location | | ------------------- | ------------------------- | ---------------------------------------------- | ------------ | | **Publishable Key** | `pk_live_*` / `pk_test_*` | Initialize SDK, client-side operations | Frontend | | **Secret Key** | `sk_live_*` / `sk_test_*` | Create sessions, list cards, server operations | Backend only | ### Session-Based Auth After creating a session via the backend (`POST /v1/sessions`), the returned `session_token` authenticates all subsequent operations within that session (card collection, transactions, FIDO authentication; FIDO is the standard behind passkeys). Session tokens are: * **Short-lived**: expire after a configured duration. * **Single-use**: tied to a specific merchant, customer, and order. * **Revocable**: can be revoked via `POST /v1/sessions/:id/revoke`. ## Environments | Environment | Key Prefix | Base URL | Purpose | | -------------- | ------------------------- | --------------------------------- | ----------------------- | | **Sandbox** | `pk_test_*` / `sk_test_*` | `https://sandbox.api.prava.space` | Development and testing | | **Production** | `pk_live_*` / `sk_live_*` | `https://api.prava.space` | Live transactions | Sandbox is self-serve: start building immediately. Switching to **production** is done from the [Prava Dashboard](https://dashboard.prava.space) and may require some additional verification; contact [support@prava.space](mailto:support@prava.space) when you're ready to go live. ## Domain Allowlisting Each merchant account has a list of **allowed domains**. The SDK iframe (the embedded frame that renders the card form) and session middleware validate that requests originate from an allowed domain. Add your frontend domains in the [Prava Dashboard](https://dashboard.prava.space). ## Response Headers Every API response includes an `X-Response-ID` header — a unique identifier for that request. Include this ID when contacting support to help us trace issues quickly. ``` X-Response-ID: resp_a1b2c3d4e5f6 ``` ## Webhooks Webhook **event delivery is coming soon**. Today you can already configure a `webhook_url` on your merchant account and you receive a `webhook_secret` (`whsec_…`) at merchant creation. Keep it safe; it will be used to verify event signatures once delivery ships. Until then, poll [Get Payment Result](/api-reference/get-payment-result) for payment outcomes; the [API journey](/api-reference/overview) is fully synchronous and complete without webhooks. # Changelog Source: https://docs.prava.space/changelog What's new in Prava and these docs. **Prava MCP is live** * Connect any MCP client to `https://mcp.pay.prava.space/mcp` and your agent can pay and shop with your card. One URL, OAuth sign-in, 12 tools, and a credential firewall: payment credentials never reach the agent. Start at [Prava MCP](/mcp/overview). **Docs** * New end-to-end guides: [Add Payments to Your AI App](/guides/add-payments-to-your-ai-app), [REST Checkout Walkthrough](/guides/rest-checkout-walkthrough), and the [Go-Live Checklist](/guides/go-live-checklist). * New reference pages: [Errors](/api-reference/errors) (every code in one catalog), [Testing in Sandbox](/api-reference/testing), and [Delete Card](/api-reference/delete-card). * New audience pages: [Your Prava Pay Dashboard](/prava-pay/your-wallet) for agent owners, [For Merchants](/integration/merchants), [Use Cases](/use-cases), and [Compliance & Verification](/guides/compliance). * Sandbox [test cards and test OTP](/api-reference/test-cards) published. * Navigation reorganized: SDK, MCP, and CLI are sibling integration methods under Developer Guide; Capabilities and FAQ have their own tabs. * [Payments](/concepts/payments) restructured around the public flow — what you drive vs. what Prava does for you — plus a new [Glossary](/concepts/glossary). * Fresh visual identity: brand fonts, light theme, imagery, and an interactive API playground backed by an OpenAPI spec. **API** * `POST /v1/deleteCard` documented in the API reference and OpenAPI spec. * `@prava-sdk/cli` v3.x — agent linking (`prava setup`), payment sessions, and agentic shopping (`prava shop search → product → quote → checkout`). * Agent skills: `prava-pay`, `prava-shopping`, and `prava-sdk-integration` installable via `npx skills add`. * Docs MCP server at `docs.prava.space/mcp` and `llms.txt` for AI-assisted integration — see [Use Prava Docs with AI](/ai). # Choosing Your Integration Source: https://docs.prava.space/choosing-your-integration Three ways to build on Prava — and exactly when to use each. Prava has **three** integration paths. (A **merchant** wanting to *sell to* AI shoppers doesn't integrate at all; see [Agentic Commerce](/integration/overview).) Which path you want comes down to two questions: 1. **Who owns the interface**: an **AI agent**, or your own application? 2. If it's your application, **how do you want to present card entry**: inside your own UI, or hand off to a Prava-hosted page? With Prava the payment itself is **always AI/agent-mediated**; what differs is *who owns the interface* and *where the card UI lives*. All three paths share the same secure core: the card is entered on Prava's surface and turned into one-time, scoped credentials. ## Decide in 10 seconds ```mermaid theme={null} flowchart TD A{Who owns the
interface?} -->|AI agent| AGENT[Agent
Prava Pay] A -->|Your application| B{Card form in
your own UI?} B -->|Yes| EMB[Embedded
SDK + API] B -->|No| HOST[Hosted
Full API] ``` If the surface your user interacts with is an **AI agent platform** (OpenClaw, Hermes, ChatGPT; [supported identifiers](/prava-pay/linking)), connect Prava as that agent's payment capability: via **[MCP](/mcp/overview)** (Model Context Protocol: one URL, any MCP client) or the **[CLI](/prava-pay/overview)** (agents that run shell commands). You own an **AI application, or an app backed by a legal business entity**: * **Yes**: keep the user on your page → **[Embedded (SDK + API)](/sdk/integration-modes)**. * **No**: minimal frontend, redirect out and back → **[Hosted (full API)](/sdk/integration-modes#hosted-mode-default)**. **The MCP connector is live.** Add `https://mcp.pay.prava.space/mcp` to AI apps like Hermes, ChatGPT, or OpenClaw and pay with no CLI at all. See [Prava MCP](/mcp/overview). ## Compare the three | | **Embedded** (SDK + API) | **Hosted** (full API) | **Agent** (Prava Pay) | | ----------------------------- | ----------------------------------------------- | ----------------------------------------------------- | --------------------------------------------------------------- | | **Who owns the interface** | Your AI application / business | Your AI application / business | An AI agent platform: OpenClaw, Hermes, ChatGPT | | **Where the card is entered** | An iframe (embedded frame) **inside your page** | Prava-hosted page (redirect) | Prava-hosted page (the owner enters it) | | **What you build** | Session (backend) + SDK wiring (frontend) | Session (backend) + a redirect & `callback_url` route | Nothing: connect via [MCP](/mcp/connect) or run the `prava` CLI | | **What Prava hosts** | The secure card iframe | The full checkout page | The full checkout + agent control plane | | **Frontend footprint** | Small JS (`@prava-sdk/core`) | Almost none | None (MCP or CLI) | | **How you get the result** | `onSuccess` callback, in-app | Redirect to your `callback_url` | CLI output / exit codes | | **Best when** | You want a native-feeling checkout on your page | You want the fastest, lowest-maintenance integration | The interface is an AI agent that shops or pays | | **Start here** | [SDK Overview](/sdk/overview) | [API Reference](/api-reference/overview) | [Prava Pay](/prava-pay/overview) | ## Pick your path Card UI inside your own page. Use when you want checkout to feel native to your product. Redirect to a Prava-hosted page. Use when you want to ship fast with minimal frontend code. For **AI-agent-owned interfaces**. Connect via [MCP](/mcp/overview) or the CLI. Embedded and Hosted are two ways to use the **same session**; see [Integration Modes](/sdk/integration-modes) for the mechanics. **Agent** (Prava Pay) is a separate product for AI-agent-owned interfaces; start at its [overview](/prava-pay/overview). # Accounts & Agents Source: https://docs.prava.space/concepts/accounts How an owner's Prava account holds cards and authorizes AI agents to pay — and the controls that keep it safe. Prava doesn't have a standalone "wallet" object. What it has is an **account** owned by a person or business (the **agent owner**), managed from the [Prava Pay dashboard](https://pay.prava.space). The account holds the enrolled cards and saved addresses, and the owner authorizes one or more **AI agents** to pay on their behalf. **Two portals, two audiences.** [dashboard.prava.space](https://dashboard.prava.space) is the [**developer console**](/dashboard): sign up, create API keys, manage allowed domains, switch to production. [pay.prava.space](https://pay.prava.space) is the [**Prava Pay dashboard**](/prava-pay/your-wallet) for agent *owners*: approve agent links, enroll cards, set spending controls. A developer integrating the API only needs the console; an agent owner only needs the Prava Pay dashboard. With Prava, the agent never holds funds or a raw card. It acts against the **owner's account** under the owner's controls, and only ever receives one-time, scoped credentials at the moment of purchase. ## The model * **Agent owner**: a dashboard user who owns the account and manages everything at [pay.prava.space](https://pay.prava.space). * **Cards & addresses**: enrolled once and held on the owner's account. * **Agents**: the owner links and approves each agent; every agent gets its own secure identity. * **Shared access**: all of an owner's agents draw on the **same** cards. There are no per-agent card permissions. ## Authorizing agents An agent is connected through the [linking flow](/prava-pay/linking): it requests access, and the owner approves it in the browser. From then on an agent is either **active** or **revoked**. It's all-or-nothing, not a permissions matrix. Revoking an agent cuts off its access immediately. ## Spending controls These are the controls that actually exist and are enforced: | Control | What it does | Where it's enforced | | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | | **Checkout quota** | A set number of checkouts allowed per owner (a lifetime count), consumed as agents buy. | Prava (application level) | | **Concurrency limit** | Caps how many checkouts can be open at once (a small number). | Prava (application level) | | **Per-purchase approval** | Every spend requires explicit approval before it happens. The [Skills](/prava-pay/skills) enforce a hard stop for agents; SDK/hosted flows use passkey confirmation (a biometric or security-key approval on the user's device). | Owner / device | | **Per-merchant amount limits** | When a card is enrolled for agentic commerce, each authorization is locked to the merchant and amount. | Card network (Visa) | Quota and concurrency are **Prava application-level** limits. Merchant/amount locking is enforced by the **card network**. See [Guardrails](/concepts/guardrails) for the network-level detail. ## Cards on the account Cards are enrolled through Prava's secure collection ([`collectPAN`](/sdk/cards/collect-pan) or a hosted page) and tokenized: the number is replaced with a secure stand-in, and the raw card never touches the app or the agent. Each card carries: * a **status** (`active` / `deleted`), * an **agentic-commerce** flag (`isAgenticCommerceEnrolled`) indicating it's enabled for AI-initiated purchases, and is visible from the dashboard and the [List Cards](/api-reference/list-cards) API. ## Related How an owner approves an agent against their account. The network-level constraints on what an authorized purchase can do. # Glossary Source: https://docs.prava.space/concepts/glossary Every Prava term in one place — and how the same thing is named across the API, CLI, and dashboard. ## Same thing, different names Prava's surfaces sometimes use different names for the same value. This table is the map: | Concept | REST API | Prava Pay CLI | Prose / Concepts | | ---------------------------- | ------------------------------- | ------------------------ | --------------------------- | | One-time virtual card number | `token` | `Token` | Payment token (virtual PAN) | | One-time security code | `dynamic_cvv` | `Cryptogram` | Single-use CVV | | Card expiry for the token | `expiry_month` + `expiry_year` | `Expiry` (`MM/YYYY`) | Token expiry | | Payment container | session (`session_id`, `ses_…`) | session (`--session-id`) | Session | | Spending permission | mandate (in statuses) | — (handled server-side) | Mandate | ## The portals **Two portals, two audiences.** [dashboard.prava.space](https://dashboard.prava.space) is the [**developer console**](/dashboard): sign up, create API keys, manage allowed domains, switch to production. [pay.prava.space](https://pay.prava.space) is the [**Prava Pay dashboard**](/prava-pay/your-wallet) for agent *owners*: approve agent links, enroll cards, set spending controls. A developer integrating the API only needs the console; an agent owner only needs the Prava Pay dashboard. ## Core terms | Term | Definition | | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Session** | The container for one payment: customer + merchant + order, created by `POST /v1/sessions` (or `prava sessions create`). Expires after 15 minutes. | | **Transaction** | A single payment attempt within a session (`addCard` or `savedCard` flow). | | **Passkey** | WebAuthn biometric/device approval (Touch ID, Face ID, security key). Required for every payment; no fallback. | | **Mandate** | A card-network-level spending permission: merchant, amount threshold, frequency (`one_time` today), duration. Prava registers it after passkey approval. | | **Payment token** | The single-use virtual card credentials (number + CVV + expiry) issued against an active mandate: merchant-locked, amount-scoped, short-lived. | | **Enrollment** | Securely collecting and tokenizing a card (via [`collectPAN`](/sdk/cards/collect-pan) or a hosted page). Yields an `enrollmentId`. | | **Agentic commerce enrollment** | A card flag (`isAgenticCommerceEnrolled`) enabling AI-initiated purchases with network-level (Visa) merchant/amount locking. | | **Agent owner** | The person/business whose account holds the cards and who approves agents; manages everything at [pay.prava.space](https://pay.prava.space). | | **Agent linking** | Connecting an agent to an owner's account: `prava setup` → owner approves in the browser → agent is `active` (or later `revoked`). | | **Quote / checkout (shopping)** | In agentic shopping, a quote locks a price (\~15 min) for a discovered product; checkout completes the purchase. See [Agentic Shopping](/prava-pay/shopping). | | **UCP** | Shopify's Universal Commerce Protocol: product search, catalogs, and quotes across participating merchants. See [UCP](/integration/ucp). | | **Browser Harness** | Prava's checkout automation: confirms the true final total on the live merchant checkout and pays with the one-time token. See [Browser Harness](/integration/browser-harness). | ## Key types | Key | Prefix | Lives | Used for | | --------------- | ------------------------- | ------------ | ------------------------------------------------ | | Publishable key | `pk_test_*` / `pk_live_*` | Frontend | Initializing the SDK | | Secret key | `sk_test_*` / `sk_live_*` | Backend only | Sessions, results, card management | | Session token | — | Per-session | Authenticates in-session operations (SDK/iframe) | ## Session vs mandate vs credential Easy to conflate; they nest: 1. A **session** (15 min) is the workspace for one payment. 2. Inside it, passkey approval creates a **mandate**, the permission (merchant + max amount + duration). 3. Against the mandate, Prava issues the **payment token**, the actual single-use card credentials you check out with. Expiry of one doesn't imply the others: a token can expire while its mandate is active (getting a new token requires a new invocation), and a mandate can outlive the card-entry window. # Guardrails Source: https://docs.prava.space/concepts/guardrails Rule-based controls that constrain what AI agents can spend and where. ## Guardrails **Guardrails** are rule-based controls enforced at multiple levels to ensure AI agents cannot exceed their authorized scope. They operate across four enforcement layers: ### 0. Owner-Set Account Controls Before any payment mechanics apply, the account owner's own controls do. These are set from the [Prava Pay dashboard](https://pay.prava.space) and enforced by Prava on every purchase: * **Checkout quota**: a set number of checkouts allowed per owner, consumed as agents buy. * **Concurrency limit**: caps how many checkouts can be open at once. * **Per-purchase approval**: every spend needs explicit approval before it happens. * **Agent revocation**: revoking an agent cuts off its access immediately. See [Accounts & Agents](/concepts/accounts) for the full model. ### 1. Mandate-Level Constraints Every payment intent creates a **mandate**, a spending permission registered at the card network level. The mandate itself is a guardrail. It specifies: | Constraint | What it controls | | ---------------------- | -------------------------------------------------------- | | **Merchant** | The merchant the mandate is scoped to | | **Amount threshold** | Maximum amount for the transaction | | **Frequency** | Currently `one_time` (recurring frequencies are planned) | | **Effective duration** | When the mandate expires (`expiresAt`) | | **Product scope** | The products in the purchase (via mandate line items) | The mandate's **amount** is enforced at the card-network level through the tokenized credential (the substitute card number issued for the purchase): a transaction outside the mandate amount is declined. The remaining constraints are applied by Prava. ### 2. Session-Level Controls Each session is scoped to: * A specific **merchant** and **order** * A configured set of **allowed domains** (origin validation) * A **time limit**: sessions expire and cannot be reused * **Idempotency**: duplicate transactions within a session are detected and rejected ### 3. Authentication Controls * **Passkey (WebAuthn)**: a biometric or security-key approval on the user's device, required for every intent mutation (register, update, delete). Prevents unauthorized agents from creating mandates. * **Device binding**: passkeys are registered per browser, so a passkey on one device cannot be used on another. * **WebAuthn required**: if the user's device does not support WebAuthn (passkeys), transactions cannot be performed. There is no fallback mechanism; this ensures the highest level of authentication security. ## Defense in Depth ``` Owner controls (quota, approval) → User approval (Passkey) → Mandate (amount-scoped) → Session (time-scoped) → one-time credential ``` Every layer must pass for a payment to succeed. An AI agent with a valid mandate but an expired session is blocked; a transaction outside the mandate amount is declined. This layered approach ensures no single point of failure. # How Prava Works Source: https://docs.prava.space/concepts/how-it-works The lifecycle behind every Prava payment — from intent to a completed checkout. Prava sits between the party that wants to pay (an app or an AI agent) and the merchant. Its job is to keep the sensitive parts (the real card, the spending rules, the final authorization) out of the caller's hands, while still letting the payment go through. ## The core idea An app or agent never holds a real card number. Instead it works with **permission to spend** and, at the moment of purchase, a **one-time credential** that only works for that exact purchase. ```mermaid theme={null} flowchart LR A["App or AI agent
proposes a purchase"] B["Prava
secure card entry
owner's limits applied
network authorization"] C["Merchant
checkout completes"] A -->|purchase request| B -->|one-time credential| C ``` ## The lifecycle An app creates a **session** for a specific order, or an agent creates a payment for a known merchant and total. The amount and merchant are pinned up front. The cardholder enters their card in Prava's secure surface: an embedded iframe (SDK) or a hosted page (Prava Pay). The raw card number never reaches the app, the agent, or their servers. Prava checks the purchase against the account's guardrails (spending limits, approvals, saved addresses) before anything is charged. See [Guardrails](/concepts/guardrails). Prava returns credentials scoped to that single purchase: locked to the merchant, the amount, and a short time window. They can't be reused or repurposed. The credential is used at the merchant's checkout to complete the payment. The outcome is confirmed from the payment itself, so the status you get back is the real one. ## Why it's safe by design The card is entered in Prava's secure surface and tokenized (the number is replaced with a secure stand-in). Apps and agents never see it. What the caller receives works for **one** purchase: right merchant, right amount, short-lived. Spending limits and approvals are enforced by Prava on every purchase, not by the caller. If an outcome is ever uncertain, Prava declines to guess rather than risk charging twice. ## Three ways to integrate The same lifecycle powers all three integration paths: | | [Embedded](/sdk/integration-modes) | [Hosted](/sdk/integration-modes#hosted-mode-default) | [Agent](/prava-pay/overview) | | ---------- | ---------------------------------- | ---------------------------------------------------- | ------------------------------------------------ | | Runs in | Your web app (SDK) | Your backend (API) | An AI agent, via [MCP](/mcp/overview) or the CLI | | Card entry | Iframe inside your page | Prava-hosted page (redirect) | Prava-hosted page | | Best for | Native checkout in your product | Fast, minimal-frontend checkout | Agents buying on someone's behalf | Not sure which to pick? See [Choosing Your Integration](/choosing-your-integration). For the money-side details (mandates, credential scoping, and status), see [Payments](/concepts/payments). For the controls an owner sets, see [Guardrails](/concepts/guardrails). # Payments Source: https://docs.prava.space/concepts/payments Understand the payment lifecycle — from session creation to settlement. ## The lifecycle you drive From your side, a complete payment is **three API calls and one hand-off**. This is the entire public surface; everything else on this page is what Prava does for you in between: ```mermaid theme={null} flowchart LR A["1 Create session
POST /v1/sessions"] --> B["2 Card entry
Prava's secure surface"] B --> C["3 Payment result
GET …/payment-result"] C --> D["4 Checkout
use token at merchant"] D --> E["5 Report status
POST …/report-status"] ``` | Step | What happens | Who does it | | --------------------- | ---------------------------------------------------------- | --------------------------------------------------------------------------------------------- | | **1. Session** | Pin the order: customer, merchant, amount, line items | Your backend ([Create Session](/api-reference/create-session)) | | **2. Card entry** | Cardholder enters the card + approves with a passkey | The user, on Prava's surface (embedded [`collectPAN`](/sdk/cards/collect-pan) or hosted page) | | **3. Payment result** | Receive the one-time credentials (`token` + `dynamic_cvv`) | Your backend ([Get Payment Result](/api-reference/get-payment-result)) | | **4. Checkout** | Use the credentials like a normal card at the merchant | Your app / AI agent | | **5. Report status** | Tell Prava the outcome (`APPROVED` / `DECLINED`) | Your backend ([Report Status](/api-reference/report-status)) | ### How a payment starts: two examples **Human-in-the-loop (how every payment works today).** A user tells their agent "buy this $34 book from Bookshop". The agent (or your backend) creates a session for exactly $34 at Bookshop. The user gets Prava's secure surface, enters or picks their card, and approves with a passkey. Only then does a one-time credential exist, and it works only for \$34 at Bookshop. No approval, no credential, no charge. **Recurring (planned).** The same shape, approved once: a user will approve "up to \$12/month at this merchant" a single time, and the agent can invoke that mandate monthly within the limits without a fresh passkey per charge. Until this ships, every mandate is `one_time` and each payment needs its own approval. ### 1. Session A **session** is the starting point. Created server-to-server by the merchant using a secret key (`POST /v1/sessions`), it bundles: * **Customer identity**: `user_id`, `user_email` * **Order details**: `total_amount`, `currency`, product line items * **Merchant context**: merchant name, URL, country code * **Purchase context**: product descriptions, unit prices, quantities The session returns a `session_token` and `iframe_url` used to initialize the SDK on the frontend (or to redirect the cardholder in hosted mode). Sessions expire after **15 minutes**. ## What Prava does for you in the middle Between card entry (step 2) and the payment result (step 3), Prava runs the secure machinery below. **None of it requires an API call from you.** It's described here so you can interpret the states you see in [payment results](/api-reference/get-payment-result) and understand the guarantees. ### Transactions A **transaction** represents a single payment attempt within a session. It can be one of two flow types: | Flow Type | Description | | ----------- | -------------------------------------------- | | `addCard` | User enrolls a new card and pays in one flow | | `savedCard` | User pays with a previously enrolled card | Each transaction is deduplicated using a deterministic idempotency key derived from the order, flow type, and card fingerprint, so retrying the same request cannot cause a double charge. **Transaction statuses** (reflected in the [payment result](/api-reference/get-payment-result)): | Status | Meaning | | ----------------- | ----------------------------------------------------------------------------------------------------- | | `pending` | Transaction created, awaiting authentication | | `awaiting_result` | Credentials issued; checkout in progress, awaiting your [status report](/api-reference/report-status) | | `completed` | Payment authorized and completed | | `failed` | Payment failed (declined, timeout, or error) | ### Authentication (FIDO / Passkey) Before a payment can be authorized, the user authenticates via **Passkey** (WebAuthn) on Prava's surface: Prava's backend generates a challenge, and the user confirms via biometric (Touch ID, Face ID) or security key. The signed assertion proves the user explicitly approved the transaction. ### Mandates A **mandate** is a card-network-level spending permission. Once the user authenticates, Prava registers a mandate with the card network that specifies: * **Merchant**: who can charge the card * **Amount threshold**: maximum per-transaction amount * **Frequency**: currently `one_time` (recurring frequencies are planned) * **Effective duration**: how long the mandate remains active (`effective_until_minutes`, default 15) Each mandate also carries **line items** (product IDs, descriptions, unit prices, quantities) that break down the purchase at a product level; the amount threshold and quantity limit apply at the **mandate level**, not per line item. **Mandate statuses** (surfaced in payment results and status reports): | Status | Meaning | | ----------- | ---------------------------------------------- | | `pending` | Created, awaiting network confirmation | | `active` | Live and usable for payment token generation | | `consumed` | Fully used (all allowed invocations exhausted) | | `cancelled` | Revoked by user or system | | `expired` | Past its effective date | ### Payment tokens — the credential you receive Against an active mandate, Prava generates **payment tokens**: a virtual card number (PAN), expiry, and CVV, scoped to the mandate constraints. These are what step 3 hands back to you: * **Single-use**: each token can only be used once * **Merchant-locked & amount-scoped**: enforced at the network level and by Prava; transactions outside the mandate are declined * **Short-lived**: use tokens promptly after they're issued Same credential, different names: the API calls these `token` + `dynamic_cvv`; the Prava Pay CLI prints `Token` + `Cryptogram`. See the [Glossary](/concepts/glossary). ## Merchant Network & Shopify App Prava integrates with merchant platforms and card networks to complete payments. The **Prava Shopify app** is available by invite: **This step needs Prava's help — everything else is self-serve.** Email [support@prava.space](mailto:support@prava.space) with your account email, your entity/merchant name, and what you're requesting. We'll take it from there. ## Settlement & Refunds * **Settlement** follows standard card network flows. Prava supports multiple settlement models; details are confirmed during merchant onboarding. * **Refunds** follow standard refund flows and can be issued through the API. * **Disputes** are routed to the responsible parties as per the settlement agreement. # Your Developer Dashboard Source: https://docs.prava.space/dashboard What dashboard.prava.space is for: keys, domains, environments, and your payment activity. The **Prava Dashboard** at [dashboard.prava.space](https://dashboard.prava.space) is the console for **developers and merchants** integrating Prava. Everything your integration needs from Prava's side lives here, and all of it is self-serve. **Two portals, two audiences.** [dashboard.prava.space](https://dashboard.prava.space) is the [**developer console**](/dashboard): sign up, create API keys, manage allowed domains, switch to production. [pay.prava.space](https://pay.prava.space) is the [**Prava Pay dashboard**](/prava-pay/your-wallet) for agent *owners*: approve agent links, enroll cards, set spending controls. A developer integrating the API only needs the console; an agent owner only needs the Prava Pay dashboard. ## What you do here | Task | Where it fits | | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | | **Sign up** with email (one-time code) or Google | First step of the [Quickstart](/quickstart) | | **Create API keys** — enter your entity name, get `pk_test_*` + `sk_test_*` instantly | [Authentication](/authentication) explains each key | | **Manage allowed domains** for the SDK iframe | Required before [`collectPAN`](/sdk/cards/collect-pan) runs on your site | | **Switch environments** — sandbox and production are separate views with separate keys | [Testing in Sandbox](/api-reference/testing) · [Go-Live Checklist](/guides/go-live-checklist) | | **See your payment activity** — transactions and orders as they flow through your integration | Statuses match the [payment lifecycle](/concepts/payments) | | **Go live** — enable production; may require entity verification | [Compliance & Verification](/guides/compliance) | ## Sandbox first, production when ready New accounts start in **sandbox**: keys work immediately against `sandbox.api.prava.space`, no waiting and no verification. Production is a separate environment you enable from the dashboard when you're ready; going live may require verification of your entity ([what's needed](/guides/compliance)). ## Not the dashboard you're looking for? If your agent pays with **your own card** and you want to approve agents, enroll cards, or set spending limits, that's the **Prava Pay dashboard** at [pay.prava.space](https://pay.prava.space) — see [Your Prava Pay Dashboard](/prava-pay/your-wallet). ## Next Sign up, create keys, and make your first session. Everything between sandbox and production. # Developer FAQ Source: https://docs.prava.space/developer-faq Common SDK and REST API integration questions, answered. *Questions about agentic commerce, merchants, or refunds? See the [Agentic Commerce FAQs](/integration/faqs). CLI issues? See [Prava Pay Troubleshooting](/prava-pay/troubleshooting).* ## Keys & environments **Which key goes where?** `pk_*` (publishable) initializes the SDK in the browser; `sk_*` (secret) stays server-side for sessions, results, and card management. Full table: [Authentication](/authentication). **My key returns `AUTH_1001` but it's correct.** Check environment pairing: `sk_test_*` only works against `sandbox.api.prava.space`, `sk_live_*` only against `api.prava.space`. **Where do I get keys?** Self-serve at [dashboard.prava.space](https://dashboard.prava.space); see the [Quickstart](/quickstart). No invite or waiting. ## Sessions **How long does a session last?** 15 minutes (`expires_at` in the create response). Create sessions when the user is ready to pay; once expired, create a new one rather than reusing it. **Can I create multiple sessions per user?** Technically yes, but create one per checkout flow and complete or [revoke](/api-reference/revoke-session) it before starting another. **`payment-result` stays `pending` forever.** The cardholder hasn't completed card entry + passkey approval (Touch ID / Face ID) on the Prava surface. Confirm they opened the `iframe_url` (hosted) or that [`collectPAN`](/sdk/cards/collect-pan) mounted without errors (embedded). ## The iframe / collectPAN **The iframe won't load (`IFRAME_LOAD_ERROR`).** Verify you passed the `iframe_url` from the session response, and that your page's domain is in **allowed domains** in the dashboard; other origins are rejected. **Can I style the card form?** The card fields live inside Prava's PCI-scoped iframe and aren't arbitrarily styleable from your page. Position and size the container; the iframe handles the rest. **Do I ever touch the card number?** No. You receive `{ enrollmentId, last4, brand, expMonth, expYear }`, never the PAN (the full card number). That's what keeps you out of PCI scope, the security compliance that applies once you handle card data. ## Tokens & checkout **Can I reuse a token?** No — payment tokens are single-use, merchant-locked, amount-scoped, and short-lived. If a checkout fails, report `DECLINED` and start a new session. **What's a `dynamic_cvv`?** The single-use CVV paired with the token (the CLI calls it a `Cryptogram`). Terminology map: [Glossary](/concepts/glossary). **Do I have to call report-status?** Yes, always — `APPROVED` or `DECLINED`. It closes the loop with transaction records and the card network. Unreported checkouts leave the transaction in `awaiting_result`. ## Going further **Webhooks?** Coming soon: configuration exists today, delivery is rolling out. Poll [Get Payment Result](/api-reference/get-payment-result) meanwhile. See [Authentication → Webhooks](/authentication#webhooks). **Test cards?** Published on [Test Cards](/api-reference/test-cards), together with the test OTP, organized by card network. Sandbox only. **Pricing?** Contact [support@prava.space](mailto:support@prava.space) for current pricing. **Something not covered here?** [support@prava.space](mailto:support@prava.space); include the `X-Response-ID` header of any failing request. # Add Payments to Your AI App Source: https://docs.prava.space/guides/add-payments-to-your-ai-app The end-to-end tutorial: from a fresh dashboard account to a completed, reported payment inside your own application. This is the complete integration, start to finish. When you're done, your AI app can take a purchase your agent proposes, collect the user's card **without touching it**, receive one-time credentials, and complete the checkout. **What you'll build:** one backend endpoint that creates sessions, one frontend surface that collects the card, and one backend callback that reads the result and reports the outcome. Prefer zero frontend code? The [REST checkout walkthrough](/guides/rest-checkout-walkthrough) does the same flow with hosted card entry and nothing but cURL. ## Prerequisites (all self-serve) 1. Sign up at [dashboard.prava.space](https://dashboard.prava.space) and create an API key; you get `pk_test_*` + `sk_test_*` instantly ([Quickstart](/quickstart) has the details). 2. Add your app's domain to **allowed domains** in the dashboard. 3. Install the SDK: `npm install @prava-sdk/core`. Building with an AI coding agent? Install the **prava-sdk-integration skill** and your agent knows this whole flow, with ready-made templates: ```bash theme={null} npx --yes skills add https://github.com/Prava-Payments/prava-skills \ --skill prava-sdk-integration --global --yes --full-depth ``` ## Step 1 — Backend: create a session Your server pins the order and returns the session to your frontend. The secret key never leaves the server. ```typescript Next.js (app router) theme={null} // app/api/prava/session/route.ts export async function POST(req: Request) { const { userId, email, amount, merchant, items } = await req.json(); const res = await fetch('https://sandbox.api.prava.space/v1/sessions', { method: 'POST', headers: { Authorization: `Bearer ${process.env.PRAVA_SECRET_KEY}`, // sk_test_… 'Content-Type': 'application/json', }, body: JSON.stringify({ user_id: userId, user_email: email, total_amount: amount, // e.g. "49.99" currency: 'USD', integration_type: 'embedding', // card UI inside your page purchase_context: [{ merchant_details: merchant, // { name, url, country_code_iso2 } product_details: items, // [{ description, unit_price, quantity }] }], }), }); const session = await res.json(); // → { session_id, session_token, iframe_url, order_id, expires_at } return Response.json({ sessionId: session.session_id, sessionToken: session.session_token, iframeUrl: session.iframe_url, }); } ``` ```typescript Express theme={null} // server.ts import express from 'express'; const app = express(); app.use(express.json()); app.post('/api/prava/session', async (req, res) => { const { userId, email, amount, merchant, items } = req.body; const r = await fetch('https://sandbox.api.prava.space/v1/sessions', { method: 'POST', headers: { Authorization: `Bearer ${process.env.PRAVA_SECRET_KEY}`, // sk_test_… 'Content-Type': 'application/json', }, body: JSON.stringify({ user_id: userId, user_email: email, total_amount: amount, currency: 'USD', integration_type: 'embedding', purchase_context: [{ merchant_details: merchant, product_details: items, }], }), }); const session = await r.json(); res.json({ sessionId: session.session_id, sessionToken: session.session_token, iframeUrl: session.iframe_url, }); }); app.listen(3000); ``` Full request schema: [Create Session](/api-reference/create-session). Sessions expire after **15 minutes**: create them when the user is ready to pay, not earlier. ## Step 2 — Frontend: collect the card Mount Prava's PCI-compliant iframe in your page. The raw card number never touches your DOM, JavaScript, or servers. ```tsx React theme={null} import { useEffect, useRef } from 'react'; import { PravaSDK } from '@prava-sdk/core'; export function PayWithPrava({ order }: { order: OrderInput }) { const prava = useRef(new PravaSDK({ publishableKey: 'pk_test_xxx' })); useEffect(() => () => prava.current.destroy(), []); // cleanup on unmount async function collect() { const { sessionId, sessionToken, iframeUrl } = await fetch('/api/prava/session', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(order), }).then((r) => r.json()); await prava.current.collectPAN({ sessionToken, iframeUrl, container: '#card-form', onSuccess: async (card) => { // card = { enrollmentId, last4, brand, expMonth, expYear } await fetch(`/api/prava/complete?sessionId=${sessionId}`, { method: 'POST' }); }, onError: (err) => console.error(err.code, err.message), }); } return ( <>
); } ``` ```html Vanilla JS theme={null}
``` The user enters the card and approves with a **passkey** (Touch ID / Face ID) inside the iframe. Prava handles the authentication, mandate registration (recording the user's permission to pay), and tokenization (swapping the real card for one-time credentials); [none of that is your code](/concepts/payments#what-prava-does-for-you-in-the-middle). Every option and callback: [Collect Card Details](/sdk/cards/collect-pan). ## Step 3 — Backend: get the credentials and check out After collection, poll the payment result. When a line item reaches `awaiting_result` it carries the one-time credentials. ```typescript theme={null} // Poll until the credentials are ready (they arrive right after card entry completes) async function getCredentials(sessionId: string) { const r = await fetch( `https://sandbox.api.prava.space/v1/sessions/${sessionId}/payment-result`, { headers: { Authorization: `Bearer ${process.env.PRAVA_SECRET_KEY}` } }, ); const result = await r.json(); if (result.status !== 'awaiting_result') return null; // keep polling ('pending') const li = result.transactions[0].line_items[0]; return { txnRefId: li.txn_ref_id, cardNumber: li.token, // one-time virtual card number cvv: li.dynamic_cvv, // single-use CVV expMonth: li.expiry_month, expYear: li.expiry_year, }; } ``` Use these exactly like normal card details at the merchant's checkout: your agent fills the payment form, or your backend calls the merchant's payment API. They're **single-use, merchant-locked, and amount-scoped**, so use them promptly. ## Step 4 — Backend: report the outcome Always close the loop, whether the checkout succeeded or failed: ```typescript theme={null} await fetch( `https://sandbox.api.prava.space/v1/sessions/${sessionId}/report-status`, { method: 'POST', headers: { Authorization: `Bearer ${process.env.PRAVA_SECRET_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ txn_ref_id: credentials.txnRefId, txn_status: 'APPROVED', // or 'DECLINED' if the checkout failed }), }, ); ``` This updates transaction records and relays the outcome to the card network; the payment result's `status` becomes `completed`. Details: [Report Status](/api-reference/report-status). ## Step 5 — Test it Run the whole flow in sandbox with a [test card](/api-reference/test-cards). See [Testing in Sandbox](/api-reference/testing) for the checklist and what behaves differently. ## Ship it Swap to live keys, verify domains, and what needs Prava's sign-off. Every code you might hit along this flow, with recovery steps. # Compliance & Verification Source: https://docs.prava.space/guides/compliance What verification each integration path needs, and what Prava is (and is not) as a regulated matter. Two questions come up before production: "what do you need from me?" and "what exactly is Prava, legally?" Both answers are short. ## Verification by integration path ### SDK / API integrators: KYB KYB (Know Your Business) is business identity verification. If you integrate the [SDK + API](/sdk/overview) and take live payments in your product, your business gets verified when you [switch to production](/guides/go-live-checklist). Have these ready (indicative; we confirm the exact list during verification): * Legal entity name and country of incorporation * Registration / incorporation number and date * Registered business address * Beneficial owners (name, ownership stake) * Business website and a working contact Sandbox needs none of this. Build and test first; verify when you go live. Specifics: [support@prava.space](mailto:support@prava.space). ### MCP / CLI users: KYC via card verification KYC (Know Your Customer) is personal identity verification. If you connect an agent through [MCP](/mcp/overview) or the [CLI](/prava-pay/overview), there is no separate KYC form. Identity verification is inherited from your card: when you enroll it, the card is verified with your issuing bank (including OTP verification, a one-time code, where the issuer requires it), and every payment is approved with a passkey (Touch ID / Face ID) on your device. Your bank has already verified you; Prava builds on that. ## What Prava is — and is not From our [Terms & Conditions](https://www.prava.space/terms-conditions): > Prava is not a bank, card issuer, acquirer, payment processor, money transmitter, stored value > provider, or deposit-taking institution. Prava is a **technology service provider for agentic commerce**, operated by **Prava Payments Inc** (Delaware, USA). Payment processing, authorization, clearing, settlement, money movement, refunds, and chargebacks are handled by the card networks, banks, and licensed processors in the flow. Prava never holds your money. On data security: * Prava maintains **PCI DSS Level 2 / SAQ-D** compliance (PCI DSS is the card industry's data-security standard) and uses **Skyflow, a PCI DSS Level 1 vault provider**, for card vaulting. * Prava does not store raw card numbers or CVV in its application databases. * AI agents and apps never receive the user's underlying card number or CVV. Full texts: [Terms & Conditions](https://www.prava.space/terms-conditions) · [Privacy Policy](https://www.prava.space/privacypolicy) · [Security](https://www.prava.space/security) # Go-Live Checklist Source: https://docs.prava.space/guides/go-live-checklist Everything between a working sandbox integration and live payments — and exactly which steps need Prava. You've completed the flow in sandbox ([tutorial](/guides/add-payments-to-your-ai-app) · [REST walkthrough](/guides/rest-checkout-walkthrough)). Here's the full path to production. ## The checklist Run one complete payment in sandbox: session → card entry → passkey → credentials → checkout → report-status → `completed`. If any step surprises you, fix it now; [Errors](/api-reference/errors) has every code. Production is a separate environment you enable at [dashboard.prava.space](https://dashboard.prava.space) (the [developer dashboard](/dashboard)). Going live may require additional verification of your entity: **This step needs Prava's help — everything else is self-serve.** Email [support@prava.space](mailto:support@prava.space) with your account email, your entity/merchant name, and what you're requesting. We'll take it from there. | | Sandbox | Production | | -------- | --------------------------------- | ------------------------- | | Keys | `pk_test_*` / `sk_test_*` | `pk_live_*` / `sk_live_*` | | Base URL | `https://sandbox.api.prava.space` | `https://api.prava.space` | Keys are environment-bound: a `sk_test_*` key against the production host fails with `AUTH_1001`. Your production frontend domain(s) must be in **allowed domains** in the dashboard; the iframe and session middleware reject other origins. `sk_live_*` lives only in server-side env/secret storage, never in client code, version control, or logs. Rotate immediately if exposed. * Report **every** outcome via [Report Status](/api-reference/report-status), including `DECLINED`. * Handle session expiry (15 min): create sessions late, and re-create rather than reuse. * Log the `X-Response-ID` header from every Prava response; it's how support traces issues. ## Quick self-audit | Check | Where | | -------------------------------------------- | -------------------------------------------- | | One full sandbox payment reached `completed` | [Testing in Sandbox](/api-reference/testing) | | Production enabled + entity verified | dashboard.prava.space | | Live keys deployed server-side only | your infra | | Production domains allowlisted | dashboard.prava.space | | Declines and expiry handled + reported | your code | Questions at any step: [support@prava.space](mailto:support@prava.space). Include the `X-Response-ID` of any failing request. # REST Checkout Walkthrough Source: https://docs.prava.space/guides/rest-checkout-walkthrough The complete payment flow with nothing but cURL — hosted card entry, zero frontend code. The fastest way to see a full Prava payment: **hosted mode**, where Prava serves the card-entry page and your side is three HTTP calls. Everything below is copy-pasteable; swap in your own `sk_test_*` key. ## 1. Create the session ```bash theme={null} curl -X POST https://sandbox.api.prava.space/v1/sessions \ -H "Authorization: Bearer sk_test_xxx" \ -H "Content-Type: application/json" \ -d '{ "user_id": "user_123", "user_email": "user@example.com", "total_amount": "49.99", "currency": "USD", "integration_type": "full_checkout", "callback_url": "https://yourapp.com/payment-done", "purchase_context": [{ "merchant_details": { "name": "Example Store", "url": "https://examplestore.com", "country_code_iso2": "US" }, "product_details": [{ "description": "Wireless Headphones", "unit_price": "49.99", "quantity": 1 }] }] }' ``` Response: ```json theme={null} { "session_id": "ses_abc123", "session_token": "eyJhbGciOi…", "iframe_url": "https://sandbox.collect.prava.space?session=…", "order_id": "ord_xyz789", "expires_at": "2026-01-01T00:15:00Z" } ``` The session expires at `expires_at`, **15 minutes** after creation. Don't create it until the cardholder is ready. ## 2. Send the cardholder to the hosted page Redirect (or link) the cardholder to the `iframe_url`. On Prava's hosted page they enter the card and approve with a **passkey** (Touch ID / Face ID); Prava then redirects them to your `callback_url`. No SDK, no frontend code, no card data anywhere near you. Behind the scenes Prava runs authentication, mandate registration (recording the cardholder's permission to pay), and tokenization (swapping the real card for one-time credentials): [what Prava does for you](/concepts/payments#what-prava-does-for-you-in-the-middle). ## 3. Poll the payment result ```bash theme={null} curl https://sandbox.api.prava.space/v1/sessions/ses_abc123/payment-result \ -H "Authorization: Bearer sk_test_xxx" ``` While the cardholder is still on the hosted page you'll see `"status": "pending"`. Once they finish: ```json theme={null} { "session_id": "ses_abc123", "order_id": "ord_xyz789", "status": "awaiting_result", "transactions": [{ "txn_id": "txn_001", "status": "awaiting_result", "line_items": [{ "txn_ref_id": "tli_001", "merchant_name": "Example Store", "merchant_url": "https://examplestore.com", "total_amount": "49.99", "status": "awaiting_result", "token": "4323126882557932", "dynamic_cvv": "957", "expiry_month": "12", "expiry_year": "2028", "products": [{ "product_ref_id": "prd_001", "external_product_id": null, "name": "Wireless Headphones", "unit_price": "49.99", "quantity": 1 }] }] }] } ``` `token` + `dynamic_cvv` + expiry are the **one-time card credentials**. Use them at the merchant's checkout like a normal card; they're single-use, merchant-locked, and amount-scoped. ## 4. Report the outcome After the checkout runs (note the `txn_ref_id` from step 3): ```bash theme={null} curl -X POST https://sandbox.api.prava.space/v1/sessions/ses_abc123/report-status \ -H "Authorization: Bearer sk_test_xxx" \ -H "Content-Type: application/json" \ -d '{ "txn_ref_id": "tli_001", "txn_status": "APPROVED" }' ``` Report `DECLINED` if the checkout failed; either way, always report. Re-poll the payment result and the status is now `"completed"` (or `"failed"`). ## That's the whole flow ``` create session → cardholder pays on hosted page → poll result → checkout with token → report status ``` The same flow with Prava's card UI inside your own page. What you need to run this walkthrough end to end. # Introduction Source: https://docs.prava.space/index An overview of the Prava Payments API Prava — the payment stack for agentic checkout Prava turns a user's permission into a one-time, merchant-scoped card credential, so your agent can check out anywhere without ever touching raw card data. In-chat, in-voice, or any AI-native interface. Go from zero to a working session in minutes. ## What do you want to do? The interface is an AI agent (OpenClaw, Hermes, ChatGPT). Connect Prava Pay via MCP (Model Context Protocol) or the CLI. No card data, no crypto. You own the interface. Embed the card UI in your page (SDK) or redirect to a Prava-hosted page (API). Pick your integration path. You're a merchant or marketplace. Let agents discover, quote, and check out, with network-level enforcement. See Agentic Commerce. Your agent pays with your card. See exactly what it can and can't do, and the controls that keep you in charge. No code required. See how Prava works in a live demo. No setup required. ## The core ideas in 60 seconds Think of Prava as a **programmable payment proxy**. Instead of your AI agent handling raw credit card numbers (which creates massive compliance and security risks), it handles **permissions to spend**: approved by the user with a passkey and exchanged for a one-time, scoped credential. It asks to buy a specific thing: merchant, amount, constraints. The user approves on their device with a **Passkey** (Touch ID / Face ID). Prava trades that permission with the **card network** for a one-time, merchant-specific credential. The agent uses that ephemeral credential to complete the checkout at the merchant. This decouples the *agent's ability to buy* from the *user's sensitive data*. Four terms carry the whole system. A **session** is the workspace for one payment. A **passkey** is the user's biometric approval (Touch ID / Face ID). A **mandate** is the resulting permission: this merchant, up to this amount, for this long. A **payment token** is the single-use card credential issued against it. Deeper reading: [How Prava Works](/concepts/how-it-works) · [Payments](/concepts/payments) · [Glossary](/concepts/glossary). ## What Prava provides PCI scope is the security compliance you take on when handling card data. No sensitive card data exposure. Your agent never sees real card numbers. Users authorize specific purchases via Passkey authentication. Merchant-specific, amount-scoped credentials enforced at the card network level. Cards can be enrolled for AI-agent-initiated transactions with network-level enforcement. Works with any merchant checkout: Stripe, Braintree, Adyen, or custom PSPs. A single SDK for complex payment orchestration. ## Who benefits This documentation is for **developers building AI-powered applications** that need to make purchases on behalf of users. For the commerce capabilities behind that (discovery, quoting, and checkout), see [Agentic Commerce](/integration/overview). * AI shopping/stylist apps (in-chat discovery → buy) * Travel and booking agents (flight/hotel reservations) * Personal assistant apps (scheduling, recurring purchases) * Marketplaces and merchants who want to accept purchases triggered by LLMs or agents ## Start here Three paths, decided in 10 seconds. Go from zero to a working session. The full flow, end to end. # Browser Harness Source: https://docs.prava.space/integration/browser-harness Prava's checkout automation completes a real merchant checkout and confirms the true total before charging. The **Browser Harness** is the second half of the agentic-commerce pipeline: it **starts from a [UCP](/integration/ucp) quote** and turns that estimate into a placed, verified order. Most merchants don't expose a direct payment API, so to actually place an order, someone has to go through the merchant's own checkout. The Browser Harness does exactly that: it drives the real Shopify checkout, works out the true final total, and pays with the one-time tokenized card (a single-use stand-in for the real card). An AI application completes the purchase without ever touching the raw card or the merchant's checkout code. The Browser Harness is the **checkout** step after a UCP [quote](/integration/ucp). It powers [`prava shop checkout`](/prava-pay/shopping#checkout) and hosted agentic purchases. ## What it does Fills the merchant's checkout (contact, shipping address, delivery option) and submits payment with the one-time token. No merchant integration required. Reconciles subtotal, shipping, and tax against the live checkout and waits for the amount to settle **before** charging, so there are no surprise totals. On success you get an **order id** and a payment **status**: a checkable confirmation the order was placed. It learns a merchant's checkout on the first run and completes later runs consistently, healing automatically if the page changes. ## Why it matters The amount is **reconciled and stable** before payment is attempted. If the total shifts mid-checkout (late shipping, tax, or add-ons), reconciliation restarts rather than charging the wrong amount. A checkout runs as a single, tracked flow rather than a blind retry loop, so a purchase either completes and returns an order, or fails cleanly for you to re-quote. It pays with the **single-use, merchant-scoped** credential from the session. Even inside the checkout, the credential can't be reused or redirected to another merchant. ## How it fits the flow ```mermaid theme={null} flowchart LR U["UCP quote
price the item"] B["Browser Harness
confirm true total"] R["Order placed
order id + status"] U --> B -->|pays with one-time token| R ``` The [UCP](/integration/ucp) quote gives an estimate; the Browser Harness turns it into a **completed, verified order**. Today it targets **Shopify** checkouts. ## Next How products are discovered and quoted before checkout. The full discover → quote → checkout → pay pipeline. # Dining & Reservations Source: https://docs.prava.space/integration/dining Agent-booked tables and food orders. Coming soon. **Coming soon.** Dining is on the Prava roadmap and is not live yet. The plan: your agent books tables and orders food through platforms such as OpenTable and Toast, paying deposits or bills with one-time, amount-locked credentials. Same approval model as every Prava flow: the agent proposes, you approve with a passkey, Prava executes. Food delivery is already possible today in supported regions via merchant skills (for example Swiggy and Zepto) paired with [Prava Pay](/prava-pay/overview). Building in dining and want early access? [support@prava.space](mailto:support@prava.space). # Commerce & Merchant FAQ Source: https://docs.prava.space/integration/faqs Common questions about agentic commerce, merchants, refunds, and security. *This FAQ covers **Agentic Commerce** (UCP + Browser Harness) and merchant questions. For SDK/REST API integration questions, see the [Developer FAQ](/developer-faq).* ## General **Q: Do merchants need to change their checkout?** A: No. Prava tokenizes payments and routes them to the merchant through existing card/acquirer flows. The merchant's checkout remains unchanged: Prava's payment tokens work like regular card details at any standard checkout. **Q: Who is the merchant-of-record (MoR)?** A: The merchant-of-record is the merchant providing goods or services to the user; that is, the merchant where the AI application is performing the transaction. Prava acts as a payment infrastructure layer and does not take on MoR responsibilities. **Q: What about refunds and disputes?** A: Refunds follow the same flow as existing payment rails. The merchant can issue refunds directly through their existing systems, or the AI application can initiate refunds on behalf of the user through the merchant. Prava does not expose separate refund endpoints. Disputes are handled through the standard card network dispute process. **Q: How does card data stay secure?** A: Card data is collected via a PCI DSS compliant iframe served from Prava's domain. Raw card numbers never touch your servers, DOM, or JavaScript. All tokenization happens in an isolated, sandboxed iframe with origin-locked PostMessage communication. **Q: What happens if a payment token is used at the wrong merchant?** A: The card network will decline the transaction. Payment tokens are merchant-locked: they can only be used at the merchant specified in the mandate (the user-approved authorization that fixes a payment's merchant, amount, and frequency). This is enforced both at the network level and by Prava. **Q: Can AI agents make purchases without user approval?** A: No. Every payment requires explicit user authentication via Passkey (biometric/device confirmation). The agent cannot bypass this step. Once a mandate is approved, the agent can invoke it within the mandate's constraints (amount, merchant, frequency), but the user approved those constraints upfront. **Q: What card networks are supported?** A: Prava supports major card networks. Contact us at [support@prava.space](mailto:support@prava.space) for the latest list of supported networks. ## Technical **Q: What currencies does Prava support?** A: Prava supports 47+ currencies including USD, EUR, GBP, INR, CAD, AUD, JPY, SGD, AED, and many more. The full list is validated at session creation: if you pass an unsupported ISO 4217 currency code, you'll receive a validation error. **Q: How long does a session last?** A: Sessions expire after **15 minutes** by default. The `expires_at` field in the [Create Session](/api-reference/create-session) response tells you exactly when. Once expired, the session token can no longer be used for any SDK operations. You can also configure mandate duration using the `effective_until_minutes` field in `purchase_context` (also defaults to 15 minutes). **Q: What happens if the user closes the browser mid-flow?** A: The session remains valid until it expires. If the user returns within the session window, they can resume. If they need to start over, create a new session. Duplicate transactions are prevented by idempotency: the system generates a deterministic key from the order, flow type, and card, so retries won't create duplicate charges. **Q: What happens if the user's device doesn't support passkeys (WebAuthn)?** A: Transactions cannot be performed without WebAuthn support. Passkey authentication is a hard requirement; there is no fallback mechanism. This ensures the highest level of security for user authorization. Supported devices include those with Touch ID, Face ID, Windows Hello, or a FIDO2 security key. **Q: Are payment tokens single-use?** A: Yes. Each payment token (virtual card number + CVV) can be used exactly **once**. If checkout fails, you must invoke the intent again to get a fresh token (subject to the mandate's constraints). Tokens are also merchant-locked, amount-scoped, and time-limited. **Q: How does mandate expiry work?** A: When a session is created, the `effective_until_minutes` field (default: 15 minutes) sets how long the mandate is valid. Prava checks mandate expiry when you call [Get Payment Result](/api-reference/get-payment-result) or [Report Status](/api-reference/report-status). If the mandate has expired, the status will be reflected in the response and further invocations will be rejected. **Q: Can I create multiple sessions for the same user at the same time?** A: Technically yes, but it's not recommended. Each session is independent with its own token and expiry. In practice, create one session per checkout flow and either complete it or revoke it before creating another. **Q: Is there idempotency protection for transactions?** A: Yes. Each transaction gets a deterministic idempotency key derived from the order ID, flow type, and card identifier. If a duplicate request is detected, the existing transaction is returned instead of creating a new one. This prevents double charges from network retries or repeated submissions. **Q: What is the maximum number of products per session?** A: There is no hard upper limit. You must include at least 1 product in the `product_details` array. However, only 1 `purchase_context` entry (i.e., one merchant) is supported per session; multi-merchant sessions are not currently available. ## Merchants **Q: Are you a Shopify merchant?** A: Prava can already reach Shopify stores through [UCP](/integration/ucp) (Shopify's agent-commerce protocol) and the [Browser Harness](/integration/browser-harness), with no setup needed on your end. If you'd like a direct integration, the **Prava Shopify app** is available by **invite only**. Contact **[support@prava.space](mailto:support@prava.space)** to request merchant onboarding. ## Support * **Developer Support**: [support@prava.space](mailto:support@prava.space) # For Merchants Source: https://docs.prava.space/integration/merchants Selling to AI shoppers: what already works with zero changes, and what to request from Prava. Agents paying with Prava look like **normal card customers** to you. This page tells you what works today with no integration at all, and what to ask Prava for if you want more. ## Works today — zero changes on your end Prava's payment tokens are real card credentials: they work at any standard checkout through your existing payment service provider, or PSP (Stripe, Braintree, Adyen, custom). Nothing to install, nothing to change. If you're a Shopify store advertising [UCP](/integration/ucp) (Shopify's protocol that lets agents shop a store), agents can already discover your products, get quotes, and complete checkout via the [Browser Harness](/integration/browser-harness). No setup on your end. Payments arrive through your normal card/acquirer flow: you stay merchant-of-record, refunds and disputes work exactly as they do today. Details: [FAQs](/integration/faqs). ## By request — email us Two things are currently provisioned by Prava per merchant: | What | Why you'd want it | How to get it | | ------------------------------------------------------- | ------------------------------------------------------------------------ | -------------------------------- | | **Prava Shopify app** (direct integration, invite-only) | Native order attribution and richer agent checkout on your Shopify store | Request "Shopify app onboarding" | | **Settlement model confirmation** | Confirmed during merchant onboarding | Ask when onboarding | **This step needs Prava's help — everything else is self-serve.** Email [support@prava.space](mailto:support@prava.space) with your account email, your entity/merchant name, and what you're requesting. We'll take it from there. When you write, include: your store name and URL, your platform (Shopify or other), and roughly the volume of agent-driven orders you expect. That's everything we need to start. ## How an agent purchase reaches you ```mermaid theme={null} flowchart LR A["Agent
discovers & quotes
(UCP)"] --> B["Browser Harness
confirms true total
at your checkout"] B --> C["Your checkout
one-time card credential
via your existing PSP"] C --> D["You
order + payment,
business as usual"] ``` The credential the agent pays with is **single-use, locked to you, and amount-scoped**: an agent can't reuse it elsewhere or change the total after you quoted it. ## Next The full discover → quote → checkout → pay pipeline. MoR, refunds, disputes, security, supported currencies. # Agentic Commerce Source: https://docs.prava.space/integration/overview The commerce capabilities that let an AI application discover, quote, checkout, and pay. Prava is more than a payment token. It provides the **commerce capabilities** an AI application needs to turn an intent to buy into a completed order: discovering products, pricing them accurately, and completing a real checkout on the merchant's site. ## The capability pipeline ```mermaid theme={null} flowchart LR D["Discover
UCP search & catalog"] Q["Quote
live price, reconciled"] C["Checkout
Browser Harness"] P["Pay
one-time tokenized card"] D --> Q --> C --> P ``` Two capabilities do the heavy lifting, and they work as a pair: Search products, read catalogs, and open quotes across participating Shopify merchants via the Universal Commerce Protocol. Prava's checkout automation confirms the true total from a UCP quote and completes the real merchant checkout. ## Who this is for AI applications and agents that want to **find and buy** on a user's behalf rather than only process a payment. These same capabilities power [Prava Pay's agentic shopping](/prava-pay/shopping) (`prava shop search → product → quote → checkout`); this section explains what's behind those commands. Whichever surface you use, the card is always collected on Prava's secure surface and turned into a one-time, merchant-scoped credential; the AI application never handles a raw card. ## How the pieces fit Search across participating Shopify merchants and read product/variant details. See [UCP](/integration/ucp). UCP opens a checkout for an initial price; the [Browser Harness](/integration/browser-harness) reconciles it against the live checkout so the amount is correct before anyone commits. The Browser Harness fills the checkout, confirms the final total, pays with the one-time token, and returns a verifiable order. # Travel Source: https://docs.prava.space/integration/travel Agent-booked flights, hotels, and trips. Coming soon. **Coming soon.** Travel booking is on the Prava roadmap and is not live yet. The plan: your agent searches and books travel (flights, hotels, packages) through providers such as Duffel and lastminute, and pays with the same one-time, amount-locked credentials used everywhere else in Prava. Same approval model: the agent proposes the itinerary and total, you approve with a passkey, the booking completes. Until then, agents can already pay travel merchants that accept standard card checkout using a [payment session](/prava-pay/sessions) for a known total. Building in travel and want early access? [support@prava.space](mailto:support@prava.space). # UCP Integration Source: https://docs.prava.space/integration/ucp How Prava discovers and prices products across Shopify merchants with the Universal Commerce Protocol. **UCP (Universal Commerce Protocol)** is Shopify's agentic-commerce protocol: a standard way for an agent to **search products, read catalogs, and open a checkout** across participating Shopify merchants. Prava integrates with UCP so an AI application can discover *what* to buy and get an initial price, before the [Browser Harness](/integration/browser-harness) confirms the real total and completes the order. UCP is Shopify-specific. Any Shopify store that advertises UCP is reachable through it. ## What UCP gives you Query a single merchant, or Shopify's **global catalog** across all participating merchants at once. Full product details: variants, options (size/color), pricing, availability, and images. Open a checkout and get an initial price (subtotal, tax estimate, and shipping options where the merchant exposes them). ## How Prava integrates with UCP Prava speaks UCP to Shopify stores that advertise it, and layers a consistent discovery-to-purchase pipeline on top: A Shopify store that advertises UCP exposes a commerce endpoint Prava can call. Prava can target **one merchant** directly, or search Shopify's **global catalog** spanning every participating merchant with a single query. Prava runs the search, normalizes the results into a consistent shape (product id, title, price estimate, image, merchant), and fetches a product's **variants** (options, prices, and availability) so the exact item can be chosen. For the chosen variant and destination, Prava asks UCP to **open a checkout**. That returns a checkout and an **initial proposal**: subtotal, a tax estimate, and (for merchants that expose it) shipping options. This is an *estimate*, not the binding total. Prava drives that live checkout with the [Browser Harness](/integration/browser-harness), which reads and **reconciles the true final total** (shipping + tax settled) and completes the purchase with the one-time tokenized card. This is the first half of the [pipeline](/integration/overview): exactly the chain the CLI exposes as [`prava shop search → product → quote → checkout`](/prava-pay/shopping). ## Supported merchants UCP reaches **participating Shopify merchants** (for example Fashion Nova, SKIMS, Alo Yoga, Steve Madden, Glossier, Everlane, Harry's, and Ridge) plus any Shopify store that advertises UCP. **Coverage varies by merchant.** Some merchants expose shipping options and tax through UCP; others don't. Treat UCP prices as an **estimate**: the **binding final total** is always confirmed at checkout by the [Browser Harness](/integration/browser-harness) before any charge. ## Next How the true total is confirmed and the order is completed from a UCP quote. Run this discovery-to-purchase flow from the command line. # Connect Your Agent Source: https://docs.prava.space/mcp/connect Add the Prava MCP to your client, sign in once, and your agent can pay. Connecting takes about a minute. You add the server URL to your MCP client, the client opens a browser window, and you sign in to approve. No API keys, no manual registration: the server supports automatic client registration (OAuth 2.1 dynamic client registration with PKCE), so any standard MCP client can connect itself. ## Add the server ```bash Claude Code theme={null} claude mcp add --transport http prava https://mcp.pay.prava.space/mcp ``` ```json Cursor (mcp.json) theme={null} { "mcpServers": { "prava": { "url": "https://mcp.pay.prava.space/mcp" } } } ``` ```text Any MCP client theme={null} Server URL: https://mcp.pay.prava.space/mcp Transport: Streamable HTTP Auth: OAuth (automatic — the client discovers and registers itself) ``` ## Sign in and approve 1. Your client opens a browser window to Prava's sign-in. 2. Sign in with your **Prava Pay account** (the same account as [pay.prava.space](https://pay.prava.space); create one there if you're new). 3. Review the requested scopes (`payments:read`, `payments:write`, `checkout:run`) and approve. 4. Done. The connection appears as an agent in your [Prava Pay dashboard](https://pay.prava.space), alongside any CLI-linked agents. Ask your agent to call the `ping` tool to confirm the connection. First time with Prava? You'll also add a card and a delivery address. Your agent can walk you through both: card entry happens on Prava's secure page (via a `payment_url`), and addresses via the `shop_add_address` tool. The agent never sees the card. ## Staying connected * **Tokens refresh automatically.** The client holds a refresh token; you won't re-approve on every session. * **Revoking access**: revoke the agent in your [Prava Pay dashboard](https://pay.prava.space). Its tokens stop working immediately. * **Reconnecting**: remove and re-add the server in your client, then sign in again. ## Troubleshooting | Symptom | Cause | Fix | | -------------------------------------------------- | --------------------------------------- | ---------------------------------------------------- | | `401` / "Missing or malformed Authorization" | The client isn't sending a bearer token | Re-run the OAuth flow (remove and re-add the server) | | `403 insufficient_scope` on a tool call | The token lacks that tool's scope | Reconnect and approve all requested scopes | | "This token has no agent context" | The connection wasn't fully approved | Reconnect; finish the sign-in and approval step | | Tool calls fail after you revoked in the dashboard | Expected: revocation is immediate | Reconnect if it was a mistake | Still stuck? [support@prava.space](mailto:support@prava.space). ## Next What each tool does and the order to call them in. Approvals, cards, spending controls, revocation. # Prava MCP Overview Source: https://docs.prava.space/mcp/overview Connect any MCP-capable AI agent to Prava payments. No CLI, no SDK, no card data. The **Prava MCP** is a hosted [Model Context Protocol](https://modelcontextprotocol.io) server. MCP is the standard way to give an AI agent new abilities: you add a server URL to your agent, and the agent gains that server's tools. Adding Prava's server gives your agent the ability to pay and shop with your card, under your approval. ``` https://mcp.pay.prava.space/mcp ``` Works with any MCP client: Claude Code, Cursor, Poke, or your own app. Setup is one URL and one sign-in. See [Connect](/mcp/connect). This is the **payments** MCP. It is not the same as the **docs** MCP (`docs.prava.space/mcp`), which only lets an agent read these docs. See [Use Prava Docs with AI](/ai). ## What your agent can do | Ability | Tools | | ------------------------------------- | --------------------------------------------------------------------- | | Pay a known total at a named merchant | `create_payment_session`, `get_payment_status` | | Find and buy products | `shop_search`, `shop_product`, `shop_quote`, `shop_checkout` | | Manage delivery addresses | `shop_list_addresses`, `shop_add_address`, `shop_set_default_address` | | See your cards and agents | `list_cards`, `list_agents` | Full parameters and flows: [Tools reference](/mcp/tools). ## The credential firewall The MCP is built so that payment credentials never reach the agent. This is stronger than the CLI path, where the agent handles a one-time token itself. Example: your agent buys you a phone case. 1. The agent finds the case and locks a price (`shop_quote`): \$18.50. 2. It creates a payment session for exactly \$18.50 and sends you a `payment_url`. 3. You open the link and approve with your passkey (a fingerprint or face confirmation on your device). The agent only ever learns the *status*: `get_payment_status` returns `completed`, never the card token. 4. The agent calls `shop_checkout`. Prava's gateway pulls the one-time credentials server-side, completes the merchant checkout, and returns only the outcome: `{ status: "paid", order_id: … }`. At no point does the agent, the LLM, or the MCP client see a card number, token, or CVV. ## Scopes Access is scoped with OAuth. A scope is a named permission: your MCP client requests these when you connect, and you approve them once. | Scope | Grants | | ---------------- | ----------------------------------------------------------------------------- | | `payments:read` | Read cards (masked), agents, payment status, product search, masked addresses | | `payments:write` | Lock quotes, add or change delivery addresses | | `checkout:run` | Create payment sessions and execute checkouts | ## MCP, CLI, or SDK? | | **Prava MCP** | **[Prava CLI](/prava-pay/overview)** | **[Prava SDK](/sdk/overview)** | | ---------------------- | ------------------------------------- | ------------------------------------------- | ------------------------------ | | You are | An agent owner or agent-platform user | An agent owner on a machine with a terminal | A developer building an AI app | | Setup | Add one URL, sign in | Install `@prava-sdk/cli`, link via browser | API keys + backend + frontend | | Agent sees credentials | Never (gateway-side) | Yes, one-time scoped token | Your backend handles them | | Best when | Your agent platform supports MCP | Your agent runs shell commands | You own the product surface | All three share the same account, cards, guardrails, and approval model. An agent connected via MCP shows up in your [Prava Pay dashboard](https://pay.prava.space) next to CLI-linked agents, and you can revoke it the same way. ## Next One URL, one sign-in. Claude Code, Cursor, and generic clients. Every tool, its parameters, and the buy-flow chain. # Tools Reference Source: https://docs.prava.space/mcp/tools Every Prava MCP tool: parameters, scopes, and the order to call them in. The server exposes 12 tools. Your MCP client discovers them automatically; this page is the map of what they do and how they chain together. ## The buy-flow chain To buy a product, tools must run in this order. The key rule: `create_payment_session` authorizes payment but never places an order. Only `shop_checkout` buys. ```mermaid theme={null} flowchart LR A[shop_search] --> B[shop_product] --> C[shop_quote] C --> D[create_payment_session] D --> E{{User approves
payment_url}} E --> F[shop_checkout] ``` 1. `shop_search` finds products. `shop_product` gets the exact variant. 2. `shop_quote` locks a live total and returns a `checkout_session_id`. 3. `create_payment_session` for that exact total returns a `payment_url`. 4. The user opens the `payment_url` and approves with their passkey (a biometric confirmation on their device). 5. `shop_checkout` places the order. Credentials stay server-side. Paying a known total at a named merchant (a bill, an invoice, a checkout the user is already on) skips discovery: `create_payment_session` → user approves → done. ## Payments ### create\_payment\_session — `checkout:run` Creates a payment session and returns a `payment_url` for the user to approve. Charges nothing by itself. | Parameter | Type | Notes | | ------------------ | ---------------- | ------------------------------------------------------------ | | `total_amount` | string, required | Decimal string, e.g. `"49.99"` | | `currency` | string, required | 3-letter ISO 4217, e.g. `USD` | | `merchant_name` | string, required | Display name shown to the user | | `merchant_url` | string, required | Merchant website URL | | `merchant_country` | string, required | 2-letter ISO 3166-1, e.g. `US` | | `products` | array, required | Line items: `{ description, unit_price, quantity? }` | | `idempotency_key` | string, optional | Same key returns the original session instead of a duplicate | Returns `{ session_id, payment_url, expires_at, replayed }`. ### get\_payment\_status — `payments:read` Checks a payment session. Returns **status only**: `pending`, `completed`, `failed`, or `not_found`. Payment credentials never leave the gateway. | Parameter | Type | Notes | | ------------ | ---------------- | ----------------------------- | | `session_id` | string, required | From `create_payment_session` | ## Shopping ### shop\_search — `payments:read` | Parameter | Type | Notes | | ---------- | ---------------- | ------------------------------------- | | `query` | string, required | e.g. `"running shoes size 10"` | | `merchant` | string, optional | Merchant domain to scope the search | | `cursor` | string, optional | Pagination cursor from a prior search | Returns product listings: `product_id`, price estimate, merchant. ### shop\_product — `payments:read` | Parameter | Type | Notes | | ------------ | ---------------- | -------------------------------------- | | `product_id` | string, required | From `shop_search` | | `merchant` | string, optional | Merchant domain from the search result | Returns purchasable offers and variants: `variant_id`, price, availability. ### shop\_quote — `payments:write` Opens a checkout and locks a live price. Requires a delivery address on file (check with `shop_list_addresses`). | Parameter | Type | Notes | | ------------ | ---------------- | -------------------------------- | | `variant_id` | string, required | From `shop_product` | | `merchant` | string, required | Merchant domain from that offer | | `quantity` | number, optional | Defaults to 1 | | `address_id` | string, optional | Defaults to your default address | Returns `checkout_session_id` plus the exact total to pay. ### shop\_checkout — `checkout:run` The required final step. Places the order for a prior quote, paying with an **approved** payment session. The session's amount must match the quote total. | Parameter | Type | Notes | | --------------------- | ---------------- | ------------------------------------------------- | | `checkout_session_id` | string, required | From `shop_quote` | | `payment_session_id` | string, required | An approved session from `create_payment_session` | Returns `{ status, order_id, amount, replayed }`. If the payment isn't approved yet, the tool says so; poll `get_payment_status` until `completed`, then retry. ## Addresses Address reads are masked. Full address details stay server-side and go only to the merchant. ### shop\_list\_addresses — `payments:read` No parameters. Returns masked summaries (id, label, short summary, which is default) plus whether a contact phone is on file. ### shop\_add\_address — `payments:write` | Parameter | Type | Notes | | ------------------------------- | ----------------- | ----------------------------------- | | `first_name`, `last_name` | string, required | | | `street` | string, required | Line 1; `street2` optional | | `city`, `region`, `postal_code` | string, required | | | `country` | string, required | 2-letter ISO 3166-1 | | `label` | string, optional | e.g. `"Home"` | | `phone` | string, optional | Contact phone, saved to the profile | | `set_default` | boolean, optional | Make this the default | ### shop\_set\_default\_address — `payments:write` | Parameter | Type | Notes | | ------------ | ---------------- | -------------------------- | | `address_id` | string, required | From `shop_list_addresses` | ## Account ### list\_cards — `payments:read` No parameters. Returns the user's saved cards, masked: last4, brand, expiry. Never full numbers. ### list\_agents — `payments:read` No parameters. Returns the user's connected agents, including this connection. ### ping — no scope Health probe. Returns `{ pong: true }` and the server time. Use it to confirm the connection after [setup](/mcp/connect). # Linking an Agent Source: https://docs.prava.space/prava-pay/linking Connect an agent to a Prava account with a one-time, owner-approved link. Before an agent can pay, its owner has to **link and approve** it. Linking establishes a secure identity for the agent on this machine, and approval happens in the owner's browser, so nothing can spend money without a human explicitly allowing it. ## The linking lifecycle `prava setup` creates the agent's identity locally and prints a one-time approval URL. The owner opens the URL, signs in to Prava, and approves the agent. `prava setup poll` waits for the approval and finalizes the link. ## `prava setup` ```bash theme={null} prava setup --name "Claude Code" --platform claude-code --description "Anthropic's coding agent" ``` | Option | Required | Description | | ---------------------- | ----------- | ------------------------------------------------------------------------------ | | `--name ` | Yes | Human-friendly agent name shown on the approval screen (e.g. `"Claude Code"`). | | `--platform ` | Recommended | The agent's platform identifier (see below). | | `--description ` | No | Short description shown to the owner when approving. | It prints the approval URL and **exits immediately**; it does not block: ``` To link this agent, open this URL and approve: https://pay.prava.space/link-agent?lid=lk_xxxxxxxx Link expires in 15 minutes. Run `prava setup poll` to wait for approval. ``` Give the agent a **specific** name and platform (`"Claude Code"`, `claude-code`) rather than a generic `"My Agent"`. The owner sees this when deciding whether to approve. Use the identifier that matches the agent's runtime. If none fits, use `custom`. `claude-code` · `codex` · `cursor` · `gemini-cli` · `hermes` · `aider` · `goose` · `copilot-cli` · `github-copilot` · `windsurf` · `cline` · `continue` · `amazon-q` · `roo-code` · `kilo-code` · `sourcegraph-cody` · `tabnine` · `augment-code` · `amp` · `zed` · `kiro` · `blackbox` · `opencode` · `qwen-code` · `kimi-cli` · `mistral-vibe` · `warp` · `coro-code` · `devin` · `openhands` · `jules` · `swe-agent` · `manus` · `openai-operator` · `claude-computer-use` · `replit-agent` · `bolt` · `v0` · `lovable` · `custom` ## Approve in the browser The owner opens the printed URL, signs in at [pay.prava.space](https://pay.prava.space), and clicks **Approve**. The link is valid for **15 minutes**. If it lapses, run `prava setup` again. ## `prava setup poll` After the owner approves, confirm the link: ```bash theme={null} prava setup poll ``` ``` Waiting for approval of "Claude Code"... ... Linked! Agent ID: aa_7kMnP2 Ready to create sessions. ``` `poll` waits up to 15 minutes. Other outcomes: | Output | Meaning | | ------------------------------------------- | -------------------------------------------- | | `Setup denied by user.` | The owner rejected the agent in the browser. | | `Link expired. Run \`prava setup\` again.\` | The 15-minute window passed before approval. | | `No agent configured. Run: prava setup …` | No link was started on this machine yet. | ## `prava status` Check the current link state at any time: ```bash theme={null} prava status ``` A linked, active agent prints: ``` Agent: Claude Code (aa_7kMnP2) Status: active Linked: 2026-07-06 ``` Other states: ``` Agent: Claude Code Status: pending Link: https://pay.prava.space/link-agent?lid=lk_xxxxxxxx ``` Open the link and approve, then run `prava setup poll`. ``` Link expired. Run `prava setup` again. ``` Start over with `prava setup`. ``` No agent configured. Run: prava setup --name "" ``` Nothing is linked on this machine yet. Run `prava setup`. ## One agent per machine Each machine holds a **single** linked agent identity. That identity is stored locally in your home directory and is what proves requests come from *this* approved agent. * To **switch** to a different agent, remove the local identity and run `prava setup` again. * Keep the identity private: anyone who can read it can act as this agent (within the owner's guardrails). Prefer a non-default location for the identity file? Set `PRAVA_STATE_DIR` to a directory you control before running `prava setup`. ## Owner controls Approval isn't the end of the owner's control. From the Prava dashboard, an owner can **revoke** any agent (after which its requests stop working immediately) or suspend their whole account. Spending limits and saved addresses are configured there too, and Prava enforces them on every purchase. See [Guardrails](/concepts/guardrails). # Prava Pay Overview Source: https://docs.prava.space/prava-pay/overview Give an AI agent the ability to pay at real merchants — safely, from the command line. *Not sure this is the right path? See [Choosing Your Integration](/choosing-your-integration).* **Prava Pay** lets an AI agent (Claude Code, Cursor, Codex, or any coding agent) make real purchases on a person's behalf. This section covers the **CLI**: a single command-line tool, `@prava-sdk/cli`, that the agent runs directly. No servers to stand up, no card data to handle. Agent platform supports MCP? MCP is the standard protocol for giving an agent tools. You may not need the CLI at all: connect via the [Prava MCP](/mcp/overview) instead (one URL, same account and guardrails). Builders of autonomous or human-in-the-loop agents that need to **buy something** (order a product, pay a merchant, complete a checkout) without ever touching a raw card number. ## How it fits together Prava Pay sits between the agent and the merchant. The agent describes *what* it wants to buy; Prava handles the sensitive parts: collecting the card, issuing **one-time, merchant-scoped credentials**, and completing the payment. ```mermaid theme={null} flowchart LR A["Agent (CLI)"] P["Prava
secure card entry
owner's limits enforced"] M["Merchant
checkout completes"] A -->|buy this| P -->|one-time token| M ``` The person who owns the account (the **agent owner**) stays in control the whole time: * They **link and approve** an agent once, from their browser. An unapproved agent can do nothing. * They **enter card details themselves** in Prava's secure page. The agent never sees the real card. * They set the **guardrails** (spending limits, saved addresses) that Prava enforces on every purchase. The owner does all of this from the **Prava Pay dashboard** at [pay.prava.space](https://pay.prava.space): **Two portals, two audiences.** [dashboard.prava.space](https://dashboard.prava.space) is the [**developer console**](/dashboard): sign up, create API keys, manage allowed domains, switch to production. [pay.prava.space](https://pay.prava.space) is the [**Prava Pay dashboard**](/prava-pay/your-wallet) for agent *owners*: approve agent links, enroll cards, set spending controls. A developer integrating the API only needs the console; an agent owner only needs the Prava Pay dashboard. The agent only ever receives a **one-time payment credential** scoped to a single, already-priced purchase. It cannot reuse it, change the amount, or pay a different merchant. ## The two ways to pay Prava Pay supports two flows, depending on how much the agent needs to do: You already know the merchant, items, and total. Create a **payment session**, the owner enters the card, and you receive credentials to complete checkout. See [Payment sessions](/prava-pay/sessions). The agent **discovers** the product too: search across merchants, compare offers, get a live quote, then pay. See [Agentic shopping](/prava-pay/shopping). ## Install Prava Pay is a global CLI. The **owner** installs it once on the machine the agent runs on: ```bash theme={null} npm install -g @prava-sdk/cli ``` Verify the install: ```bash theme={null} prava --version # 3.0.3 ``` Always install the CLI **yourself** as the owner. Don't have the agent install or update it silently: installation is a trust action, and keeping it explicit keeps you in the loop. Alongside the CLI, install the matching **skill** so the agent knows which command to run when: ```bash theme={null} npx --yes skills add https://github.com/Prava-Payments/prava-skills \ --skill prava-shopping --global --yes --full-depth ``` ## Guided by Skills The CLI does the work; **Skills guide the agent on *which* command to call and *when*.** A skill encodes the correct flow, paces the agent (one decision per turn), and forces a confirmation before any spend, keeping the human in control. The CLI runs without a skill, but will nudge you to install one if it's missing. * **`prava-pay`** pays for a known item: `setup` → `sessions create` → `poll` → checkout. * **`prava-shopping`** finds *and* buys: `search` → `product` → `quote` → `checkout`. See **[Skills](/prava-pay/skills)** for the full list, install commands, and how they keep the agent safe. ## What you'll do next Run `prava setup`, approve the agent in your browser, and confirm the link. See [Linking an agent](/prava-pay/linking). Create a payment session (or shop for a product), enter your card, and let the agent complete the checkout. See the [Quickstart](/prava-pay/quickstart). Know what each message means and how to recover. See [Troubleshooting](/prava-pay/troubleshooting). ## Prava Pay vs. the Prava SDK Prava Pay (CLI) and the [Prava SDK](/sdk/overview) solve different problems: | | **Prava Pay (CLI)** | **Prava SDK** | | ---------- | ------------------------------- | ----------------------------------------------------- | | Runs where | On the agent's machine | In a merchant's web app | | Audience | AI agents & their owners | Merchants embedding Prava at checkout | | Card entry | Owner enters it in Prava's page | Shopper enters it in an embedded iframe | | You get | A tool the agent calls | A JS library ([`collectPAN`](/sdk/cards/collect-pan)) | They share the same secure backbone. Pick Prava Pay when the **interface is owned by an AI agent**. # Quickstart Source: https://docs.prava.space/prava-pay/quickstart From zero to a completed purchase in one sitting. This walks through the **happy path** end to end: link an agent, create a payment session, enter a card, and complete a purchase. Every command and output below is exactly what the CLI prints. Steps marked **You** are done by the account owner (a human, in a browser). Everything else is run by the agent (or you, at a terminal). ## Prerequisites * The CLI installed: `npm install -g @prava-sdk/cli` (see [Overview](/prava-pay/overview)). * A Prava account at [pay.prava.space](https://pay.prava.space). ## 1. Link the agent ```bash theme={null} prava setup --name "Claude Code" --platform claude-code ``` The CLI prints a link and exits immediately: ``` To link this agent, open this URL and approve: https://pay.prava.space/link-agent?lid=lk_xxxxxxxx Link expires in 15 minutes. Run `prava setup poll` to wait for approval. ``` **You:** open that URL, sign in, and click **Approve**. Then wait for the approval to land: ```bash theme={null} prava setup poll ``` ``` Waiting for approval of "Claude Code"... .. Linked! Agent ID: aa_7kMnP2 Ready to create sessions. ``` Run `prava status` any time to confirm the agent is linked and active. ## 2. Create a payment session You know the merchant and items, so create a session for the exact order: ```bash theme={null} prava sessions create \ --total-amount "8.50" \ --currency USD \ --merchant-name "Blue Bottle Coffee" \ --merchant-url "https://bluebottlecoffee.com" \ --merchant-country US \ --product '{"description":"Latte","unit_price":"5.00","quantity":1}' \ --product '{"description":"Croissant","unit_price":"3.50","quantity":1}' ``` ``` Session created. Session ID: ses_abc123 Payment URL: https://collect.prava.space?session=… Share this URL to complete card entry. Run `prava sessions poll --session-id ses_abc123` to wait for card entry. ``` ## 3. Enter the card **You:** open the **Payment URL**, enter your card in Prava's secure page, and confirm. Meanwhile, the agent waits for the card to be tokenized (swapped for a one-time card number and code that stand in for the real card): ```bash theme={null} prava sessions poll --session-id ses_abc123 ``` ``` Waiting for card entry on session ses_abc123... .... Card tokenized. Token: 4323126882557932 Cryptogram: 957 Expiry: 12/2028 ``` These credentials are **single-use and short-lived**: use them right away to complete the checkout. Don't store or reuse them. ## 4. Complete the checkout Use the token and cryptogram at the merchant's checkout to finish the purchase. If you're using **agentic shopping** (Prava discovered the product), the last step is a single command: ```bash theme={null} prava shop checkout \ --checkout-session-id ches_xxx \ --token 4323126882557932 \ --cryptogram 957 \ --expiry-month 12 --expiry-year 2028 \ --yes ``` ``` ✓ Paid. Amount: $8.50 USD Order: ord_abc123 ``` That's the whole loop. 🎉 ## Where to go next The full linking lifecycle, platforms, and status states. Every flag for `sessions create` and `sessions poll`. Search, compare, quote, and pay: the discovery flow. What each message means and how to recover. # Payment Sessions Source: https://docs.prava.space/prava-pay/sessions Create a payment for a known order and receive one-time card credentials. A **payment session** is the direct way to pay when you already know the merchant, the items, and the total. You create the session, the owner enters their card in Prava's secure page, and you receive **one-time credentials** to complete the checkout. Use this flow when *you* found the product. If you want Prava to help **discover** the product too, see [Agentic shopping](/prava-pay/shopping) instead. The agent must be [linked](/prava-pay/linking) first. If it isn't, session commands exit with `Agent not linked. Run: prava setup --name ""`. ## `prava sessions create` ```bash theme={null} prava sessions create \ --total-amount "8.50" \ --currency USD \ --merchant-name "Blue Bottle Coffee" \ --merchant-url "https://bluebottlecoffee.com" \ --merchant-country US \ --product '{"description":"Latte","unit_price":"5.00","quantity":1}' \ --product '{"description":"Croissant","unit_price":"3.50","quantity":1}' ``` | Option | Required | Description | | --------------------------- | -------- | ---------------------------------------------------------------------------------------------------- | | `--total-amount ` | Yes | Total as a string, e.g. `"8.50"`. Must equal the sum of `unit_price × quantity` across all products. | | `--currency ` | Yes | ISO 4217 code, e.g. `USD`, `EUR`, `GBP`, `INR`. | | `--merchant-name ` | Yes | Merchant display name (shown to the owner). | | `--merchant-url ` | Yes | Full merchant URL **including `https://`**. | | `--merchant-country ` | Yes | ISO 3166-1 alpha-2 country, e.g. `US`. | | `--product ` | Yes | A product line item as JSON. **Repeat** the flag for each distinct item. | Each `--product` is a JSON object: ```json theme={null} { "description": "Latte", "unit_price": "5.00", "quantity": 1 } ``` For multiple units of the same item, set `"quantity"`; don't repeat `--product` or bake counts into the description. The total must reconcile with the line items. On success: ``` Session created. Session ID: ses_abc123 Payment URL: https://collect.prava.space?session=… Share this URL to complete card entry. Run `prava sessions poll --session-id ses_abc123` to wait for card entry. ``` Give the **Payment URL** to the owner. They open it and enter their card in Prava's secure page; the card details never pass through the agent. ## `prava sessions poll` Wait for the card to be entered and tokenized (exchanged for one-time credentials that stand in for the real card): ```bash theme={null} prava sessions poll --session-id ses_abc123 ``` | Option | Required | Description | | ------------------- | -------- | ---------------------------------------- | | `--session-id ` | Yes | The `Session ID` from `sessions create`. | While waiting it prints dots; once the owner completes card entry: ``` Waiting for card entry on session ses_abc123... .... Card tokenized. Token: 4323126882557932 Cryptogram: 957 Expiry: 12/2028 ``` `poll` waits up to **10 minutes** for the owner to enter the card; that's the CLI's card-entry window. The underlying session expires **15 minutes** after creation; once it does, create a new session. ### What you receive | Field | Format | Use it as | | -------------- | --------------- | ------------------------------------ | | **Token** | 16-digit number | The card number at checkout. | | **Cryptogram** | 3-digit number | The CVV / security code at checkout. | | **Expiry** | `MM/YYYY` | The card expiry at checkout. | These credentials are **single-use and expire quickly**. Complete the checkout immediately after polling; don't cache, log, or reuse them. If they expire, create a new session. ## Exit codes Session commands use exit codes so an agent can branch reliably: | Code | Meaning | | ---- | -------------------------------------------------------------------------- | | `0` | Success. | | `1` | Error or timeout (e.g. session expired, tokenization failed). | | `2` | Not linked / no agent configured; run [`prava setup`](/prava-pay/linking). | ## Common outcomes | Output | What it means | What to do | | -------------------------------------------------------- | ----------------------------------------------------- | ---------------------------------------------------------------------------------- | | `Session expired. Run \`prava sessions create\` again.\` | The owner didn't finish card entry within 10 minutes. | Re-create the session; have the card ready. | | `Tokenization failed.` | The card couldn't be processed. | Try again, or a different card. See [Troubleshooting](/prava-pay/troubleshooting). | | `Invalid product JSON: …` | A `--product` value isn't valid JSON. | Fix the JSON: `'{"description":"…","unit_price":"…","quantity":1}'`. | | `Failed to create session: …` | The order was rejected. | Check the amount reconciles with the line items and the URL includes `https://`. | Next: use the token to [complete a checkout](/prava-pay/shopping#checkout), or drive the merchant's checkout directly with the returned credentials. # Agentic Shopping Source: https://docs.prava.space/prava-pay/shopping Discover a product, compare offers, get a live quote, and pay — all from the CLI. When the agent needs to **find** the product too, Prava Pay adds a discovery flow on top of payments: **search → product → quote → checkout**. Each step hands you exactly what you need to run the next. For the richest guided experience (pacing, confirmations, masked previews), install the `prava-shopping` skill alongside the CLI. The `shop` commands work without it, but the skill adds the guardrails that keep the agent honest with the user. ```bash theme={null} npx --yes skills add https://github.com/Prava-Payments/prava-skills \ --skill prava-shopping --global --yes --full-depth ``` The agent must be [linked](/prava-pay/linking) before any `shop` command works. ## Delivery addresses Physical goods need a delivery address and a contact phone. These are stored on the **owner's account** and hydrated server-side at purchase time: the agent only ever sees **masked summaries**, never full street addresses or phone numbers. The agent never handles the buyer's full address or phone. It selects *which* saved address by id; Prava fills in the real details privately. See [Guardrails](/concepts/guardrails). ### List addresses ```bash theme={null} prava shop address list ``` ``` 2 saved addresses (masked): 1. Home [default] San Francisco, CA 94103, US address-id: addr_home1 2. Work Oakland, CA 94612, US address-id: addr_work2 ``` ### Add an address The primary place to add addresses is the Prava dashboard; this is the CLI fallback. ```bash theme={null} prava shop address add \ --first-name John --last-name Doe \ --line1 "123 Main St" --city "San Francisco" \ --region CA --postal 94103 --country US \ --phone "+1 415 555 0100" --label "Home" --default ``` | Option | Required | Description | | --------------------------------------------- | -------- | ---------------------------------------------------------------------------- | | `--first-name`, `--last-name` | Yes | Recipient name. | | `--line1` | Yes | Street address. `--line2` optional. | | `--city`, `--region`, `--postal`, `--country` | Yes | City, state/province, ZIP, ISO country. | | `--phone` | No | Contact phone **with country code**. Stored on the account, not per-address. | | `--label` | No | e.g. `"Home"`. | | `--default` | No | Make this the default delivery address. | ### Set the default ```bash theme={null} prava shop address set-default --address-id addr_work2 ``` ## Search ```bash theme={null} prava shop search --query "dark roast coffee" --intent "gift for a coffee lover" --limit 10 --ships-to US ``` | Option | Required | Description | | ---------------------- | -------- | ------------------------------------------------------------------------------ | | `--query ` | Yes | Tight keyword query. | | `--intent ` | No | The user's full natural-language request (occasion, budget); improves ranking. | | `--limit ` | No | Max results (default 10). | | `--cursor ` | No | Next-page cursor from a previous search. | | `--merchant ` | No | Restrict to one merchant domain. | | `--ships-to ` | No | ISO destination country, e.g. `US`. | | `--json` | No | Raw JSON output for chaining. | ``` 3 results for "dark roast coffee": 1. Hair Bender Whole Bean $17.00 USD · stumptowncoffee.com product-id: prod_abc Next: prava shop product --product-id --merchant ``` ## Product Show a product's variants and offers across sellers: ```bash theme={null} prava shop product --product-id prod_abc --merchant stumptowncoffee.com ``` | Option | Required | Description | | --------------------- | -------- | --------------------------------------- | | `--product-id ` | Yes | `product-id` from search results. | | `--merchant ` | No | Merchant domain from the search result. | | `--json` | No | Raw JSON output. | Offers are listed **orderable first, then cheapest**. Prices shown are item-only: **shipping and tax are added at the quote step**. ``` 2 offers from 2 sellers, 2 orderable (orderable first, then cheapest) — prices are item-only; shipping is added at quote: 1. 12 oz — $17.00 USD [whole bean] · stumptowncoffee.com variant-id: var_123 Next: prava shop quote --variant-id --merchant ``` ## Quote Pricing a variant opens a **checkout session** with the *final*, binding total: subtotal + shipping * tax. This is a spend-adjacent step, so it asks for confirmation. ```bash theme={null} prava shop quote --variant-id var_123 --merchant stumptowncoffee.com --quantity 1 --address-id addr_home1 --yes ``` | Option | Required | Description | | --------------------- | -------- | ---------------------------------------------------------------------------------- | | `--variant-id ` | Yes | `variant-id` from product details. | | `--merchant ` | Yes | Merchant domain. | | `--quantity ` | No | Quantity (default 1). | | `--email ` | No | Buyer email (optional if on file). | | `--address-id ` | No | Which saved address to ship to (default: the owner's default address). | | `--retries ` | No | Retry on timeout / server error (default 1; `0` to disable). | | `-y, --yes` | No | Confirm. Pass **only** after the user has approved the seller, variant, and price. | | `--json` | No | Raw JSON output. | ``` Quote for stumptowncoffee.com Total: $22.45 USD (subtotal $17.00 + shipping $4.50 + tax $0.95) Shipping: Standard (5–7 days) Expires: in ~15m checkout-session-id: ches_xxx Next: mint a card session for $22.45 USD (prava sessions create … → approve → prava sessions poll), then: prava shop checkout --checkout-session-id ches_xxx --token --cryptogram --expiry-month --expiry-year ``` A quote **expires** (about 15 minutes) and locks the price. Get the card credentials and check out promptly. If it lapses, quote again. Between the quote and the checkout, mint the card credentials with a [payment session](/prava-pay/sessions): `prava sessions create` for the quoted total → owner enters the card → `prava sessions poll` to get the token and cryptogram. ## Checkout Pay the quoted total with the card credentials. This is the actual charge, so it also confirms. ```bash theme={null} prava shop checkout \ --checkout-session-id ches_xxx \ --token 4323126882557932 --cryptogram 957 \ --expiry-month 12 --expiry-year 2028 \ --cardholder-name "John Doe" --yes ``` | Option | Required | Description | | --------------------------------------------- | -------- | -------------------------------------------------------------------------- | | `--checkout-session-id ` | Yes | From `shop quote`. | | `--token ` | Yes | Network token from `prava sessions poll`. | | `--cryptogram ` | Yes | Dynamic CVV from `prava sessions poll`. | | `--expiry-month `, `--expiry-year ` | No | Card expiry (defaults from the session if omitted). | | `--cardholder-name ` | No | Cardholder name. | | `-y, --yes` | No | Confirm the charge. Pass **only** after the user approves the final total. | | `--json` | No | Raw JSON output. | On success: ``` ✓ Paid. Amount: $22.45 USD Order: ord_abc123 ``` The total charged is **bound to the quote**: it can't drift between quote and payment. Prava pays exactly the amount you saw, at exactly the merchant you quoted. ## Confirmation & safety Both `quote` and `checkout` are gated by a confirmation: * At an interactive terminal, they prompt `[y/N]`. * For an agent (non-interactive), they **refuse** unless `--yes` is passed, making a skipped confirmation a deliberate, visible act. Pass `--yes` **only after the user has actually approved** the seller, item, and total. It exists to record the user's approval, not to bypass it. ## Exit codes | Code | Meaning | | ---- | -------------------------------------------------- | | `0` | Success. | | `1` | Error, declined, or not confirmed. | | `2` | Not linked / confirmation refused without `--yes`. | For the full list of failure messages and how to recover, see [Troubleshooting](/prava-pay/troubleshooting). # Skills Source: https://docs.prava.space/prava-pay/skills How Prava skills guide an AI agent to call the right CLI command at the right time. The `prava` CLI does the work; **skills tell the agent *when* to call *what*.** A skill is a small instruction file installed into the agent's runtime that encodes the correct flow, the pacing, and the safety stops, so the agent invokes `setup`, `sessions`, and `shop` commands in the right order and pauses for the user's approval before spending. The CLI works **without** any skill: every `prava` command runs on its own. The skill adds the *guidance*: which command comes next, when to stop and confirm, and how to keep the user in control. If the matching skill is missing, the CLI nudges you (once) to install it. ## Why skills matter Left to itself, an agent might chain commands or spend without checking. Skills prevent that by encoding: * **One decision per turn**: after `search`, wait for the user to pick; after `product`, wait for them to confirm the seller/variant; after `quote`, wait for them to approve the total. * **Confirmation hard-stops**: the agent must present the merchant, item, and final amount and get an explicit "yes" before `sessions create` / `checkout`. *"Buy me X"* is intent, not price approval. * **PII masking**: the agent works with masked address summaries only; full details stay server-side. * **Move promptly**: card credentials are single-use and short-lived, so mint → poll → checkout without pausing. ## The skills | Skill | Version | What it guides | | --------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------ | | **`prava-pay`** | 2.3.1 | Completing a payment once merchant, item, and price are known: `setup` → `status` → `sessions create` → `sessions poll` → checkout. | | **`prava-shopping`** | 1.5.1 | Discover **and** buy a product: `shop search` → `shop product` → `shop quote` → `shop checkout`, with pacing and confirmation gates. | | **`prava-sdk-integration`** | 1.1.0 | For coding agents *integrating the [SDK](/sdk/overview)* into an app (sessions, iframe, polling); not for CLI payments. | | **`swiggy-prava-skill`** | — | Order from Swiggy via its MCP (Swiggy's tool server for agents), pay with Prava credentials. | | **`zepto-prava-skill`** | — | Order from Zepto via its MCP, pay with Prava credentials. | Most agents want **`prava-pay`** (to pay for a known item) or **`prava-shopping`** (to find *and* buy). ## Install a skill Install into the agent's global skills directory (e.g. `~/.claude/skills//SKILL.md`): ```bash One skill (recommended) theme={null} npx --yes skills add https://github.com/Prava-Payments/prava-skills \ --skill prava-shopping --global --yes --full-depth ``` ```bash All skills theme={null} npx --yes skills add https://github.com/Prava-Payments/prava-skills \ --global --yes --full-depth ``` Pass **`--full-depth`** with `--skill `. Otherwise sibling skills are pulled in alongside the one you asked for. Valid `--skill` names: `prava-pay`, `prava-shopping`, `prava-sdk-integration`, `swiggy-prava-skill`, `zepto-prava-skill`. ## Keeping skills current The CLI checks your installed skill version on every call. If it's behind the server minimum, it prints: ``` Skill update required (minimum: 2.3.1). Run: npx skills update prava-pay -g ``` Update, then re-run your command. If the notice repeats, restart the agent session so it reloads the skill. See [Troubleshooting](/prava-pay/troubleshooting#keeping-the-cli-up-to-date). ## Platforms Skills work across coding agents: Claude Code, Cursor, Codex, Gemini CLI, Hermes, and [many more](/prava-pay/linking#prava-setup). The agent's platform is set with `--platform` when you run [`prava setup`](/prava-pay/linking). ## Next steps Connect and approve the agent before it can pay. The search → quote → checkout flow the shopping skill guides. # Troubleshooting Source: https://docs.prava.space/prava-pay/troubleshooting What each message means and exactly how to recover. Prava Pay is designed to fail **loudly and safely**: every error tells you what happened and what to do next, and no failure ever leaves you double-charged. This page maps the messages you'll see to a recovery step. **Exit codes** let an agent branch without parsing text: `0` = success · `1` = error/declined · `2` = not linked or confirmation refused. ## Linking problems Nothing is linked on this machine yet. Run [`prava setup`](/prava-pay/linking) and approve in the browser. The approval window (15 minutes) passed. Start over with `prava setup`, open the new URL, and approve promptly. The owner rejected the agent on the approval screen. If that was a mistake, run `prava setup` again and approve it. Linking is time-sensitive. Sync the machine's clock (enable automatic network time) and re-run `prava setup`. A network issue. Check connectivity and retry. If you're behind a proxy or firewall, make sure outbound HTTPS to `prava.space` is allowed. A session or shop command ran before the agent was approved. Complete linking first (`prava setup` → approve → `prava setup poll`), then retry. ## Card entry & payment sessions The owner didn't finish card entry within the 10-minute window. Re-create the session and have the card ready before opening the payment URL. The card couldn't be processed. Try the payment session again, or use a different card. If it keeps failing, contact [support](mailto:support@prava.space). A `--product` value isn't valid JSON. Use the exact shape, quoted for your shell: `'{"description":"…","unit_price":"…","quantity":1}'`. The order was rejected. Check that `--total-amount` equals the sum of `unit_price × quantity` and that `--merchant-url` includes `https://`. The token and cryptogram (the one-time card number and security code) from `sessions poll` are **single-use and short-lived**. If you wait too long to check out, mint a fresh session; don't try to reuse old credentials. ## Shopping: quotes & checkout Quotes and checkouts drive a live merchant checkout and can take 20–40 seconds. For **quotes**, retry (the CLI retries once by default; raise it with `--retries`). For **checkout**, see the note on unknown outcomes below before retrying. A quote is valid for about 15 minutes and locks the price. Once it lapses, run `prava shop quote` again to get a fresh `checkout-session-id` and total. You've hit the limit on concurrent open quotes. Wait for existing quotes to be paid or to expire (\~15 min), then quote again. The account's purchase limit has been reached. This is an owner-level guardrail: the owner needs to raise the limit or contact [support](mailto:support@prava.space). See [Guardrails](/concepts/guardrails). Physical goods need a delivery address and a contact phone. Add them in the Prava dashboard, or via `prava shop address add … --phone "+1 …"`. See [Agentic shopping](/prava-pay/shopping#delivery-addresses). `quote` and `checkout` require confirmation. At a terminal, answer `y`. For an agent, pass `--yes` **only after the user has approved** the seller, item, and total. ## Payment outcomes The result of a checkout is read from the payment itself rather than the network response alone, so the status you see is the real one. Success. The charge went through; an order id is shown when the merchant provides one. The payment was declined or the merchant rejected it. This checkout is done; start a new [quote](/prava-pay/shopping#quote) to try again. This checkout was already completed earlier. Prava recognized the repeat and did **not** charge again. Treat the earlier success as authoritative. **Unknown outcome (rare).** If a checkout times out or the merchant response is lost, Prava does **not** guess: it leaves the payment un-finalized rather than risk a double charge, and asks you to wait rather than blindly retry. Do **not** immediately re-run checkout with the same credentials; re-quote if you need to try again, and reconcile the earlier attempt first if in doubt. ## Keeping the CLI up to date Prava tells you when a version is behind: The CLI is too old to talk to the server. Update it, then retry the command (you do **not** need to re-link): ```bash theme={null} npm update -g @prava-sdk/cli ``` An optional bug-fix update is available. Not required, but recommended: ```bash theme={null} npm update -g @prava-sdk/cli ``` A Prava skill (e.g. `prava-pay`, `prava-shopping`) is behind. Update it, then retry. If the notice repeats, restart the agent session so it reloads the skill: ```bash theme={null} npx skills update prava-shopping -g ``` ## Still stuck? Run `prava status` to confirm the agent is linked and active, and re-check the command flags against the [Payment sessions](/prava-pay/sessions) and [Agentic shopping](/prava-pay/shopping) references. For anything else, reach us at [support@prava.space](mailto:support@prava.space). # Your Prava Pay Dashboard Source: https://docs.prava.space/prava-pay/your-wallet For agent owners: what your AI agent can and can't do with your card, and how you stay in control. No code required. This page is for the **person whose card gets used**: the agent owner. You don't need to read any other page in these docs to use Prava safely. Your home base is the **Prava Pay dashboard** at [pay.prava.space](https://pay.prava.space). That's where you approve agents, add your card, and control spending. (If you're a developer looking for API keys, you want [dashboard.prava.space](https://dashboard.prava.space) instead. Different product.) ## What your agent can never do You enter card details only on Prava's secure page. The agent receives a **one-time credential** for a single approved purchase, never your real card number. Every purchase needs your explicit approval: a **passkey** (Touch ID / Face ID) and/or an in-chat confirmation before any money moves. The credential is locked to the merchant and amount you approved. It can't be reused, resized, or pointed at another store. Revoke an agent any time from the wallet; its access ends immediately. ## Setting up (once, \~2 minutes) Your agent asks to link by showing you a URL (it ran `prava setup`). Open it, sign in at [pay.prava.space](https://pay.prava.space), check the agent's name, and click **Approve**. The link expires after 15 minutes, so approve promptly. When the agent makes its first payment, you'll get a secure Prava page to enter your card; it's saved to your account for future purchases. Your card details go to Prava, never to the agent. If your agent shops for physical goods, add a delivery address and contact phone in the dashboard once. The agent only ever sees a masked summary. ## Every purchase, from your side 1. The agent finds or is told what to buy, and shows you the **merchant, items, and total**. 2. You confirm — in the conversation, and with a **passkey** for card actions. 3. Prava issues the one-time credential and the checkout runs. 4. The result shows up in your dashboard's transaction history. If the agent skips the confirmation, the purchase doesn't happen: the confirmation is enforced by Prava, not by the agent's goodwill. ## Your controls | Control | What it does | | --------------------------- | -------------------------------------------------------------------------------------- | | **Approve / revoke agents** | An agent is either approved by you or it can do nothing. Revocation is instant. | | **Checkout quota** | A cap on how many purchases your agents can make in total. | | **Concurrency limit** | How many checkouts can be open at once. | | **Per-purchase approval** | Nothing is charged without your confirmation. | | **Passkey security** | Card actions require your device's biometrics; there is no password fallback to phish. | More detail on how these are enforced: [Guardrails](/concepts/guardrails) · [Accounts & Agents](/concepts/accounts). ## Common questions **What if my agent goes rogue or gets compromised?** Revoke it at [pay.prava.space](https://pay.prava.space). Anything already approved is still merchant- and amount-locked; anything new requires approvals it can no longer get. **Can my agent see my address or phone?** No. It sees masked summaries. Full details stay on Prava's servers and go only to the merchant fulfilling your order. **What if a payment looks wrong?** Check the transaction in your dashboard, then contact [support@prava.space](mailto:support@prava.space). Refunds follow the standard card-network process. **Do I need to understand the developer docs?** No. This page plus the dashboard is everything an owner needs. The curious can read [How Prava Works](/concepts/how-it-works). # Quickstart Source: https://docs.prava.space/quickstart Start building with Prava Getting started with Prava is **self-serve**. Sign up at the [Prava Dashboard](https://dashboard.prava.space) ([what it offers](/dashboard)), create an API key, and you'll have sandbox credentials in a few minutes, with no invite code and no waiting. **Want to see Prava in action first?** Try the [Interactive Playground](https://playground.prava.space/) to visualize the full payment flow before you build. ## Set up your account Go to [dashboard.prava.space](https://dashboard.prava.space) and create an account with your email (one-time code) or Google. You'll land in the dashboard right away. In the dashboard, create an API key by entering your **entity name** (and optional website). Prava provisions your merchant and issues your keys instantly: * A **publishable key** (`pk_test_*`) — safe for the browser, used to initialize the SDK. * A **secret key** (`sk_test_*`) — server-side only, used to create sessions. New keys are **sandbox** by default. See [Authentication & Environments](/authentication) for how keys and environments work. Add the frontend domain(s) that will host the SDK iframe (the embedded frame that renders Prava's card form) to your **allowed domains** in the dashboard. Requests from other origins are rejected. **Going to production?** Production is a separate environment you switch to from the dashboard, and going live may require some additional verification. Reach out at [support@prava.space](mailto:support@prava.space) when you're ready. You can build and test everything in sandbox first. ## Create your first session A session is the workspace for a single payment. Once you have your credentials, create a session from your backend: ```bash theme={null} curl -X POST https://sandbox.api.prava.space/v1/sessions \ -H "Authorization: Bearer sk_test_xxx" \ -H "Content-Type: application/json" \ -d '{ "user_id": "user_123", "user_email": "user@example.com", "total_amount": "49.99", "currency": "USD", "purchase_context": [{ "merchant_details": { "name": "Your Store", "url": "https://yourstore.com", "country_code_iso2": "US" }, "product_details": [{ "description": "Example Product", "unit_price": "49.99", "quantity": 1 }] }] }' ``` The response gives you a `session_token` and an `iframe_url`. ## Choose how to collect the card From that one session you can collect the card two ways: * **Hosted (default)**: redirect the cardholder to the `iframe_url`; Prava returns them to your `callback_url` when done. No frontend SDK needed. * **Embedded**: set `integration_type: "embedding"` on the session and mount Prava's card UI inside your own page with the SDK (below). See [Integration Modes](/sdk/integration-modes) for a side-by-side comparison and the hosted snippet. ## Embed the card form (embedded mode) ```typescript theme={null} import { PravaSDK } from '@prava-sdk/core'; const prava = new PravaSDK({ publishableKey: 'pk_test_xxx', }); await prava.collectPAN({ sessionToken: session.session_token, iframeUrl: session.iframe_url, container: '#card-form', onSuccess: (data) => console.log(`Collected: ${data.brand} •••• ${data.last4}`), onError: (err) => console.error(err.code, err.message), }); ``` See the [SDK Overview](/sdk/overview) for the full API reference. ## Where next The complete integration in your app: backend, frontend, credentials, reporting. The same flow with hosted card entry and nothing but cURL. Test cards, sandbox behaviors, and a full test run. # Collect Card Details Source: https://docs.prava.space/sdk/cards/collect-pan Securely collect and tokenize user payment cards ## Overview The `collectPAN()` method securely collects credit card details from users through an isolated iframe. The card data is tokenized via Prava's PCI DSS vault and returns only safe metadata — your servers never see the raw PAN. ## Method Signature ```typescript theme={null} prava.collectPAN(options: CollectPANOptions): Promise ``` ## Parameters Configuration options for card collection Session token obtained from your backend Iframe URL from your backend session response CSS selector or DOM element to mount the card form (e.g., `#card-form`) Called when the iframe is loaded and ready for input Real-time validation state on every keystroke Called on successful card enrollment Called on error Called when the user dismisses the payment flow Custom styles for the card form. Supports `base`, `invalid`, and `focus` states as CSS property maps. ## Return Value Unique identifier for the collected card Last 4 digits of the card number Card brand (`visa`, etc.) Card expiration month (1-12) Card expiration year (4 digits) The `enrollmentId` identifies the collected card on the server side. Use your backend and the [API Reference](/api-reference/overview) to read payment results and report status for that card. ## Example ```typescript React theme={null} import { useEffect, useRef, useState } from 'react'; import { PravaSDK } from '@prava-sdk/core'; import type { CollectPANResult } from '@prava-sdk/core'; function CardEnrollment({ session }: { session: { session_token: string; iframe_url: string } }) { const containerRef = useRef(null); const sdkRef = useRef(null); const [enrolled, setEnrolled] = useState(null); useEffect(() => { const sdk = new PravaSDK({ publishableKey: 'pk_live_xxx' }); sdkRef.current = sdk; return () => sdk.destroy(); }, []); const handleEnroll = async () => { if (!sdkRef.current || !containerRef.current) return; const result = await sdkRef.current.collectPAN({ sessionToken: session.session_token, iframeUrl: session.iframe_url, container: containerRef.current, onReady: () => console.log('Form loaded'), onChange: (state) => { console.log('Complete:', state.isComplete); }, onSuccess: (data) => { console.log(`Enrolled: •••• ${data.last4}`); setEnrolled(data); }, onError: (err) => console.error(err.code, err.message), }); }; return (
{enrolled &&

Card enrolled: {enrolled.brand} •••• {enrolled.last4}

}
); } ``` ```typescript Vanilla JavaScript theme={null} import { PravaSDK } from '@prava-sdk/core'; const prava = new PravaSDK({ publishableKey: 'pk_live_xxx' }); // Get session from your backend const session = await fetch('/api/session').then(r => r.json()); // Collect card via secure iframe const card = await prava.collectPAN({ sessionToken: session.session_token, iframeUrl: session.iframe_url, container: '#card-form', onReady: () => console.log('Form loaded'), onChange: (state) => { document.getElementById('submit-btn').disabled = !state.isComplete; }, onSuccess: (data) => { console.log(data.enrollmentId, data.last4, data.brand); }, onError: (err) => console.error(err.code, err.message), }); ``` ```html theme={null}
``` ## Flow Diagram ```mermaid theme={null} sequenceDiagram participant App as Your App participant SDK as Prava SDK participant Iframe as Secure Iframe participant Backend as Prava Backend participant Network as Card Network App->>SDK: collectPAN(sessionToken, iframeUrl) SDK->>Iframe: Mount iframe Iframe-->>SDK: onReady() SDK-->>App: Form ready Note over Iframe: User enters card details Iframe->>Backend: Tokenize via PCI DSS vault Backend->>Network: Provision token Network-->>Backend: Enrollment complete Backend-->>Iframe: Success Iframe-->>SDK: onSuccess(result) SDK-->>App: CollectPANResult ``` ## Under the Hood When you call `collectPAN()`, here's what happens: The SDK injects a secure, sandboxed iframe into your specified container. The iframe is served from Prava's domain — card data never touches your DOM or servers. The iframe validates the session token with Prava's backend to ensure the request is legitimate and not expired. The user enters their card details (number, expiry, CVV) in the iframe form with real-time validation. When the user submits, the iframe tokenizes the PAN via Prava's PCI DSS vault. Your servers never see the raw card number. The enrollment result (with `enrollmentId`, `last4`, `brand`, `expMonth`, `expYear`) is returned to your app. ## Validation States The `onChange` callback receives a `CardValidationState` object on every keystroke: ```typescript theme={null} interface CardValidationState { cardNumber: FieldState; expiry: FieldState; cvv: FieldState; isComplete: boolean; // true when all fields are valid } interface FieldState { isEmpty: boolean; isValid: boolean; isFocused: boolean; error?: string; } ``` Use `isComplete` to enable/disable your submit button: ```typescript theme={null} onChange: (state) => { submitButton.disabled = !state.isComplete; } ``` ## Error Handling ### Common Errors | Code | Cause | Resolution | | -------------------- | ----------------------------------------------------------- | -------------------------------------------------------- | | `SDK_ALREADY_ACTIVE` | `collectPAN` called while another collection is in progress | Call `destroy()` first | | `INVALID_CONFIG` | Missing `iframeUrl` (or a bad `publishableKey`) | Pass the `iframe_url` from your backend session response | | `IFRAME_LOAD_ERROR` | Iframe failed to load | Check network, verify `iframeUrl` | | `SDK_INIT_ERROR` | SDK initialization failed | Verify browser compatibility and config | ## Security Notes **Never** attempt to bypass the iframe or collect card data directly. The iframe is sandboxed with `allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox` and a permissions policy of `payment; publickey-credentials-get; publickey-credentials-create`. Card data never touches your DOM, JS, or servers. PostMessage communication is origin-locked. The iframe resolves its backend from its own hostname — merchants cannot inject a fake backend URL. ## Next Steps Retrieve a customer's enrolled cards server-side Read the outcome after the card is collected # Integration Modes Source: https://docs.prava.space/sdk/integration-modes Two ways to collect a card from one session — embedded (SDK) or hosted (redirect). Every card collection starts the same way: your backend creates a **session** ([`POST /v1/sessions`](/api-reference/create-session)) and gets back a `session_token` and an `iframe_url`. From there you choose **how** the cardholder enters their card — and you pick by setting one field, `integration_type`, on the session. Prava's card UI runs **inside your app** via the [SDK](/sdk/overview). You control the surrounding page and get the result in-app. You **redirect** the cardholder to a Prava-hosted page. No SDK, minimal code — Prava returns them to your `callback_url` when done. Either way, the card is entered on **Prava's secure surface** and tokenized — your app never touches the raw card number. There is no mode where you collect the PAN yourself. ## At a glance | | **Embedded** (`"embedding"`) | **Hosted** (`"full_checkout"`, default) | | ------------------------- | ------------------------------------------------ | ---------------------------------------------------- | | SDK required | Yes (`@prava-sdk/core`) | No | | Where the card UI renders | An iframe inside your page | A full Prava-hosted page you redirect to | | What you build | A container + SDK wiring | A redirect + a `callback_url` route | | How you get the result | `onSuccess` callback, in-app | Prava redirects the user to your `callback_url` | | Best when | You want the flow to feel native to your product | You want the fastest, lowest-maintenance integration | Both modes use the **same session** and the **same `iframe_url`** — embed it, or open it directly. ## Embedded mode Create the session with `integration_type: "embedding"`, then mount the card UI with the SDK. ```typescript theme={null} // 1. Backend: create the session const session = await createSession({ /* user_id, user_email, total_amount, currency, purchase_context … */ integration_type: 'embedding', }); // 2. Frontend: mount Prava's secure iframe in your page import { PravaSDK } from '@prava-sdk/core'; const prava = new PravaSDK({ publishableKey: 'pk_test_xxx' }); await prava.collectPAN({ sessionToken: session.session_token, iframeUrl: session.iframe_url, container: '#card-form', onSuccess: (data) => console.log(`Collected •••• ${data.last4}`), onError: (err) => console.error(err.code, err.message), }); ``` ```html theme={null}
``` The result arrives in `onSuccess` without leaving your page. See [Collect Card Details](/sdk/cards/collect-pan) for every option and callback, and remember to call `prava.destroy()` on cleanup. ## Hosted mode (default) Leave `integration_type` off (it defaults to `"full_checkout"`) and give Prava a `callback_url`. Then send the cardholder to the returned `iframe_url` — no SDK needed. ```typescript theme={null} // 1. Backend: create the session (hosted is the default) const session = await createSession({ /* user_id, user_email, total_amount, currency, purchase_context … */ callback_url: 'https://yourapp.com/checkout/complete', // must be https }); // 2. Frontend: send the user to the hosted page const url = new URL(session.iframe_url); url.searchParams.set('session_token', session.session_token); window.location.href = url.toString(); // or window.open(url, '_blank') ``` When the cardholder finishes, Prava redirects them back to your `callback_url`. Your backend then reads the outcome with [Get Payment Result](/api-reference/get-payment-result) and reports the final status with [Report Status](/api-reference/report-status). `callback_url` must be an **https** URL. Without it, a hosted checkout has nowhere to return the user. ## Which should I use? You want the card step to feel native, keep the user on your page, and react to the result in-app. You want to ship fast with minimal frontend code and are happy to redirect out and back. ## Next steps The library behind embedded mode. `integration_type`, `callback_url`, and the full request/response. # SDK Overview Source: https://docs.prava.space/sdk/overview Embed secure, PCI-safe card collection into your web app with the Prava SDK. *Not sure this is the right path? See [Choosing Your Integration](/choosing-your-integration).* ## What is the Prava SDK? The **Prava SDK** (`@prava-sdk/core`) is a lightweight browser library for **securely collecting a card** in your web app. It mounts a PCI-compliant iframe, handles the card entry and authentication inside it, and hands you back a safe result — the raw card number never touches your page, your JavaScript, or your servers. The SDK is the **client-side** piece of a session-based integration. Your **backend** creates the session (see the [API Reference](/api-reference/overview)); the SDK renders the secure card form for that session. If instead you want an **AI agent** to pay from the command line, use [Prava Pay](/prava-pay/overview). The SDK powers the **embedded** integration mode — Prava's card UI inside your own page. Don't want to embed? Use **hosted** mode (a redirect, no SDK). See [Integration Modes](/sdk/integration-modes) to choose. ## What it does (and doesn't) The SDK's job is deliberately small — one secure operation, done well: | The SDK handles | Your backend handles | | ---------------------------------------------------- | ------------------------------------------ | | Rendering the secure card iframe | Creating the session (`POST /v1/sessions`) | | Card entry, validation, and in-iframe authentication | Reading the payment result | | Returning a safe, tokenized result | Reporting the final transaction status | Everything money-related — mandates, one-time payment credentials, spending limits — is enforced by Prava and the card network, not by code running in your page. See [Payments](/concepts/payments) and [Guardrails](/concepts/guardrails) for how that works. ## Install ```bash npm theme={null} npm install @prava-sdk/core ``` ```bash yarn theme={null} yarn add @prava-sdk/core ``` ```bash pnpm theme={null} pnpm add @prava-sdk/core ``` ## Quick start ```typescript theme={null} import { PravaSDK } from '@prava-sdk/core'; const prava = new PravaSDK({ publishableKey: 'pk_live_xxx', // from the Prava Dashboard }); ``` The publishable key is safe to ship to the browser. Your **secret key** stays on your server and is never used here. Your server creates a session with your secret key and returns the `session_token` and `iframe_url` to your frontend: ```typescript theme={null} // Server-side (never expose your secret key to the browser) const session = await fetch('https://api.prava.space/v1/sessions', { method: 'POST', headers: { Authorization: `Bearer ${SECRET_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ user_id: 'user_123', user_email: 'user@example.com', total_amount: '49.99', currency: 'USD', purchase_context: [{ merchant_details: { name: 'Amazon', url: 'https://amazon.com', country_code_iso2: 'US' }, product_details: [{ description: 'Wireless Headphones', unit_price: '49.99', quantity: 1 }], }], }), }).then((r) => r.json()); // → { session_id, session_token, iframe_url, expires_at, order_id } ``` Full request/response details: [Create Session](/api-reference/create-session). ```typescript theme={null} const result = await prava.collectPAN({ sessionToken: session.session_token, iframeUrl: session.iframe_url, container: '#card-form', onSuccess: (data) => console.log(`Collected •••• ${data.last4}`), onError: (err) => console.error(err.code, err.message), }); ``` ```html theme={null}
``` See [Collect Card Details](/sdk/cards/collect-pan) for every option and callback.
After the card is collected and the transaction runs, your backend reads the outcome and reports the final status. See [Get Payment Result](/api-reference/get-payment-result) and [Report Status](/api-reference/report-status).
## API surface The SDK exposes exactly two methods: | Method | Description | | ----------------------------------------------- | -------------------------------------------------------------------------------- | | [`collectPAN(options)`](/sdk/cards/collect-pan) | Mounts the secure iframe, collects the card, resolves with a `CollectPANResult`. | | `destroy()` | Tears down the iframe and listeners. Call it on unmount/cleanup. | ```typescript theme={null} // Clean up when your component unmounts prava.destroy(); ``` ## Authentication | Key | Where it's used | Where it lives | | ----------------------------------------------- | --------------------------------- | -------------- | | **Publishable key** (`pk_live_*` / `pk_test_*`) | Initialize the SDK in the browser | Frontend | | **Secret key** (`sk_*`) | Create sessions, read results | Backend only | Never put your secret key in client-side code or version control. ## Result & error types ```typescript theme={null} interface CollectPANResult { enrollmentId: string; last4: string; brand: string; // "visa", "mastercard", … expMonth: number; expYear: number; } interface PravaError { code: string; message: string; details?: Record; } ``` Error codes, every `collectPAN` option, and validation states are documented on [Collect Card Details](/sdk/cards/collect-pan) — the canonical `collectPAN` reference. ## Requirements * **Browsers:** Chrome 80+, Firefox 80+, Safari 14+, Edge 80+. * **Backend:** Your server implements the session API (`POST /v1/sessions`) with your secret key. * **Keys:** Obtain `publishableKey` and `secretKey` from the [Prava Dashboard](https://dashboard.prava.space). ## Next steps Every `collectPAN` option, callback, and validation state. Create sessions, read payment results, and report status server-side. The lifecycle behind a Prava payment. Let an AI agent pay from the command line. # Use Cases Source: https://docs.prava.space/use-cases What people build with Prava, shown as concrete flows. Prava does one thing: it lets an AI agent pay with a real card, without ever holding that card. Here is what that looks like in practice. ## An agent buys something for you You tell your agent "order me a phone case under \$20". The agent, connected via [MCP](/mcp/overview) (Model Context Protocol) or the [CLI](/prava-pay/overview): 1. Searches products across merchants and picks a case: \$18.50. 2. Locks the live price with a quote. 3. Sends you a payment link. You open it and approve with Face ID. 4. The agent completes the checkout. You get an order id; the agent never saw your card. If the total had changed mid-checkout, the charge would not have gone through. The credential is locked to that merchant and that amount. **Start here:** [Connect via MCP](/mcp/connect) or [Prava Pay quickstart](/prava-pay/quickstart). ## An agent pays a bill you already know No product discovery needed. You say "pay my $54.20 internet bill at Comcast". The agent creates a payment session for exactly $54.20, you approve with a passkey, and it pays with the one-time credentials. Wrong-amount or wrong-merchant use is declined. **Start here:** [Payment sessions (CLI)](/prava-pay/sessions) or [create\_payment\_session (MCP)](/mcp/tools#create-payment-session--checkoutrun). ## Your AI app takes payments from its users You're building a shopping assistant, a travel planner, or a booking bot. Your users need to pay *inside your product*. You integrate the [SDK + API](/sdk/overview): your backend creates a session, your frontend shows Prava's secure card form, and your app receives one-time credentials to complete the purchase. Your servers never touch a card number, which keeps you out of PCI scope (the security compliance that applies once you handle card data). Example: a stylist app's user picks a $79 jacket. The app creates a session for $79, the user enters their card and approves in the embedded form, the app checks out at the boutique with the one-time token, then reports the result. **Start here:** [Add Payments to Your AI App](/guides/add-payments-to-your-ai-app). ## Human-in-the-loop, by design Every flow above has the same shape: **the agent proposes, the human approves, Prava executes.** The approval is a passkey (Touch ID / Face ID) on the user's own device. An agent cannot skip it, and an approval for one purchase authorizes nothing else. ## Recurring payments (planned) A mandate is the user's recorded permission to pay. Today every mandate is one-time: one approval, one purchase. Recurring mandates are planned. Once shipped, a user will approve a subscription once (say, \$12/month at one merchant), and the agent can invoke it monthly within those limits, without a fresh passkey each time. Until then, each payment needs its own approval. ## Where to next Agent, embedded, or hosted. Decided in 10 seconds. The lifecycle behind every flow above. For merchants: what works with zero changes.