Incidentio Cli

by paymogf64ee4f1e477No licenseListed Oct 8, 2026Updated Oct 8, 2026Repository updated 2 months ago

Invoke the `incidentio` CLI to drive the incident.io API — incidents, actions, follow-ups, alerts/alert sources/routes, escalations & on-call schedules, catalog (types/entries/resources), custom fields, severities, incident types/roles/statuses/timestamps, status pages (including creating and managing public pages, components, layout, subscribers, templates), workflows, users, teams, API keys, heartbeats, maintenance windows, and settings. Uses the public Bearer API (OpenAPI-generated commands) plus internal dashboard (cookie) commands generated from captured HARs, hand-curated internal endpoints, and a `raw` escape hatch for any un-codified path. Use whenever a task needs incident.io data or actions, such as "list our incidents", "create an incident", "show the on-call schedule", "build or manage a status page", "list status page subscribers", "tune a dashboard setting", or "hit an internal dashboard endpoint".

Instructions onlyDevOps & Cloud
AI-generated overview

Drives the incident.io API through the incidentio CLI to manage incidents, on-call schedules, status pages and settings.

What it does
This skill instructs an agent to call the incidentio command-line tool, which wraps incident.io's public Bearer-token API plus internal dashboard endpoints. It covers incidents, actions, follow-ups, alerts and alert routes, escalations and on-call schedules, the service catalog, custom fields, severities, status pages, workflows, users, teams, secrets and housekeeping commands. Commands return pretty-printed JSON, and a raw escape hatch can hit any endpoint for probing or reverse-engineering.
When to use it
Use it when a task needs incident.io data or actions, such as listing or creating incidents, checking an on-call schedule, building or managing a status page, or tuning dashboard settings. It also fits probing or codifying incident.io endpoints that are not yet wrapped.
Requirements
The incidentio binary must be installed and reachable, with network access to api.incident.io and app.incident.io. A Bearer API key is required, supplied via the --api-key flag, the INCIDENT_API_KEY environment variable, or stored credentials; internal dashboard commands additionally need an imported browser session cookie and an organisation id. No scripts ship with the skill.

incident.io CLI

Invoke the incidentio binary. Source of truth: paymog/incidentio-cli.

Unlike blacksmith's cookie-replay, incident.io has a real public API keyed by Bearer tokens. incidentio speaks it directly — no browser, no cookies. Commands are generated from incident.io's official OpenAPI specs (one per resource tag), so every documented endpoint is available. The API base is https://api.incident.io.

Auth (required before any command)

A Bearer API key. Create one at Settings → API keys (app.incident.io/settings/api-keys); when creating it you choose its scopes (e.g. incidents.create), and the scope set is fixed after creation.

sh
incidentio auth set <api-key>     # store it (chmod 600, ~/.config/incidentio/creds.json)incidentio auth status            # show masked key + last-updatedincidentio auth logout            # clear stored credentials

Resolution order: --api-key <key> flag → $INCIDENT_API_KEY → stored credential. For a one-off or CI, export INCIDENT_API_KEY and skip auth set.

A 401/403 means the key is invalid, revoked, or lacks the scope the endpoint needs (e.g. calling incidents create with a read-only key). Re-check the key's scopes in the dashboard.

Usage

sh
incidentio list [filter]         # every command (optionally filtered by substring)incidentio list incidents        # all verbs for the `incidents` resourceincidentio <resource> <verb> [flags]

Commands are two tokens: <resource> <verb>. Output is pretty-printed JSON — pipe to jq, or pass --raw for the unformatted response.

Flags

FlagMeaning
--api-key <key>API key for this call (else $INCIDENT_API_KEY or stored)
--<param> <value>path params: --id, --user-id, --schedule-id, --alert-source-config-id
--query key=valuequery param, repeatable (incl. bracket filters — see below)
--body-file <path>JSON request body from file (for POST/PUT)
--body-json '<json>'inline JSON request body
--set a.b=valueset a body field, repeatable
--rawprint the raw response, no JSON formatting
--auth cookie|bearer(raw only) force the auth mode; otherwise inferred from the path

Bracket query filters

List endpoints use bracket keys (Rails-style). Pass them verbatim to --query; repeat for multi-value filters. incident.io's list filters are powerful but the documented param names appear in incidentio list <resource> output.

sh
# incidents in the "live" status category, severity rank >= a given severityincidentio incidents list --query 'status_category[one_of]=live' --query 'severity[gte]=<sev-id>'# created within a date range (tilde-separated)incidentio incidents list --query 'created_at[date_range]=2026-06-01~2026-06-30'# any-of multiple modesincidentio incidents list --query 'mode[one_of]=standard' --query 'mode[one_of]=retrospective'

Dates are ISO-8601 UTC (2026-06-04T00:00:00.000Z). For "now" compute it: date -u +%FT%T.000Z (macOS) or date -u -d '30 days ago' +%FT%T.000Z (GNU).

Command surface

Run incidentio list for the authoritative set (~324 commands: ~179 public Bearer-API commands across 53 resources, plus ~145 internal/dashboard commands marked 🍪, and the raw escape hatch). Grouped highlights (GET unless noted):

Incidents

sh
incidentio incidents list [--query status[one_of]=<id>] [--query page_size=50]incidentio incidents show --id <id>incidentio incidents create --body-json '{"name":"...","severity_id":"...","visibility":"public"}'incidentio incidents edit --id <id>            # edit fields/status of an incidentincidentio incidents import-postmortem-document --id <id>

Actions, follow-ups, attachments

sh
incidentio actions list --query incident_id=<id>incidentio follow-ups list --query incident_id=<id>incidentio follow-ups create --body-json '{"incident_id":"...","content":"..."}'incidentio follow-ups connect-external-issue --id <id> --body-json '{...}'incidentio incident-attachments list --query incident_id=<id>

Alerts & alert sources

sh
incidentio alerts listincidentio alerts show --id <id>incidentio alerts resolve --id <id>incidentio alert-sources listincidentio alert-routes listincidentio alert-routes show --id <id>incidentio alert-routes update --id <id> --body-json '{...}'   # public API, no version fieldincidentio alert-events create-http --alert-source-config-id <id> --body-json '{...}'incidentio heartbeat ping --alert-source-config-id <id>   # GET ping
# Alert route with incident-template custom-field binding (🍪 dashboard API)# Rules:#   1. version = current_version + 1 (optimistic concurrency — GET it first).#   2. OMIT the `users` key from every escalation_config.escalation_targets entry;#      the GET payload carries an invalid users binding that PUT rejects. API restores it.#   3. Custom-field bindings: static option OR navigation-expression reference.#      Static:     array_value:[{reference:"",value:"<opt-id>",label:"<label>",sort_key:0}]#      Expression: array_value:[{reference:"expressions[\"<expr-ref>\"]"}]#   4. Navigation expressions (derive component array from Service catalog attribute):#      Declare in top-level `expressions` array; bind by reference in custom_fields.# merge_strategy: "first-wins" | "last-wins" | "append"incidentio alert-routes show-route --id <route-id>incidentio alert-routes update-route --id <route-id> --body-json '{  "version":4,  "escalation_config":{"escalation_targets":[    {"type":"schedule","id":"<sched-id>"}  ]},  "incident_template":{    "custom_fields":[{      "custom_field_id":"<field-id>",      "merge_strategy":"first-wins",      "binding":{"array_value":[{"reference":"","value":"<option-id>","label":"<label>","sort_key":0}]}    }]  }}'# With a catalog navigation expression (auto-derive Affected Components from alert Service):incidentio alert-routes update-route-expr --id <route-id> --body-json '{  "version":5,  "expressions":[{    "id":"01EXPR001","label":"Affected Components","reference":"affected_components",    "returns":{"type":"CatalogEntry[\"<component-type-id>\"]","array":true},    "root_reference":"alert.attributes.<service-alert-attr-id>",    "operations":[{"operation_type":"navigate",      "returns":{"type":"CatalogEntry[\"<component-type-id>\"]","array":true},      "navigate":{"reference":"catalog_attribute[\"components\"]","reference_label":"Components"}}]  }],  "incident_template":{    "custom_fields":[{      "custom_field_id":"<affected-components-field-id>",      "merge_strategy":"first-wins",      "binding":{"array_value":[{"reference":"expressions[\"affected_components\"]"}]}    }]  }}'

Escalations & on-call schedules

sh
incidentio escalations list [--query status=active]incidentio escalations create --body-json '{...}'incidentio escalations cancel-escalation --id <id>incidentio escalation-paths listincidentio schedules listincidentio schedules show --id <id>incidentio schedule-entries list --schedule-id <id>incidentio schedule-overrides list --schedule-id <id>incidentio schedule-replicas list --schedule-id <id>

Catalog (service catalog as code)

sh
# Public API (Bearer)incidentio catalog-types listincidentio catalog-types show --id <type-id>            # includes .schema.attributes[].idincidentio catalog-types create --body-json '{...}'incidentio catalog-types update-type-schema --id <id> --body-json '{...}'incidentio catalog-entries list --query catalog_type_id=<id>incidentio catalog-entries create --body-json '{...}'   # simple entriesincidentio catalog-entries update --id <id> --body-json '{...}'incidentio catalog-entries bulk-update-entries --body-json '{...}'incidentio catalog-resources list
# Catalog entries with attribute_values (🍪 dashboard API — verified contract)# Required body: catalog_type_id, name, external_id, attribute_values (pass {} when none)# Attribute shapes:#   plain text:       {"<attr-id>":{"value":"some text"}}#   catalog-relation: {"<attr-id>":{"array_value":["<entry-id>",...]}}#   intentional clear (create only): {"<attr-id>":{"value":null}}# Get attribute IDs: incidentio catalog-types show --id <type-id> | jq '.catalog_type.schema.attributes[].id'incidentio catalog-entries create-entry --body-json '{  "catalog_type_id":"<type-id>",  "name":"My Service",  "external_id":"my-service",  "attribute_values":{    "<attr-id>":{"value":"production"},    "<rel-attr-id>":{"array_value":["<related-entry-id>"]}  }}'incidentio catalog-entries update-entry --id <entry-id> --body-json '{  "name":"My Service",  "attribute_values":{"<attr-id>":{"value":"staging"}}}'

Config (custom fields, severities, types, roles, statuses, timestamps)

sh
# Public API (Bearer)incidentio custom-fields listincidentio custom-fields create --body-json '{...}'      # basic fieldsincidentio custom-field-options list --query custom_field_id=<id>incidentio severities listincidentio incident-types listincidentio incident-roles listincidentio incident-statuses listincidentio incident-timestamps list
# Catalog-backed multi-select custom field (🍪 dashboard API — supports catalog_type_id,# field_mode, condition_groups not available in the public API)# Required body: name, description, field_type:"multi_select", catalog_type_id, field_mode,#   dynamic_options, cannot_be_unset, options, condition_groupsincidentio custom-fields create-catalog-backed --body-json '{  "name":"Affected Services",  "description":"Which services are affected by this incident",  "field_type":"multi_select",  "catalog_type_id":"<catalog-type-id>",  "field_mode":"dashboard",  "dynamic_options":true,  "cannot_be_unset":false,  "options":[],  "condition_groups":[]}'

Status pages

sh
# Read (Bearer public API)incidentio status-pages listincidentio status-pages show --status-page-id <id>          # includes current_structureincidentio status-page-incidents list --query status_page_id=<id>incidentio status-page-maintenances list --query status_page_id=<id>
# Manage the page itself (🍪 internal — the public API cannot create pages/components)# Simple page:incidentio status-pages create --body-json '{"name":"Acme","subpath":"acme","theme":"light"}'# Catalog-backed parent page with auto-generated sub-pages per catalog entry:incidentio status-pages create --body-json '{  "name":"Acme","subpath":"acme","theme":"light",  "parent_page_options":{    "page_type":"parent",    "split_by_catalog_type_id":"<catalog-type-id>",    "split_by_component_attribute_id":"<component-attr-id>",    "sub_pages":[      {"defined_by_catalog_entry_id":"<entry-id>","name":"Team A","subpath":"team-a"}    ]  }}'# Update — name, subpath, support_label are ALL required even for a single-field change:incidentio status-pages update --status-page-id <id> --body-json '{"name":"Acme","subpath":"acme","support_label":"Report a problem","allow_search_engine_indexing":false}'incidentio status-page-components create --body-json '{"name":"API","status_page_id":"<id>"}'incidentio status-page-components delete --id <component-id>incidentio status-page-structures create --body-json '{"status_page_id":"<id>","items":[  {"group":{"name":"Core","display_aggregated_uptime":true,"hidden":false,"components":[    {"component_id":"<id>","display_uptime":true,"hidden":false}]}},  {"component":{"component_id":"<id>","display_uptime":true,"hidden":false}}]}'
# Audit subscribers / templates (🍪)incidentio status-page-subscriptions --query status_page_id=<id>incidentio status-page-templates --query status_page_id=<id>
# Retrospective status-page incident (Bearer) — bulk-import historical incidentsincidentio status-page-incidents create-status-page-retrospective-incident --body-json '{  "status_page_id":"<id>",  "name":"Elevated API latency",  "idempotency_key":"historical-2021-08-17",  "updates":[    {"incident_status":"investigating","message":"Looking into it.","published_at":"2021-08-17T13:28:57Z"},    {"incident_status":"resolved","message":"Fixed.","published_at":"2021-08-17T14:00:00Z",     "component_statuses":[{"component_id":"<id>","component_status":"operational"}]}  ]}'

Notes: creating/branding a page and defining its components/layout is internal-only (cookie session); the public Bearer API only lists/shows and publishes incidents/maintenance. Public-page components are page-native objects (create them, then place them with status-page-structures), not a custom field (that model is for internal pages). theme is light|dark. Team plan allows one public page (a second create returns 422 exceeded your allowance). Logo/favicon/brand color are uploads done in the dashboard.

For catalog-backed parent pages: split_by_catalog_type_id and split_by_component_attribute_id identify which catalog type backs the sub-pages and which attribute on that type points to components; each sub_pages entry maps a catalog entry to a sub-page slug.

Users, teams, API keys, workflows, secrets

sh
incidentio users list --query email=<email>incidentio users show --id <id>incidentio teams listincidentio api-keys listincidentio api-keys rotate --id <id># api-keys create/update accept optional `comments` string
# Workflows (public Bearer CRUD)incidentio workflows listincidentio workflows show --id <id>incidentio workflows create --body-json '{...}'   # public shape — see OpenAPIincidentio workflows update --id <id> --body-json '{...}'
# Secrets store (public Bearer — prefer this for scripting)# Create: {name, value, description?, owning_team_ids?}# Rotate: {value} — bumps version; value never returned (only last_four_chars)incidentio secrets list [--query team_ids=<id>]incidentio secrets show --id <id>                 # includes versions[] historyincidentio secrets create --body-json '{"name":"pagerduty_token","value":"..."}'incidentio secrets rotate --id <id> --body-json '{"value":"new-secret"}'incidentio secrets update --id <id> --body-json '{"name":"pagerduty_token"}'incidentio secrets delete --id <id>

Housekeeping

sh
incidentio utilities identity              # validate the key + show the identityincidentio maintenancewindows listincidentio ipallowlists showincidentio telemetry update --id <id> --body-json '{...}'

Dashboard / internal API (🍪 — needs a browser session)

These hit app.incident.io/api/*, which rejects API keys ("Cannot use API keys to authenticate to internal APIs"). They replay a logged-in browser session. Import one first:

sh
# devtools → Network → right-click any /api/ request → Copy as cURLincidentio auth import '<paste curl>'        # or: pbpaste | incidentio auth importincidentio auth set-org 01G9XY4BZ7YGBPJ3K50NB30YXS   # x-incident-organisation-id (auto-captured from the curl if present)

Then commands marked 🍪 in list work. Highlights — things the public API can't do:

sh
incidentio saved-views --query context=incidents     # saved filter viewsincidentio insights trends --query start_date=2026-06-01 --query end_date=2026-06-30incidentio insights custom-dashboardsincidentio policies                                 # policy listincidentio policies update --id <id> --body-json '{...}'  # flip run_on_private_incidents etc.incidentio secrets list-internal                  # cookie twin of public secrets.*incidentio workflows triggers                     # alert.updated|attached, scheduled, ...incidentio workflows show-internal --id <id>      # expands webhook.send signing paramsincidentio workflows create-internal --body-json '{  "trigger":"alert.updated",  "workflow":{"name":"...","once_for":["alert"],"condition_groups":[],"steps":[],"expressions":[],    "runs_on_incident_modes":["standard"],"continue_on_step_error":false,    "runs_on_incidents":"newly_created","state":"draft","private_incident_scope":"none"}}'incidentio policy-violationsincidentio incident-timelines timeline --incident-timeline <id>            # full timelineincidentio incident-timelines activity-log --incident-timeline <id>        # activity logincidentio debriefs incident-debriefs --query incident_id=<id>incidentio incident-suggestions for-incident --query incident_id=<id>      # AI suggestionsincidentio postmortems templates --query incident_id=<id>incidentio schedule-reportsincidentio user-preferencesincidentio identity self                  # who am I + scopes (dashboard identity)

A 401 "No authorization material" on a 🍪 command means the session cookie isn't being recognized (wrong/expired) — re-import a fresh Copy-as-cURL. Org id resolves --org → $INCIDENT_ORG_ID → stored.

Raw requests & reverse-engineering new endpoints

Not every endpoint is codified. incidentio raw <METHOD> </path> hits any endpoint with your stored creds — the fast path for probing and reverse-engineering internal routes:

sh
incidentio raw GET  /api/status_pages                    # cookie inferred (/api/*)incidentio raw GET  /v2/incidents --query page_size=1    # bearer inferred (else)incidentio raw POST /api/status_pages --body-json '{}'   # probe: 422 names required fieldsincidentio raw PUT  /api/settings/self --body-json '{...}'   # tune a settingincidentio raw DELETE /api/status_pages/<id> --auth cookie

Auth is inferred (/api/* → cookie, otherwise bearer); override with --auth cookie|bearer. Inline any IDs directly in the path (raw does no :param substitution).

Codify a new endpoint (recipe):

  1. Probe with an empty/partial body: incidentio raw POST /api/<thing> --body-json '{}'.
  2. Read the 422 validation_error — the source.field / message names the required fields; retry with an intentionally invalid enum value to learn allowed values.
  3. Add a Command to src/commands/manual-internal.ts (it's merged at load time and survives HAR regeneration), then bun run build.

Recipes

Validate your API key

sh
incidentio utilities identity | jq '.identity'

Open incidents, newest first

sh
incidentio incidents list --query 'status_category[one_of]=live' \  | jq '.incidents[] | {id, name, severity: .severity.name, status: .incident_status.name}'

Page through a list (cursor pagination)

list responses include a pagination_meta.after cursor; pass it back as --query after=<cursor>:

sh
incidentio incidents list --query page_size=250 > first.jsonAFTER=$(jq -r '.pagination_meta.after // empty' first.json)[ -n "$AFTER" ] && incidentio incidents list --query page_size=250 --query "after=$AFTER" > second.json

Declare an incident

sh
incidentio incidents create --body-json '{  "name": "API 5xx spike",  "severity_id": "<sev-id>",  "summary": "Elevated 5xx from the edge.",  "visibility": "public"}' | jq '.incident.id'

Need the severity/status IDs first? incidentio severities list / incidentio incident-statuses list.

Which verbs does a resource have?

sh
incidentio list schedules      # shows list/create/show/update/delete + nested schedule-entries/overrides/replicasincidentio list catalog-entries   # shows create/create-entry (🍪)/update/update-entry (🍪)/...

Create a catalog entry with component relations (🍪)

sh
# 1. Get the catalog type's attribute IDsincidentio catalog-types show --id <type-id> | jq '.catalog_type.schema.attributes[] | {id, name}'
# 2. Create an entry with attribute valuesincidentio catalog-entries create-entry --body-json '{  "catalog_type_id":"<type-id>",  "name":"payments-service",  "external_id":"payments-service",  "attribute_values":{    "<team-attr-id>":{"array_value":["<team-catalog-entry-id>"]},    "<tier-attr-id>":{"value":"tier-1"}  }}'

Update an alert route to bind a custom field (🍪)

sh
# 1. GET current route — capture version and escalation_targets (you'll need to strip `users`)VERSION=$(incidentio alert-routes show-route --id <route-id> | jq '.version')
# 2. PUT with version+1. Two critical rules:#    a) version = $VERSION + 1 (optimistic concurrency)#    b) omit `users` from every escalation_config.escalation_targets entry#       (the GET payload has an invalid users binding the PUT rejects; API restores it after PUT)incidentio alert-routes update-route --id <route-id> --body-json '{  "version":'$((VERSION+1))',  "name":"My Route",  "escalation_config":{"escalation_targets":[    {"type":"schedule","id":"<sched-id>"}  ]},  "incident_template":{    "custom_fields":[{      "custom_field_id":"<field-id>",      "merge_strategy":"first-wins",      "binding":{"array_value":[{"reference":"","value":"<option-id>","label":"P1 - Critical","sort_key":0}]}    }]  }}'

Derive Affected Components from alert Service via catalog navigation expression (🍪)

A navigation expression lets an alert route auto-populate a catalog-backed custom field by navigating from an alert attribute (e.g. Service) through a catalog relationship (e.g. Components).

sh
# 1. Find the IDs you need:#    - service-alert-attr-id: the alert source attribute ID for the Service field#    - component-type-id: catalog type ID for Components (incidentio catalog-types list)#    - affected-components-field-id: the incident custom field ID (incidentio custom-fields list)VERSION=$(incidentio alert-routes show-route --id <route-id> | jq '.version')
# 2. PUT the route with an expression + expression-reference binding.#    Same rules: version+1, omit users from escalation_targets.incidentio alert-routes update-route-expr --id <route-id> --body-json '{  "version":'$((VERSION+1))',  "escalation_config":{"escalation_targets":[{"type":"schedule","id":"<sched-id>"}]},  "expressions":[{    "id":"<expr-id>",    "label":"Affected Components",    "reference":"affected_components",    "returns":{"type":"CatalogEntry[\"<component-type-id>\"]","array":true},    "root_reference":"alert.attributes.<service-alert-attr-id>",    "operations":[{      "operation_type":"navigate",      "returns":{"type":"CatalogEntry[\"<component-type-id>\"]","array":true},      "navigate":{"reference":"catalog_attribute[\"components\"]","reference_label":"Components"}    }]  }],  "incident_template":{    "custom_fields":[{      "custom_field_id":"<affected-components-field-id>",      "merge_strategy":"first-wins",      "binding":{"array_value":[{"reference":"expressions[\"affected_components\"]"}]}    }]  }}'# After a successful PUT the API echoes the expressions array back intact.

Notes:

  • root_reference points at the alert source attribute that holds a Service catalog entry.
  • operations[0].navigate.reference is the catalog attribute name on the Service type that holds its component entries (verify via incidentio catalog-types show --id <service-type-id>).
  • The expression reference value becomes the key in expressions["<reference>"] binding.
  • The Affected Components custom field must be a catalog-backed multi_select created via custom-fields create-catalog-backed (dashboard internal API) targeting the Component catalog type.

Secrets, signed webhooks, alert-triggered workflows

sh
# List triggers (🍪) — includes alert.updated, alert.attached, scheduledincidentio workflows triggers | jq '.triggers[] | select(.name|test("alert|scheduled"))'
# Create an alert-triggered workflow (🍪). NOTE the split body: top-level `trigger`# string + nested `workflow` object. Public `workflows create` uses a flatter shape.incidentio workflows create-internal --body-json '{  "trigger":"alert.updated",  "workflow":{    "name":"Alert resolved webhook",    "once_for":["alert"],    "condition_groups":[],    "steps":[{      "id":"step1",      "name":"webhook.send",      "param_bindings":[        {"value":{"literal":"https://example.com/hook"}},        {"value":{"literal":"POST"}},        {"array_value":[{"literal":"Authorization: Bearer {{secrets.my_token}}"}]},        {"value":{"literal":"{\"ok\":true}"}},        {"value":{"literal":"<secret-id>"}},        {"value":{"literal":""}},        {"value":{"literal":"X-Signature"}}      ]    }],    "expressions":[],    "runs_on_incident_modes":["standard"],    "continue_on_step_error":false,    "runs_on_incidents":"newly_created",    "state":"draft",    "private_incident_scope":"none"  }}'
# webhook.send param order (from show-internal): endpoint, method, headers# (TemplatedText plain_single_line_with_secrets), body, signing_secret (type Secret),# generated_signing_secret, signature_header_name (HMAC-SHA256).# Prefer public `secrets create` for the signing secret when you have a Bearer key.

Opt a policy into private incidents (🍪)

sh
# GET first — subjects/operations come back as expanded objectsincidentio policies | jq '.policies[] | {id,name,run_on_private_incidents}'
# PUT write shape differs: subject/operation are bare strings; follow_up needs due_date_configincidentio policies update --id <id> --body-json '{  "enabled":true,  "name":"...",  "description":"...",  "policy_type":"follow_up",  "conditions":[{"conditions":[{"subject":"incident.severity","operation":"gte",    "param_bindings":[{"value":{"literal":"<sev-id>"}}]}]}],  "requirements":{"conditions":[{"conditions":[{"subject":"follow_up.status","operation":"not_one_of",    "param_bindings":[{"array_value":[{"literal":"outstanding"}]}]}]}]},  "run_on_private_incidents":true,  "due_date_config":{"incident_timestamp_id":"<ts-id>","days":{"value":{"literal":"30"}},    "calculation_type":"seven_days"}}'

Regenerate the command catalog

Commands live in src/commands/generated.ts, generated from incident.io's per-tag OpenAPI specs (fetched live from docs.incident.io):

sh
bun run codegen

The generator discovers the REST tag set from docs.incident.io/llms.txt, fetches each /openapi/tags/<tag>.json, derives a <resource> <verb> name per operation, and resolves collisions (e.g. catalog-types update-type vs update-type-schema; heartbeat ping vs ping-post). After regenerating, rebuild (bun run build).

Dashboard/internal commands are generated from captured browser HAR(s) via bun run codegen:har <a.har> [b.har ...]. It harvests GET and write endpoints (POST→create, PUT/PATCH→update, DELETE→delete), templates ULID/Slack IDs to :param, drops anything the public Bearer API already covers, and overwrites generated-internal.ts — so pass every HAR you want represented in a single invocation (e.g. app.incident.io.har app.incident.io2.har app.incident.io3.har). Hand-verified internal endpoints that appear in no HAR (e.g. status-page create/update/components/structures) live in src/commands/manual-internal.ts and are merged at load time, so they survive regeneration.

Common issues

not authenticated

No key found via flag, env, or store. Run incidentio auth set <key> or export INCIDENT_API_KEY=<key>.

HTTP 401 / HTTP 403 (public/Bearer commands)

The key is invalid/expired, or lacks the scope the endpoint requires (e.g. a read-only key calling a write verb). Check the key's scopes in Settings → API keys; scopes are fixed at creation — rotate or create a new key if you need more.

needs a browser session / 401 "No authorization material" (🍪 dashboard commands)

Dashboard commands (app.incident.io/api/*) reject API keys. They need a logged-in browser session: re-import via incidentio auth import <curl> (Copy-as-cURL from app.incident.io), and ensure the org id is set (auth set-org or --org). HARs from Chrome/Brave usually strip cookies — use Copy-as-cURL.

HTTP 422 with a validation message

The body/query is the wrong shape (missing required field, bad enum, wrong type). The error body names the offending source.field — fix the --body-json/--query/--set value.

HTTP 429

Rate limit (default 1200 req/min/key). The error body includes rate_limit.retry_after. Back off and retry; don't hammer.

unknown command

Commands are <resource> <verb>. Run incidentio list <resource> to see the exact verbs. If you typed just the resource, the CLI suggests its verbs. CRUD verbs are collapsed (incidents list/create/show/update/delete); non-CRUD actions keep their name (incidents edit, escalations cancel-escalation, catalog-entries bulk-update-entries).

Missing endpoint

The endpoint isn't in the generated catalog. Re-run bun run codegen to pick up newly published incident.io endpoints, then rebuild.

Source and attribution

Source:paymog/incidentio-cliinskills/incidentio-cliat commitf64ee4f

License: No license

Content belongs to its original authors. SourceWeft indexes it from a public repository.

Report or request removal