---
title: "Take a cycler out of service for planned work"
method: POST
path: "/cyclers/{cycler_id}/service_events"
tags: ["Cyclers"]
---

# Take a cycler out of service for planned work

`POST /cyclers/{cycler_id}/service_events`

Open a service event, taking every channel on the cycler out of service.

One call replaces taking each channel out individually: calibration is
performed on the instrument, so the whole cycler goes down as one event and
comes back as one. Channels already out of service keep their existing
incident (a broken relay is more specific than "being serviced").

Rejected with 409 if the cycler already has an open event, or if any channel
still has a running measurement.

## Path parameters

- `cycler_id` string, required

## Request body

- object

## Response `201`

Successful Response

- CyclerServiceEvent — One instrument-level service on a cycler. A service event is the thing a channel incident belongs to when the whole cycler is down. Calibration is performed on the *instrument*, so a 40-channel cycler going in for its annual calibration is one event, not 40 unrelated outages that happen to share a category. Channel incidents opened for the event carry its id in ``service_event_id``, which is what lets one action close them together. At most one event per cycler may be open (``performed_at`` null) — a unique partial index enforces it, so "the current service" is a single unambiguous row and the open-event write path is safe to retry.
  - `id` string, required — Unique identifier for the service event
  - `organization_id` string, required — Organization this service event belongs to.
  - `project_id` string, required — Project that owns the cycler (pinned to it by FK)
  - `cycler_id` string, required — The cycler being serviced
  - `event_type` 'calibration' | 'preventive_maintenance' | 'firmware' | 'repair' | 'other', required — What kind of work took (or will take) a cycler out of service. Mirrors the ``cycler_service_events_shape_check`` CHECK constraint. Stored as CHECK-constrained text rather than a Postgres enum so the allowed set can be widened by swapping a constraint — ``ALTER TYPE ... ADD VALUE`` does not compose with the additive-migration rule. ``calibration`` is the one that feeds ``cyclers.last_calibrated_at``: completing an event of this type is what advances the calibration clock.
  - `scheduled_for` string, date-time, nullable — When the visit is booked. Required while the event is open; null is allowed on an event recorded after the fact.
  - `scheduled_until` string, date-time, nullable — When the booked visit is expected to end, making the booking a span rather than a start instant. Null on rows predating the column and on events recorded after the fact. A plan, not a constraint: a visit may legitimately run past it.
  - `performed_at` string, date-time, nullable — When the service was actually done. Null means the event is still open — at most one such event per cycler.
  - `notes` string, nullable — Free-text detail about the service
  - `created_by` string, nullable — User who opened or recorded the event
  - `performed_by` string, nullable — User who recorded the service as done
  - `created_at` string, date-time, required — Timestamp when the event row was created
  - `updated_at` string, date-time, required — Timestamp when the event row was last updated

## Other responses

- `422` — Validation Error

---

[API](https://skmtc.dev/ionworks/apis/fastapi.md) · [All operations](https://skmtc.dev/ionworks/apis/fastapi/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/ionworks/fastapi/revisions/7337a3cbdaf2/schema)
