---
title: "Off, read-only and maintenance"
url: /runtime/switches
---

# Off, read-only and maintenance

> Take a Verb, a Noun or the whole product offline or read-only during an incident, or on a maintenance window, and tell every caller when to come back.

A [switch](/runtime/switches.md) takes a Verb, a Noun or the product offline or read-only within seconds, until a person clears it.

```mermaid title="A switch, from on to on again"
stateDiagram-v2
  [*] --> on: every deploy
  on --> read_only: a window opens
  on --> off: Switch.set
  read_only --> on: it ends
  off --> on: cleared
```

## During an incident
1. Call `Switch.set` with a scope, a mode and a reason: `{ "scope": "Deal.win", "mode": "off", "reason": "a bad payout rule" }`.
2. Every edge refuses that Verb within 10 seconds, on every [Interface](/every-product.md) at once. The rest of the product keeps answering.
3. The status page, the offer card and each Customer's portal show the switch and its reason. A Watch on `Switch.set` tells each Customer's agent.
4. Work already running holds at its next step and keeps its place and its charge.
5. Clear the switch with `Switch.set` and `"mode": "on"`. Held work resumes where it stopped.

A member with the admin role sets and clears switches. Every switch is kept with who set it, when and why.

## What a caller meets
Every refusal is a [Problem](/reference.md#errors) with `Retry-After` and a `status_page` link, and costs nothing.

| Mode | Reads | Writes and paid calls | Work already running |
| --- | --- | --- | --- |
| `read_only` | Answer as before | 503 [`read_only`](/reference.md#read_only) | Holds at its next write, then resumes |
| `off` | 503 [`switched_off`](/reference.md#switched_off) | 503 `switched_off` | Holds at its next step, then resumes |

A held [Action](/workflow.md#the-lifecycle)'s service clocks and an outcome's deadline pause for as long as the switch holds it.

## A weekly window
A [maintenance window](/runtime/switches.md#windows) is a switch on a schedule, written in the file and announced `notice` ahead.

A window may read only, or switch the product off.

The sales tracker reads only for 30 minutes each week, with three days' notice.

```ts title="definition.ts"
import { define } from 'apis.dev'

/** Deals for each Customer, read-only for half an hour every Sunday night. */
export default define({
  name: 'pipeline', domain: 'pipeline.apis.dev',
  Company: { name: 'string', domain: 'url!#', deals: '<-Deal.company' },
  Deal: {
    title: 'string', amount: 'decimal(12,2)', company: '->Company.deals',
    stage: { open: [], qualify: 'opened', win: 'qualified', lose: ['opened', 'qualified'] },
  },
  offer: {
    Free: { month: 0, seats: 1, includes: { 'Deal.open': 25 } },
    Team: { month: 49, seats: 5, includes: { 'Deal.open': 1000 } },
    'Deal.open': 0.001,
  },
  operate: {
    maintenance: { every: 'every Sun 02:00', length: '30m', mode: 'read_only', notice: '72h' },
  },
})
```

Customers hear of each window in the portal, by email and through their Watches. The offer card carries the next one, so a buyer's agent plans around it.

## What a write meets in the window
A rep's write during the window waits, and the list it read a second earlier still answers.

```http title="Response"
HTTP/1.1 503 Service Unavailable
Content-Type: application/problem+json
Retry-After: 1260
X-Cost: price=0, currency="USD"
X-Request-Id: req_5e9a1c07d3b48f62e0a5

{
  "type": "https://pipeline.apis.dev/docs/errors#read_only",
  "title": "Service Unavailable",
  "status": 503,
  "detail": "Read-only for maintenance until 02:30 UTC; reads answer.",
  "code": "read_only",
  "instance": "req_5e9a1c07d3b48f62e0a5",
  "status_page": "https://pipeline.apis.dev/status"
}
```