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

# Integration

> Learn how to enroll customers, manage subscriptions and follow recurring debits with the HUB2 API.

## Prerequisites

### API key permissions

Subscriptions use the same HUB2 API keys and the same `ApiKey`, `MerchantId` and `Environment` headers as the rest of the API. Grant the key the permissions required by the endpoints you call.

| Permission | Allows |
| :- | :- |
| `Api.subscription_intent_create` | Create subscription links and start subscription intents |
| `Api.subscription_intent_read` | Retrieve the status of an enrollment |
| `Api.subscription_list` | List subscriptions and retrieve their charge history |
| `Api.subscription_cancel` | Cancel a subscription |
| `Api.subscription_suspend` | Suspend a subscription |
| `Api.subscription_charge` | Charge an on-demand subscription |

> **[API Keys](https://docs.hub2.io/integration/en/getting_started/api_keys.md)**

### Account requirements

* Your merchant must be registered in Côte d'Ivoire. Otherwise, a subscription intent is refused with a `403` error and the code `card_on_file_ineligible_country`.
* Card-on-file must be enabled for your merchant by HUB2. Otherwise, a subscription intent is refused with a `403` error and the code `card_on_file_disabled`.
* The same two conditions apply to a subscription link. The link can be created, but the customer cannot complete the enrollment from its page.
* A closed or suspended merchant cannot use these endpoints (`403`, codes `merchant_closed` and `merchant_suspended`).
* A parent merchant that owns submerchants must call the endpoints with the `MerchantId` of the submerchant. With its own `MerchantId`, it receives a `403` error with the code `group_owner_transaction_forbidden`. A submerchant only sees and manages the subscriptions it enrolled. See [submerchants](https://docs.hub2.io/integration/en/submerchants/submerchants_integration.md).

## Enroll a customer

Enrollment stores the customer card and creates the subscription. Choose one of the two methods.

| | Subscription link | Subscription intent |
| :- | :- | :- |
| Endpoint | `POST /subscription-links` | `POST /subscription-intents` |
| Consent and e-mail collected by | A page hosted by HUB2 | Your own interface |
| Subscription types | Scheduled | Scheduled and on demand |
| Follow the result with | `subscription.created` webhook, subscription list | `subscription.created` webhook, enrollment status endpoint |

### Enroll with a subscription link

[Create a subscription link](https://docs.hub2.io/api-reference/subscriptions/create-a-subscription-link.md)

The link describes the recurring debit. Share the URL returned in the response with your customer.

```bash Curl theme={null}
curl --location 'https://api.hub2.io/subscription-links' \
--header 'ApiKey: [REDACTED]' \
--header 'MerchantId: [REDACTED]' \
--header 'Environment: sandbox' \
--header 'Content-Type: application/json' \
--data '{
    "purchaseReference": "SUB_2026_001",
    "description": "Monthly plan",
    "amount": 5000,
    "currency": "XOF",
    "frequency": "monthly",
    "startDate": "2026-12-01",
    "country": "CI",
    "expirationDate": "2026-11-30T23:59:59.000Z",
    "successUrl": "https://merchant.example.com/subscription/success",
    "failureUrl": "https://merchant.example.com/subscription/failure"
}'
```

| Field | Required | Description |
| :- | :- | :- |
| `amount` | Yes | Amount debited on each occurrence, a positive integer in whole units of the currency (`5000` XOF). Currencies with decimals are not supported for scheduled debits: the amount is billed as whole units, not in the smallest unit |
| `currency` | Yes | Three-letter ISO 4217 currency code, for example `XOF` |
| `purchaseReference` | Yes | Your reference for this subscription, 100 characters at most. It becomes the `customerId` of the subscription |
| `frequency` | Yes | `weekly` or `monthly` |
| `startDate` | Yes | ISO 8601 date of the first debit |
| `country` | Yes | Two-letter ISO 3166-1 alpha-2 code of the country where the enrollment is processed |
| `description` | No | Description of the link, 300 characters at most |
| `expirationDate` | No | Expiration date of the link. When omitted, the link expires 15 minutes after creation |
| `successUrl` | No | `https` URL where the customer is sent after a successful enrollment |
| `failureUrl` | No | `https` URL where the customer is sent after a failed enrollment |

When you omit `successUrl` or `failureUrl`, the customer lands on a result page hosted by HUB2.

The response is a link object. The `url` field is the page to share, and `status` is `active` until the link is used or expires.

```json theme={null}
{
    "id": "pl_Doc7fK2mQx4vN8sT1aB3c",
    "url": "https://pay.hub2.io/pl_Doc7fK2mQx4vN8sT1aB3c",
    "status": "active",
    "purchaseReference": "SUB_2026_001",
    "amount": 5000,
    "currency": "XOF",
    "expirationDate": "2026-11-30T23:59:59.000Z"
}
```

Creating a link with the `purchaseReference` of a link that is still active returns that existing link instead of creating a new one.

#### What the customer sees

**1. The enrollment page**

The customer opens the link. The page shows your business name and the recurring amount. The customer enters an e-mail address, ticks the consent box that authorizes the recurring debits, and clicks the enrollment button.

**2. The card page**

The customer is sent to a secure card page to enter the card and complete the 3D Secure authentication. The customer is not charged at this stage.

**3. The return**

The customer comes back to your `successUrl` or `failureUrl`.

The link is single use. It becomes `completed` as soon as the customer has been sent to the card page, whatever the final result of the card authentication. If HUB2 refuses the enrollment before the card page, the link stays `active` and the customer can try again.

> [!WARNING]
>
> Do not use the `payment_links.completed` or `payment_links.paid` webhooks, or a redirection to your `successUrl`, as proof that the subscription exists. Wait for the `subscription.created` webhook, or list the subscriptions with `customerId` set to your `purchaseReference`.

### Enroll with a subscription intent

[Enroll a customer](https://docs.hub2.io/api-reference/subscriptions/enroll-a-customer.md)

A subscription intent is created in three steps:

1. Create the subscription intent from your server.
2. Send the customer to the card page, by performing the action returned by the API.
3. Retrieve the outcome of the enrollment.

#### 1. Create the subscription intent

Collect the customer consent in your interface before the call. The consent is mandatory: the request is rejected when it is missing or when `given` is not `true`.

A scheduled subscription requires `amount`, `frequency` and `startDate`. An on-demand subscription does not accept `frequency` or `startDate`, because nothing is scheduled. For an on-demand subscription, `amount` is ignored in the `live` environment, but still validated when sent; in `sandbox` it selects the simulated outcome.

```bash Scheduled theme={null}
curl --location 'https://api.hub2.io/subscription-intents' \
--header 'ApiKey: [REDACTED]' \
--header 'MerchantId: [REDACTED]' \
--header 'Environment: sandbox' \
--header 'Content-Type: application/json' \
--data-raw '{
    "customerReference": "cust_01924059",
    "email": "customer@example.com",
    "billingMode": "rbs_scheduled",
    "amount": 5000,
    "currency": "XOF",
    "frequency": "monthly",
    "startDate": "2026-12-01",
    "consent": {
        "given": true,
        "timestamp": "2026-11-03T10:00:00.000Z",
        "reference": "consent-3f6d2c1e-9a47-4b0e-8d52-7c1a5e9b0f34"
    },
    "onSuccessRedirectionUrl": "https://merchant.example.com/subscription/success",
    "onFailedRedirectionUrl": "https://merchant.example.com/subscription/failure"
}'
```

```bash On demand theme={null}
curl --location 'https://api.hub2.io/subscription-intents' \
--header 'ApiKey: [REDACTED]' \
--header 'MerchantId: [REDACTED]' \
--header 'Environment: sandbox' \
--header 'Content-Type: application/json' \
--data-raw '{
    "customerReference": "cust_01924059",
    "email": "customer@example.com",
    "billingMode": "merchant_initiated",
    "currency": "XOF",
    "consent": {
        "given": true,
        "timestamp": "2026-11-03T10:00:00.000Z",
        "reference": "consent-3f6d2c1e-9a47-4b0e-8d52-7c1a5e9b0f34"
    },
    "onSuccessRedirectionUrl": "https://merchant.example.com/subscription/success",
    "onFailedRedirectionUrl": "https://merchant.example.com/subscription/failure"
}'
```

| Field | Required | Description |
| :- | :- | :- |
| `customerReference` | Yes | Your reference for the customer, 255 characters at most. It becomes the `customerId` of the subscription |
| `email` | Yes | E-mail address of the customer. HUB2 sends the enrollment and debit notifications to it |
| `currency` | Yes | Three-letter ISO 4217 currency code |
| `consent` | Yes | The consent of the customer, described below |
| `billingMode` | No | `rbs_scheduled` (default) or `merchant_initiated` |
| `amount` | Scheduled: required. On-demand: `sandbox` outcome only | Recurring amount, a positive integer in whole units of the currency, up to 9999999999. A currency with decimals is not rejected but billed as whole units: `1500` EUR is billed as 1,500 EUR, unlike the charge `amount`, which is in the smallest unit |
| `frequency` | Scheduled only | `weekly` or `monthly`. Refused for an on-demand subscription |
| `startDate` | Scheduled only | ISO 8601 date of the first debit. Refused for an on-demand subscription |
| `onSuccessRedirectionUrl` | No | `https` URL where the customer is sent after a successful enrollment, 2084 characters at most |
| `onFailedRedirectionUrl` | No | `https` URL where the customer is sent after a failed enrollment, 2084 characters at most |

The `consent` object has three fields:

| Field | Description |
| :- | :- |
| `given` | Must be `true` |
| `timestamp` | ISO 8601 date and time at which the customer gave consent |
| `reference` | Your identifier of this consent, 64 characters at most, with letters, digits and hyphens only |

> [!WARNING]
>
> The consent `reference` must be unique within your merchant account, submerchants of your group included. Another HUB2 merchant may use the same value. Use a UUID, optionally with a prefix, as in the example. Reusing a reference to retry the same enrollment of the same customer is safe. A reference already used for another customer, or already enrolled for another submerchant of your group, makes the enrollment fail with the failure cause `consent_reference_in_use`, after the customer has completed the card page. A reference already enrolled in the other mode (`sandbox` or `live`), or, for a submerchant, already used by its group owner or another submerchant, is refused immediately with a `409` error and the code `card_on_file_consent_reference_in_use` when the subscription intent is created.

The response contains the id of the enrollment payment and its status. Keep the `paymentId`: it identifies the enrollment in the next steps.

```json theme={null}
{
    "paymentId": "pay_Doc5GBc53h6dHvuuq4vlc",
    "status": "action_required",
    "action": {
        "type": "redirection",
        "data": {
            "url": "https://card-page.example.com/pay",
            "method": "post"
        }
    }
}
```

| `status` | Meaning |
| :- | :- |
| `action_required` | The customer must complete the card page. The `action` object describes how to send the customer there |
| `pending` | The enrollment is being processed. Retrieve its status |
| `successful` | The subscription was created |
| `failed` | The enrollment was refused. Retrieve its status to read the failure cause |

#### 2. Send the customer to the card page

When `status` is `action_required`, `action` has the same shape as the `nextAction` of a payment. Build a form from `action.data.url`, `action.data.method` and the fields of `action.data.data`, and submit it from the customer browser.

```javascript theme={null}
function openCardPage(action) {
  const form = document.createElement('form');
  form.method = action.data.method;
  form.action = action.data.url;

  for (const [name, value] of Object.entries(action.data.data ?? {})) {
    const input = document.createElement('input');
    input.type = 'hidden';
    input.name = name;
    input.value = String(value);
    form.appendChild(input);
  }

  document.body.appendChild(form);
  form.submit();
}
```

The customer enters the card, completes the 3D Secure authentication and comes back to `onSuccessRedirectionUrl` or `onFailedRedirectionUrl`. If HUB2 has not recorded an outcome yet when the customer returns, the customer is sent to the failure URL. A redirection is therefore not enough to conclude: always confirm the outcome with the status endpoint or the `subscription.created` webhook.

#### 3. Retrieve the outcome of the enrollment

[Retrieve an enrollment status](https://docs.hub2.io/api-reference/subscriptions/retrieve-an-enrollment-status.md)

Subscribe to the `subscription.created` webhook to learn that the enrollment succeeded. No webhook is sent when an enrollment fails, so poll the status of the enrollment to detect failures and abandoned enrollments.

```bash Curl theme={null}
curl --location 'https://api.hub2.io/subscription-intents/pay_Doc5GBc53h6dHvuuq4vlc/status' \
--header 'ApiKey: [REDACTED]' \
--header 'MerchantId: [REDACTED]' \
--header 'Environment: sandbox'
```

```json theme={null}
{
    "paymentId": "pay_Doc5GBc53h6dHvuuq4vlc",
    "status": "successful",
    "subscriptionId": 42,
    "createdAt": "2026-11-03T10:00:02.113Z",
    "updatedAt": "2026-11-03T10:01:45.870Z"
}
```

| `status` | Meaning | Next step |
| :- | :- | :- |
| `pending` | The enrollment was created and nothing came back yet | Keep polling |
| `action_required` | The customer was sent to the card page and the enrollment is not concluded | Keep polling. A customer who abandons the page stays in this status and is never reported as `failed`: apply your own timeout |
| `successful` | The subscription exists. `subscriptionId` is present | Store `subscriptionId`, the identifier to use with `/subscriptions` endpoints |
| `failed` | The enrollment was refused or could not be recorded. `failureCause` is present | Read `failureCause.code` and let the customer try again |

A failed enrollment returns a `failureCause` with a `code`, a `message` and, for invalid parameters, the list of the parameters concerned in `params`.

```json theme={null}
{
    "paymentId": "pay_Doc5GBc53h6dHvuuq4vlc",
    "status": "failed",
    "failureCause": {
        "code": "authentication_failed",
        "message": "Payment failed. The customer failed to authenticate the payment."
    },
    "createdAt": "2026-11-03T10:00:02.113Z",
    "updatedAt": "2026-11-03T10:01:45.870Z"
}
```

The failure causes specific to an enrollment are:

| Code | Description |
| :- | :- |
| `authentication_failed` | The customer did not complete the 3D Secure authentication, or it failed |
| `issuer_authentication_unavailable` | The authentication service of the card issuer could not be reached |
| `consent_reference_in_use` | The consent reference is already used by another enrollment. Use a unique reference |
| `internal_error` | The enrollment could not be recorded. Try again |

An enrollment can also fail with the other card failure causes, for example `card_declined` or `card_expired`. See the complete list in [Failure causes for payments](https://docs.hub2.io/integration/en/payments/payments_failure_causes.md). Handle unknown codes as a generic failure.

The status endpoint answers `404` when no enrollment with this id belongs to your merchant in the current environment.

> [!WARNING]
>
> This endpoint is limited to 5 requests per 5 seconds for the same enrollment. Beyond that, the API returns `HTTP 429 - Too Many Requests`. Poll every few seconds and stop once the status is `successful` or `failed`. See the [rate limits](https://docs.hub2.io/documentation/en/limits.md).

## Manage subscriptions

Use the `id` of the subscription, returned as `subscriptionId` by the enrollment status endpoint, in the endpoints below.

### Subscription statuses

| Status | Meaning |
| :- | :- |
| `active` | The subscription is running. A scheduled subscription is debited on schedule, and an on-demand subscription can be charged |
| `delinquent` | A scheduled subscription whose debits are in payment failure. It can still be suspended or cancelled |
| `suspended` | Debits are paused, and an on-demand subscription cannot be charged |
| `cancelled` | Final status. No debit will ever be made again |
| `completed` | Final status. A scheduled subscription that reached the end of its schedule |
| `created`, `pending` | Transitional statuses. Treat them as not yet active |

### List subscriptions

[Retrieve a subscription collection](https://docs.hub2.io/api-reference/subscriptions/retrieve-a-subscription-collection.md)

```bash theme={null}
curl --location 'https://api.hub2.io/subscriptions?status=active&billingMode=rbs_scheduled&page=1&perPage=10' \
--header 'ApiKey: [REDACTED]' \
--header 'MerchantId: [REDACTED]' \
--header 'Environment: sandbox'
```

The following filters are available: `customerId` (exact match on your customer reference), `billingMode` (`rbs_scheduled` or `merchant_initiated`) and `status`. The results are paginated with `page` (default `1`) and `perPage` (default `10`), and the `Content-Range` header gives the range and the total.

The response is an array of subscriptions. Each item looks like this:

```json theme={null}
{
    "id": 42,
    "cybersourceSubscriptionId": "6000123456789012345678",
    "status": "active",
    "billingMode": "rbs_scheduled",
    "frequency": "monthly",
    "amount": "5000.00",
    "currency": "XOF",
    "startDate": "2026-12-01",
    "customerId": "cust_01924059",
    "createdAt": "2026-11-03T10:01:45.870Z"
}
```

For an on-demand subscription, `cybersourceSubscriptionId`, `frequency`, `amount` and `startDate` are `null`.

### Suspend a subscription

[Suspend a subscription](https://docs.hub2.io/api-reference/subscriptions/suspend-a-subscription.md)

```bash theme={null}
curl --location --request POST 'https://api.hub2.io/subscriptions/42/suspend' \
--header 'ApiKey: [REDACTED]' \
--header 'MerchantId: [REDACTED]' \
--header 'Environment: sandbox'
```

Only an `active` or `delinquent` subscription can be suspended. The call returns the updated subscription and triggers the `subscription.suspended` webhook. The API does not offer an operation to resume a suspended subscription.

### Cancel a subscription

[Cancel a subscription](https://docs.hub2.io/api-reference/subscriptions/cancel-a-subscription.md)

```bash theme={null}
curl --location --request POST 'https://api.hub2.io/subscriptions/42/cancel' \
--header 'ApiKey: [REDACTED]' \
--header 'MerchantId: [REDACTED]' \
--header 'Environment: sandbox'
```

> [!WARNING]
>
> Cancellation is final. It stops future debits only: HUB2 does not refund or prorate any amount already debited. To take a customer back, enroll the customer again.

An `active`, `suspended` or `delinquent` subscription can be cancelled. The call returns the updated subscription and triggers the `subscription.cancelled` webhook. Cancelling a cancelled or completed subscription is refused with a `409` error.

### Charge an on-demand subscription

[Charge a subscription](https://docs.hub2.io/api-reference/subscriptions/charge-a-subscription.md)

Only a `merchant_initiated` subscription in the `active` status can be charged.

```bash Curl theme={null}
curl --location 'https://api.hub2.io/subscriptions/42/charge' \
--header 'ApiKey: [REDACTED]' \
--header 'MerchantId: [REDACTED]' \
--header 'Environment: sandbox' \
--header 'Content-Type: application/json' \
--data '{
    "amount": 1500,
    "currency": "XOF",
    "reference": "invoice-2026-11-0042"
}'
```

| Field | Required | Description |
| :- | :- | :- |
| `amount` | Yes | Amount to charge, a positive integer in the smallest unit of the currency. For a currency with decimals, the amount must correspond to a whole number of currency units: `1550` for EUR (15.50) is refused with a `400` error |
| `currency` | Yes | Must be the currency of the subscription |
| `reference` | Yes | Your idempotency key for this charge, 255 characters at most |

The response describes the charge:

```json theme={null}
{
    "id": 318,
    "status": "success",
    "amount": "1500.00",
    "transactionDate": "2026-11-20T08:12:44.020Z",
    "merchantReference": "invoice-2026-11-0042",
    "indicator": "MIT",
    "createdAt": "2026-11-20T08:12:44.020Z"
}
```

Amounts in the response, the history and webhooks are decimal strings in major units: a charge of `1500` XOF returns `"1500.00"`, a charge of `1500` EUR cents returns `"15.00"`.

| `status` | Meaning |
| :- | :- |
| `success` | The charge was accepted |
| `failed` | The charge was declined. The HTTP status is still `200` |
| `pending` | The outcome is not known yet. Send the same request again to resolve it |

`indicator` is `MIT` for a debit that you or HUB2 initiate on the stored card, and `CIT` for an operation initiated by the customer.

**Retry safely.** The `reference` is your idempotency key for a given subscription. After a timeout or a `5xx` error, send the same request again with the same `reference` and the same `amount`: the card is never charged twice, and the call returns the charge once its outcome is known. Reusing a `reference` with a different `amount` is refused with a `409` error. Use a new `reference` for each new charge.

On-demand charges do not trigger `payment.succeeded` or `payment.failed` webhooks. The response of the call and the charge history give the result.

### Retrieve the charge history

[Retrieve a subscription charge history](https://docs.hub2.io/api-reference/subscriptions/retrieve-a-subscription-charge-history.md)

```bash theme={null}
curl --location 'https://api.hub2.io/subscriptions/42/transactions?page=1&perPage=10' \
--header 'ApiKey: [REDACTED]' \
--header 'MerchantId: [REDACTED]' \
--header 'Environment: sandbox'
```

The history lists the debits of a subscription, most recent first, with the same `page`, `perPage` and `Content-Range` behavior as the subscription list. Each item has an `id`, an `amount` (a decimal string such as `"5000.00"`), a `status` (`pending`, `success` or `failed`), a `transactionDate`, the `indicator`, and the `merchantReference` you sent for an on-demand charge (`null` for a scheduled debit).

A scheduled debit first appears as `pending`. Its final status is set once HUB2 has confirmed the outcome, which can take some time after the debit date.

## Webhooks

Subscriptions use the usual webhook mechanism. Register an endpoint and subscribe to the events below. See the [Webhooks Integration Guide](https://docs.hub2.io/integration/en/webhooks/webhooks_overview.md).

| Event | Sent when |
| :- | :- |
| `subscription.created` | A customer is successfully enrolled |
| `subscription.suspended` | A subscription is suspended |
| `subscription.cancelled` | A subscription is cancelled |
| `payment.succeeded` | A scheduled debit is confirmed as successful |
| `payment.failed` | A scheduled debit fails |

The `data` object of each event contains the fields below. Amounts are decimal strings in major units (`"15.00"` for 15 EUR).

```json theme={null}
{
    "owner": "1234",
    "type": "subscription.created",
    "data": {
        "subscriptionId": "6000123456789012345678",
        "customerId": "cust_01924059",
        "frequency": "monthly",
        "amount": "5000.00",
        "startDate": "2026-12-01"
    },
    "test": false,
    "id": "evt_Doc432799e0e6bc3187ca",
    "createdAt": "2026-11-03T10:01:46.002Z"
}
```

| Event | Fields of `data` |
| :- | :- |
| `subscription.created` | `subscriptionId`, `customerId`, `frequency`, `amount`, `startDate` |
| `subscription.suspended`, `subscription.cancelled` | `subscriptionId`, `customerId` |
| `payment.succeeded`, `payment.failed` (scheduled debit) | `subscriptionId`, `customerId`, `transactionReference`, `amount` |

> **Important**
>
> The `data` of a scheduled debit differs from the `data` of a one-off payment: it has no payment `id` or `intentId`. Check for `subscriptionId` before reading `data.id`: when it is present, the event is a scheduled debit, and a handler that reads `data.id` first gets no value.

To match an event with a subscription:

* `subscriptionId` in the events is the value of the `cybersourceSubscriptionId` field of the subscription, not its `id`.
* `transactionReference` is the value of the `cybersourceReference` field of the debit in the charge history.
* `customerId` is the `customerReference` of the intent, or the `purchaseReference` of the link.
* For an on-demand subscription, `subscriptionId` is `null`, and `frequency`, `amount` and `startDate` are `null` in `subscription.created`. Match the event with `customerId`, and list the subscriptions with this `customerId` to get the `id`.

The `payment.failed` event does not contain a failure cause.

## Errors

The API reference lists the HTTP status codes of each endpoint. The error format is described in [Error management](https://docs.hub2.io/documentation/en/error_management.md).

| HTTP status | Code | Endpoints | Cause |
| :- | :- | :- | :- |
| `400` | | Link, intent, charge | Invalid request. For a subscription intent: `frequency` or `startDate` sent for an on-demand subscription, a missing or invalid consent, a consent reference with forbidden characters, a non-HTTPS redirection URL. For a charge: an amount that is not a whole number of currency units |
| `401` | | All | Missing or wrong credentials |
| `403` | `card_on_file_disabled` | Intent | Card-on-file is not enabled for your merchant. Contact HUB2 Support |
| `403` | `card_on_file_ineligible_country` | Intent | Your merchant is not registered in Côte d'Ivoire |
| `403` | `merchant_closed`, `merchant_suspended` | Link, intent, list, cancel, suspend, charge, history | Your merchant is closed or suspended |
| `403` | `group_owner_transaction_forbidden` | Link, intent, list, cancel, suspend, charge, history | The `MerchantId` is a parent merchant. Use the `MerchantId` of a submerchant |
| `404` | | Status, cancel, suspend, charge, history | The enrollment or the subscription does not exist, belongs to another merchant, or exists in another environment |
| `409` | `card_on_file_consent_reference_in_use` | Subscription intent | The consent reference is already enrolled in the other mode (`sandbox` or `live`), or a submerchant used a consent reference already used in its group |
| `409` | | Cancel, suspend | The status of the subscription does not allow the operation |
| `409` | | Charge | The subscription is not `merchant_initiated`, is not `active`, uses another currency, or the `reference` was already used with a different `amount` |
| `429` | | All | Rate limit exceeded |
| `503` | | Enrollment, cancel, suspend, charge | The service is temporarily unavailable. Try again later |

## Sandbox

In the `sandbox` environment, no real card page is displayed. The outcome of an enrollment is simulated, and you read it with the enrollment status endpoint.

For a scheduled subscription, the `amount` of the request selects the outcome:

| `amount` | Outcome |
| :- | :- |
| `100` | The enrollment succeeds and the subscription is created |
| `200` | Failure, `authentication_failed` |
| `201` | Failure, `card_declined` |
| `202` | Failure, `card_expired` |
| `204` | Failure, `customer_insufficient_funds` |
| `302` | Failure, `service_unavailable` |
| Any other value | Failure, `bad_parameters` |

For an on-demand subscription, omit `amount` to simulate a successful enrollment. Other values select the outcome in the same way as in the table.


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