# Direct Link Pay-In: APM

## When to use

Trigger an APM pay-in with no OMNO UI. Your backend creates and starts the transaction, then redirects the customer to the PSP.

## Steps

### 1. Create the pay-in

`POST /public/payin` with a `directRouteId` and the required `CreatePayInRequest` fields.

```bash
curl -X POST "https://api.omno.com/public/payin" \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "orderId": "order-2026-000202",
    "amount": 100.00,
    "currency": "EUR",
    "kycVerified": true,
    "previousTransactionCount": 5,
    "customer": {
      "externalUserId": "3183818381183",
      "firstName": "John",
      "lastName": "Doe",
      "email": "john.doe@example.com",
      "phoneNumber": "1 1234567890",
      "dateOfBirth": "1990-12-01",
      "billing": {
        "address": "123 Main St",
        "city": "Atlanta",
        "state": "GA",
        "countryCode": "US",
        "postalCode": "30308"
      }
    },
    "directRouteId": "944732c4608c441aa91379478b91f76e",
    "returnUrls": {
      "success": "https://merchant.example.com/success",
      "failure": "https://merchant.example.com/failure"
    }
  }'
```

Response:

```json
{
  "paymentId": "944732c4608c441aa91379478b91f76e",
  "status": "CREATED",
  "iframeUrls": {
    "card": "https://checkout.omno.com/payments/payin/944732c4608c441aa91379478b91f76e/card",
    "apm": "https://checkout.omno.com/payments/payin/944732c4608c441aa91379478b91f76e/apm"
  }
}
```

> ℹ️ **Note:** `iframeUrls` are returned for every pay-in but are **not** used in Direct Link. Use `redirectUrl` from step 2. [TBD: confirm whether `iframeUrls` are returned or omitted when `directRouteId` is set]


`CreatePayInRequest` fields:

| Field | Type | Required | Description | Example |
|  --- | --- | --- | --- | --- |
| `orderId` | string | Yes | Your unique identifier for the pay-in. | `order-2026-000202` |
| `amount` | **number** | Yes | Pay-in amount. | `100.00` |
| `currency` | string | Yes | ISO 4217 currency. | `EUR` |
| `kycVerified` | boolean | Yes | Whether the customer is KYC verified in your system. | `true` |
| `previousTransactionCount` | integer | Yes | Previous transactions in your system. | `5` |
| `customer` | object | Yes | [`CustomerRequest`](https://docs.omno.com/apis). |  |
| `returnUrls` | object | Yes | `success` and `failure` URLs. |  |
| `directRouteId` | string | Yes (Direct Link) | Direct Link route ID. | `944732c4608c441aa91379478b91f76e` |
| `routerId` | string | No | Specific router ID. |  |
| `merchantAdditionalData` | object | No | Additional data for the transaction. |  |


### 2. Start processing

`POST /public/payin/{payinId}/process/direct-link` with `browser` and `additionalData`.

```bash
curl -X POST \
  "https://api.omno.com/public/payin/944732c4608c441aa91379478b91f76e/process/direct-link" \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "browser": {
      "acceptHeader": "text/html,application/xhtml+xml",
      "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
      "language": "en-US",
      "timeZoneOffset": -60,
      "timeZone": "Europe/Berlin",
      "colorDepth": 24,
      "screenHeight": 1080,
      "screenWidth": 1920,
      "windowHeight": 969,
      "windowWidth": 1920,
      "javaEnabled": false,
      "javascriptEnabled": true,
      "customerIp": "203.0.113.10"
    },
    "additionalData": {}
  }'
```

Response:

```json
{
  "paymentId": "944732c4608c441aa91379478b91f76e",
  "status": "PENDING",
  "redirectUrl": "https://psp.example.com/redirect/944732c4608c441aa91379478b91f76e"
}
```

### 3. Redirect the customer

Send the customer to `redirectUrl` from the response to complete the payment.

### 4. Final status

Determine the outcome from [webhooks](/guides/getting-started/webhooks) or [GET transaction](https://docs.omno.com/apis).

## The `Browser` object

Collect these values client-side (they support 3DS and fraud checks).

| Field | Type | How to collect |
|  --- | --- | --- |
| `acceptHeader` | string | Server-side `Accept` request header. |
| `userAgent` | string | `navigator.userAgent`. |
| `language` | string | `navigator.language`. |
| `timeZoneOffset` | integer | `new Date().getTimezoneOffset()` (minutes). |
| `timeZone` | string | `Intl.DateTimeFormat().resolvedOptions().timeZone`. |
| `colorDepth` | integer | `screen.colorDepth`. |
| `screenHeight` | integer | `screen.height`. |
| `screenWidth` | integer | `screen.width`. |
| `windowHeight` | integer | `window.innerHeight`. |
| `windowWidth` | integer | `window.innerWidth`. |
| `javaEnabled` | boolean | `navigator.javaEnabled()`. |
| `javascriptEnabled` | boolean | `true` when collected in-browser. |
| `customerIp` | string | Customer IP (from your server). |


## `additionalData` per APM

`additionalData` carries method-specific fields for the chosen APM.

> **[TBD: required `additionalData` keys per APM]**


## Failure states

- **Decline** — `DECLINED` (via webhook/GET).
- **PSP failure** — `FAILED`.
- **Timeout** — `TIMEOUT`; may resolve later.
- **Customer abandons** — no completion at the PSP; reconcile via GET.
- **Pending resolving asynchronously** — `PENDING`; final status via webhook.