# Handoff

## Request or return a handoff

**post** `/v1/chats/{chat}/handoff`

Requests a human takeover, or returns control to your system.

**Behavior**

- `to: "human"` moves the chat to `human_pending`; closed to your sends
  until a human accepts or control returns.
- `to: "partner"` returns control to your system.
- The event log records who asked: a request carries actor `partner`, a
  return carries actor `human`, and an operator name appears only beside
  a `human` actor.

### Path Parameters

- `chat: string`

### Body Parameters

- `to: "human" or "partner"`

  `human` requests a human (owner → `human_pending`); `partner` returns control. Anything else returns HTTP 422 `code` 1071.

  - `"human"`

  - `"partner"`

- `reason: optional string`

  Free-text reason, recorded on the ownership event (absent → empty).

### Returns

- `ok: true`

  - `true`

### Example

```http
curl https://messages.api.linqapp.com/v1/chats/$CHAT/handoff \
    -H 'Content-Type: application/json' \
    -H "Authorization: Bearer $LINQ_AMB_API_KEY" \
    -d '{
          "to": "human"
        }'
```

#### Response

```json
{
  "ok": true
}
```

## Accept a pending handoff

**post** `/v1/chats/{chat}/handoff/accept`

Take a pending handoff live (`human_pending` → `human`). Auth stays the
partner's API key: the `human` actor is honest about the role, not an
authenticated person. When the accept is made through our agent console
with a named operator, the event log records WHICH human accepted it
(`operator` on the events read).

**Behavior**

- Legal only from `human_pending`. Once applied, the chat is `human`,
  and a second accept returns HTTP 409 `code` 2025.
- Two accepts arriving together resolve to one; first writer wins, and
  the loser records nothing, so the transcript never shows an accept
  that did not take effect.
- When our console attributes the accept to a named operator, that
  operator claims the chat (`assigned_operator`); a different operator
  accepting the same entry returns HTTP 409 `code` 2009,
  naming the holder. The claim is released when the chat returns to
  your system (`POST …/handoff` with `to: "partner"`), which is also
  how a claim held by someone unreachable is cleared: return it, then
  accept it again.
- A claim does not restrict sending; the desk is shared at the brand
  level.

### Path Parameters

- `chat: string`

### Body Parameters

- `reason: optional string`

  Free-text reason, recorded on the ownership event.

### Returns

- `ok: true`

  - `true`

### Example

```http
curl https://messages.api.linqapp.com/v1/chats/$CHAT/handoff/accept \
    -X POST \
    -H "Authorization: Bearer $LINQ_AMB_API_KEY"
```

#### Response

```json
{
  "ok": true
}
```

## Domain Types

### Handoff Request Response

- `HandoffRequestResponse object { ok }`

  The bare `{"ok":true}` acknowledgement; failures ride the error envelope instead.

  - `ok: true`

    - `true`

### Handoff Accept Response

- `HandoffAcceptResponse object { ok }`

  The bare `{"ok":true}` acknowledgement; failures ride the error envelope instead.

  - `ok: true`

    - `true`
