> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://ixopay.ferndocs.com/developer-hub/documentation/reference/features/risk-checks/external-risk-checks/riskified/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://ixopay.ferndocs.com/_mcp/server.
# Riskified
When processing transactions through the IXOPAY platform, Riskified external risk checks can be utilized to enhance your transaction security. This guide covers the fields Riskified expects and the steps required to initiate these checks.
## Transaction fields
Riskified fraud screening relies primarily on standard fields already used by the [Transaction API](https://documentation.ixopay.com/api/transaction/transaction-api), enriched with a small set of `extraData` keys that have no standard field equivalent.
### Standard fields
| IXOPAY API field | Riskified API field | Required | Notes |
| ---------------- | -------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `amount` | `order.total_price` | **Yes** | Decimal string, e.g. `49.99` |
| `currency` | `order.currency` | **Yes** | ISO 4217, e.g. `EUR` |
| `language` | `order.client_details.accept_language` | Recommended | ISO 639-1, e.g. `en`. `extraData["3ds:browserLanguage"]` — the browser language observed during a 3DS/redirect flow — takes precedence over this field when present |
### `customer` object fields
| IXOPAY API field | Riskified API field | Required | Notes |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `customer.email` | `order.customer.email` | **Yes** | Max 255 characters |
| `customer.emailVerified` | `order.customer.verified_email` | Recommended | Boolean |
| `customer.firstName` | `order.customer.first_name` | Recommended | Used for both billing and shipping; max 50 characters |
| `customer.lastName` | `order.customer.last_name` | Recommended | Used for both billing and shipping; max 50 characters |
| `customer.ipAddress` | `order.browser_ip` | **Yes** | IPv4 or IPv6; max 50 characters. Falls back to `extraData["3ds:browserIpAddress"]` — the client IP observed during a 3DS/redirect flow — when not set |
| `customer.billingAddress1`, `customer.billingAddress2`, `customer.company`, `customer.billingCity`, `customer.billingState`, `customer.billingPostcode`, `customer.billingCountry`, `customer.billingPhone` | `order.billing_address.address1`, `.address2`, `.company`, `.city`, `.province`/`.province_code`, `.zip`, `.country`/`.country_code`, `.phone` | Recommended | Billing address, `billingCountry` as ISO 3166-1 alpha-2 |
| `customer.shippingAddress1`, `customer.shippingAddress2`, `customer.shippingCompany`, `customer.shippingCity`, `customer.shippingState`, `customer.shippingPostcode`, `customer.shippingCountry`, `customer.shippingPhone` | `order.shipping_address.address1`, `.address2`, `.company`, `.city`, `.province`/`.province_code`, `.zip`, `.country`/`.country_code`, `.phone` | Recommended | Shipping address, `shippingCountry` as ISO 3166-1 alpha-2 |
| `customer.identification` | `order.customer.id` | Conditional | Fallback only — takes effect when no customer profile (vault) is linked to the transaction, which normally supplies this ID. Left empty if neither is set. Riskified requires one of the two sources whenever `customer.extraData["account_type"]` is `registered`. |
### `items[]` array fields
Include one entry per product in the order.
| IXOPAY API field | Riskified API field | Required | Notes |
| -------------------------------------- | ------------------------------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------- |
| `items[n].price` | `line_items[].price` | Recommended | Decimal string or number |
| `items[n].quantity` | `line_items[].quantity` | Recommended | Integer ≥ 1 |
| `items[n].name` | `line_items[].title`; also the fallback for `line_items[].brand` | Recommended | Product display name; always sent as `title`, and used as `brand` unless `items[n].extraData.brand` is set |
| `items[n].identification` | `line_items[].sku`; also the fallback for `line_items[].product_id` | Recommended | Your SKU/product ID; always sent as `sku`, and used as `product_id` unless `items[n].extraData.product_id` is set |
| `items[n].description` | `line_items[].category` | Optional | Used as a fallback product category unless `items[n].extraData.category` is set |
| `items[n].extraData.product_id` | `line_items[].product_id` | Optional | Distinct product ID, if different from the SKU |
| `items[n].extraData.category` | `line_items[].category` | Optional | Product category; takes precedence over `items[n].description` |
| `items[n].extraData.brand` | `line_items[].brand` | Optional | Brand name; takes precedence over `items[n].name` |
| `items[n].extraData.sub_category` | `line_items[].sub_category` | Optional | Product sub-category |
| `items[n].extraData.requires_shipping` | `line_items[].requires_shipping` | Optional | `true`/`false` or `1`/`0` |
| `items[n].extraData.delivered_to` | `line_items[].delivered_to` | Conditional | Required for mixed shipment orders. Either `shipping_address` or `store_pickup` |
### `l2l3Data` fields
| IXOPAY API field | Riskified API field | Required | Notes |
| ------------------------ | --------------------------- | ----------- | ------------------------------------------------------------------------------------------ |
| `l2l3Data.freightAmount` | `shipping_lines.price` | Recommended | Transaction-level total shipping cost |
| `items[n].l2l3Data.type` | `line_items[].product_type` | **Yes** | Either `physical` or `digital`; sent verbatim, so any other value is rejected by Riskified |
### `extraData` fields
Custom key-value pairs passed inside the transaction-level `extraData` object.
| `extraData` key | Riskified API field | Required | Notes |
| ----------------------------------- | --------------------------------- | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `extraData["riskified_session_id"]` | `order.cart_token` | Automatic — usually set for you | Session ID from the Riskified beacon script. Attached to the payment token by payment.js at tokenization, or seeded with the transaction UUID by the HPP beacon script — see Initializing the risk script. Only set this manually if your integration uses neither path. Falls back to the transaction UUID if not set |
| `extraData["referring_site"]` | `order.referring_site` | Optional | The webpage the customer arrived from before checkout — not the payment page itself (e.g. an external referrer like `https://search.example.net/?q=artisan+goods`, or an internal one like `https://shop.example.org/products/shoes`) |
| `extraData["source"]` | `order.source` | **Yes** | Riskified enum, sent through as-is — not free text. One of: `desktop_web`, `mobile_web`, `mobile_app`, `mobile_app_android`, `mobile_app_ios`, `web`, `chat`, `third_party`, `phone`, `in_store`, `shopify_draft_order`, `unknown`, `subscription`, `ai_agent`. Any other value is rejected by Riskified |
| `extraData["user_agent"]` | `order.client_details.user_agent` | Recommended | Browser `User-Agent` string. Falls back to `extraData["3ds:browserUserAgent"]` when not set |
| `extraData["total_discounts"]` | `order.total_discounts` | **Yes** | Total discount amount; send `0` if no discounts apply |
| `extraData["shipping_title"]` | `shipping_lines.title` | Recommended | Display name of the selected shipping method |
| `extraData["note"]` | `order.note` | Optional | Free-text note about the order |
| `extraData["order_id"]` | `order.id` | Optional | Your own order ID. If set, it's used instead of the transaction UUID and reused for every subsequent event (`checkoutDenied`, `decision`) linked to this transaction |
### `customer.extraData` fields
Custom key-value pairs passed inside the `extraData` object of the `customer` object.
| `customer.extraData` key | Riskified API field | Required | Notes |
| --------------------------------------------- | ----------------------------- | ----------- | ----------------------------------------------------------------- |
| `customer.extraData["account_type"]` | `order.customer.account_type` | Recommended | `guest` or `registered` |
| `customer.extraData["account_creation_date"]` | `order.customer.created_at` | Recommended | ISO 8601 timestamp; falls back to the transaction date if omitted |
### Automatically populated fields
These `order`-level fields are derived entirely from existing transaction and merchant data — there's nothing to configure.
| Riskified field | Source |
| ------------------- | ------------------------------------------------------------------------------------------------- |
| `order.email` | Customer email (duplicated at the top level of `order`, in addition to `order.customer.email`) |
| `order.created_at` | Transaction creation timestamp |
| `order.vendor_name` | Merchant name configured on the project |
| `order.gateway` | Adapter/gateway name; `_3ds` is appended when the transaction carries a 3D Secure liability shift |
### Payment details
IXOPAY platform also builds `payment_details` automatically from the transaction's card and BIN data. The shape sent depends on the payment method, and both shapes are sent with the `decide` and `decision` calls.
**Card payments** :
| Riskified field | Source |
| --------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| `type` | `card` |
| `avs_result_code` | AVS result code recorded on the transaction |
| `cvv_result_code` | CVV2 match code from the transaction result data |
| `credit_card_bin` | Card BIN digits |
| `credit_card_company` | Card brand from the BIN lookup |
| `credit_card_country` | Issuing country (ISO 3166-1 alpha-2) from the BIN lookup |
| `credit_card_number` | Masked PAN, e.g. `XXXX-XXXX-XXXX-1234` |
| `authorization_id` | Scheme transaction identifier from the payment network response |
| `authentication_result.liability_shift` | Derived from the card's ECI value; `true` for an authenticated or attempted 3D Secure liability shift |
| `authentication_result.eci` | ECI value recorded on the card |
**PayPal payments**
| Riskified field | Source |
| --------------- | ------------------------------------------------------------ |
| `payment_type` | `paypal` |
| `payer_email` | Wallet owner email recorded on the transaction, if available |
| `mid` | Merchant ID from the connector's configuration |
### Decision
`decision` is sent automatically once the transaction succeeds, to confirm the approval to Riskified. It carries the same `payment_details` shape as `decide`, plus:
| Riskified field | Source |
| -------------------------- | --------------------------------------------------------------------------------- |
| `decision.external_status` | Always `approved` — IXOPAY platform only reports successful transactions this way |
| `decision.decided_at` | Transaction creation timestamp |
| `decision.amount` | Transaction amount |
| `decision.currency` | Transaction currency |
### Checkout denied
If a payment or validation error occurs after the initial `decide` call, IXOPAY platform automatically sends a minimal `checkoutDenied` call:
| Riskified field | Source |
| ------------------------------------------------------------ | -------------------------------------------- |
| `checkout.id` | Same order ID resolved for the `decide` call |
| `checkout.payment_details[0].authorization_error.created_at` | Transaction timestamp |
| `checkout.payment_details[0].authorization_error.error_code` | Error code from the transaction error |
| `checkout.payment_details[0].authorization_error.message` | Error message from the transaction error |
## Initializing the risk script
Riskified correlates its device fingerprinting beacon with an order via a session ID (`cart_token`). How that session ID reaches the transaction depends on whether you use payment.js or an IXOPAY platform hosted payment page.
* Using payment.js
* Without payment.js
With payment.js, the Riskified beacon is handled automatically once an active Riskified risk rule is configured: the beacon loads on the merchant's page, and its session ID is attached to the payment token as `additionalData.riskified_session_id` during tokenization. When the transaction is created with that `transactionToken`, the session ID is carried over as `extraData["riskified_session_id"]` before the risk check runs.
> **important**
>
> Tokenize first, then create the transaction. The beacon session ID travels with the token — a transaction created before tokenization cannot carry it and falls back to the transaction UUID as `cart_token`.
If *Init Scripts Automatically* is disabled on the connector, initialize the beacon manually:
```html
```
If your payment form is an IXOPAY platform hosted payment page instead of payment.js or your own checkout page, the Riskified `decide` call is made when the transaction is **created** — before the customer opens the payment page — so a session ID generated on the page can never reach that call. Instead, seed the beacon with the **transaction UUID** : IXOPAY platform automatically sends the UUID as `cart_token` when no `riskified_session_id` extra data is present.
Add to the payment template:
```html
```
Remember to accurately include all necessary information for successful risk checks with Riskified. If you encounter issues, review your connector configuration or the extra data you're providing.