> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ecomm365.eu/llms.txt
> Use this file to discover all available pages before exploring further.

# Direct API Quickstart

> Submit a Sandbox card payment from your server and confirm the final Payment status

Direct API lets your checkout collect card details and send them from your
server to Flowlix. Every new Direct Payment goes through server-side
authentication. Your integration handles the initial request, a browser action
only when the Payment requires one, and retrieval of the authoritative result.

<Warning>
  Raw card number, expiry, and CVC enter your systems on this path. Use Direct
  API only from an approved PCI-compliant card-data environment. Never log or
  persist the request body.
</Warning>

## Prerequisites

* Complete [Authentication](/guides/authentication).
* Use only test card data in [Sandbox](/guides/testing); never a real card.
* Collect the shopper's literal IP address and an HTTPS `return_url`.
* Resolve the shopper to a stable opaque `merchant_customer_id`.

## Wallet contract availability

<Warning>
  **Experimental: Apple Pay and Google Pay.** Wallet support and this guidance
  are still being refined and are not final. The fields, validation, and
  supported wallet route described on this page can change. Confirm the
  supported route and provider setup with Flowlix before enabling a new
  merchant. Ordinary `type: card` requests are not affected.
</Warning>

Apple Pay and Google Pay direct processing accepts the decrypted wallet inputs
described below. For the source token semantics, see Apple's [payment token
format reference](https://developer.apple.com/documentation/passkit/payment-token-format-reference)
and Google's [payment-data cryptography guide](https://developers.google.com/pay/api/web/guides/resources/payment-data-cryptography).
The gateway forwards prepared wallet credentials to the PCI-scoped provider
route; upstream wallet setup, decryption, and verification remain outside this
public API. Encrypted wallet input is reserved for a later release and is
rejected in this contract. Do not send real wallet credentials to test syntax
or availability. Ordinary card processing remains available as described below.

Wallet payments can be rejected by the existing antifraud checks before they
are submitted to the processor. In that case the Payment is `FAILED` with
`failure_code: fraud_filter`; no additional browser action is required. Handle
this terminal result as described in [Payment lifecycle](/guides/payment-lifecycle).
For Apple Pay and Google Pay `CRYPTOGRAM_3DS`, credential-based checks use the
wallet's stable token identity. Different wallet tokens for the same funding
card have separate credential histories. Google Pay `PAN_ONLY` uses the same
card identity as an ordinary card payment.

### Wallet input reference

Use `payment_method_data.type` with exactly one matching `apple_pay` or
`google_pay` object. `type` is the merchant-facing method and selects the
wallet validation and provider execution path; it is not inferred from the
card number or network. Each wallet object contains `format` and `data`.
`format: decrypted` means that an enabled upstream wallet integration has
already prepared the typed fields below. It does not mean that arbitrary card
data is accepted, and it is not inferred from which fields happen to be
present. Keep the same common request fields, authentication, and
`Idempotency-Key` header as a card request. Ordinary `type: card` requests
retain their existing card fields, including CVC, and do not gain a `format`
field.

For `format: decrypted`, wallet `data.card` requires `number`, `exp_month`, and
`exp_year`; `holder_name` is optional. No wallet card accepts CVC.

| Wallet data | Fill these fields when | What the fields affect |
| - | - | - |
| Apple Pay 3DSecure | Always provide `card` and `cryptogram`; provide `eci` when the prepared Apple credential includes it. Apple EMV/China UnionPay input is not supported. | The card fields identify the prepared wallet card; the cryptogram and optional ECI carry the wallet/network authentication context to the provider. |
| Google Pay `CRYPTOGRAM_3DS` | Always provide `auth_method`, `card`, and `cryptogram`; provide `eci` when the prepared Google credential includes it. | Selects the device-token branch and sends its cryptogram, plus any supplied ECI, to the provider. A name containing `3DS` does not by itself mean that a browser challenge will occur. |
| Google Pay `PAN_ONLY` | Always provide `auth_method` and `card`, and provide the request's `browser_information`. Do not provide `cryptogram` or `eci`. | Selects the PAN branch. The provider uses the browser context for its 3DS request; the issuer may complete it frictionlessly or require a customer challenge. |

Google `auth_method` is required and case-sensitive: preserve the value from
the prepared Google credential and send exactly `PAN_ONLY` or
`CRYPTOGRAM_3DS`. It describes the credential variant, not the eventual
3DS result. Missing, null, or unknown values are invalid. Omitting a
cryptogram never selects PAN\_ONLY, and incomplete CRYPTOGRAM\_3DS input is not
processed as PAN\_ONLY.

`cryptogram` is required for Apple 3DSecure and Google
`CRYPTOGRAM_3DS`. It is exactly 28 standard-Base64 characters from the prepared
wallet credential, not a token you create from the card fields, not a private
key, and not CVC. Submit it unchanged, without trimming or re-encoding it.
Flowlix may repeat an eligible provider call for the same payment order using
the same credential; this does not authorize a new merchant payment attempt.
Its presence supplies authentication context; it does not by
itself make the Payment successful, and matching the input shape is not proof
that the credential can authorize a payment.

Only Apple 3DSecure and Google `CRYPTOGRAM_3DS` accept `eci`. Omit `eci` when the
prepared credential does not supply it. When supplied, it must be exactly two
decimal digits, such as `"05"`; preserve the value without normalization. ECI
carries the network's e-commerce authentication context to the provider and can
affect authorization processing; it is not a success signal and must not be
invented from
`auth_method`, a redirect URL, or a Payment status. Do not trim, pad, default,
or convert it to a number. Explicit null is invalid; empty and one-digit ECI
values are invalid.

These JSON fragments illustrate the three decrypted inputs. Add the common
fields shown in the card request below. The card is synthetic test data and the
Base64 value encodes a placeholder, not a usable wallet credential.

<CodeGroup>
  ```json Apple Pay theme={null}
  {
    "payment_method_data": {
      "type": "apple_pay",
      "apple_pay": {
        "format": "decrypted",
        "data": {
          "card": { "number": "4635440000002207", "exp_month": 12, "exp_year": 2028 },
          "cryptogram": "c3ludGhldGljLWNyZWRlbnRpYWw=",
          "eci": "05"
        }
      }
    }
  }
  ```

  ```json Google Pay CRYPTOGRAM_3DS theme={null}
  {
    "payment_method_data": {
      "type": "google_pay",
      "google_pay": {
        "format": "decrypted",
        "data": {
          "auth_method": "CRYPTOGRAM_3DS",
          "card": { "number": "4635440000002207", "exp_month": 12, "exp_year": 2028 },
          "cryptogram": "c3ludGhldGljLWNyZWRlbnRpYWw=",
          "eci": "05"
        }
      }
    }
  }
  ```

  ```json Google Pay PAN_ONLY theme={null}
  {
    "payment_method_data": {
      "type": "google_pay",
      "google_pay": {
        "format": "decrypted",
        "data": {
          "auth_method": "PAN_ONLY",
          "card": { "number": "4635440000002207", "exp_month": 12, "exp_year": 2028 }
        }
      }
    }
  }
  ```
</CodeGroup>

The PAN\_ONLY fragment also needs the browser object below. These are input
examples only; the Base64 value is not a usable wallet credential.
`format: encrypted` with an opaque `data.token` is reserved and rejected in
this release. Do not mix encrypted and decrypted fields, add unknown
properties, or send a provider vault reference, private key, Apple merchant ID,
or Google merchant ID as a payment input.

### Browser information

`browser_information` belongs alongside `payment_method_data` in the direct
request. It is required for Google PAN\_ONLY and optional for other direct
methods. When present, it supplies the payer's browser context to the
provider's authentication and risk checks; it does not report whether a
3DS challenge actually occurred. Whenever supplied, it must contain all nine
fields below, with their exact JSON types. Unknown keys and explicit nulls are
rejected.

| Field | Source and effect | JSON type and Flowlix input limit |
| - | - | - |
| `accept_header` | The payer browser's HTTP `Accept` header; contributes to provider browser authentication context. | Nonempty string, maximum 2048 characters. |
| `javascript_enabled` | Whether JavaScript was enabled in the payer browser when the values were collected. | Boolean. |
| `language` | The payer browser's preferred language. | Nonempty string, maximum 35 characters. |
| `user_agent` | The payer browser's user-agent value. | Nonempty string, maximum 2048 characters. |
| `screen_width` / `screen_height` | The payer browser's screen dimensions in CSS pixels. | Integers, 1 to 32768. |
| `java_enabled` | Whether the payer browser reported Java support. | Boolean. |
| `color_depth` | The payer browser's display color depth in bits. | Integer, 1 to 64. |
| `time_zone_offset` | The payer browser's timezone offset, expressed as UTC minus local time in minutes. | Integer, -840 to 840. |

For example, the value of `browser_information` can have this shape:

```json Browser information theme={null}
{
  "accept_header": "text/html,application/xhtml+xml",
  "javascript_enabled": true,
  "language": "en-GB",
  "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/128.0.0.0 Safari/537.36",
  "screen_width": 1920,
  "screen_height": 1080,
  "java_enabled": false,
  "color_depth": 24,
  "time_zone_offset": -240
}
```

Collect the actual payer-browser values; never use the example values or your
server's environment as substitutes. The existing `customer_ip_address` is the
checkout payer's literal IPv4 or IPv6 address and also supplies the browser IP
used by the provider. It is not your calling server's transport IP. Do not add
a second browser-IP property. Existing customer-IP processing is unchanged.

The nine browser values are transient payment-processing input: they are not
returned in Payment responses or events and are excluded from idempotency
identity. Do not log or persist browser or wallet request bodies. Supplying
browser information does not change ordinary-card authentication. See the
[complete request schema](/payments-api/payments/create-a-direct-api-payment).

## 1 Create a Payment

```bash theme={null}
curl -X POST "$FLOWLIX_BASE_URL/v1/payments" \
  -H "Authorization: Bearer $FLOWLIX_API_KEY" \
  -H "Idempotency-Key: order-1234567890-direct-1" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 4999,
    "currency": "EUR",
    "merchant_reference": 1234567890,
    "customer_ip_address": "203.0.113.7",
    "merchant_customer_id": "cust_opaque_42",
    "payment_method_data": {
      "type": "card",
      "card": {
        "number": "4635440000002207",
        "exp_month": 12,
        "exp_year": 2027,
        "cvc": "314",
        "holder_name": "Jenny Rosen"
      }
    },
    "return_url": "https://shop.example/checkout/3ds-return"
  }'
```

The card number illustrates payload shape only. It does not guarantee a
successful, declined, or 3D Secure outcome. In Sandbox the outcome comes from
the amount, not from the card: see
[Sandbox testing](/guides/testing#choose-the-outcome-with-the-amount).

`amount`, `currency`, `payment_method_data`, `customer_ip_address`,
`merchant_customer_id`, and `return_url` are required. Billing details and the
reconciliation reference are optional. Set `currency` to a
[supported payment currency](/introduction#amounts-and-currencies) for a
new Direct Payment. A well-formed unsupported code returns
`422 currency_not_supported` with `param: currency` and creates no Payment.

Reuse the exact `merchant_customer_id` for the same logical customer or
recognized guest. It must be an opaque, non-PII value—not an email, phone,
login, name, order/payment/attempt ID, or fresh per-payment UUID. Case and
whitespace are significant; the allowed domain is 1–255 Unicode scalar values,
nonblank, with no U+0000. See the
[complete request schema](/payments-api/payments/create-a-direct-api-payment).

## 2 Inspect the create response

The API returns `201 Created` with the current Payment state. Store its `id`
before taking further action.

Read safe card details from `payment_method.card` and the submitted method from
`payment_method.type`; neither establishes payment success. Before launch,
update response parsers that read the former top-level `card`. See
[Payment method and card details](/guides/payment-lifecycle#payment-method-and-card-details)
for the response change and handling of unknown methods.

| Status | What to do now |
| - | - |
| `PENDING` | Keep the order pending and retrieve the existing Payment. |
| `PROCESSING` | Retrieve the Payment until it becomes terminal or requires a browser action. |
| `REQUIRES_ACTION` | Redirect the customer to `next_action.redirect_url`. |
| `SUCCEEDED` | Fulfil the order once. Reconciliation may already have confirmed the result. |
| `FAILED` | Read `failure_code`; the request created a terminal Payment even though no funds were collected. |
| `EXPIRED` | Close the attempt; create a new Payment only if the customer restarts checkout. |

A card decline is a Payment result, not an HTTP request error. This response
excerpt highlights the fields to inspect; the API reference shows full examples:

```json theme={null}
{
  "id": "pay_q7Mk2Np8Vr4Xt6Yz9Ab3Cd5E",
  "status": "FAILED",
  "failure_code": "insufficient_funds",
  "failure_message": "The card has insufficient funds."
}
```

## 3 Complete a browser 3D Secure action when required

Server-side authentication is attempted for every new Direct Payment. A
browser redirect is required only when the returned Payment has
`status: REQUIRES_ACTION` and `next_action.reason: three_d_secure`.

1. Redirect the browser to the exact opaque `next_action.redirect_url`.
2. Retrieve the Payment while the browser action is in progress.
3. If a later response supplies a different redirect URL, use the latest one.
4. When the browser reaches `return_url`, retrieve the Payment again; do not
   treat the return itself as success.

## 4 Confirm the final status

```bash theme={null}
export PAYMENT_ID="pay_q7Mk2Np8Vr4Xt6Yz9Ab3Cd5E"

curl "$FLOWLIX_BASE_URL/v1/payments/$PAYMENT_ID" \
  -H "Authorization: Bearer $FLOWLIX_API_KEY"
```

Continue while the status is `PENDING`, `REQUIRES_ACTION`, or `PROCESSING`.
Stop at `SUCCEEDED`, `FAILED`, or `EXPIRED`. Only `SUCCEEDED` authorizes order
fulfilment.

## Safe retries

Flowlix makes bounded retries of eligible provider failures before returning.
If preparation fails before authentication or wallet execution was sent, the
Payment can end `FAILED` with `processor_unavailable` or `processor_error`;
these technical failures are not proof of an issuer decline. Once execution
may have reached the provider, Flowlix reads the existing order to reconcile it.
An unavailable read leaves the Payment nonterminal, not failed solely because
of the outage. Store the returned ID and retrieve that Payment.

These provider retries are bounded by the active request's deadline. They do
not extend the Payment's lifetime or guarantee recovery from a provider outage.
An unresolved Payment is not a reason to submit a replacement with a new key.

* Persist the key and non-card fields, but never persist the raw card-data
  request body. Retry it only while the original body remains available inside
  your approved PCI handling boundary.
* Card fields are excluded from Direct Payment idempotency. The same key and
  unchanged non-card fields replay the original Payment even if different card
  fields are supplied; they do not attempt the different card.
* `idempotency_key_in_use` means wait briefly and retry with an equivalent
  effective request identity.
* `idempotency_key_reused` means the key was paired with different non-card
  fields; resend the original request or use a new key for a genuinely new
  Payment.
* A new card attempt always uses a new key.
* A terminal failed Payment is never resubmitted. A customer retry creates a
  new Payment with a new key.

See [Idempotency](/guides/idempotency) and
[Operation failures](/guides/operation-failures).

## Next steps

* [Create a Refund](/guides/refunds) after a test Payment succeeds.
* [Submit and retrieve a Payout](/guides/payouts).
* [Receive Payment and Refund events](/guides/webhooks).
* Add complete [API error handling](/guides/errors).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.