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

# Channel service

> Take a channel out of service with a reason, and read the incident history that records every outage span

A channel that is broken, being serviced, or otherwise unusable can be marked
**out of service** so it stops appearing as available capacity in the
[Lab view](/operate/lab-view). Every transition in and out of that state is
recorded as an **incident**, giving you a per-channel downtime history.

## Marking a channel out of service

Toggle `out_of_commission` to remove a channel from the pool without deleting
it or breaking historical measurements that reference it. Every change to
`out_of_commission` also opens or closes a row in the channel's
[incident history](#channel-incident-history), so you can look back and see
when — and why — a channel was down.

Alongside `out_of_commission`, pass three optional fields to describe the
outage:

| Field               | Applies when          | Description                                                                                                                   |
| ------------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `incident_category` | Taking out of service | Cause of the outage. One of `hardware_failure`, `maintenance`, `calibration`, `decommissioned`, `other`. Defaults to `other`. |
| `incident_notes`    | Taking out of service | Free-text detail about what happened.                                                                                         |
| `resolution_notes`  | Returning to service  | What was done to bring the channel back.                                                                                      |

These three fields **only annotate a transition** in `out_of_commission` — they
are ignored on a PATCH that doesn't change the flag. Re-sending the value a
channel already has is a no-op, not a new outage.

```python theme={null}
# Take a channel out of service and record why.
client.channel.update(
    channel_id,
    {
        "out_of_commission": True,
        "incident_category": "hardware_failure",
        "incident_notes": "Cell holder cracked; awaiting replacement part",
    },
)

# Later, bring it back and record the fix.
client.channel.update(
    channel_id,
    {
        "out_of_commission": False,
        "resolution_notes": "Replaced cell holder and recalibrated",
    },
)
```

The Lab view shows the channel as `out_of_commission` regardless of any
in-flight measurement.

<Note>
  `out_of_commission` cannot be set at channel creation — the create endpoint
  rejects it with a `BAD_REQUEST`. Create the channel first, then PATCH it to
  take it out of service so the outage is recorded in the incident history.
</Note>

Trying to mark a channel out of commission while a
[time-series measurement](/data/measurements) is still open on it returns a
`CONFLICT` (HTTP 409) — close the measurement first (patch its `end_time`).

Use `incident_category` of `decommissioned` for a permanent retirement rather
than a repair-in-progress: the incident stays open indefinitely and is excluded
from repair-time statistics.

## Channel incident history

Every transition of `out_of_commission` is recorded as an **incident** — one
row per outage, opened when the channel goes down and closed when it comes
back. Use the history to audit downtime, attribute outages to a cause, or
compute per-channel mean time to repair.

At most one incident can be open per channel at a time (the current outage).
Sending `out_of_commission: true` for a channel that's already down is a no-op
— it does not fragment the existing outage into a new row.

### Incident fields

| Field              | Description                                                                                                                                                        |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `id`               | Unique identifier for the incident.                                                                                                                                |
| `channel_id`       | The channel that went out of service.                                                                                                                              |
| `category`         | Cause of the outage: `hardware_failure`, `maintenance`, `calibration`, `decommissioned`, or `other`.                                                               |
| `notes`            | Free-text detail captured when the channel went down (from `incident_notes`).                                                                                      |
| `started_at`       | When the channel went out of service.                                                                                                                              |
| `started_by`       | User who took the channel out of service (may be `null` for system actions).                                                                                       |
| `resolved_at`      | When the channel returned to service. `null` means the outage is still open.                                                                                       |
| `resolved_by`      | User who returned the channel to service.                                                                                                                          |
| `resolution_notes` | What was done to fix it.                                                                                                                                           |
| `is_estimated`     | `true` for backfilled rows whose `started_at` is derived from the channel's last-modified timestamp and is only an upper bound. Excluded from repair-time metrics. |

### Reading incident history

List a channel's incidents through the HTTP API, most recently started first:

```bash theme={null}
GET /channels/{channel_id}/incidents?limit=20&offset=0
```

```python theme={null}
# There is no dedicated incidents sub-client yet; call the endpoint directly.
# `client.get` takes the full path only, so put query parameters in the string.
history = client.get(f"/channels/{channel_id}/incidents?limit=20")

for incident in history["items"]:
    span = incident["resolved_at"] or "still open"
    print(f"{incident['started_at']} → {span}  ({incident['category']})")

print(f"{history['count']} of {history['total']} incidents shown")
```

The response is a paginated envelope (`items`, `count`, `total`). Pass `limit`
(1–100, default 20) and `offset` to page through longer histories.

<Note>
  Channels that already existed before incident history was introduced have a
  single **backfilled** row with `is_estimated: true`. Its `started_at` is the
  channel's last-modified time and is only an upper bound on when the outage
  actually began — treat those rows as "we know it was down by this time" and
  skip them when computing repair-time statistics.
</Note>

## Next steps

<CardGroup cols={2}>
  <Card title="Equipment" icon="server" href="/operate/equipment">
    Sites, cyclers, channels, and how measurements link to them.
  </Card>

  <Card title="Lab view" icon="grid-2" href="/operate/lab-view">
    Live channel occupancy for the project.
  </Card>
</CardGroup>
