> ## 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.

# Record a cycler's open service as done

> Record service as done on a cycler.

Two paths behind one endpoint, chosen by whether the cycler is currently out
of service:

- **Open event** (it was taken out first): that event is completed and every
  channel incident pointing at it is closed in one write, so the cycler
  cannot end up half returned to service.
- **No open event**: the common case of "I calibrated this, log it". A
  single already-completed event is recorded from the request's
  ``event_type``. Nothing was taken out, so nothing needs returning, and no
  one has to open an event purely so it can be closed.

Either way a ``calibration`` advances ``last_calibrated_at`` — the only
supported way that field moves — and optionally updates
``calibration_interval_days``.

Addressed by cycler rather than by event id because a cycler has at most one
open event: the caller already knows which cycler it serviced, and looking
up the event id first would be a round-trip that proves nothing.



## OpenAPI

````yaml https://api.ionworks.com/openapi.json post /cyclers/{cycler_id}/service_events/complete
openapi: 3.1.0
info:
  title: FastAPI
  version: 0.1.0
servers:
  - url: https://api.ionworks.com
    description: Production
security: []
paths:
  /cyclers/{cycler_id}/service_events/complete:
    post:
      tags:
        - Cyclers
      summary: Record a cycler's open service as done
      description: >-
        Record service as done on a cycler.


        Two paths behind one endpoint, chosen by whether the cycler is currently
        out

        of service:


        - **Open event** (it was taken out first): that event is completed and
        every
          channel incident pointing at it is closed in one write, so the cycler
          cannot end up half returned to service.
        - **No open event**: the common case of "I calibrated this, log it". A
          single already-completed event is recorded from the request's
          ``event_type``. Nothing was taken out, so nothing needs returning, and no
          one has to open an event purely so it can be closed.

        Either way a ``calibration`` advances ``last_calibrated_at`` — the only

        supported way that field moves — and optionally updates

        ``calibration_interval_days``.


        Addressed by cycler rather than by event id because a cycler has at most
        one

        open event: the caller already knows which cycler it serviced, and
        looking

        up the event id first would be a round-trip that proves nothing.
      operationId: >-
        complete_cycler_service_event_cyclers__cycler_id__service_events_complete_post
      parameters:
        - name: cycler_id
          in: path
          required: true
          schema:
            type: string
            title: Cycler Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
              title: Raw Data
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CyclerServiceEvent'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
components:
  schemas:
    CyclerServiceEvent:
      properties:
        id:
          type: string
          title: Id
          description: Unique identifier for the service event
        organization_id:
          type: string
          title: Organization Id
          description: Organization this service event belongs to.
        project_id:
          type: string
          title: Project Id
          description: Project that owns the cycler (pinned to it by FK)
        cycler_id:
          type: string
          title: Cycler Id
          description: The cycler being serviced
        event_type:
          $ref: '#/components/schemas/ServiceEventType'
          description: What kind of work this is
        scheduled_for:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
          title: Scheduled For
          description: >-
            When the visit is booked. Required while the event is open; null is
            allowed on an event recorded after the fact.
        performed_at:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
          title: Performed At
          description: >-
            When the service was actually done. Null means the event is still
            open — at most one such event per cycler.
        notes:
          anyOf:
            - type: string
            - type: 'null'
          title: Notes
          description: Free-text detail about the service
        created_by:
          anyOf:
            - type: string
            - type: 'null'
          title: Created By
          description: User who opened or recorded the event
        performed_by:
          anyOf:
            - type: string
            - type: 'null'
          title: Performed By
          description: User who recorded the service as done
        created_at:
          type: string
          format: date-time
          title: Created At
          description: Timestamp when the event row was created
        updated_at:
          type: string
          format: date-time
          title: Updated At
          description: Timestamp when the event row was last updated
      type: object
      required:
        - id
        - organization_id
        - project_id
        - cycler_id
        - event_type
        - created_at
        - updated_at
      title: CyclerServiceEvent
      description: >-
        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.
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    ServiceEventType:
      type: string
      enum:
        - calibration
        - preventive_maintenance
        - firmware
        - repair
        - other
      title: ServiceEventType
      description: >-
        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.
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
              - type: string
              - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
        input:
          title: Input
        ctx:
          type: object
          title: Context
      type: object
      required:
        - loc
        - msg
        - type
      title: ValidationError

````