---
title: "Attach or detach a case's contact"
method: PUT
path: "/contact_cases/{id}"
tags: ["Case"]
---

# Attach or detach a case's contact

`PUT /contact_cases/{id}`

Attaches the case to a specific existing Contact, or detaches it,
via a direct contact_id write (VOIP-1253). Send a non-empty
contact_id to attach; send an empty string to detach (mirrors
bin-conference-manager's pre_flow_id/post_flow_id PUT convention at
the HTTP-layer JSON-schema level -- see §5.5.1 for the precise
conversion point; a literal empty string is only ever accepted at
this outer HTTP layer, never at the internal RPC-layer uuid.UUID
struct). The target
contact_id must belong to the same customer as the case; a
cross-tenant contact_id is rejected as not found. Every
attach/detach is recorded as a case_contact_attributed/
case_contact_detached event, queryable via bin-timeline-manager's
audit log (no separate resolution history endpoint needed).

## Path parameters

- `id` string, uuid, required

## Request body

- object
  - `contact_id` string, required — The contact to attach. Empty string detaches. Deliberately NOT format: uuid (round-6 correction) -- see §5.5.1: the hand-written internal RPC struct's uuid.UUID field cannot unmarshal a literal empty string, so the HTTP-layer oapi-codegen-generated field must stay a plain string and the "" -> uuid.Nil conversion happens explicitly in the PutContactCasesId handler, mirroring PutConferencesId/pre_flow_id's existing pattern exactly.

## Response `200`

- ContactManagerCase
  - `id` string, uuid — Unique identifier for the case.
  - `customer_id` string, uuid — Unique identifier of the associated customer.
  - `peer_type` string — Remote endpoint type (e.g. "tel", "email") this case is scoped to.
  - `peer_target` string — Remote endpoint address (normalized, e.g. "+155****4567").
  - `reference_type` string — Origin channel type (e.g. "call", "conversation_message").
  - `contact_id` string, uuid — The resolved contact this case is attributed to. Nullable until resolved.
  - `owner_type` string — Type of the case owner.
  - `owner_id` string, uuid — ID of the case owner.
  - `status` 'open' | 'closed' — Case lifecycle status.
  - `opened_at` string, date-time — Timestamp when the case was opened. Nullable.
  - `closed_at` string, date-time — Timestamp when the case was closed. Nullable.
  - `closed_reason` string — Reason the case was closed (e.g. "agent_closed", "timeout").
  - `closed_by_type` string — Type of the actor that closed the case (e.g. "agent", "system").
  - `closed_by_id` string, uuid — ID of the actor that closed the case. Nullable.
  - `name` string — Optional freeform case name/title, settable only at creation time.
  - `detail` string — Optional freeform case detail, settable only at creation time.
  - `previous_case_id` string, uuid — ID of the prior (now-closed) case this case continues from, if any.
  - `tm_create` string, date-time — Timestamp when this case was created.
  - `tm_update` string, date-time — Timestamp when this case was last updated.

## Other responses

- `400` — Invalid request (INVALID_ARGUMENT).
- `401` — Authentication required (UNAUTHENTICATED).
- `403` — Insufficient permission (PERMISSION_DENIED).
- `404` — Resource not found (NOT_FOUND).
- `500` — Internal error (INTERNAL).

## Changes

- **2026-07-13** `80e2d646af2d` — 1 info
  - endpoint added
- **2026-05-30** `995c56ba9219` — 1 breaking
  - api path removed without deprecation

[Change history](https://skmtc.dev/voipbin/apis/voipbin-api/changes/contact_cases/:id/put.md)

---

[API](https://skmtc.dev/voipbin/apis/voipbin-api.md) · [All operations](https://skmtc.dev/voipbin/apis/voipbin-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/voipbin/voipbin-api/revisions/2f2bcffc5f29/schema)
