# Enrolments

Create, list, retrieve, and cancel network alert enrolments for one or more merchants.

**Versioning notice (important):**

* This is the first `v2` API documentation for enrolments.
* `/v1/enrolments` is deprecated and should not be used for new integrations.
* Use `/v2/enrolments` with `enrolments_v2:*` abilities.
* New API keys are provisioned with `enrolments_v2:*` abilities for enrolments access.
* Some legacy keys created before `/v1/enrolments` was deprecated may include both `enrolments:*` and `enrolments_v2:*` abilities.

**Base URL:** `https://api.chargebackstop.com/v2/enrolments/`

**Authentication:** Bearer token via API key.

Required abilities:

* `enrolments_v2:read` for GET endpoints
* `enrolments_v2:write` for POST and DELETE endpoints

**Access scope model:**

* This API is partner-level only.
* Admin partner-group keys can access enrolments across organisations available to their partner.
* Non-admin partner-group keys can access enrolments only for organisations assigned to their group.
* Organisation-level keys receive `401 Unauthorised` on all endpoints.

***

## Migration from `/v1/enrolments` to `/v2/enrolments`

Use this section to migrate existing enrolments integrations from v1 to v2.

### At a glance

| Area                                    | v1                                    | v2                                                                                                                                                                                 | Action                         |
| --------------------------------------- | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ |
| Base path                               | `/v1/enrolments`                      | `/v2/enrolments`                                                                                                                                                                   | Update request URLs            |
| Create request merchant field           | `merchant_id` (string)                | `merchant_ids` (array of strings)                                                                                                                                                  | Update payload shape           |
| Create response merchant field          | `merchant_id`                         | `merchant_ids`                                                                                                                                                                     | Update response parsing        |
| List/get/delete response merchant field | `merchant_id`                         | `merchant_ids`                                                                                                                                                                     | Update response parsing        |
| New create validation                   | Not applicable                        | Rejects duplicate merchant IDs and mixed-organisation merchant sets                                                                                                                | Handle new `422` codes         |
| Merchant reference                      | `merchant_id` identifies one merchant | `merchant_ids` supports one or more merchants; missing merchant references return `404 Not found`; mixed-organisation merchant sets return `422 MERCHANTS_DIFFERENT_ORGANISATIONS` | Update merchant error handling |

### Endpoint mapping

All operations map 1:1 to v2 routes:

* `POST /v1/enrolments` -> `POST /v2/enrolments`
* `GET /v1/enrolments` -> `GET /v2/enrolments`
* `GET /v1/enrolments/{enrolment_id}` -> `GET /v2/enrolments/{enrolment_id}`
* `DELETE /v1/enrolments/{enrolment_id}` -> `DELETE /v2/enrolments/{enrolment_id}`

### Authentication and abilities changes

* v2 is still partner-level only.
* Organisation-level keys are still not allowed.
* v2 endpoints require `enrolments_v2:read` and `enrolments_v2:write`.
* New API keys are provisioned with v2 enrolments abilities.
* Some legacy keys created before v1 deprecation may include both `enrolments:*` and `enrolments_v2:*`.

### Request and response schema differences

v1 create body (single merchant):

```json
{
  "merchant_id": "mrch_abc123",
  "type": "ETHOCA_ALERT",
  "ethoca_alert": {
    "descriptors": [
      {
        "descriptor": "ECOMM.COM",
        "match_type": "STARTS_WITH"
      }
    ]
  }
}
```

v2 create body (one or more merchants):

```json
{
  "merchant_ids": ["mrch_abc123", "mrch_def456"],
  "type": "ETHOCA_ALERT",
  "ethoca_alert": {
    "descriptors": [
      {
        "descriptor": "ECOMM.COM",
        "match_type": "STARTS_WITH"
      }
    ]
  }
}
```

v1 response field:

```json
{
  "merchant_id": "mrch_abc123"
}
```

v2 response field:

```json
{
  "merchant_ids": ["mrch_abc123", "mrch_def456"]
}
```

Other core fields (`id`, `organisation_id`, `type`, `status`, `ethoca_alert`, `verifi_rdr`, timestamps, `note`) stay the same contract shape.

### Behaviour differences to handle

* `merchant_ids` must not contain duplicates.
* All merchants in one create request must be accessible to the key.
* All merchants in one create request must belong to the same organisation.
* Out-of-scope organisation and merchant references are returned as `404 Not found`.
* Handle additional `422` business codes: `DUPLICATE_MERCHANT_IDS` and `MERCHANTS_DIFFERENT_ORGANISATIONS`.

### What does not change

* `type` values used by this API remain `ETHOCA_ALERT` and `VERIFI_RDR`.
* `GET /v2/enrolments` still supports `merchant_id`, `organisation_id`, `type`, and `status` filters.
* Deleting an enrolment still cancels it, and only `PENDING` enrolments can be cancelled.

### Migration checklist

{% stepper %}
{% step %}

#### Switch routes

Switch all enrolments routes from `/v1/enrolments` to `/v2/enrolments`.
{% endstep %}

{% step %}

#### Update API key abilities

Ensure API keys include `enrolments_v2:read` and `enrolments_v2:write`.
{% endstep %}

{% step %}

#### Update payloads

Update create payload builders from `merchant_id` to `merchant_ids`.
{% endstep %}

{% step %}

#### Update response parsing

Update response parsing from `merchant_id` to `merchant_ids` across create/list/get/delete flows.
{% endstep %}

{% step %}

#### Handle new 422 codes

Add handling for new `422` codes: `DUPLICATE_MERCHANT_IDS` and `MERCHANTS_DIFFERENT_ORGANISATIONS`.
{% endstep %}

{% step %}

#### Re-run tests

Re-run integration tests for enrolment create, list filters, detail, and cancel flows.
{% endstep %}
{% endstepper %}

***

## POST /v2/enrolments - Create enrolment

Create a new enrolment for one or more merchants.

**API level:** Partner-level only\
**Authentication:** `enrolments_v2:write`

### Request body

| Field          | Type           | Required      | Validation                                                                                                     | Description                               |
| -------------- | -------------- | ------------- | -------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |
| `merchant_ids` | array\[string] | Yes           | At least one ID; duplicates are rejected; all merchants must be accessible and belong to the same organisation | Merchants to associate with the enrolment |
| `type`         | string         | Yes           | `ETHOCA_ALERT` or `VERIFI_RDR`                                                                                 | Enrolment type                            |
| `ethoca_alert` | object         | Conditionally | Required when `type` is `ETHOCA_ALERT`                                                                         | Ethoca Alert configuration                |
| `verifi_rdr`   | object         | Conditionally | Required when `type` is `VERIFI_RDR`                                                                           | Verifi RDR configuration                  |

### Ethoca Alert configuration

| Field                      | Type           | Required | Validation                     | Description                          |
| -------------------------- | -------------- | -------- | ------------------------------ | ------------------------------------ |
| `descriptors`              | array\[object] | Yes      | At least one descriptor        | Descriptor rules for Ethoca matching |
| `descriptors[].descriptor` | string         | Yes      | Minimum length 1               | Descriptor text                      |
| `descriptors[].match_type` | string         | Yes      | `STARTS_WITH` or `EXACT_MATCH` | Descriptor matching mode             |

### Verifi RDR configuration

| Field  | Type           | Required      | Validation                                                    | Description |
| ------ | -------------- | ------------- | ------------------------------------------------------------- | ----------- |
| `bin`  | string         | Conditionally | Provide together with `caid`, or omit both and provide `arns` | Visa BIN    |
| `caid` | string         | Conditionally | Provide together with `bin`, or omit both and provide `arns`  | Visa CAID   |
| `arns` | array\[string] | Conditionally | At least one item when used                                   | Visa ARNs   |

### Important behaviour

* The API rejects mixed-organisation merchant lists with `MERCHANTS_DIFFERENT_ORGANISATIONS`.
* The enrolment is created in `PENDING` status.
* Responses return `merchant_ids` (plural), not `merchant_id`.

#### Ethoca descriptor validation

Ethoca validates descriptors asynchronously after the enrolment is created. This can take a few days. If the descriptor is too generic or already enrolled by another merchant globally across Ethoca's network, the enrolment moves to `ACTION_REQUIRED`. When this happens, our support team reaches out to the merchant to agree on an alternative descriptor. This typically takes 1–2 business days. Once resolved, the enrolment is resubmitted. To track enrolment status, poll the GET endpoint every 2–3 hours or subscribe to a webhook to watch for status changes.

### Example 1 request

```bash
curl -X POST "https://api.chargebackstop.com/v2/enrolments/" \
  -H "Authorization: Bearer <partner_api_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_ids": ["mrch_abc123", "mrch_def456"],
    "type": "ETHOCA_ALERT",
    "ethoca_alert": {
      "descriptors": [
        {
          "descriptor": "ECOMM.COM",
          "match_type": "STARTS_WITH"
        }
      ]
    }
  }'
```

### Example 1 response

```json
{
  "id": "enrl_abc123",
  "organisation_id": "org_xyz456",
  "merchant_ids": ["mrch_abc123", "mrch_def456"],
  "type": "ETHOCA_ALERT",
  "status": "PENDING",
  "ethoca_alert": {
    "descriptors": [
      {
        "descriptor": "ECOMM.COM",
        "match_type": "STARTS_WITH",
        "status": "PENDING"
      }
    ]
  },
  "created_at": "2026-02-16T12:00:00Z",
  "updated_at": "2026-02-16T12:00:00Z",
  "note": null,
  "links": [
    {
      "rel": "self",
      "uri": "/v2/enrolments/enrl_abc123"
    }
  ]
}
```

### Example 2 request

```bash
curl -X POST "https://api.chargebackstop.com/v2/enrolments/" \
  -H "Authorization: Bearer <partner_api_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_ids": ["mrch_abc123"],
    "type": "VERIFI_RDR",
    "verifi_rdr": {
      "bin": "424242",
      "caid": "ABC1234567"
    }
  }'
```

### Example 2 response

```json
{
  "id": "enrl_def456",
  "organisation_id": "org_xyz456",
  "merchant_ids": ["mrch_abc123"],
  "type": "VERIFI_RDR",
  "status": "PENDING",
  "verifi_rdr": {
    "bin": "424242",
    "caid": "ABC1234567",
    "arns": null
  },
  "created_at": "2026-02-16T12:05:00Z",
  "updated_at": "2026-02-16T12:05:00Z",
  "note": null,
  "links": [
    {
      "rel": "self",
      "uri": "/v2/enrolments/enrl_def456"
    }
  ]
}
```

***

## GET /v2/enrolments - List enrolments

Return enrolments accessible to the authenticated partner-group key.

**API level:** Partner-level only\
**Authentication:** `enrolments_v2:read`

### Query parameters

| Parameter         | Type    | Description                              |
| ----------------- | ------- | ---------------------------------------- |
| `merchant_id`     | string  | Filter by one accessible merchant ID     |
| `organisation_id` | string  | Filter by one accessible organisation ID |
| `type`            | string  | Filter by enrolment type                 |
| `status`          | string  | Filter by enrolment status               |
| `limit`           | integer | Number of results per page               |
| `offset`          | integer | Number of results to skip                |

### Important behaviour

* Filtering by a non-existent organisation returns `404 Not found`.
* Filtering by a non-existent merchant returns `404 Not found`.

### Example 3 request

```bash
curl -X GET "https://api.chargebackstop.com/v2/enrolments/?merchant_id=mrch_abc123&type=ETHOCA_ALERT&status=PENDING&limit=20&offset=0" \
  -H "Authorization: Bearer <partner_api_key>"
```

### Example 3 response

```json
{
  "items": [
    {
      "id": "enrl_abc123",
      "organisation_id": "org_xyz456",
      "merchant_ids": ["mrch_abc123", "mrch_def456"],
      "type": "ETHOCA_ALERT",
      "status": "PENDING",
      "ethoca_alert": {
        "descriptors": [
          {
            "descriptor": "ECOMM.COM",
            "match_type": "STARTS_WITH",
            "status": "PENDING"
          }
        ]
      },
      "created_at": "2026-02-16T12:00:00Z",
      "updated_at": "2026-02-16T12:00:00Z",
      "note": null,
      "links": [
        {
          "rel": "self",
          "uri": "/v2/enrolments/enrl_abc123"
        }
      ]
    }
  ],
  "count": 1
}
```

***

## GET /v2/enrolments/{enrolment\_id} - Get enrolment by ID

Retrieve one accessible enrolment by ID.

**API level:** Partner-level only\
**Authentication:** `enrolments_v2:read`

### URL parameters

| Parameter      | Type   | Description  |
| -------------- | ------ | ------------ |
| `enrolment_id` | string | Enrolment ID |

### Example 4 request

```bash
curl -X GET "https://api.chargebackstop.com/v2/enrolments/enrl_abc123" \
  -H "Authorization: Bearer <partner_api_key>"
```

### Example 4 response

```json
{
  "id": "enrl_abc123",
  "organisation_id": "org_xyz456",
  "merchant_ids": ["mrch_abc123", "mrch_def456"],
  "type": "ETHOCA_ALERT",
  "status": "PENDING",
  "ethoca_alert": {
    "descriptors": [
      {
        "descriptor": "ECOMM.COM",
        "match_type": "STARTS_WITH",
        "status": "PENDING"
      }
    ]
  },
  "created_at": "2026-02-16T12:00:00Z",
  "updated_at": "2026-02-16T12:00:00Z",
  "note": null,
  "links": [
    {
      "rel": "self",
      "uri": "/v2/enrolments/enrl_abc123"
    }
  ]
}
```

***

## DELETE /v2/enrolments/{enrolment\_id} - Cancel enrolment

Cancel one accessible enrolment.

**API level:** Partner-level only\
**Authentication:** `enrolments_v2:write`

### URL parameters

| Parameter      | Type   | Description            |
| -------------- | ------ | ---------------------- |
| `enrolment_id` | string | Enrolment ID to cancel |

### Important behaviour

* Only enrolments in `PENDING` status can be cancelled.
* Cancellation sets status to `CANCELLED` and sets `note` to `Enrolment cancelled via API request`.

### Example 5 request

```bash
curl -X DELETE "https://api.chargebackstop.com/v2/enrolments/enrl_abc123" \
  -H "Authorization: Bearer <partner_api_key>"
```

### Example 5 response

```json
{
  "id": "enrl_abc123",
  "organisation_id": "org_xyz456",
  "merchant_ids": ["mrch_abc123", "mrch_def456"],
  "type": "ETHOCA_ALERT",
  "status": "CANCELLED",
  "ethoca_alert": {
    "descriptors": [
      {
        "descriptor": "ECOMM.COM",
        "match_type": "STARTS_WITH",
        "status": "PENDING"
      }
    ]
  },
  "created_at": "2026-02-16T12:00:00Z",
  "updated_at": "2026-02-16T12:10:00Z",
  "note": "Enrolment cancelled via API request",
  "links": [
    {
      "rel": "self",
      "uri": "/v2/enrolments/enrl_abc123"
    }
  ]
}
```

***

## Common error responses

All error responses use this shape:

```json
{
  "errors": [
    {
      "code": "ERROR_CODE",
      "message": "Human-readable message"
    }
  ]
}
```

<details>

<summary>401 Unauthorised</summary>

Returned for invalid or expired API keys, and for organisation-level keys calling this partner-only API.

```json
{
  "errors": [
    {
      "code": "UNAUTHORISED",
      "message": "Unauthorised"
    }
  ]
}
```

</details>

<details>

<summary>403 Forbidden (missing v2 ability)</summary>

Returned when the key is valid but does not have the required v2 ability.

```json
{
  "errors": [
    {
      "code": "FORBIDDEN",
      "message": "Forbidden"
    }
  ]
}
```

</details>

<details>

<summary>404 Not found</summary>

Returned when the requested resource does not exist. This includes non-existent enrolments and non-existent `organisation_id` or `merchant_id` filters.

```json
{
  "errors": [
    {
      "code": "NOT_FOUND",
      "message": "Not found"
    }
  ]
}
```

</details>

<details>

<summary>422 Unprocessable Entity (mixed organisation merchants)</summary>

```json
{
  "errors": [
    {
      "code": "MERCHANTS_DIFFERENT_ORGANISATIONS",
      "message": "All merchants must belong to the same organisation"
    }
  ]
}
```

</details>

<details>

<summary>422 Unprocessable Entity (enrolment not pending)</summary>

```json
{
  "errors": [
    {
      "code": "ENROLMENT_NOT_PENDING",
      "message": "Only enrolments in PENDING state can be cancelled"
    }
  ]
}
```

</details>

Other business validation codes you may receive:

| Code                            | Meaning                                                  |
| ------------------------------- | -------------------------------------------------------- |
| `DUPLICATE_MERCHANT_IDS`        | `merchant_ids` contains duplicate values                 |
| `MISSING_ETHOCA_ALERT_CONFIG`   | `ethoca_alert` is required for `ETHOCA_ALERT` enrolments |
| `MISSING_VERIFI_RDR_CONFIG`     | `verifi_rdr` is required for `VERIFI_RDR` enrolments     |
| `INVALID_MATCH_TYPE`            | Ethoca descriptor `match_type` is invalid                |
| `INVALID_VERIFI_RDR_CONFIG`     | Verifi payload does not provide valid BIN/CAID or ARNs   |
| `ETHOCA_ALERT_ENROLLMENT_ERROR` | Upstream Ethoca enrolment validation failed              |
| `VERIFI_RDR_ENROLLMENT_ERROR`   | Upstream Verifi enrolment validation failed              |


---

# Agent Instructions: 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:

```
GET https://docs.chargebackstop.com/developer/api-documentation/enrolments.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
