# API Reference
Source: https://docs.ozura.com/api-reference/overview
Complete API reference for Ozura's payment and vault services.
Vault endpoint reference is available in the **Vault** section below. Checkout and OzuraPay API reference coming soon.
## Base URLs
| Service | Sandbox | Production |
| - | - | - |
| OzuraPay API | `https://sandbox.payapi.v2.ozurapay.com` | `https://payapi.v2.ozurapay.com` |
| Checkout | `https://sandbox-checkout.ozura.com` | `https://checkout.ozura.com` |
| Vault | `https://api.ozuravault.com` (sandbox uses a test vault key) | `https://api.ozuravault.com` |
Vault is a single environment — the same host serves test and production traffic, and the key (test vs. prod) determines which mode you're in. OzuraPay, by contrast, has fully separate **sandbox** and **production** environments at different hosts. See [Sandbox](/guides/sandbox) for the full setup.
## Authentication
All API requests require authentication via API key or JWT token. See [Vault → Authentication](/api-reference/vault/authentication) for details.
## Vault API
For Vault endpoint details (tokenize, proxy, verify API key, status codes, common errors), see **[Vault](/api-reference/vault/overview)** in the sidebar.
# Application Management
Source: https://docs.ozura.com/api-reference/vault/applications
API reference for Vault applications — create, list, delete, API keys, permissions.
# Applications
Applications hold API keys used for tokenization and (optionally) provisioning. Management endpoints require a **JWT**; the verify-key endpoint uses the API key itself.
## Endpoints
| Method | Path | Description |
| - | - | - |
| POST | `/api/applications` | Create application (returns API key once) |
| GET | `/api/applications` | List applications |
| GET | `/api/applications/{id}` | Get application details |
| PUT | `/api/applications/{application_id}` | Update application (body: name, permissions) |
| DELETE | `/api/applications/{id}` | Delete application |
| GET | `/api/applications/key` | Verify API key (see [Verify API Key](/api-reference/vault/verify-api-key)) |
## POST /api/applications
**Auth:** JWT with admin or owner role.
**Request body:**
| Field | Type | Required | Description |
| - | - | - | - |
| `name` | string | Yes | Application name (3–50 characters) |
| `permissions.tokenization.allowed` | boolean | Yes | Allow tokenize, detokenize, proxy |
| `permissions.provisioning.allowed` | boolean | No | Allow creating sub-projects (default: false) |
**Response (201):** `data` includes `id`, `name`, **`api_key`** (shown only once — store it securely), `permissions`, `created_at`.
## Permissions
* **tokenization** — Create tokens, detokenize, use proxy, test tokens.
* **provisioning** — Call `/internal/provision` to create sub-projects.
## Base URL
Use the [Vault base URL](/api-reference/vault/overview) (sandbox or production).
# Authentication
Source: https://docs.ozura.com/api-reference/vault/authentication
API reference for Vault authentication — API keys, JWT, and auth endpoints.
Vault supports **API key** authentication (server-to-server) and **JWT** authentication (dashboard and management). **POST /tokenize** requires **X-Pub-Key** when using a production vault API key; test/sandbox API keys do not require it. Some requests support **hybrid** (API key + JWT for user attribution).
## API key
**Header:** `X-API-Key: `
**Use for:** Tokenization, proxy, verify key. For **POST /tokenize** you must also send **X-Pub-Key** when using a production vault API key. Test/sandbox API keys do not require X-Pub-Key.
**Endpoints that accept API key:**
| Endpoint | Permission |
| - | - |
| POST /tokenize | tokenization (+ X-Pub-Key for production keys) |
| POST /proxy/transaction | tokenization |
| POST /test-tokens | tokenization (+ X-Pub-Key for production keys) |
| GET /api/applications/key | any (verify key) |
API key format may vary (e.g. provisioned keys); docs often show keys starting with `key_` as an example. Never expose API keys in client-side code.
## JWT
**Header:** `Authorization: Bearer ` or cookie (after login).
**Use for:** Dashboard and management — projects, applications, audit logs, user settings, MFA. JWT is required for management endpoints (projects, applications, audit logs, token management, user settings); [Applications](/api-reference/vault/applications) also documents the verify-key endpoint (API key).
### Auth endpoints (explicit paths)
| Method | Path | Description |
| - | - | - |
| POST | /auth/login | Email/password login, returns JWT |
| POST | /auth/logout | Log out |
| GET | /auth/verify | Validate current JWT |
| POST | /auth/change-password | Body: current\_password, new\_password (JWT) |
| POST | /auth/forgot-password | Body: email |
| GET | /auth/verify-reset-token | Query: token |
| POST | /auth/reset-password | Body: token, new\_password |
| POST | /auth/reset-expired-password | Body: token, current\_password, new\_password |
| GET | /auth/password-expiry-info | JWT |
| POST | /auth/send-verification-code | Email verification flow |
| POST | /auth/verify-email-code | Verify email code |
| POST | /auth/register | Registration (when email verification used) |
| POST | /auth/mfa/verify | Body: code |
| POST | /auth/mfa/verify-backup | Backup code |
| POST | /auth/mfa/disable | Body: code |
| GET | /auth/mfa/status | MFA status |
| GET | /auth/mfa/verify-page | MFA verify page |
| GET | /mfa-status | Root path for MFA status |
**MFA setup** is via **POST /user/settings/mfa** (toggle/setup); response can include `totp_uri` and `backup_codes` when enabling. There is no separate /auth/mfa/setup path.
## Hybrid
Send both `X-API-Key` and `Authorization: Bearer `. The API key authorizes the request; the JWT identifies the user for audit logging (e.g. on tokenize or proxy).
## Verify API key
See [GET /api/applications/key](/api-reference/vault/verify-api-key) to validate a key and read its project and permissions.
## Base URL
Use the [Vault base URL](/api-reference/vault/overview) (sandbox or production).
# Common Errors
Source: https://docs.ozura.com/api-reference/vault/common-errors
Frequent Vault API errors and how to resolve them.
Solutions for frequently encountered Vault API errors.
## Authentication
| Message | Causes | Solutions |
| - | - | - |
| `Invalid API key` | Wrong key, deleted key, wrong environment | Verify key, check dashboard, use correct env |
| `Token expired` | JWT expired | Re-authenticate via `/auth/login`, get new JWT |
| `MFA verification required` | MFA not verified | Call `/auth/mfa/verify` or use backup code |
## Tokens and CVC
| Message | Causes | Solutions |
| - | - | - |
| `Token not found` | Invalid token, wrong project, deleted | Check token ID, project, and that token exists |
| `CVC session expired or not found` | >90 sec, already used, invalid ID | Re-tokenize with CVV for new session |
| `Invalid card number` | Luhn fail, wrong length, non-numeric | Card must be 13–19 digits; validate Luhn, strip spaces/dashes |
| `Card has expired` | Past expiration | Use current or future expiry |
## Permissions
| Message | Causes | Solutions |
| - | - | - |
| `This API key does not have permission for this operation` | Key lacks permission | Check app permissions in dashboard |
| `Only admins and owners can...` | Insufficient role | Use admin/owner user or request access |
## Rate limits
| Message | Solution |
| - | - |
| `Rate limit exceeded` | Wait `retry_after_seconds`; use exponential backoff |
| `Too many failed attempts` | Wait for lockout; verify credentials before retrying |
## Proxy
| Message | Causes | Solutions |
| - | - | - |
| `Failed to proxy request` | Bad URL, PSP down, network | Check `proxy_url`, PSP status, retry |
| `Invalid proxy URL` | Malformed or non-HTTPS | Use full `https://` URL |
## Validation
| Message | Solutions |
| - | - |
| `Missing required field: X` | Include all required fields; check JSON shape |
| `Invalid value for X` | Check type (string vs number) and format per [API Reference](/api-reference/vault/overview) |
## Getting help
If the error persists: check [Guides](/guides/vault/overview), review [audit logs](/guides/vault/audit-logs/overview), and contact support with error message, redacted request, timestamp, and project ID.
# Errors Overview
Source: https://docs.ozura.com/api-reference/vault/errors-overview
Vault API error format and where to find status codes and common errors.
Vault returns errors as JSON with `success: false` and a `message` field. Use the HTTP status code and message to decide whether to retry, fix the request, or prompt the user.
## Response format
```json theme={null}
{
"success": false,
"message": "Human-readable error message",
"data": null
}
```
## Reference
| Page | Content |
| - | - |
| [HTTP status codes](/api-reference/vault/status-codes) | All status codes and typical causes |
| [Common errors](/api-reference/vault/common-errors) | Frequent messages and how to fix them |
## Quick reference
| Code | Meaning |
| - | - |
| 400 | Bad Request — fix input |
| 401 | Unauthorized — check API key or JWT |
| 403 | Forbidden — permissions or MFA |
| 404 | Not Found — resource doesn't exist |
| 409 | Conflict — duplicate or state conflict |
| 429 | Too Many Requests — back off; use `retry_after_seconds` if present |
| 5xx | Server/upstream error — retry with backoff |
# Vault API Overview
Source: https://docs.ozura.com/api-reference/vault/overview
Base URL and authentication for Ozura Vault API.
# Vault API
Reference for Ozura Vault endpoints. For integration steps and concepts, see the [Vault Guides](/guides/vault/overview).
Deployed backends may use different hostnames (e.g. Azure); the path segments below are correct regardless. There is no version prefix (e.g. no `/v1`).
## Authentication
All requests require one of:
* **API Key** — `X-API-Key` header (server-to-server). Get an API key by creating an account at [Ozura Vault](https://ozuravault.com). **POST /tokenize** and **POST /test-tokens** also require **X-Pub-Key** when using a production vault API key. If your API key is a test/sandbox key, `X-Pub-Key` is not required. For a production pub key, contact **[ammar@ozura.com](mailto:ammar@ozura.com)**. See [Verify API Key](/api-reference/vault/verify-api-key).
* **JWT** — `Authorization: Bearer ` or cookie (user context).
Details: [Authentication](/api-reference/vault/authentication).
## Endpoints
| Method | Path | Description |
| - | - | - |
| POST | [/tokenize](/api-reference/vault/tokenize) | Create a token from card or bank data |
| POST | [/proxy/transaction](/api-reference/vault/proxy-transaction) | Forward request to a PSP with card data injected |
| GET | [/api/applications/key](/api-reference/vault/verify-api-key) | Verify API key and read permissions |
| — | [Authentication](/api-reference/vault/authentication) | API key vs JWT, which endpoints use which |
\| POST | [Test tokens](/api-reference/vault/test-tokens) | Create test token with long-lived CVC (X-Pub-Key + API key/JWT) |
\| GET/POST/PUT/DELETE | [Applications](/api-reference/vault/applications) | Create, list, update, delete apps; verify key (API key) |
## Errors
* [Errors overview](/api-reference/vault/errors-overview)
* [HTTP status codes](/api-reference/vault/status-codes)
* [Common errors and solutions](/api-reference/vault/common-errors)
# Proxy
Source: https://docs.ozura.com/api-reference/vault/proxy-transaction
Forward a request to a PSP with card data injected from a token.
# POST /proxy/transaction
Send a request to your payment processor (PSP) with card data injected from a token. Your server never sees raw card data. Supports JSON, form-encoded, and XML bodies. For JSON, send `token`, `cvc_session_id`, and `proxy_url` in the body; for form/XML you may also send them via headers **X-Token**, **X-Proxy-URL**, and **X-CVC-Session-ID** (or in query/form).
## Request
```json theme={null}
{
"token": "tok_a1b2c3d4e5f6",
"cvc_session_id": "550e8400-e29b-41d4-a716-446655440000",
"proxy_url": "https://api.stripe.com/v1/charges",
"request_data": {
"amount": 2000,
"currency": "usd",
"source": {
"number": "${cardNumber}",
"exp_month": "${expirationMonth}",
"exp_year": "${expirationYear}",
"cvc": "${cvv}"
}
},
"http_headers": {
"Authorization": "Bearer sk_test_xxx"
}
}
```
### Request body
| Field | Type | Required | Description |
| - | - | - | - |
| `token` | string | Yes | Token to use |
| `cvc_session_id` | string | No | CVC session for CVV (if tokenized with CVV) |
| `proxy_url` | string | Yes | Full PSP endpoint URL (HTTPS) |
| `request_data` | object | Yes | Request body with placeholders (see below) |
| `http_headers` | object | No | Headers to send (e.g. PSP API key) |
| `http_method` | string | No | HTTP method (default: POST) |
### Placeholders in `request_data`
Replaced with real card data before the request is sent:
| Placeholder | Value |
| - | - |
| `${cardNumber}` | Full card number |
| `${expirationMonth}` | 2-digit month (01–12) |
| `${expirationYear}` | 2- or 4-digit year |
| `${cvv}` or `${cvc}` | Security code (if `cvc_session_id` provided) |
Bank: `${accountNumber}`, `${routingNumber}`.
## Response
### 200 OK
Proxy returns the PSP response wrapped:
```json theme={null}
{
"success": true,
"proxy_response": {
"status_code": 200,
"headers": { "content-type": "application/json" },
"body": {
"id": "ch_1234567890",
"object": "charge",
"amount": 2000,
"status": "succeeded"
}
}
}
```
## Error responses
| Status | Example message |
| - | - |
| 400 | `Invalid proxy URL` |
| 404 | `Token not found`, `CVC session expired or not found` |
| 502 | `Failed to proxy request` (upstream error) |
| 504 | `Request timeout` |
## Delete CVC session
**DELETE /proxy/delete-cvc-session/** — Delete a CVC session by UUID (e.g. after a successful transaction). **Auth:** API key. Response: `{ "success": true, "message": "CVC session deleted!" }`. Production CVC sessions have a **90-second TTL** and are deleted after use in proxy; use this endpoint to delete explicitly when needed (e.g. test sessions).
See [Status codes](/api-reference/vault/status-codes) and [Common errors](/api-reference/vault/common-errors).
# HTTP Status Codes
Source: https://docs.ozura.com/api-reference/vault/status-codes
Status codes returned by Ozura Vault API.
Reference for HTTP status codes returned by Vault endpoints.
## Success
| Code | Status | Description |
| - | - | - |
| 200 | OK | Request succeeded |
| 201 | Created | Resource created (e.g. token) |
## Client errors
| Code | Status | Common causes |
| - | - | - |
| 400 | Bad Request | Invalid JSON, missing/invalid fields, invalid card number |
| 401 | Unauthorized | Missing or invalid API key, expired JWT |
| 403 | Forbidden | MFA required, insufficient permissions, account locked |
| 404 | Not Found | Token or resource doesn't exist or was deleted |
| 409 | Conflict | Duplicate resource (e.g. application name) |
| 429 | Too Many Requests | Rate limit exceeded; use `retry_after_seconds` if present |
## Server errors
| Code | Status | Action |
| - | - | - |
| 500 | Internal Server Error | Retry with backoff |
| 502 | Bad Gateway | Proxy/upstream error; retry |
| 503 | Service Unavailable | Retry after delay |
| 504 | Gateway Timeout | Upstream timeout; retry |
## Example error body
```json theme={null}
{
"success": false,
"message": "Human-readable error message",
"data": null
}
```
For specific messages and troubleshooting, see [Common errors](/api-reference/vault/common-errors). For integration patterns, see [Error handling](/guides/vault/errors/overview).
# Test Tokens
Source: https://docs.ozura.com/api-reference/vault/test-tokens
Create a test card token with a long-lived CVC session (1-year TTL).
# POST /test-tokens
Create a test card token with a **long-lived CVC session** (1-year TTL). Test CVC sessions are not deleted after use in proxy (unlike production CVC sessions). Use for development and testing.
**Auth:** **X-API-Key** (or JWT) is always required. **X-Pub-Key** is required when using a production vault API key; test/sandbox API keys do not require it. For a production pub key, contact **[ammar@ozura.com](mailto:ammar@ozura.com)**.
## Request
```json theme={null}
{
"cardNumber": "4111111111111111",
"expirationMonth": "12",
"expirationYear": "2025",
"cvc": "123",
"firstName": "Test",
"lastName": "User"
}
```
| Field | Type | Required | Description |
| - | - | - | - |
| `cardNumber` | string | Yes | Test card number |
| `expirationMonth` | string | Yes | 2-digit month (01–12) |
| `expirationYear` | string | Yes | 2- or 4-digit year |
| `cvc` | string | Yes | Security code |
| `firstName` | string | No | Cardholder first name |
| `lastName` | string | No | Cardholder last name |
## Response
**201 Created.** Includes `token`, `cvc_session_id`, `mask`, `bin`, `test_mode: true`, and `expires_in` (e.g. `"1 year (test session)"`). The CVC session is long-lived and is **not** deleted after use in proxy.
See [POST /tokenize](/api-reference/vault/tokenize) for production tokenization. CVC sessions from `/tokenize` have a **90-second TTL** and are deleted after use.
# Tokenize
Source: https://docs.ozura.com/api-reference/vault/tokenize
Create a token from card or bank account data.
# POST /tokenize
Create a token from sensitive card or bank account data. Card data can include an optional CVV, which is stored in a separate **CVC session** (90-second TTL; deleted after use in proxy).
**Auth:** **X-API-Key** (or JWT) is always required. **X-Pub-Key** is required when using a production vault API key; test/sandbox API keys do not require it. Get an API key by creating an account at [Ozura Vault](https://ozuravault.com). For a production pub key, contact **[ammar@ozura.com](mailto:ammar@ozura.com)**.
## Request
```json theme={null}
{
"type": "card",
"data": {
"cardNumber": "4111111111111111",
"expirationMonth": "12",
"expirationYear": "2029",
"cvv": "123"
}
}
```
### Request body
| Field | Type | Required | Description |
| - | - | - | - |
| `type` | string | Yes | `"card"` or `"bank"` |
| `data.cardNumber` | string | Yes (card) | 13–19 digit card number |
| `data.expirationMonth` | string | Yes (card) | 2-digit month (01–12) |
| `data.expirationYear` | string | Yes (card) | 2- or 4-digit year |
| `data.cvv` | string | No | 3–4 digit security code |
| `data.cvc` | string | No | Alias for `cvv` |
### CVV/CVC handling
* If `cvv` or `cvc` is provided: stored in a separate CVC session, **90-second TTL**, deleted after use in proxy; response includes `cvc_session_id`.
* If omitted: no CVC session; no `cvc_session_id` in response; CVC cannot be used in proxy.
For long-lived test CVC sessions (e.g. 1-year), use [POST /test-tokens](/api-reference/vault/test-tokens).
## Response
### 201 Created
```json theme={null}
{
"success": true,
"token": "tok_a1b2c3d4e5f6g7h8i9j0",
"mask": {
"card_number": "411111******1111"
},
"cvc_session_id": "550e8400-e29b-41d4-a716-446655440000",
"bin": "411111"
}
```
| Field | Type | Description |
| - | - | - |
| `success` | boolean | Request success |
| `token` | string | Token identifier |
| `data` | object | Masked payload (e.g. `number`, `expiration_month`, `expiration_year`) for display |
| `mask.card_number` | string | Masked card for display |
| `cvc_session_id` | string | Present if CVV was sent |
| `bin` | string | First 6 digits (BIN) |
| `fingerprint` | string | If deduplication enabled |
| `deduplicated` | boolean | If existing token was returned |
Request may include optional `maskingConfig` for custom masking behavior.
## Validation
* **Card number:** 13–19 digits, Luhn valid; spaces/dashes stripped.
* **Expiration:** month 01–12; year current or future.
* **CVV:** 3 digits (Visa, MC, Discover), 4 (Amex).
## Error responses
| Status | Example message |
| - | - |
| 400 | `Invalid card number`, `Card has expired`, `Missing required field: cardNumber` |
| 401 | `Invalid API key` |
| 403 | Permission denied |
See [Status codes](/api-reference/vault/status-codes) and [Common errors](/api-reference/vault/common-errors).
# Verify Key
Source: https://docs.ozura.com/api-reference/vault/verify-api-key
Verify an API key and read its project and permissions.
# GET /api/applications/key
Validate an API key and return associated project and application info and permissions.
## Request
**Headers**
| Header | Required | Description |
| - | - | - |
| `X-API-Key` | Yes | The API key to verify |
No request body.
## Response
### 200 OK
```json theme={null}
{
"success": true,
"message": "API key is valid",
"data": {
"project_id": "proj_xyz789",
"application_id": "app_abc123",
"name": "My Application",
"permissions": {
"tokenization": { "allowed": true },
"provisioning": { "allowed": false }
}
}
}
```
## Error responses
| Status | Example message |
| - | - |
| 401 | `Invalid API key` |
See [Status codes](/api-reference/vault/status-codes).
# API Reference
Source: https://docs.ozura.com/guides/payments/checkout/api-reference
Complete reference for all Ozura Checkout API endpoints
Complete reference for all Ozura Checkout API endpoints.
## Base URL
```
https://checkout.ozura.com
```
**Environments:** Use `https://checkout.ozura.com` for production. For staging, use the URL provided by your Ozura representative.
## Authentication
All requests require these headers:
| Header | Description |
| :- | :- |
| `X-API-KEY` | Your Vault API Key |
| `X-OZURA-API-KEY` | Your Merchant API Key |
| `Content-Type` | `application/json` |
## Rate Limits
Rate limits are applied per API key to ensure fair usage. If you exceed rate limits, you'll receive a `429 Too Many Requests` response.
## Create Session
Creates a checkout session and returns a checkout URL.
```
POST /api/sessions/create
```
### Request Body
| Field | Type | Required | Default | Description |
| :- | :- | :-: | :- | :- |
| `merchantId` | string | **Yes** | — | Your Merchant ID |
| `merchantName` | string | **Yes** | — | Business name shown to customer |
| `amount` | string/number | **Yes**\* | — | Payment amount in major currency units (dollars for USD, not cents): `"25.00"` charges \$25.00 |
| `currency` | string | No | `"USD"` | Currency code |
| `successUrl` | string | **Yes** | — | Redirect after successful payment |
| `cancelUrl` | string | **Yes** | — | "Go back" destination |
| `errorUrl` | string | **Yes** | — | Redirect if payment fails |
| `embedMode` | string | No | `"redirect"` | `"redirect"`, `"popup"`, or `"new_tab"`. `"iframe"` is coming soon; for now it is accepted but runs as `"redirect"` with an `EMBED DOWNGRADE` warning. See [Integration Modes](/guides/payments/checkout/integration-modes#iframe-mode-coming-soon) |
| `parentOrigin` | string | Cond. | — | Origin of the page that opens checkout (e.g. `"https://yoursite.com"`). Required for `embedMode: "popup"`; needed for `"new_tab"` to receive events. Result events are sent only to this origin |
| `checkoutMode` | string | No | `"payment"` | `"payment"`, `"donation"`, or `"recurring"` |
| `recurringConfig` | object | Cond. | — | Required when `checkoutMode: "recurring"`. See [Recurring Mode](/guides/payments/checkout/recurring-mode) |
| `donationConfig` | object | No | — | Donation settings. See [Donation Mode](/guides/payments/checkout/donation-mode) |
| `surchargePercent` | string/number | No | No surcharge (treated as `0%` at charge time) | Credit card surcharge, max `"3.00"` (3%). Shown as a line item when set |
| `transactionChannel` | string | No | `"ecommerce"` | `"ecommerce"` or `"moto"`. Forwarded to the payment processor |
| `taxExempt` | boolean | No | `false` | Skip sales tax calculation for this session |
| `items` | array | No | — | Cart items (alternative to `amount`). Not allowed with `recurring` mode |
| `metadata` | object | No | `{}` | Custom data (max 10KB), returned in the success URL |
| `appearance` | object | No | `{}` | Styling options — see [Appearance Reference](/guides/payments/checkout/appearance-reference) |
| `idempotencyKey` | string | No | — | Unique key for network retry protection |
| `allowPopup` | boolean | No | `true` | Accepted for compatibility; currently has no effect |
| `hideHeader` | boolean | No | `false` | Hide the merchant name / logo header |
| `donationConfig` | object | No | — | Donation preset amounts and limits. See [Donation Mode](/guides/payments/checkout/donation-mode) |
\*`amount` is required when `checkoutMode` is `"payment"` or `"recurring"` and `items` is not provided. Not required for `"donation"` mode.
**Deprecated fields:** `taxRate` and `shippingCost` are accepted and validated but **ignored** — they have no effect on pricing. Do not send these fields in new integrations.
### Response
```json theme={null}
{
"success": true,
"data": {
"sessionId": "session_xxxxxxxxxxxxxx",
"checkoutUrl": "https://checkout.ozura.com/checkout/session_xxxxxxxxxxxxxx",
"expiresAt": "2025-01-07T12:15:00.000Z",
"embedCode": "",
"pricing": {
"subtotal": 55.00,
"tax": 0,
"shipping": 0,
"total": 55.00,
"currency": "USD"
}
}
}
```
The response may also include a top-level `"warnings"` array with advisory messages, for example an `EMBED DOWNGRADE` message when you request `embedMode: "iframe"` (the session then runs as `"redirect"`). On an idempotent replay (same `idempotencyKey`), `"cached": true` is added at the top level and a new session is not created.
## Get Session
Retrieve session details. The session ID acts as the access token — no additional authentication headers are required.
```
GET /api/sessions/{sessionId}
```
### Response
The full session object is returned. Key fields include:
```json theme={null}
{
"success": true,
"data": {
"session": {
"id": "session_xxxxxxxxxxxxxx",
"status": "pending",
"amount": "25.00",
"currency": "USD",
"merchantId": "your_merchant_id",
"checkoutMode": "payment",
"embedMode": "redirect",
"taxExempt": false,
"appearance": {},
"metadata": {
"merchantName": "My Store",
"orderId": "order_123"
},
"recurringConfig": null,
"surchargePercent": null,
"transactionChannel": "ecommerce",
"expiresAt": "2025-01-07T12:15:00.000Z",
"createdAt": "2025-01-07T11:45:00.000Z"
},
"pricing": {
"subtotal": 25.00,
"tax": 0,
"shipping": 0,
"total": 25.00,
"currency": "USD"
},
"items": []
}
}
```
`merchantName` is stored inside `metadata`, not as a top-level session field. For recurring sessions, `recurringConfig` contains the full plan config object. The `metadata` object may contain internal fields used by the checkout page — do not expose, log, or forward the full `metadata` object to clients.
### Session Statuses
| Status | Description |
| :- | :- |
| `pending` | Awaiting payment |
| `completed` | Payment successful |
| `cancelled` | Customer cancelled |
| `expired` | Session timed out |
| `failed` | Payment failed |
## Cancel Session
Cancels a pending session. This endpoint is called **automatically** by the Ozura checkout page when the customer clicks "Cancel" — you do not need to call it from your server.
```
POST /api/sessions/{sessionId}/cancel
```
This endpoint is browser-facing. It is called by the checkout page using a short-lived internal session credential. It cannot be called from your server using your Vault API Key — requests with `X-API-KEY` are rejected. If a session is not cancelled explicitly, it expires automatically after 30 minutes.
### Response
```json theme={null}
{
"success": true
}
```
## Create Payment Link
Creates a reusable payment URL.
```
POST /api/payment-links/create
```
### Request Body
| Field | Type | Required | Default | Description |
| :- | :- | :-: | :- | :- |
| `merchantId` | string | **Yes** | — | Your Merchant ID |
| `merchantName` | string | **Yes** | — | Business name shown to customer |
| `amount` | string | **Yes**\* | — | Payment amount |
| `currency` | string | No | `"USD"` | Currency code |
| `successUrl` | string | **Yes** | — | Redirect after payment |
| `cancelUrl` | string | **Yes** | — | Redirect if cancelled |
| `errorUrl` | string | **Yes** | — | Redirect if failed |
| `checkoutMode` | string | No | `"payment"` | `"payment"`, `"donation"`, or `"recurring"` |
| `recurringConfig` | object | Cond. | — | Required when `checkoutMode: "recurring"`. See [Recurring Mode](/guides/payments/checkout/recurring-mode) |
| `donationConfig` | object | No | — | Donation settings. See [Donation Mode](/guides/payments/checkout/donation-mode) |
| `surchargePercent` | string/number | No | No surcharge | Credit card surcharge, max `"3.00"` (3%) |
| `transactionChannel` | string | No | `"ecommerce"` | `"ecommerce"` or `"moto"` |
| `taxExempt` | boolean | No | `false` | Skip sales tax calculation for this link's sessions |
| `embedMode` | string | No | `"redirect"` | `"redirect"`, `"popup"`, `"iframe"`, `"new_tab"`. Stored on the link but **payment links always open checkout as redirect** — this field is persisted for informational purposes only |
| `parentOrigin` | string | Cond. | — | Required if `embedMode` is `"popup"` or `"iframe"` (validation only; payment links always open as redirect) |
| `allowPopup` | boolean | No | `true` | Accepted for compatibility; currently has no effect |
| `hideHeader` | boolean | No | `false` | Hide the merchant name / logo header |
| `items` | array | No | — | Cart items (alternative to `amount`) |
| `metadata` | object | No | `{}` | Custom data (max 10KB) |
| `merchantPayLinkReference` | string | No | — | Link-scoped correlation ID returned on **every** transaction from this link, inside the success `metadata`. Max 128 chars: letters, numbers, `. _ : -`. |
| `appearance` | object | No | `{}` | Styling options — see [Appearance Reference](/guides/payments/checkout/appearance-reference) |
| `idempotencyKey` | string | No | — | Unique key for network retry protection |
| `expiresInDays` | number | No | `7` | Days until link expires |
| `usageLimit` | number | No | `null` (unlimited) | Max successful payments before the link is deactivated |
\*Required when `checkoutMode` is `"payment"` or `"recurring"` and `items` is not provided. Not required for donation mode.
**Correlating payments to your orders.** Payment links support three correlation references, all returned in the success-redirect `metadata` (see [Correlate Payments to Your Orders](/guides/payments/checkout/payment-links#correlate-payments-to-your-orders) for the full guide):
* `merchantPayLinkReference` — set here at link creation; static, returned on **every** transaction from the link (attribute a link/campaign).
* `merchantReference` — set **per checkout** via a `?ref=` query parameter appended to the link URL (not a request-body field); unique per payer.
* `merchantRecurringReference` — for recurring links, set inside [`recurringConfig`](/guides/payments/checkout/recurring-mode); also persisted on the plan so you can look the plan up by it.
### Response
```json theme={null}
{
"success": true,
"data": {
"paymentLinkId": "pl_xxxxxxxxxxxxxx",
"url": "https://checkout.ozura.com/pay/pl_xxxxxxxxxxxxxx",
"expiresAt": "2025-01-14T00:00:00.000Z"
}
}
```
On an idempotent replay (same `idempotencyKey`), the response also includes `"cached": true`. Either response may include a top-level `"warnings"` array with merchant-facing advisory messages.
## Check Payment Link Availability
Check if a payment link is still valid. No authentication required.
```
GET /api/payment-links/{linkId}/check-availability
```
### Response (Available)
```json theme={null}
{
"success": true,
"available": true,
"message": "Payment link is available"
}
```
### Response (Unavailable)
```json theme={null}
{
"success": false,
"available": false,
"error": "Payment link usage limit reached"
}
```
## Error Responses
All errors follow this format:
```json theme={null}
{
"success": false,
"error": "Human-readable error message",
"details": ["Field-level detail — only present for validation errors"]
}
```
`details` is only included when there are multiple specific validation failures (e.g. several invalid fields at once). Single-field and non-validation errors return `error` only.
### Common Error Codes
| Code | Meaning |
| :- | :- |
| `400` | Bad request (validation failed) |
| `401` | Invalid or missing API credentials |
| `404` | Session not found |
| `409` | Conflict — session already completed, or `idempotencyKey` reuse rejected (see below) |
| `410` | Session expired |
| `429` | Too many requests (rate limit exceeded) |
| `500` | Server error (retry recommended) |
### Idempotency Conflict Codes
When you replay a request with an `idempotencyKey` that does not match the original active session, the API returns `409` with one of these machine-readable `errorCode` values:
| `errorCode` | Meaning |
| :- | :- |
| `IDEMPOTENCY_KEY_EXPIRED_SESSION` | The session previously created with this `idempotencyKey` has expired. Use a new `idempotencyKey` to create a fresh session |
| `IDEMPOTENCY_KEY_TERMINAL_SESSION` | The session for this `idempotencyKey` is in a terminal state (`cancelled` or `failed`). Use a new `idempotencyKey` |
# Customize Appearance
Source: https://docs.ozura.com/guides/payments/checkout/appearance
Brand the checkout page with your colors and logo
Brand the hosted checkout page with your colors, logo, and typography. Pass an `appearance` object when you [create a session](/guides/payments/checkout/create-session) or [payment link](/guides/payments/checkout/payment-links).
**Current checkout layout:** On desktop, the customer sees a **soft outer page background** (default lavender) with a **single centered card** (\~960px): **payment form on the left** (\~56%) and **order summary on the right** (\~44%), plus a **brand bar** above the card. On mobile, branding and summary stack above the form. There is no API flag to switch to a different layout.
The full field list and edge cases (including fields accepted by the API but not yet used in the UI) are in the [Appearance Reference](/guides/payments/checkout/appearance-reference).
## Basic Customization
```json theme={null}
{
"merchantId": "your_merchant_id",
"merchantName": "My Store",
"amount": "25.00",
"successUrl": "https://mystore.com/success",
"cancelUrl": "https://mystore.com/cancel",
"errorUrl": "https://mystore.com/error",
"appearance": {
"primaryColor": "#4F46E5",
"buttonBackgroundColor": "#4F46E5",
"textColor": "#111827"
}
}
```
`merchantName` is **required** when creating a session; it is stored on the session and drives the **store title** next to your logo (it is not set through `appearance` alone).
## Appearance Options (common)
### Colors
| Option | Description | Example |
| :- | :- | :- |
| `primaryColor` | Main brand color (accents, selected payment method border, default pay button chain) | `"#4F46E5"` |
| `primaryHoverColor` | Primary hover | `"#4338CA"` |
| `secondaryColor` | Secondary accents where used | `"#64748B"` |
| `backgroundColor` | Sets global/custom fallbacks (inputs, many panels). **Not** the outer lavender canvas behind the desktop card—use `cssVariables` with key `page-background` for that (see reference). | `"#FFFFFF"` |
| `textColor` | Default text | `"#111827"` |
| `borderColor` | Borders and some dividers | `"#D1D5DB"` |
| `buttonBackgroundColor` | Pay button background (overrides `primaryColor` for the button) | `"#4F46E5"` |
| `buttonHoverColor` | Pay button hover | `"#4338CA"` |
### Logo
| Option | Description | Example |
| :- | :- | :- |
| `logoUrl` | HTTPS URL to your logo | `"https://mystore.com/logo.png"` |
| `logoPosition` | `inline` (default) or `top` | `"inline"` |
| `logoMaxHeight` | Present in the API model; **not applied** by the live page today (logo size follows `logoPosition` and viewport). | `"50px"` |
| `showMerchantName` | If `true`, show name with logo. If omitted/false with a logo, **logo only**. Without `logoUrl`, the **name always shows**. | `true` |
### Layout (inside the desktop card)
| Option | Description | Example |
| :- | :- | :- |
| `leftSideBackground` | **Order summary** column (visually the **right** side of the card) | `"#F9FAFB"` |
| `rightSideBackground` | **Payment form** column (visually the **left** side of the card) | `"#FFFFFF"` |
| `formBackground` | Form container / panel fill | `"#FFFFFF"` |
| `mobileBackground` | Mobile sections (summary, donation block, etc.) | `"#FFFFFF"` |
| `borderRadius` | Corner scale for themed controls | `"lg"` |
Valid `borderRadius` values: `none`, `sm`, `base`, `lg`, `xl`, `full`.
## Example: Dark panels
```json theme={null}
{
"appearance": {
"primaryColor": "#8B5CF6",
"buttonBackgroundColor": "#8B5CF6",
"buttonHoverColor": "#7C3AED",
"backgroundColor": "#1F2937",
"textColor": "#F9FAFB",
"borderColor": "#374151",
"leftSideBackground": "#111827",
"rightSideBackground": "#1F2937",
"formBackground": "#374151"
}
}
```
## Example: Brand colors + logo
```json theme={null}
{
"appearance": {
"primaryColor": "#FF6B00",
"primaryHoverColor": "#E55D00",
"logoUrl": "https://mystore.com/logo.png",
"showMerchantName": true
}
}
```
## Outer page background (desktop)
The area **behind** the centered card uses CSS variable `--custom-page-background` (default lavender). Set it via `appearance.cssVariables`:
```json theme={null}
{
"appearance": {
"primaryColor": "#4F46E5",
"cssVariables": {
"page-background": "#EEF2FF"
}
}
}
```
Values in `cssVariables` are sanitized as **colors or size/dimension values** (e.g. `px`, `em`, `%`). Values that don't parse as either are silently dropped. Use `customCss` for arbitrary styling (within [security limits](/guides/payments/checkout/appearance-reference#custom-css)).
As a shorthand, you can also set `pageBackground` directly in the `appearance` object instead of via `cssVariables`:
```json theme={null}
{
"appearance": {
"primaryColor": "#4F46E5",
"pageBackground": "#EEF2FF"
}
}
```
Both forms are equivalent. `cssVariables.page-background` takes precedence if both are set.
## Full options
See the [Appearance Reference](/guides/payments/checkout/appearance-reference) for every key, mobile overrides, cart colors, `customCss`, accessibility warnings, and limitations (for example **pay button label** is derived from tax context—not a single `appearance` string).
# Appearance Reference
Source: https://docs.ozura.com/guides/payments/checkout/appearance-reference
Complete list of all checkout appearance customization options
Complete list of `appearance` fields for Ozura Checkout. The hosted page uses a **centered card** layout on desktop (form left, summary right inside the card; brand bar above). Field names match the Checkout API.
**Naming note:** `leftSideBackground` styles the **order summary** panel (the visually **right** column inside the card). `rightSideBackground` styles the **payment form** panel (the visually **left** column). The names are historical; use the descriptions below, not the word “left/right” alone.
## Global Colors
| Key | Type | Description |
| :- | :- | :- |
| `primaryColor` | Hex | Main brand color (accents, active payment-method border, default pay button chain) |
| `primaryHoverColor` | Hex | Hover for primary-colored controls |
| `secondaryColor` | Hex | Secondary accent where used |
| `backgroundColor` | Hex | Sets global/custom fallbacks (inputs, many panels). **Not** the outer lavender canvas—use `cssVariables.page-background` (see [Customize Appearance](/guides/payments/checkout/appearance)). |
| `textColor` | Hex | Default text |
| `borderColor` | Hex | Borders and some dividers |
## Layout (Desktop — inside the card)
| Key | Type | Description |
| :- | :- | :- |
| `leftSideBackground` | Hex | **Order summary** panel background (right portion of the card) |
| `rightSideBackground` | Hex | **Payment form** panel background (left portion of the card) |
| `formBackground` | Hex | Form container background |
| `dividerColor` | Hex | Divider color; on mobile, used with `showTopDivider` for a line under the top section |
| `showTopDivider` | Boolean | When `true` and `dividerColor` is set, draws a divider under the mobile header area |
## Layout (Mobile)
| Key | Type | Description |
| :- | :- | :- |
| `mobileBackground` | Hex | Mobile sections (summary, donation block, etc.) |
| `mobileTopBackground` | Hex | Top branding / header band |
| `mobileBottomBackground` | Hex | Lower sections where applied |
| `mobileFormBackgroundColor` | Hex | Form area background |
| `mobileStickyTop` | Boolean | If `true`, pins the mobile branding header and order summary to the top of the viewport as the customer scrolls down to fill in the form |
## Typography
| Key | Type | Description |
| :- | :- | :- |
| `fontFamily` | String | Font stack (sanitized); does **not** style the fixed “Powered by OzuraPay” footer |
| `fontSize` | String | `sm`, `base`, `lg`, `xl` (drives scaled sizing on the page) |
| `fontWeight` | String | In API/types; **not applied** by checkout today |
## Spacing & Borders
| Key | Type | Description |
| :- | :- | :- |
| `spacing` | String | `compact`, `normal`, `spacious` |
| `borderRadius` | String | `none`, `sm`, `base`, `lg`, `xl`, `full` |
## Text Colors
| Key | Type | Description |
| :- | :- | :- |
| `headingTextColor` | Hex | Titles and headings (including merchant name styling chain) |
| `bodyTextColor` | Hex | Body text where wired |
| `labelTextColor` | Hex | Form labels |
| `formLabelTextColor` | Hex | Labels inside card/ACH sections |
| `secondaryTextColor` | Hex | Secondary/helper text |
| `amountTextColor` | Hex | Amount displays |
| `emailTextColor` | Hex | Email field |
| `cardTextColor` | Hex | Card payment fields |
| `achTextColor` | Hex | ACH / bank fields |
## Input Fields
| Key | Type | Description |
| :- | :- | :- |
| `inputBackgroundColor` | Hex | Input background |
| `inputBorderColor` | Hex | Default border (the new layout often uses borderless inputs with a focus ring; variable still applies where used) |
| `inputFocusBorderColor` | Hex | Focus border where used |
| `inputErrorBorderColor` | Hex | Error state |
## Primary Button (Pay / Donate)
| Key | Type | Description |
| :- | :- | :- |
| `buttonBackgroundColor` | Hex | Pay button background |
| `buttonHoverColor` | Hex | Pay button hover |
| `buttonBorderColor` | Hex | Pay button border |
The **button label** (e.g. `Pay $10.00 + Tax`, `Donate $25.00`) is **not** a single `appearance` field—it is computed from donation mode, tax estimate, country, and tax-exempt flags.
## Payment Method Selector
| Key | Type | Description |
| :- | :- | :- |
| `paymentMethodBorderColor` | Hex | Border around the payment method **container** and related inputs |
| `paymentMethodButtonBackgroundColor` | Hex | Inactive method tab background |
| `paymentMethodButtonHoverBackgroundColor` | Hex | Hover background |
| `paymentMethodButtonActiveBackgroundColor` | Hex | Selected method tab background |
| `paymentMethodButtonTextColor` | Hex | Method tab text |
| `paymentMethodIconColor` | Hex | Icons on method tabs |
## Mobile Text & Input Overrides
| Key | Type | Description |
| :- | :- | :- |
| `mobileHeadingTextColor` | Hex | Headings on mobile |
| `mobileAmountTextColor` | Hex | Amount on mobile |
| `mobileSecondaryTextColor` | Hex | Secondary text on mobile |
| `mobileLabelTextColor` | Hex | Labels on mobile |
| `mobileInputTextColor` | Hex | Input text on mobile |
| `mobileInputBackgroundColor` | Hex | Input background on mobile |
| `mobileInputBorderColor` | Hex | Input border on mobile |
| `mobileButtonBackgroundColor` | Hex | Pay button on mobile |
| `mobileButtonHoverColor` | Hex | Pay button hover on mobile |
| `mobileFormBackgroundColor` | Hex | Form section on mobile |
## Cancel Button
| Key | Type | Description |
| :- | :- | :- |
| `cancelButtonText` | String | Label (default: `Cancel and Return`) |
| `cancelButtonBackgroundColor` | Hex | Background |
| `cancelButtonTextColor` | Hex | Text color |
| `cancelButtonHoverColor` | Hex | **Text** color on hover (not background) |
| `cancelButtonBorderColor` | Hex | Border (`1px solid …` when set) |
## Branding
| Key | Type | Description |
| :- | :- | :- |
| `logoUrl` | URL | Logo image (HTTPS) |
| `logoPosition` | String | `inline` or `top` |
| `logoMaxHeight` | String | In API; **not read** by live logo component |
| `showMerchantName` | Boolean | Show name with logo (see [Customize Appearance](/guides/payments/checkout/appearance)) |
| `hideBranding` | Boolean | In API; **not used** by the checkout page today |
The **store name** comes from the required **`merchantName`** field when you create the session (stored on the session for the UI).
## Cart Display (Itemized Checkout)
When using an `items` array:
| Key | Type | Description |
| :- | :- | :- |
| `cartFontFamily` | String | Font for cart block (falls back to `fontFamily`) |
| `cartProductNameColor` | Hex | Product name |
| `cartBrandColor` | Hex | Brand line |
| `cartDescriptionColor` | Hex | Description |
| `cartVariantColor` | Hex | Variant line |
| `cartCategoryColor` | Hex | Category |
| `cartSkuColor` | Hex | SKU |
| `cartPriceColor` | Hex | Price |
| `cartOriginalPriceColor` | Hex | Strikethrough price |
| `cartQuantityColor` | Hex | Quantity |
| `cartDiscountBadgeColor` | Hex | Discount badge background |
| `cartDiscountBadgeTextColor` | Hex | Discount badge text |
| `cartTaxFreeColor` | Hex | Tax-free indicator |
| `cartSubtotalColor` | Hex | Subtotal |
| `cartTotalColor` | Hex | Total |
| `cartTaxNoteColor` | Hex | Tax note text |
Line content (images, badges, links) is controlled by each [cart item](/guides/payments/checkout/create-session) field, not `appearance`.
## API Warnings
Session and payment-link creation may return **`warnings`** (not errors) when colors fail WCAG contrast checks or mobile/desktop combinations look mismatched. Surface these in your dashboard when present.
## Pre-Made Themes
### Minimal Light
```json theme={null}
{
"appearance": {
"primaryColor": "#6366F1",
"primaryHoverColor": "#4F46E5",
"backgroundColor": "#FAFAFA",
"textColor": "#1F2937",
"fontFamily": "Poppins, sans-serif",
"borderRadius": "lg"
}
}
```
### Violet Dark Mode
```json theme={null}
{
"appearance": {
"primaryColor": "#8b5cf6",
"buttonBackgroundColor": "#8b5cf6",
"leftSideBackground": "#0f0a1a",
"rightSideBackground": "#1a0f2e",
"formBackground": "#1a0f2e",
"headingTextColor": "#ffffff",
"textColor": "#e5e7eb",
"inputBackgroundColor": "#1a0f2e",
"inputBorderColor": "#8b5cf6",
"cssVariables": {
"page-background": "#0c0518"
}
}
}
```
### Emerald Clean
```json theme={null}
{
"appearance": {
"primaryColor": "#10b981",
"buttonBackgroundColor": "#10b981",
"buttonHoverColor": "#059669",
"inputFocusBorderColor": "#10b981",
"borderRadius": "base"
}
}
```
## Custom CSS
| Key | Type | Description |
| :- | :- | :- |
| `customCss` | String | Raw CSS injected in a `