---
title: "Delete Custom Disposition"
method: DELETE
path: "/dialer/dispositions/{disposition_id}"
tags: ["Dialer"]
---

# Delete Custom Disposition

`DELETE /dialer/dispositions/{disposition_id}`

## Request

- Base URL: `https://be.graph8.com/api/v1`
- URL: `https://be.graph8.com/api/v1/dialer/dispositions/{disposition_id}`
- Auth: HTTP bearer

CONTACTS NOBODY. Deletes one of the organisation's CUSTOM dispositions.

The gap `voice_router` named as deliberately absent - "voice's own
dispositions router never exposed a delete endpoint" - which is no longer
true: it does, and this publishes it.

REFUSED WITH 409 IF ANY CALL IS ALREADY DISPOSITIONED WITH IT. So this
only ever removes an unused value - history cannot be rewritten by it, and
a 409 means "this one is in use", not "try again".

The organisation is carried in a HEADER, not the path, and voice resolves
the row with it (`_load_disposition_for_org`). It is the key's own org, so
one tenant cannot delete another's vocabulary by guessing an id.

ADMIN ONLY, and that gate is declared HERE. Voice declares
`require_org_admin()` on its own route, and for a server-to-server call
that dependency finds no bearer token and - with
`ENFORCE_VOICE_PERMISSIONS` off, which is everywhere today - ALLOWS the
request. `require_org_admin` from `developer_api.dependencies.admin` is
the one actually protecting this, and it refuses machine keys outright:
deleting shared org vocabulary needs a personal key held by an Admin or
Owner.

## Path parameters

- `disposition_id` integer, required: Custom disposition id.

## Headers

- `Authorization` string, nullable
- `X-Target-Org-Id` string, nullable

## Response `200`

Successful Response

- ApiResponseDialerPayload
  - `data` DialerPayload, required: An open envelope for one dialer result. Open because voice owns the shape of a session, a call stub and a queue stat, and it grows them without a contract bump here. A dropped field would otherwise turn an added upstream field into a removed public one.
  - `pagination` PaginationMeta
    - `page` integer, required: Current page number (1-indexed)
    - `limit` integer, required: Items per page
    - `total` integer, required: Total number of items
    - `has_next` boolean, required: Whether there are more pages
    - `next_cursor` string, nullable: Opaque cursor for the next page; null when there are no more items. Pass it back as the `cursor` query param to fetch the following page without computing page numbers.
    - `total_available` integer, nullable: Search endpoints only: how many records really match, when known. Can be larger than `total`, which is capped at the 10,000 results that can be paged. For company search this is a distinct-company estimate. Null when unknown; absent outside search.
    - `total_is_lower_bound` boolean, nullable: Search endpoints only: true when more records match than `total` shows (the result set is larger than the 10,000 results that can be paged). Absent outside search.
    - `notes` string[], nullable: Search endpoints only: plain-language notes about the result, e.g. that more records match than can be paged, or that a reversed `between` range was put in order.

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/graph8/apis/graph8-developer-api.md) · [All operations](https://skmtc.dev/graph8/apis/graph8-developer-api/llms.txt) · [OpenAPI document](https://skmtc.dev/graph8/apis/graph8-developer-api/revisions/2e7cfa0ca921?raw)
