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

# Troubleshooting: opt-out list label conflicts

> Creating or renaming an opt-out list answers 409 OPT_OUT_LIST_LABEL_CONFLICT when another active list in your tenant already holds that label. Resolve it by renaming the new list, renaming or soft-deleting the existing one — never by blindly retrying.

# Troubleshooting: opt-out list label conflicts

`POST /api/v1/messaging/opt-out-lists` and
`PATCH /api/v1/messaging/opt-out-lists/:id` enforce that list labels are
unique **among active lists** in your tenant. When the label you sent
belongs to a list that is currently active, the write fails with:

```json theme={null}
{
  "error": {
    "code": "OPT_OUT_LIST_LABEL_CONFLICT",
    "status": 409,
    "message": "label 'Acme FR — STOP/HELP' is already used by an active opt-out list in this tenant"
  },
  "meta": { "request_id": "req_8c2f11ab", "timestamp": "2026-09-20T10:14:03Z" }
}
```

<Note>
  The gate is deterministic — nothing is created, renamed, or billed — so a
  blind retry returns the same `409` every time. Resolve the label collision
  first, then re-send once.
</Note>

## When the gate fires

Two write surfaces raise `OPT_OUT_LIST_LABEL_CONFLICT`, and both collapse to
one rule: **the label you sent is in use by an active list.**

| Surface                                     | What triggers the 409                                                  |
| ------------------------------------------- | ---------------------------------------------------------------------- |
| `POST /api/v1/messaging/opt-out-lists`      | You created a list whose `label` matches an existing **active** list.  |
| `PATCH /api/v1/messaging/opt-out-lists/:id` | You renamed a list to a `label` another **active** list already holds. |

Distinguish this from the sibling codes on the same endpoints before you act:

* **403 `FORBIDDEN`** — your API key lacks the scope for opt-out-list writes.
  Fix the key's scope set; it never heals on retry.
* **409 `OPT_OUT_LIST_LABEL_CONFLICT`** — the label duplicates an active list
  (this page).
* **422 `VALIDATION_ERROR`** — the payload is malformed: labels over 200
  chars, keyword arrays over 32 entries (each 1–64 chars), response copy
  over 1600 chars, or a `default_lang` outside the supported set
  (`en, tr, de, es, fr, pt, it, ar, nl`). A soft-deleted list's label is
  free to reuse — only **active** lists participate in the check — so if the
  conflicting list was deleted, the label is available again.

## The correct path: resolve, then re-send once

Pick ONE of the three resolutions — each is a deliberate write, not a retry:

### 1. Locate the conflicting list

List your active opt-out lists (soft-deleted rows are excluded by default)
and find the row that owns the label:

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/messaging/opt-out-lists" \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

To also review soft-deleted rows — for example, to confirm whether the label
is genuinely free — pass `include_deleted=true` and inspect the `deleted`
flag on each entry:

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/messaging/opt-out-lists?include_deleted=true" \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

The list is keyset-paginated; follow `meta.pagination.next_cursor` until you
find the row.

### 2. Choose a resolution

* **Rename your new list** — the most common path. Re-send the `POST` with a
  distinct `label`. Labels are display names, so suffixing by brand, region,
  or campaign (`Acme FR — 2026Q3`) keeps them unique.
* **Rename the existing list** — `PATCH /api/v1/messaging/opt-out-lists/:id`
  on the conflicting list with a new `label`, then create yours. Do this when
  the existing list's name was the collision source (e.g. a renamed brand).
* **Retire the existing list** — if the conflicting list is genuinely unused,
  `DELETE /api/v1/messaging/opt-out-lists/:id` soft-deletes it. Soft-delete
  frees the label for reuse, and historic messaging services that referenced
  the list keep resolving it.

A soft-deleted list is **never** the blocker: the uniqueness check covers
active lists only, so a `deleted: true` row cannot produce this 409.

## Do not treat PATCH as an upsert

`PATCH` and `POST` have different contracts — confusing them is the second
most-common cause of a repeated 409:

* `POST /api/v1/messaging/opt-out-lists` **always creates** a new list. It is
  not an upsert: if the label exists on an active list, the write fails. To
  change a list you already have, `PATCH` it by id.
* `PATCH /api/v1/messaging/opt-out-lists/:id` **updates only the fields you
  send**. Keyword arrays included in the body fully replace that kind
  (`stop_keywords`, `help_keywords`, `start_keywords`); an empty array clears
  that kind. Fields you omit are untouched. `label` is optional in the patch —
  the 409 only appears when the patch actually renames the list into a
  collision.

Also note: per-list keyword arrays **widen** the trigger surface — they add
to the carrier-mandated STOP/HELP/START defaults, they never narrow them.
You cannot remove `STOP`; TCPA and CTIA require it.

## If it still refuses

1. Read `error.message` — it quotes the colliding label verbatim.
2. Confirm the resolution you chose actually landed (list active lists again).
3. If the label still conflicts and you cannot see an active list that owns
   it, open a ticket with `meta.request_id` from the refused response.

For the full error-shape reference, see
[OPT\_OUT\_LIST\_LABEL\_CONFLICT](/reference/error-codes#opt_out_list_label_conflict)
and the endpoint-level retry matrix in the
[opt-out lists endpoint reference](/api-reference/endpoints/_overlay/opt-out-lists).
