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

# Create Payment Intent

> Initiate a VDG (Virtual Digital Goods) payment session using the Forte Payments widget or redirect mode.

Once you have an [access token](/obtain-access-token), create a payment intent. Forte returns the data needed to initialize the payment widget or redirect the user to a hosted payment page.

***

## Widget Mode

### Endpoint

Buyers are purchasing a non-tokenized (off-chain) asset that will be delivered by you (after receiving confirmation via Forte Webhook), directly into their game account.

```text theme={null}
POST /payments/v2/intent
Authorization: Bearer {{access_token}}
```

### Request Body

You must include either `buyer.email` or `buyer.wallet` (or both). Use the same identifier format consistently for a given user across all payment sessions.

```json theme={null}
{
  "transaction_type": "BUY_VDA",
  "buyer": {
    "email": "user@example.com",
    "id": "your-internal-user-id"
  },
  "currency": "USD",
  "idempotency_key": "unique-string-per-attempt",
  "reference": "your-internal-transaction-id",
  "items": [
    {
      "id": "item-id-in-your-system",
      "title": "1,000 Gold Coins",
      "description": "Bundle of Gold",
      "amount": "9.99",
      "image_url": "https://yourgame.com/items/gold-coins.png"
    }
  ]
}
```

<Info>
  Include a unique `idempotency_key` per payment attempt to prevent duplicate processing if a request is retried.
</Info>

<Info>
  The optional `reference` field accepts a string up to 100 characters (a UUID works well). Pass your own transaction or order ID here and Forte will echo it back in all webhooks for that payment, making reconciliation straightforward without needing to map Forte's `payment_intent_id` to your own records.
</Info>

### Response

A `201` response indicates the intent was created successfully.

```json theme={null}
{
  "data": {
    "flow": "PAYMENT",
    "error_code": null,
    "payment_intent_id": "440b0e4e-ae70-4886-ad6b-1521cc22cbca",
    "widget_data": "eyJhY2Nlc3NfdG9rZW4i...",
    "notes": []
  }
}
```

<Warning>
  **Handle both status codes**

  | Status | Meaning                                                                                                                                                                                      |
  | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | `201`  | Intent created. Use `payment_intent_id` to track webhooks.                                                                                                                                   |
  | `200`  | Request succeeded but no intent was created (e.g. user is banned or sanctioned). Initialize the widget anyway — it will display the appropriate message. No `payment_intent_id` is returned. |
</Warning>

Pass the full `data` object to `initFortePaymentsWidget` — see [Integrate Widget](/integrate-widget).

***

## Redirect Mode

Use redirect mode to send users to a Forte-hosted payment page instead of embedding the widget directly.

### Endpoint

```text theme={null}
POST /payments/v2/intent/redirect
Authorization: Bearer {{access_token}}
```

The request body is identical to widget mode above.

### Response

```json theme={null}
{
  "data": {
    "redirect_url": "https://payments.forte.io/0f7660c1-a106-481f-acf9-1848e98a7230?redirect_token=eyJhbGci..."
  }
}
```

Redirect the user's browser to the `redirect_url`.

<Warning>
  The redirect URL is valid for **30 seconds** and can only be accessed **once**. If the user revisits the same URL, they will see a `401` authentication error.
</Warning>

<Info>
  Client-side widget events are not available in redirect mode. Use [webhooks](/payments/webhooks) to track payment status in both modes.
</Info>

***

## Error Codes

If `error_code` is non-null in the response, the widget will display the appropriate message to the user automatically. Common codes:

| Code    | Meaning                                                       |
| ------- | ------------------------------------------------------------- |
| `ERR01` | Customer is banned                                            |
| `ERR03` | Request originates from a sanctioned country                  |
| `ERR06` | Customer must complete KYC — widget will display the KYC gate |
| `ERR15` | Customer's wallet address is flagged (KYW check failed)       |

***

## Next Steps

<Card title="Integrate Widget" icon="code" href="/integrate-widget">
  Embed and initialize the Forte Payments widget with the response data
</Card>
