Using the API
This page covers the mechanics you need for reliable integrations. It assumes you already have a key from API overview.
Idempotent incident creation
Section titled “Idempotent incident creation”POST /api/v1/incidents requires an idempotency_key in the request body. It is any string you choose that uniquely identifies this creation attempt, for example your alert’s ID or a UUID you generate.
The key protects you from duplicates when a request times out and you retry.
| Situation | Response |
|---|---|
| First request with a key | 201 Created, a new incident |
| Repeated request with the same key | 200 OK, the incident created the first time |
Idempotency keys expire after 24 hours. After that, reusing the same key creates a new incident, so treat keys as protection for retries, not as a long-term deduplication mechanism.
Errors
Section titled “Errors”Every error is JSON with a single error object. The request_id identifies the request, so include it when you contact support.
{ "error": { "type": "validation_error", "message": "Validation failed: Name can't be blank", "request_id": "9f2c1d4e-8a7b-4c3d-b5e6-0a1b2c3d4e5f", "errors": [ { "field": "name", "message": "can't be blank" } ] }}The errors array appears only on validation errors and lists each failing field.
| Status | type | When |
|---|---|---|
| 400 | bad_request | A required parameter is missing |
| 401 | unauthorized | The token is missing, invalid, expired, or deactivated |
| 403 | forbidden | The token lacks the permission this endpoint checks |
| 404 | not_found | The resource does not exist in your workspace |
| 422 | validation_error | The request was well-formed but the data is invalid |
| 429 | rate_limit_exceeded | You exceeded 1,000 requests per minute for this key |
Pagination
Section titled “Pagination”List endpoints that can grow large, such as incidents and catalog entries, accept two query parameters.
| Parameter | Default | Maximum |
|---|---|---|
page | 1 | none |
per_page | 25 | 100 |
Every paginated response includes a pagination object.
{ "pagination": { "page": 2, "per_page": 25, "total": 132, "total_pages": 6 } }The incident list also accepts filters as query parameters. Use severity_id, status_id, or lifecycle_stage with one of triage, active, closed, or canceled. Look up the IDs from GET /api/v1/severities and GET /api/v1/statuses.
Worked example: declare, then resolve
Section titled “Worked example: declare, then resolve”First fetch the reference data you need. Severities, statuses, and types are specific to your workspace, so their IDs are too.
curl https://app.firefight.app/api/v1/severities \ -H "Authorization: Bearer ff_4kWm2xPqR8vNcT6yBhJd3fLzGaU9sEnQoXwA"{ "severities": [ { "id": "c7e2f9a1-3b4d-4c6e-8f0a-5d6b7c8e9f01", "name": "SEV1", "slug": "sev1", "rank": 1, "position": 1, "is_default": false }, { "id": "a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d", "name": "SEV2", "slug": "sev2", "rank": 2, "position": 2, "is_default": true } ]}Now declare the incident. Only name, severity_id, and idempotency_key are required. Omitting status_id uses your workspace’s default status. You can also pass incident_type_id, declared_by_id with a member’s ID, visibility as public or private, custom_fields as a key-value object, and a free-form source string that tells responders where the incident came from.
curl -X POST https://app.firefight.app/api/v1/incidents \ -H "Authorization: Bearer ff_4kWm2xPqR8vNcT6yBhJd3fLzGaU9sEnQoXwA" \ -H "Content-Type: application/json" \ -d '{ "idempotency_key": "datadog-monitor-771204-1626601964", "name": "Checkout latency spike in eu-west", "summary": "p99 latency on the checkout service exceeded 4s.", "severity_id": "a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d", "source": "datadog" }'The response is 201 Created with the full incident.
{ "incident": { "id": "0d9b2c1e-7f3a-4b8e-9c5d-2a6e8f1b4d70", "identifier": "INC-42", "name": "Checkout latency spike in eu-west", "summary": "p99 latency on the checkout service exceeded 4s.", "status": { "id": "b1f6a2d4-8c3e-4f5a-9b7d-1e2c3a4b5d6f", "name": "Investigating", "lifecycle_stage": "active" }, "severity": { "id": "a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d", "name": "SEV2", "rank": 2 }, "type": null, "lead": null, "source": "datadog", "declared_by": null, "declared_at": "2026-07-18T09:12:44Z", "detected_at": null, "resolved_at": null, "created_at": "2026-07-18T09:12:44Z", "updated_at": "2026-07-18T09:12:44Z", "custom_fields": {}, "visibility": "public" }}Update it with PATCH /api/v1/incidents/:id. You can change name, summary, status_id, severity_id, incident_type_id, or assign a lead with lead_id. Moving the incident to a status in the closed stage resolves it, and moving a closed incident back to an active-stage status reopens it.
curl -X PATCH https://app.firefight.app/api/v1/incidents/0d9b2c1e-7f3a-4b8e-9c5d-2a6e8f1b4d70 \ -H "Authorization: Bearer ff_4kWm2xPqR8vNcT6yBhJd3fLzGaU9sEnQoXwA" \ -H "Content-Type: application/json" \ -d '{ "status_id": "f0e1d2c3-b4a5-4968-8776-655443322110", "summary": "Rolled back the 14:02 deploy. Latency recovered." }'The response is 200 OK with the updated incident, including its resolved_at timestamp.