> For the complete documentation index, see [llms.txt](https://docs.chargebackstop.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.chargebackstop.com/developer/partner-integration-guide/orders-api-integration-guide.md).

# Orders API Integration Guide

{% hint style="info" %}
Use this guide for `COMPLETE` Orders API integrations only. `COMPLETE` orders must use a `CUSTOM_ORDERS` integration. If you are enriching an existing Stripe, Adyen, Shopify, or other processor integration, use `PARTIAL` orders instead. See the [Orders API reference](https://docs.chargebackstop.com/developer/api-documentation/orders).
{% endhint %}

## Reference documentation

* [Orders API](https://docs.chargebackstop.com/developer/api-documentation/orders)
* [Organisations API](https://docs.chargebackstop.com/developer/api-documentation/organisations)
* [Merchants API](https://docs.chargebackstop.com/developer/api-documentation/merchants)
* [Integrations API](https://docs.chargebackstop.com/developer/api-documentation/integrations)
* [Enrolments API](https://docs.chargebackstop.com/developer/api-documentation/enrolments)
* [Alerts API](https://docs.chargebackstop.com/developer/api-documentation/alerts)
* [Rulesets API](https://docs.chargebackstop.com/developer/api-documentation/rulesets-developer-preview)
* [Simulations API](https://docs.chargebackstop.com/developer/api-documentation/simulations)
* [Webhooks](https://docs.chargebackstop.com/developer/partner-integration-guide/webhooks)
* [Chargeback alert enrolments](https://docs.chargebackstop.com/chargeback-alerts/enrolments)
* [Verifi RDR](https://docs.chargebackstop.com/chargeback-alerts/verifi-rdr)
* [Transaction matching](https://docs.chargebackstop.com/chargeback-alerts/transaction-matching)
* [Resolution rules](https://docs.chargebackstop.com/chargeback-alerts/resolution-rules)

## What you will build

Your integration should support this lifecycle:

1. Create one ChargebackStop organisation per customer.
2. Create one or more merchants inside that organisation.
3. Create a `CUSTOM_ORDERS` integration linked to the merchant or merchants.
4. Upload `COMPLETE` orders with card transaction identifiers that support chargeback alert matching.
5. Create chargeback alert enrolments for Ethoca Alerts and/or Verifi RDR.
6. Receive `enrolment.created`, `enrolment.updated`, `alert.created`, and `alert.updated` webhooks.
7. Resolve actionable Ethoca alerts with `PATCH /v1/alerts/{alert_id}`.
8. Promote the same flow from a TEST partner account to a LIVE production partner account.

## Environments and accounts

ChargebackStop uses the same API host for test and production:

```
https://api.chargebackstop.com
```

The account mode controls whether records are TEST or LIVE.

| Environment | Account              | Purpose                                                                               | Notes                                                                                                                         |
| ----------- | -------------------- | ------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| Development | TEST partner account | Build API client, webhook receiver, order upload, and alert action workflows          | Organisations created under a TEST partner become TEST organisations. Simulation endpoints work only with TEST organisations. |
| Production  | LIVE partner account | Create real customer organisations, submit production orders, and receive real alerts | LIVE organisations cannot use simulation endpoints. Real enrolments must be approved/enabled before alerts arrive.            |

{% hint style="warning" %}
TEST IDs and LIVE IDs are separate. Do not carry `org_`, `mrch_`, `int_`, `enrl_`, `ord_`, or `netalrt_` IDs from TEST into production. Store environment-specific mappings in your system.
{% endhint %}

## Before you write code

Ask ChargebackStop for a TEST partner account. In that TEST account:

1. Open the partner dashboard.
2. Go to the partner settings or administrator group developer settings.
3. Create a TEST partner API key.
4. Add a TEST webhook endpoint for `enrolment.created`, `enrolment.updated`, `alert.created`, and `alert.updated`.
5. Reveal and store the webhook signing secret.

For local development, expose your webhook receiver through a temporary HTTPS URL, such as a tunnel, because webhook endpoint URLs must be HTTPS.

## Authentication and access model

All API requests use a bearer token:

```bash
Authorization: Bearer <api_key>
Content-Type: application/json
```

For partner-owned onboarding, create a partner-group API key in your partner dashboard. Admin partner-group keys can access all organisations under the partner account. Non-admin partner-group keys can access only organisations assigned to that group.

{% tabs %}
{% tab title="Admin partner key" %}
Use an admin partner-group key for automated customer onboarding because it can create organisations, merchants, and enrolments.

Required abilities for the full flow:

* `organisations:read`, `organisations:write`
* `merchants:read`, `merchants:write`
* `integrations:read`, `integrations:write`
* `orders:read`, `orders:write`, `orders:update`
* `enrolments_v2:read`, `enrolments_v2:write`
* `alerts:read`, `alerts:write`
* `rulesets:read`, `rulesets:write` if you will create custom RDR resolution rules
* `simulations:alerts`, `simulations:enrollments` for TEST only
  {% endtab %}

{% tab title="Restricted partner key" %}
Use a non-admin partner-group key for steady-state operations against assigned organisations, such as uploading orders, reading alerts, and actioning alerts.

Restricted keys are useful once customer organisations and merchants already exist. They should not be used for fully automated onboarding unless your operating model deliberately separates setup from daily processing.
{% endtab %}
{% endtabs %}

{% hint style="danger" %}
API keys are shown only once when created. Store them in your secrets manager immediately, never in source control, logs, or customer-visible configuration.
{% endhint %}

## Data you should store

At minimum, store these mappings in your platform:

| Your system                        | ChargebackStop ID             | Why it matters                                                                                                  |
| ---------------------------------- | ----------------------------- | --------------------------------------------------------------------------------------------------------------- |
| Customer account                   | `organisation_id`             | Required for Orders, Alerts filters, Simulations, and Integrations.                                             |
| Merchant/MID/CAID/provider account | `merchant_id`                 | Required for Integrations and Enrolments.                                                                       |
| Orders feed connection             | `integration_id`              | Required for every `COMPLETE` order.                                                                            |
| Alert provider enrolment           | `enrolment_id`                | Required for simulation, enrolment status tracking, and RDR ruleset assignment.                                 |
| Order                              | `order_id` and `reference_id` | `reference_id` is your stable idempotency key for create. `order_id` is used for `PATCH /v1/orders/{order_id}`. |
| Alert                              | `alert_id`                    | Required for `GET /v1/alerts/{alert_id}` and `PATCH /v1/alerts/{alert_id}`.                                     |

## Orders API requirements for alert matching

Send `COMPLETE` orders as soon as they are available, and backfill recent history before enabling real alert traffic. Agree the backfill window with ChargebackStop during onboarding.

Required order fields for `COMPLETE` mode:

| Field                            | Description                                                                               |
| -------------------------------- | ----------------------------------------------------------------------------------------- |
| `type`                           | Must be `COMPLETE`.                                                                       |
| `organisation_id`                | Customer organisation ID.                                                                 |
| `integration_id`                 | `CUSTOM_ORDERS` integration ID.                                                           |
| `reference_id`                   | Stable unique order identifier from your system.                                          |
| `order_datetime`                 | ISO 8601 order timestamp.                                                                 |
| `order_number`                   | Customer-facing order number.                                                             |
| `order_subtotal_amount_in_cents` | Subtotal before tax.                                                                      |
| `order_currency`                 | ISO 4217 currency code.                                                                   |
| `order_total_amount_in_cents`    | Total amount.                                                                             |
| `order_status`                   | `OPEN_PENDING`, `OPEN_PENDING_RETURN`, `CLOSED_COMPLETE`, `CLOSED_CANCELLED`, or `OTHER`. |

For alert matching, include at least one transaction per order.

Required transaction fields:

| Field                         | Description                                                         |
| ----------------------------- | ------------------------------------------------------------------- |
| `reference_id`                | Stable unique transaction identifier within the integration.        |
| `amount_in_cents`             | Transaction amount.                                                 |
| `currency`                    | ISO 4217 currency code.                                             |
| `payment_method_type`         | Usually `CARD`.                                                     |
| `authorisation_status`        | Usually `SETTLED` for completed payments.                           |
| `payment_method_reference_id` | Your payment method or payment transaction reference.               |
| `authorised_at`               | ISO 8601 authorisation timestamp.                                   |
| `payment_method_card_brand`   | Required for card transactions, for example `VISA` or `MASTERCARD`. |
| `payment_method_card_last_4`  | Required for card transactions.                                     |

Send as many matching identifiers as possible:

| Identifier                  | Recommendation                                                                     |
| --------------------------- | ---------------------------------------------------------------------------------- |
| `acquirer_reference_number` | Send whenever available. This is the strongest matching identifier.                |
| `authorisation_code`        | Send whenever available, especially with card last 4.                              |
| `payment_method_card_bin`   | Send whenever available, especially with card brand, last 4, amount, and currency. |

{% hint style="info" %}
Also send related `refunds` and `disputes` when you have them. These records help ChargebackStop identify invalid or already-refunded alerts.
{% endhint %}

## Enrolment requirements

Create a separate enrolment for each chargeback alert product the customer will use. Save the returned `enrolment_id`; it is the identifier you will use for status tracking, TEST simulations, and RDR ruleset assignment.

{% tabs %}
{% tab title="Ethoca Alerts" %}
Use `type: ETHOCA_ALERT`.

Required enrolment data:

| Field                                   | Requirement                                          |
| --------------------------------------- | ---------------------------------------------------- |
| `merchant_ids`                          | One or more merchant IDs from the same organisation. |
| `ethoca_alert.descriptors`              | One or more billing descriptors.                     |
| `ethoca_alert.descriptors[].descriptor` | The descriptor text Ethoca should enrol.             |
| `ethoca_alert.descriptors[].match_type` | `STARTS_WITH` or `EXACT_MATCH`.                      |

Ethoca enrolment is descriptor based. Do not use ARN or BIN + CAID as the Ethoca enrolment identifier. ARN, authorisation code, BIN, last 4, amount, currency, and transaction dates belong on Orders API transaction records so alerts can be matched to uploaded orders.
{% endtab %}

{% tab title="Verifi RDR" %}
Use `type: VERIFI_RDR`.

Required enrolment data:

| Field                                  | Requirement                                                                                          |
| -------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `merchant_ids`                         | One or more merchant IDs from the same organisation.                                                 |
| `verifi_rdr.bin` and `verifi_rdr.caid` | Preferred for BIN + CAID enrolment. Some partners call this BIN + CID; the API field name is `caid`. |
| `verifi_rdr.arns`                      | Use this instead of BIN + CAID when the RDR enrolment is ARN based.                                  |

Provide either `bin` and `caid` together, or provide at least one ARN in `arns`.

By default, a Verifi RDR BIN + CAID enrolment is published without a custom ruleset. That means eligible RDR cases for that BIN + CAID follow the default RDR flow and are accepted/refunded at the network level. If the customer wants exceptions, create a platform-run resolution ruleset and attach it to the RDR `enrolment_id` before go-live.
{% endtab %}
{% endtabs %}

{% hint style="warning" %}
Enrolments are created in `PENDING`. Treat the enrolment as live only after you receive `enrolment.updated` with `status: "ENABLED"` or a direct `GET /v2/enrolments/{enrolment_id}` returns `ENABLED`.
{% endhint %}

## End-to-end TEST flow

Run this flow in a TEST partner account first.

{% stepper %}
{% step %}

### Set your environment variables

```bash
export CBS_API_BASE="https://api.chargebackstop.com"
export CBS_API_KEY="cbs_REPLACE_WITH_TEST_PARTNER_API_KEY"
```

Confirm your key works by listing organisations:

```bash
curl -X GET "$CBS_API_BASE/v1/organisations/?limit=10&offset=0" \
  -H "Authorization: Bearer $CBS_API_KEY"
```

{% endstep %}

{% step %}

### Create a customer organisation

Use one organisation per customer.

```bash
curl -X POST "$CBS_API_BASE/v1/organisations/" \
  -H "Authorization: Bearer $CBS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Fitness"
  }'
```

Example TEST response:

```json
{
  "id": "org_abc123",
  "name": "[TEST] Acme Fitness",
  "mode": "TEST",
  "created_at": "2026-02-17T10:00:00Z",
  "updated_at": "2026-02-17T10:00:00Z",
  "links": [{"rel": "self", "uri": "/v1/organisations/org_abc123"}]
}
```

Save `organisation_id`.
{% endstep %}

{% step %}

### Create a merchant

Use `external_id` for the merchant identifier your team recognises, such as MID, CAID, or provider account ID.

```bash
curl -X POST "$CBS_API_BASE/v1/merchants/" \
  -H "Authorization: Bearer $CBS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "organisation_id": "org_abc123",
    "name": "Acme Fitness Main Store",
    "type": "GENERIC",
    "external_id": "mid_123456789"
  }'
```

Example response:

```json
{
  "id": "mrch_abc123",
  "organisation_id": "org_abc123",
  "name": "Acme Fitness Main Store",
  "type": "GENERIC",
  "external_id": "mid_123456789",
  "default_representment_service_type": null,
  "created_at": "2026-02-17T10:02:00Z",
  "updated_at": "2026-02-17T10:02:00Z",
  "links": [{"rel": "self", "uri": "/v1/merchants/mrch_abc123"}]
}
```

Save `merchant_id`.
{% endstep %}

{% step %}

### Create a custom orders integration

Create one `CUSTOM_ORDERS` integration for the orders feed you will use for this customer.

```bash
curl -X POST "$CBS_API_BASE/v1/integrations/" \
  -H "Authorization: Bearer $CBS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "organisation_id": "org_abc123",
    "name": "Acme Fitness Orders API",
    "type": "CUSTOM_ORDERS",
    "status": "ENABLED",
    "merchant_ids": ["mrch_abc123"]
  }'
```

Example response:

```json
{
  "id": "int_abc123",
  "organisation_id": "org_abc123",
  "name": "Acme Fitness Orders API",
  "type": "CUSTOM_ORDERS",
  "status": "ENABLED",
  "merchant_ids": ["mrch_abc123"],
  "links": [{"rel": "self", "uri": "/v1/integrations/int_abc123"}]
}
```

Save `integration_id`.
{% endstep %}

{% step %}

### Create a chargeback alert enrolment

Create one enrolment per product. The examples below use the same merchant, but production customers may need separate enrolments for different descriptors, BIN + CAID pairs, or ARN sets.

{% tabs %}
{% tab title="Ethoca Alert" %}
Ethoca enrolments use descriptors.

```bash
curl -X POST "$CBS_API_BASE/v2/enrolments/" \
  -H "Authorization: Bearer $CBS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_ids": ["mrch_abc123"],
    "type": "ETHOCA_ALERT",
    "ethoca_alert": {
      "descriptors": [
        {
          "descriptor": "ACMEFIT",
          "match_type": "STARTS_WITH"
        }
      ]
    }
  }'
```

Example response:

```json
{
  "id": "enrl_ethoca123",
  "organisation_id": "org_abc123",
  "merchant_ids": ["mrch_abc123"],
  "type": "ETHOCA_ALERT",
  "status": "PENDING",
  "ethoca_alert": {
    "descriptors": [
      {
        "descriptor": "ACMEFIT",
        "match_type": "STARTS_WITH",
        "status": "PENDING"
      }
    ]
  },
  "created_at": "2026-02-17T10:03:00Z",
  "updated_at": "2026-02-17T10:03:00Z",
  "note": null,
  "links": [{"rel": "self", "uri": "/v2/enrolments/enrl_ethoca123"}]
}
```

Save the Ethoca `enrolment_id`.
{% endtab %}

{% tab title="Verifi RDR" %}
Verifi RDR enrolments use either BIN + CAID or ARNs.

BIN + CAID example:

```bash
curl -X POST "$CBS_API_BASE/v2/enrolments/" \
  -H "Authorization: Bearer $CBS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_ids": ["mrch_abc123"],
    "type": "VERIFI_RDR",
    "verifi_rdr": {
      "bin": "424242",
      "caid": "ACMEFITCAID01"
    }
  }'
```

Example response:

```json
{
  "id": "enrl_rdr123",
  "organisation_id": "org_abc123",
  "merchant_ids": ["mrch_abc123"],
  "type": "VERIFI_RDR",
  "status": "PENDING",
  "verifi_rdr": {
    "bin": "424242",
    "caid": "ACMEFITCAID01",
    "arns": null
  },
  "created_at": "2026-02-17T10:04:00Z",
  "updated_at": "2026-02-17T10:04:00Z",
  "note": null,
  "links": [{"rel": "self", "uri": "/v2/enrolments/enrl_rdr123"}]
}
```

ARN-based example:

```json
{
  "merchant_ids": ["mrch_abc123"],
  "type": "VERIFI_RDR",
  "verifi_rdr": {
    "arns": ["74027012345678901234567"]
  }
}
```

Save the RDR `enrolment_id`. You will need it if the customer wants custom RDR decisioning through a ruleset.
{% endtab %}
{% endtabs %}

{% hint style="info" %}
RDR alerts resolve at the network level. By default, eligible RDR cases for an enrolled BIN + CAID are accepted/refunded. Configure an RDR ruleset before go-live only when the customer wants exceptions to that default flow.
{% endhint %}
{% endstep %}

{% step %}

### Enable the TEST enrolment for simulations

Simulation endpoints work only in TEST mode. Mark each TEST enrolment you want to simulate as `ENABLED`.

```bash
curl -X PATCH "$CBS_API_BASE/v1/simulate/enrolments/enrl_ethoca123" \
  -H "Authorization: Bearer $CBS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "ENABLED"
  }'
```

For a Verifi RDR TEST enrolment, use the RDR `enrolment_id` in the same endpoint.
{% endstep %}

{% step %}

### Submit a COMPLETE order

The Orders API accepts a JSON array, even when you submit one order.

```bash
curl -X POST "$CBS_API_BASE/v1/orders/" \
  -H "Authorization: Bearer $CBS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "type": "COMPLETE",
      "organisation_id": "org_abc123",
      "integration_id": "int_abc123",
      "reference_id": "order-10001",
      "order_datetime": "2026-02-17T09:59:00Z",
      "order_number": "AF-10001",
      "order_subtotal_amount_in_cents": 4900,
      "order_currency": "USD",
      "order_total_amount_in_cents": 4900,
      "order_status": "CLOSED_COMPLETE",
      "customer_email": "casey.jordan@example.com",
      "transactions": [
        {
          "reference_id": "txn-10001",
          "amount_in_cents": 4900,
          "currency": "USD",
          "payment_method_type": "CARD",
          "authorisation_status": "SETTLED",
          "payment_method_reference_id": "pay_10001",
          "authorised_at": "2026-02-17T09:59:05Z",
          "descriptor": "ACMEFIT MONTHLY",
          "payment_method_card_brand": "VISA",
          "payment_method_card_last_4": "4242",
          "payment_method_card_bin": "424242",
          "authorisation_code": "123456",
          "acquirer_reference_number": "74027012345678901234567"
        }
      ]
    }
  ]'
```

Example response:

```json
{
  "created": 1,
  "failed": 0,
  "results": [
    {
      "id": "ord_abc123",
      "reference_id": "order-10001",
      "type": "COMPLETE",
      "organisation_id": "org_abc123",
      "integration_id": "int_abc123",
      "created_at": "2026-02-17T10:05:00Z",
      "transactions": [
        {
          "reference_id": "txn-10001",
          "amount_in_cents": 4900,
          "currency": "USD",
          "payment_method_type": "CARD",
          "authorisation_status": "SETTLED",
          "payment_method_reference_id": "pay_10001",
          "authorised_at": "2026-02-17T09:59:05Z",
          "payment_method_card_brand": "VISA",
          "payment_method_card_last_4": "4242",
          "payment_method_card_bin": "424242",
          "authorisation_code": "123456",
          "acquirer_reference_number": "74027012345678901234567"
        }
      ],
      "links": [{"rel": "self", "uri": "/v1/orders/ord_abc123"}]
    }
  ],
  "errors": []
}
```

Save `order_id`.
{% endstep %}

{% step %}

### Simulate an actionable chargeback alert

Use this to test alert webhooks and alert resolution. The simulator creates a test alert and triggers the same `alert.created` webhook flow as a non-simulated alert.

```bash
curl -X POST "$CBS_API_BASE/v1/simulate/alerts" \
  -H "Authorization: Bearer $CBS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "organisation_id": "org_abc123",
    "enrolment_id": "enrl_ethoca123",
    "status": "ACTION_REQUIRED",
    "card_scheme": "VISA",
    "amount_in_cents": 4900,
    "currency_code": "USD",
    "transaction_acquirer_reference_number": "74027012345678901234567",
    "transaction_authorisation_code": "123456"
  }'
```

Example response:

```json
{
  "id": "netalrt_abc123",
  "alert_network_id": "SIM123456",
  "organisation_id": "org_abc123",
  "merchant_id": "mrch_abc123",
  "enrolment_id": "enrl_ethoca123",
  "enrolment_type": "ETHOCA_ALERT",
  "status": "ACTION_REQUIRED",
  "transaction_amount_in_cents": 4900,
  "transaction_currency_code": "USD",
  "transaction_authorised_at": "2026-02-10T10:30:00Z",
  "action_required_deadline": "2026-02-19T10:30:00Z",
  "transaction_authorisation_code": "123456",
  "transaction_acquirer_reference_number": "74027012345678901234567",
  "transaction_statement_descriptor": "ACMEFIT",
  "transaction_card_bin": "400000",
  "transaction_card_last4": "1234",
  "transaction_card_scheme": "VISA",
  "transaction_card_issuer": "Bank of America",
  "transaction_refund_outcome": null,
  "subscription_cancel_outcome": null,
  "note": null,
  "integration_id": null,
  "integration_transaction_id": null
}
```

{% hint style="warning" %}
The alert simulator validates alert API behavior, webhook delivery, and resolution flows. It does not perform Orders API transaction matching inline, so `integration_id` and `integration_transaction_id` can be `null` in simulator responses. For production matching readiness, also verify that real or ChargebackStop-coordinated matched test alerts include these fields when they match your uploaded orders.
{% endhint %}
{% endstep %}

{% step %}

### Resolve the simulated alert

For a Custom Orders-only integration, ChargebackStop cannot refund through your processor. Your system should perform the refund, cancellation, or customer handling in your own platform first, then update the alert with the outcome.

If you refunded the customer:

```bash
curl -X PATCH "$CBS_API_BASE/v1/alerts/netalrt_abc123" \
  -H "Authorization: Bearer $CBS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "REFUND",
    "note": "Refund completed in partner platform before actioning alert."
  }'
```

If you want to accept/fight the dispute and did not refund:

```bash
curl -X PATCH "$CBS_API_BASE/v1/alerts/netalrt_abc123" \
  -H "Authorization: Bearer $CBS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "ACCEPT_DISPUTE",
    "note": "Merchant chose not to refund this alert."
  }'
```

Example response:

```json
{
  "id": "netalrt_abc123",
  "organisation_id": "org_abc123",
  "merchant_id": "mrch_abc123",
  "enrolment_id": "enrl_ethoca123",
  "enrolment_type": "ETHOCA_ALERT",
  "status": "RESOLVED",
  "transaction_refund_outcome": "REFUNDED",
  "note": "Refund completed in partner platform before actioning alert.",
  "links": [{"rel": "self", "uri": "/v1/alerts/netalrt_abc123"}]
}
```

{% endstep %}
{% endstepper %}

## Webhook implementation

Create a partner webhook endpoint for:

* `enrolment.created`
* `enrolment.updated`
* `alert.created`
* `alert.updated`

Webhook endpoints must be HTTPS and should return a 2xx response within 20 seconds. Use the `X-Idempotency-Key` header and the event `id` to prevent duplicate processing. Retries use the same `X-Idempotency-Key`.

{% hint style="info" %}
Webhook payloads put the domain object in `data.object`. For enrolment events, `data.object` matches the Enrolments API response shape and `api_version` is `v2`. For alert events, `data.object` matches the Alerts API response shape.
{% endhint %}

### Verify signatures

[To verify please refer to the Webhooks documentation.](/developer/partner-integration-guide/webhooks.md#signature)

### Process enrolment webhooks

Use enrolment webhooks to manage whether a customer enrolment is live in your product.

Example `enrolment.updated` payload:

```json
{
  "id": "evt_enrolmentupdated123",
  "type": "enrolment.updated",
  "created_at": "2026-02-17T10:30:00Z",
  "data": {
    "object": {
      "id": "enrl_rdr123",
      "organisation_id": "org_abc123",
      "merchant_ids": ["mrch_abc123"],
      "type": "VERIFI_RDR",
      "status": "ENABLED",
      "verifi_rdr": {
        "bin": "424242",
        "caid": "ACMEFITCAID01",
        "arns": null
      },
      "created_at": "2026-02-17T10:04:00Z",
      "updated_at": "2026-02-17T10:30:00Z",
      "note": null
    },
    "previous_attributes": {
      "status": "PENDING"
    }
  },
  "api_version": "v2"
}
```

Recommended handling:

| Enrolment status                    | Partner system behavior                                                                                                |
| ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `PENDING`                           | Store the enrolment and show it as submitted, not live.                                                                |
| `IN_PROGRESS`                       | Show it as being configured by ChargebackStop or the alert provider.                                                   |
| `ACTION_REQUIRED`                   | Block go-live for that enrolment and show the customer-facing `note` to your operations team or customer success team. |
| `ENABLED`                           | Mark the enrolment live. Start expecting alerts for that product and identifier.                                       |
| `FAILED`                            | Mark the enrolment failed and escalate with the `note` and enrolment ID.                                               |
| `CANCELLED`, `PAUSED`, `UNENROLLED` | Mark the enrolment inactive. Stop treating the identifier as live for new alert traffic.                               |

{% hint style="warning" %}
Webhook delivery can be retried and events may be processed out of order. Before showing an enrolment as live or blocked in a customer-facing workflow, fetch `GET /v2/enrolments/{enrolment_id}` and use that response as the current source of truth.
{% endhint %}

### Process alert webhooks

Use this webhook processing pattern:

1. Verify `X-Signature`.
2. Store event `id`, `type`, `created_at`, `api_version`, and `data.object`.
3. Deduplicate by event `id`. You may also store `X-Idempotency-Key` for delivery tracing.
4. Return 2xx quickly.
5. Process the alert asynchronously.
6. Fetch the latest alert with `GET /v1/alerts/{alert_id}` before taking irreversible action.

Example `alert.created` decision logic:

```pseudo
if alert.status == "ACTION_REQUIRED" and alert.enrolment_type == "ETHOCA_ALERT":
    show alert to merchant or run partner decisioning
    if merchant_refunded_in_partner_platform:
        PATCH /v1/alerts/{id} {"action": "REFUND"}
    else if merchant_accepts_or_fights_dispute:
        PATCH /v1/alerts/{id} {"action": "ACCEPT_DISPUTE"}
else:
    store alert as informational
```

## Alert resolution rules

Ethoca and Verifi RDR resolve differently.

| Provider                       | Alert behavior                                                                  | What your integration must do                                                                                |
| ------------------------------ | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| Ethoca Alerts (`ETHOCA_ALERT`) | Alerts can arrive in `ACTION_REQUIRED` with `action_required_deadline`.         | Decide quickly, perform any off-platform refund/cancellation first, then call `PATCH /v1/alerts/{alert_id}`. |
| Verifi RDR (`VERIFI_RDR`)      | Alerts are resolved at the network level and arrive as `RESOLVED` or `INVALID`. | Configure RDR rules before go-live if needed. Do not expect an action-required workflow.                     |

### Ethoca alert resolution

For Custom Orders-only integrations, the usual manual actions are:

| API action       | Use when                                                                            | Result                                              |
| ---------------- | ----------------------------------------------------------------------------------- | --------------------------------------------------- |
| `REFUND`         | You refunded the transaction in your own platform.                                  | Alert is marked resolved with refunded outcome.     |
| `ACCEPT_DISPUTE` | You did not refund and will accept or fight the dispute outside the alert workflow. | Alert is marked resolved with not-refunded outcome. |

{% hint style="danger" %}
For Ethoca, refunding the customer in your own system is not enough. You must also action the alert through ChargebackStop before the deadline so the alert provider receives the outcome.
{% endhint %}

Use `CANCEL` and `REFUND_AND_CANCEL` only when your ChargebackStop setup has the processor/subscription context needed to support those actions, or when ChargebackStop confirms they fit your workflow.

### Verifi RDR default decisioning

For Verifi RDR, the default path is intentionally simple:

1. Create a `VERIFI_RDR` enrolment for the customer's BIN + CAID.
2. Wait until the enrolment becomes `ENABLED`.
3. If no platform-run ruleset is attached to that RDR enrolment, eligible RDR cases for that BIN + CAID are accepted/refunded by default at the network level.

RDR alerts generally arrive in ChargebackStop after the network decision has already happened. They should be stored and reconciled, not routed into an `ACTION_REQUIRED` queue.

### Customise Verifi RDR with a ruleset

Create a platform-run ruleset only when the customer wants exceptions to the default RDR refund flow. Attach the ruleset to the RDR `enrolment_id`.

RDR ruleset constraints:

| Constraint             | Detail                                                                                                                                                                        |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Required API abilities | `rulesets:read` and `rulesets:write`.                                                                                                                                         |
| Endpoint               | `POST /v1/rulesets/`.                                                                                                                                                         |
| Enrolments             | Use one or more `VERIFI_RDR` BIN + CAID enrolment IDs from the same organisation. Confirm with ChargebackStop before relying on custom rulesets for ARN-based RDR enrolments. |
| Ruleset count          | Each enrolment can have one platform-run ruleset.                                                                                                                             |
| Outcomes for RDR       | Use `REFUND` or `ACCEPT_DISPUTE`. `REFUND_AND_CANCEL` is published to Verifi as `REFUND`; handle cancellation in your own system. Do not use `CANCEL` for RDR decisioning.    |
| Rule types             | `AMOUNT` and `DESCRIPTOR`. Only one rule of each type is allowed per ruleset.                                                                                                 |
| Amount currency        | Must be `USD`.                                                                                                                                                                |

Example: keep the default refund behavior for most RDR cases, but accept the dispute instead of refunding when the amount is greater than $100 or the descriptor starts with `ACMEFIT HIGHVALUE`.

```bash
curl -X POST "$CBS_API_BASE/v1/rulesets/" \
  -H "Authorization: Bearer $CBS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "organisation_id": "org_abc123",
    "enrolment_ids": ["enrl_rdr123"],
    "outcome": "ACCEPT_DISPUTE",
    "join_operator": "OR",
    "rules": [
      {
        "type": "AMOUNT",
        "parameters": {
          "operator": "GREATER_THAN",
          "currency_code": "USD",
          "amount_in_cents": 10000
        }
      },
      {
        "type": "DESCRIPTOR",
        "parameters": {
          "descriptors": [
            {
              "value": "ACMEFIT HIGHVALUE",
              "match_type": "STARTS_WITH"
            }
          ]
        }
      }
    ]
  }'
```

Example response:

```json
{
  "id": "rset_abc123",
  "organisation_id": "org_abc123",
  "enrolment_ids": ["enrl_rdr123"],
  "outcome": "ACCEPT_DISPUTE",
  "join_operator": "OR",
  "rules": [
    {
      "id": "resrule_amount123",
      "type": "AMOUNT",
      "parameters": {
        "operator": "GREATER_THAN",
        "currency_code": "USD",
        "amount_in_cents": 10000
      },
      "created_at": "2026-02-17T10:10:00Z",
      "updated_at": "2026-02-17T10:10:00Z"
    },
    {
      "id": "resrule_descriptor123",
      "type": "DESCRIPTOR",
      "parameters": {
        "descriptors": [
          {
            "value": "ACMEFIT HIGHVALUE",
            "match_type": "STARTS_WITH"
          }
        ]
      },
      "created_at": "2026-02-17T10:10:00Z",
      "updated_at": "2026-02-17T10:10:00Z"
    }
  ],
  "created_at": "2026-02-17T10:10:00Z",
  "updated_at": "2026-02-17T10:10:00Z"
}
```

To update an existing ruleset:

* Use `PATCH /v1/rulesets/{ruleset_id}` for `enrolment_ids`, `outcome`, or `join_operator`.
* Use `PATCH /v1/rulesets/{ruleset_id}/rules/{rule_id}` for rule criteria.
* Do not send `rules` to `PATCH /v1/rulesets/{ruleset_id}`; rule updates are handled by the nested rule endpoints.

{% hint style="warning" %}
Create and verify RDR rulesets before production go-live. Enrolment and ruleset changes are published to the RDR decisioning path asynchronously, so do not make last-minute rule changes during launch without coordinating with ChargebackStop.
{% endhint %}

## Order update patterns

Create orders once with `POST /v1/orders/`. Use `PATCH /v1/orders/{order_id}` to add or update later lifecycle data such as refunds, disputes, delivery status, subscription status, or order communications.

Example refund update:

```bash
curl -X PATCH "$CBS_API_BASE/v1/orders/ord_abc123" \
  -H "Authorization: Bearer $CBS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "refunds": [
      {
        "reference_id": "refund-10001",
        "amount_in_cents": 4900,
        "currency": "USD",
        "status": "SUCCEEDED",
        "original_transaction_reference_id": "txn-10001",
        "refund_datetime": "2026-02-17T11:20:00Z"
      }
    ]
  }'
```

{% hint style="warning" %}
Do not generate a new `reference_id` when retrying the same order, transaction, refund, dispute, delivery, item, or subscription. Stable references let you reconcile duplicates and avoid creating conflicting records.
{% endhint %}

## Error handling and retries

The Orders API uses partial success after schema validation. One valid order in a batch can be created even if another order in the same batch fails.

Handle these cases explicitly:

| Case                    | Recommended handling                                                                                               |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `429 RATE_LIMITED`      | Retry with exponential backoff. Limits are 100 requests per minute per endpoint per organisation or partner group. |
| `DUPLICATE_ORDER`       | Treat as a reconciliation case. Fetch the order with `GET /v1/orders/?reference_id=<reference_id>`.                |
| `INVALID_ORDER_TYPE`    | Ensure `COMPLETE` orders use a `CUSTOM_ORDERS` integration.                                                        |
| `INVALID_INTEGRATION`   | Check `integration_id`, organisation access, and integration status.                                               |
| `MISSING_FIELD`         | Validate payloads before enqueueing. Required fields can depend on order mode and enabled product features.        |
| `422` schema validation | No orders were processed. Fix the top-level request shape or field types and retry.                                |
| `5xx`                   | Retry safely with the same `reference_id` values.                                                                  |

## Sandbox acceptance checklist

Complete this checklist before asking ChargebackStop to move you to production.

* [ ] TEST partner API key is stored in a secrets manager.
* [ ] TEST webhook endpoint receives and verifies `enrolment.created`, `enrolment.updated`, `alert.created`, and `alert.updated`.
* [ ] Webhook processing is idempotent by event `id`.
* [ ] Your API client handles `401`, `403`, `404`, `422`, `429`, and `5xx` responses.
* [ ] You can create a TEST organisation through `POST /v1/organisations/`.
* [ ] You can create a merchant through `POST /v1/merchants/`.
* [ ] You can create a `CUSTOM_ORDERS` integration through `POST /v1/integrations/`.
* [ ] You can create an Ethoca Alert enrolment through `POST /v2/enrolments/`.
* [ ] If using RDR, you can create a Verifi RDR enrolment with BIN + CAID or ARNs through `POST /v2/enrolments/`.
* [ ] You can enable a TEST enrolment with `PATCH /v1/simulate/enrolments/{enrolment_id}`.
* [ ] Your system marks an enrolment live only after `status: "ENABLED"` from `enrolment.updated` or `GET /v2/enrolments/{enrolment_id}`.
* [ ] You can submit a `COMPLETE` order through `POST /v1/orders/`.
* [ ] You can add refund or dispute lifecycle updates through `PATCH /v1/orders/{order_id}`.
* [ ] You can create a simulated `ACTION_REQUIRED` alert.
* [ ] You can resolve an Ethoca alert with `REFUND`.
* [ ] You can resolve an Ethoca alert with `ACCEPT_DISPUTE`.
* [ ] If using custom RDR decisioning, you can create a ruleset through `POST /v1/rulesets/` and attach it to the RDR `enrolment_id`.
* [ ] Your UI or operations queue shows `action_required_deadline`.
* [ ] Your system never relies on TEST IDs in production configuration.

## Production launch checklist

When sandbox testing is complete, ChargebackStop will provision or enable your LIVE production partner account.

{% stepper %}
{% step %}

### Create production credentials

In the LIVE partner account:

1. Create a new production partner API key.
2. Store it in your production secrets manager.
3. Register a production HTTPS webhook endpoint.
4. Select `enrolment.created`, `enrolment.updated`, `alert.created`, and `alert.updated`.
5. Reveal and store the webhook signing secret.

Do not reuse TEST API keys or TEST webhook secrets.
{% endstep %}

{% step %}

### Create production customer records

For each production customer:

1. Create the organisation.
2. Create the merchant or merchants.
3. Create the `CUSTOM_ORDERS` integration.
4. Save all returned IDs in your production database.

Production organisation mode will be `LIVE`.
{% endstep %}

{% step %}

### Submit production order history

Before alert traffic begins, backfill recent complete order history for each customer. Include transaction matching identifiers, refunds, and disputes wherever available.

Monitor:

* Orders accepted vs failed
* Duplicate references
* Missing ARN/auth code/BIN coverage
* Rate limiting
* Payload validation errors
  {% endstep %}

{% step %}

### Create real chargeback alert enrolments

Create real Ethoca Alert and/or Verifi RDR enrolments using production descriptors, BIN/CAID, or ARNs.

For RDR, create any custom rulesets before go-live if the customer does not want the default accept/refund flow for every eligible BIN + CAID case.

Enrolments are created in `PENDING`. Treat an enrolment as live only after `enrolment.updated` or `GET /v2/enrolments/{enrolment_id}` returns `status: "ENABLED"`.
{% endstep %}

{% step %}

### Verify first production alerts

For the first real alerts:

1. Confirm your webhook receives the enrolment events and marks each live enrolment `ENABLED`.
2. Fetch the latest alert via `GET /v1/alerts/{alert_id}`.
3. Check whether `integration_id` and `integration_transaction_id` are present for matched alerts.
4. Confirm your refund/accept-dispute workflow resolves Ethoca alerts before `action_required_deadline`.
5. Confirm `alert.updated` arrives after resolution.

Escalate unmatched alerts to ChargebackStop with the alert ID, order reference, transaction reference, ARN, auth code, card brand, BIN, last 4, amount, currency, and timestamps.
{% endstep %}
{% endstepper %}

## Operational recommendations

* Send orders continuously, not only after an alert is received.
* Keep webhook handlers fast. Verify, persist, enqueue, and return 2xx.
* Use `enrolment.updated` and `GET /v2/enrolments/{enrolment_id}` to drive customer-facing enrolment status.
* Poll `GET /v1/alerts?status=ACTION_REQUIRED` as a fallback if webhook delivery is interrupted.
* Alert your operations team well before `action_required_deadline`.
* Treat `REFUND` as a declaration that your system has already refunded when using Custom Orders without a processor integration.
* Create RDR rulesets before go-live when the customer wants exceptions to the default RDR accept/refund behavior.
* Keep TEST and LIVE credentials, webhook secrets, IDs, and queues separate.
* Rotate API keys immediately after suspected exposure.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.chargebackstop.com/developer/partner-integration-guide/orders-api-integration-guide.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
