> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.crisscross.money/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.crisscross.money/_mcp/server.

# Direct API Integration

> Explore how merchants can directly integrate with CrissCross APIs for maximum flexibility over non-card payment flows.

### Overview

Direct API Integration with CrissCross allows merchants to manage their own checkout screens and directly interact with CrissCross APIs for payments. This approach offers full control over the user experience for bank, mobile-money, and other non-card payment methods.

> **Card payments use Hosted Checkout only**
>
> **Card payments are not available via Direct API Integration.** The API's card payment variant accepts only pre-encrypted card data produced by CrissCross's hosted checkout tooling — raw card details are never accepted, and merchants cannot generate the required ciphertext themselves. Use [Hosted Checkout](/hosted-checkout) to collect card payments; it also keeps raw card data out of your systems, which can reduce your PCI-DSS scope.

---

### How It Works

1. **Create a Checkout Session**:
   Create a checkout session that captures the amount, currency, and payer details.

2. **Present Payment Options**:
   Display the payment methods returned from CrissCross on your custom checkout page.

3. **Capture and Process Payment Details**:
   Collect the details the chosen payment method needs (e.g. a mobile money number and operator) and submit them with `POST /v1/payment`.

4. **Receive Payment Status Updates**:
   Use webhooks to get real-time status updates for payments (e.g., authorized, declined).

---

### Example: Create a Checkout Session

Start by creating a checkout session. This returns a `sessionId` and (for hosted flows) may include a `paymentLink`. For direct integrations, you typically proceed by selecting a method and initiating a transaction via `POST /payment`.

```json
{
  "merchantId": "YOUR_MERCHANT_ID",
  "merchantReference": "ORDER_12345",
  "amount": 15000,
  "currency": "NGN",
  "integrationType": "direct",
  "redirectUrl": "https://merchant-site.com/redirect",
  "payerDetails": {
    "emailAddress": "jane.doe@example.com",
    "location": "NGA",
    "fullName": "Jane Doe"
  }
}
```

#### Sample Response:

```json
{
  "sessionId": "0d3f1d6c-1c49-4b89-9db6-1fdc06f24c8a",
  "payerId": "2a1c4f1a-0d44-4f16-8c8e-9a3b4c5d6e7f"
}
```

The response includes a `payerId` — a persistent identifier for the customer behind this session. Store it and pass it back in `payerDetails.payerId` on future sessions to recognise a returning payer. See [Payer ID](/payer-id).

> **Pricing in one currency, collecting in another?**
>
> Pass a `rateLockId` on session creation to charge the payer at an FX rate you've locked in advance — the session `currency` must then equal the lock's base currency (the currency you price in, e.g. `USD`), and the payer is charged in the lock's quote currency at the locked rate. The create response then carries `fxCommit` with the collection-currency `collectionAmount`, so you can show the payer both amounts without a second call — see [rate locks](/rate-locks#reuse-across-checkout-sessions) for the shape. The lock is verified when you attach it at session creation and again when you initiate the payment via `POST /v1/payment`. A lock that is unknown, expired, or cancelled returns a `422` with a machine-readable `error` code — see [when a lock is rejected](/rate-locks#when-a-lock-is-rejected) for the codes and what to do about each. Without one, the conversion falls back to [Adaptive Currency Conversion](/adaptive-currency-conversion)'s default per-session rate when the payer's collection currency differs.

---

### Card Payments

Card payments run on [Hosted Checkout](/hosted-checkout) only — the card `paymentDetails` variant requires pre-encrypted card data that merchant integrations cannot produce. To offer card alongside your direct integration, create the session with `integrationType: "hosted"` and redirect the customer to the returned `paymentLink`.

## If a transaction requires additional user interaction (e.g. redirect/challenge), use `GET /v1/payment/{transactionId}` to poll for `authState`, then submit additional data using `POST /v1/payment/authorize` for `fields` flows.

### Webhooks for Payment Status

CrissCross will notify merchants via webhooks about the status of transactions. Make sure your server is set up to receive and handle these notifications.

The delivered body is the transaction itself — there is no envelope, and the event type does not appear inside the body, so route on `status`. Sample webhook payload:

```json
{
  "transactionId": "9f3e4b2c-1a6d-4e88-9d3a-ff1234567890",
  "sessionId": "0d3f1d6c-1c49-4b89-9db6-1fdc06f24c8a",
  "merchantReference": "ORDER-2026-0142",
  "status": "COMPLETED",
  "message": "Transaction completed",
  "authState": {
    "state": "completed",
    "transitionedAt": "2026-06-02T10:30:14Z",
    "message": "Transaction completed"
  }
}
```

See [Webhook Events](/webhook-events) for the full event catalogue and payload schemas.

---

### Best Practices

* **Secure Payment Handling**: Use tokenization where possible to avoid direct exposure to sensitive data.
* **Monitor API Usage**: Ensure you are using the API efficiently to avoid rate limits.
* **Webhooks**: Use webhooks for real-time updates on payment statuses.
* **Error Handling**: Implement retry mechanisms for failed payments based on CrissCross's error codes.

---