# up4 developer documentation, full text > Every page of the up4 developer documentation (https://upfour.io/docs/api) in English, in navigation order, concatenated: contract, quickstart, basics, recipes and the full API reference. Each page starts with its title, a one-line description and its source URL. OpenAPI spec: https://upfour.io/api/openapi.json. All sources: https://upfour.io/llms.txt. For the app guide: https://upfour.io/docs/llms-full.txt. --- # API — the contract > REST API for monitors, scheduled jobs, incidents, status pages and reports: keys with scopes, envelope, pagination, errors, idempotency and rate limits, plus every route. Source: https://upfour.io/docs/api One REST API at `https://app.upfour.io/v1` with 271 routes for monitors, scheduled jobs, incidents, status pages, alerting, on-call, playbooks, clients, teams, webhooks, reports and company settings. Managing API keys, billing and personal account settings stays in the signed-in app. This page summarizes how requests are shaped, how they fail and how changes are announced; each topic has its own page. Every reference page has a **“Try it”** panel that runs real requests from your browser with your own key. ## Authentication Create an API key in the app under **Settings → API tokens** and send it as a Bearer token. Every key starts with `pls_` and carries scopes such as `monitor.read` or `incident.write`; each endpoint names the scope it needs. A key cannot create or manage other keys. [API keys and scopes](https://upfour.io/docs/api/keys-and-scopes.md) has the details and the errors. ```bash curl -s https://app.upfour.io/v1/me \ -H "Authorization: Bearer $UP4_API_KEY" ``` ## Response envelope Successful calls made with an API key return their payload as `{ "data": … }`; lists add a `pagination` object. See [responses and errors](https://upfour.io/docs/api/responses.md). ## Errors Every 4xx and 5xx response is `{ "error": { "code", "message", "request_id" } }`, with `details` when there is more to say. Branch on the code. The [error catalogue](https://upfour.io/docs/api/errors.md) lists every code with its cause and fix. ## Pagination `GET /monitors` pages with signed cursors (`limit` up to 250, `pagination.next_cursor`); the incident lists use `page` and `per_page`, and most other lists return everything at once. [Pagination](https://upfour.io/docs/api/pagination.md) has the parameters and the cursor errors. ## Idempotency Send `Idempotency-Key` on `POST` and `PATCH` requests. The same key with the same body returns the stored response for 24 hours; the same key with a different body is `409 idempotency_key_conflict`. [Idempotency](https://upfour.io/docs/api/idempotency.md) lists what is and is not stored. ## Versioning Every operation is served under `/v1`; there is no `/v2`, and the API sends no `Deprecation` or `Sunset` header. The [changelog](https://upfour.io/docs/api/changelog.md) records every change, with a filter for breaking ones, and [versioning](https://upfour.io/docs/api/versioning.md) says what `X-API-Version` does today. ## Rate limits Authenticated calls are limited per key (1,200 requests per minute, bursts of 300) and per IP address (3,000 per minute). A `429` carries `Retry-After`. [Rate limits](https://upfour.io/docs/api/rate-limits.md) explains the headers and how to back off. ## Client scope In agency mode, pass `client_id` or `all_clients=true` on list calls and `client_id` in request bodies. See [client scope](https://upfour.io/docs/api/client-scope.md). ## Conventions IDs are UUIDs. Timestamps are RFC 3339 in UTC. Boolean query values are a strict `true` or `false`. Browser API calls use `Authorization`, `Idempotency-Key` and `X-API-Version`. Documentation and marketing origins are bearer-only CORS origins and never receive credentialed session access; only the exact app origin may send the product's HttpOnly session cookie. ## Machine-readable Every route as files, generated from the router’s route inventory: [OpenAPI 3.1 (YAML)](https://upfour.io/api/openapi.yaml) · [OpenAPI 3.1 (JSON)](https://upfour.io/api/openapi.json) · [Postman collection](https://upfour.io/api/up4.postman_collection.json). Parameters are typed; most request bodies are open objects, and the field tables on the reference pages cover monitors, scheduled jobs and alert channels so far. For AI tooling there is an [llms.txt](https://upfour.io/llms.txt) index of every docs page. ## Reference Monitoring | [Monitors](https://upfour.io/docs/api/monitors.md) | The heart of the API: create, list, pause, resume and delete the things we check, as often as every ten seconds. | 28 endpoints | | --- | --- | --- | | [Monitor groups](https://upfour.io/docs/api/monitor-groups.md) | Folders for monitors — group fleets per project or per customer. | 6 endpoints | | [Scheduled jobs (heartbeats)](https://upfour.io/docs/api/heartbeats.md) | Cron jobs and backups that must call in before a deadline. Silence becomes an incident. | 12 endpoints | | [Scheduled job groups](https://upfour.io/docs/api/heartbeat-groups.md) | Folders for scheduled jobs. | 6 endpoints | | [Resource metadata](https://upfour.io/docs/api/metadata.md) | Free-form key/value metadata on monitors and scheduled jobs — owner, playbook link, cost center. | 2 endpoints | Alerting & response | [Incidents](https://upfour.io/docs/api/incidents.md) | Open, acknowledge, comment, escalate, resolve. | 27 endpoints | | --- | --- | --- | | [Severities](https://upfour.io/docs/api/severities.md) | Your incident severity ladder (SEV1…), fully customizable. | 1 endpoints | | [Severity groups](https://upfour.io/docs/api/severity-groups.md) | Group severities into reusable sets. | 1 endpoints | | [Alert channels](https://upfour.io/docs/api/notification-channels.md) | Where alerts go: email, Slack, Telegram, PagerDuty and friends. | 6 endpoints | | [Alert plans (escalation policies)](https://upfour.io/docs/api/escalation-policies.md) | Who is told, in what order, after how long. | 8 endpoints | | [On-call rotations](https://upfour.io/docs/api/oncall-schedules.md) | Rotations, overrides, and "who is on call right now". | 12 endpoints | | [Playbooks (runbooks)](https://upfour.io/docs/api/runbooks.md) | The steps you take when something breaks, attached to monitors, with shared progress. | 7 endpoints | | [Health advisor](https://upfour.io/docs/api/advisor.md) | Findings about your setup, such as monitors that alert nobody, and one call to apply a suggested fix. | 2 endpoints | Pages & reporting | [Status pages](https://upfour.io/docs/api/status-pages.md) | Public and client-facing uptime pages: structure, updates, subscribers, translations. | 53 endpoints | | --- | --- | --- | | [Status page update templates](https://upfour.io/docs/api/status-page-templates.md) | Saved wording for status page updates: title, status and message to reuse. | 4 endpoints | | [Status page design templates](https://upfour.io/docs/api/status-page-design-templates.md) | Reusable status page designs: save the look of a page once and apply it to other pages. | 6 endpoints | | [Reports](https://upfour.io/docs/api/reports.md) | Availability and SLA reporting: summaries, trends, exports, schedules, share links. | 21 endpoints | | [SLA policies](https://upfour.io/docs/api/sla-policies.md) | Targets and business-hours masks that reports measure against. | 5 endpoints | Organization | [Organization](https://upfour.io/docs/api/organization.md) | Company-level settings. | 18 endpoints | | --- | --- | --- | | [Teams](https://upfour.io/docs/api/teams.md) | Teams and membership. | 14 endpoints | | [Invites](https://upfour.io/docs/api/invites.md) | Invite colleagues — seats are unlimited everywhere. | 4 endpoints | | [Clients (agency mode)](https://upfour.io/docs/api/clients.md) | The agency partition: clients, client teams, share links, archive/unarchive. | 13 endpoints | | [Client templates](https://upfour.io/docs/api/client-templates.md) | Materialize monitors, pages and alert plans for a new client in one step. | 6 endpoints | Platform | [Token introspection](https://upfour.io/docs/api/me.md) | Who am I? What can this key do? The first call to make. | 1 endpoints | | --- | --- | --- | | [Webhook subscriptions](https://upfour.io/docs/api/webhook-subscriptions.md) | Outbound, signed webhooks — subscribe, test, inspect deliveries, redeliver. | 8 endpoints | --- # API quickstart > Your first request in a minute, then a monitor and a test webhook your server receives: an API key, GET /me, POST /monitors, a webhook subscription and its test delivery. Source: https://upfour.io/docs/api/quickstart Afterwards you have made an authenticated request, created a monitor and received a signed webhook on your own server. You need a terminal with curl and jq, and for the webhook a URL that can be reached from the internet. ## 1 · Create a key In the app open **Settings → API tokens** and create a key with the scopes `self.read`, `monitor.read`, `monitor.write`, `monitor.delete` and `webhook.write`. The key is shown once; put it in an environment variable, not in a script. ```bash export UP4_API_KEY="pls_…" ``` ## 2 · Ask who you are The first request needs only `self.read` and shows what the key may do: ```bash curl -s https://app.upfour.io/v1/me \ -H "Authorization: Bearer $UP4_API_KEY" ``` ```json { "data": { "auth_method": "api_key", "org_role": "owner", "scopes": ["self.read", "monitor.read", "monitor.write", "monitor.delete", "webhook.write"], "api_version": "2026-05-07", … } } ``` Every successful answer sits in `data`. A `401` means the header or the key is wrong: [keys and scopes](https://upfour.io/docs/api/keys-and-scopes.md) lists the causes. ## 3 · Create a monitor The `Idempotency-Key` makes a retry safe: the same key never creates a second monitor. Put the key into a variable first, so that running the command again sends the same key. The id goes into a variable for the next steps. ``` MONITOR_KEY=$(uuidgen) MONITOR_ID=$(curl -s -X POST https://app.upfour.io/v1/monitors \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $MONITOR_KEY" \ -d '{ "name": "Example", "url": "https://example.com", "monitor_type": "http", "interval_seconds": 60, "regions": ["eu"] }' | jq -r .data.id) echo $MONITOR_ID ``` The monitor runs from now on and appears in the app's monitor list. Every field it accepts: [monitors reference](https://upfour.io/docs/api/monitors.md). ## 4 · Receive a webhook Subscribe a URL of yours to the events you care about. It must be reachable from the internet and answer with a 2xx status: an endpoint of your application, or a tunnel to your machine while you try this out. Addresses in private networks are refused. ``` SUBSCRIPTION_KEY=$(uuidgen) curl -s -X POST https://app.upfour.io/v1/webhook-subscriptions \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $SUBSCRIPTION_KEY" \ -d '{ "url": "https://your-server.example/up4-hook", "events": ["monitor.status_changed", "incident.opened", "incident.resolved"] }' > subscription.json SUBSCRIPTION_ID=$(jq -r .data.id subscription.json) jq -r .data.secret subscription.json # whsec_…, shown only in this response ``` Store the `secret` with your other secrets; your server needs it to check the signature. If you run the create command again with the same key, the answer is the stored one, secret included, and no second subscription appears. Then have up4 send a test event right away. The call waits for that one delivery attempt (at most 10 seconds), which is why the `202` already carries the outcome: ```bash curl -s -X POST https://app.upfour.io/v1/webhook-subscriptions/$SUBSCRIPTION_ID/test \ -H "Authorization: Bearer $UP4_API_KEY" | jq '.data | {event, response_code, response_body}' ``` - `"response_code": 200`: your server received a `webhook.test` event. Its body is JSON with `id`, `type`, `created_at` and `data`; the `X-Up4-Signature` header signs it. `response_body` reads `"HTTP 200"`. - `"response_code": null`: up4 got no answer. `response_body` says `"Delivery timed out"` when the server did not answer in time and `"Delivery failed"` for other errors such as a refused connection. Check that the URL is public and answers quickly. A host that does not resolve, or that resolves to a private address, is refused with a `400` before anything is sent. - Any other code: your server answered with an error, and `response_body` is only the status line, for example `"HTTP 502"`. up4 never keeps the text of your answer. From now on a monitor that goes down or comes back sends `monitor.status_changed` to this URL. How to check the signature and handle retries: [webhook receiver recipe](https://upfour.io/docs/api/recipes/webhook-consumer.md). ## 5 · Clean up Delete the test monitor and the subscription when you are done. Both answer with `204`. ```bash curl -s -X DELETE https://app.upfour.io/v1/monitors/$MONITOR_ID \ -H "Authorization: Bearer $UP4_API_KEY" curl -s -X DELETE https://app.upfour.io/v1/webhook-subscriptions/$SUBSCRIPTION_ID \ -H "Authorization: Bearer $UP4_API_KEY" ``` ## Where next - [The contract](https://upfour.io/docs/api.md): envelopes, errors, versions and limits in one page. - [Pagination](https://upfour.io/docs/api/pagination.md): lists return pages; follow `pagination.next_cursor`. - [For AI assistants](https://upfour.io/docs/api/for-ai-assistants.md): which files to give a coding assistant. - [Monitors as code](https://upfour.io/docs/api/recipes/monitor-as-code.md): keep your monitors in a file in your repository. --- # For AI assistants > Which files to give a coding assistant so it builds an up4 integration from the real contract: llms.txt, the full-text Markdown files, the Markdown version of every page and the OpenAPI spec with recorded examples. Source: https://upfour.io/docs/api/for-ai-assistants A coding assistant guesses field names when it has not read the API. Give it one of the files below and it works from the same contract this documentation is generated from. ## The files | File | What it holds | | --- | --- | | [/llms.txt](https://upfour.io/llms.txt) | Index of every docs page, guide and developer docs, each with a one-line description. | | [/docs/api/llms-full.txt](https://upfour.io/docs/api/llms-full.txt) | The whole developer documentation as one Markdown file: contract, quickstart, basics, recipes and the full reference. | | [/docs/llms-full.txt](https://upfour.io/docs/llms-full.txt) | The whole app guide as one Markdown file, for integrations that also set things up in the app. | | [/api/openapi.json](https://upfour.io/api/openapi.json) | OpenAPI 3.1, every route of the public API. The same as YAML: /api/openapi.yaml. | | [/api/webhook-test-vectors.json](https://upfour.io/api/webhook-test-vectors.json) | Signed webhook bodies with their expected signatures, to test a receiver. | Every docs page also exists as Markdown: add `.md` to its address, for example [/docs/api/quickstart.md](https://upfour.io/docs/api/quickstart.md). "Copy page" at the top of each page puts that Markdown on the clipboard. ## Which one to give it - **The assistant can fetch URLs:** point it at `https://upfour.io/llms.txt` and let it open the pages it needs. - **It works from pasted context:** paste `/docs/api/llms-full.txt` once, or only the Markdown of the pages for the task, such as quickstart, keys and scopes, and the reference page of the resource. - **It generates a client or types:** give it `/api/openapi.json`. ## What the OpenAPI file adds Besides paths, parameters and scopes, the operations carry two up4 extensions: - `x-up4-example`: a request and the response it produced, recorded from a run of the real API and anonymised, so field names and shapes are the ones the API returns. Nearly every operation has one. - `x-up4-error-codes`: the error codes this operation can return. Codes every operation can return, such as `unauthorized` or `rate_limit_exceeded`, are listed once at the top in `x-up4-error-codes-every-operation`. What each code means and how to fix it: [errors](https://upfour.io/docs/api/errors.md). ## A prompt that works ``` Read https://upfour.io/llms.txt and the pages it links for the up4 API. Write a script that creates an HTTP monitor for every URL in urls.txt. Use only fields from the OpenAPI spec at https://upfour.io/api/openapi.json. Read the API key from the UP4_API_KEY environment variable, send an Idempotency-Key with every POST, and stop on the first error response, printing its code and message. ``` ## Keys and safety - Never paste an API key into a chat. Let the generated code read it from an environment variable, as above. - Give the key only the scopes the script needs; a read-only key cannot change anything. See [API keys and scopes](https://upfour.io/docs/api/keys-and-scopes.md). - Review a generated script before it runs against your Company. Writes take effect at once. --- # API keys and scopes > How an up4 API key is created, sent and checked: the Bearer header, the scope each route needs, read-only keys, rotation and the errors you get when something is missing. Source: https://upfour.io/docs/api/keys-and-scopes An API key is a Bearer token that acts as the person who created it. Each key carries scopes, and every route names the one scope it needs. Keys are created in the app and never by the API itself. ## Create a key In the app: **Settings → API tokens**. The form asks for a name, an access level (read-only or read and write), an expiry (never, 30, 90 or 365 days; the form starts at 90), the scopes and, when agency mode is on, the clients the key may see. ![API token form with the name CI integration, access level Read-only, and monitor.read and monitor.write checked.](https://upfour.io/docs/images/api-tokens.webp) **Create a scoped token.** Name, access level and permissions in the token form. 1. **Token name** Choose a name that identifies the integration, such as CI integration. 2. **Access level** The default for a token without explicit scopes. Read-only is the safe start. The screenshot shows the form before a token is created. 3. **Scopes** Select only the operations the integration needs. The example allows reading and writing monitors. - The key is shown once. up4 stores only a SHA-256 hash, so a lost key cannot be shown again; create a new one. - Every key starts with `pls_`. - Without an explicit scope list, a read-only key gets every `.read` scope and a read and write key gets every read, write and delete scope. - A key cannot create, rotate or revoke keys. Those routes answer only to a signed-in browser session. - **Rotate** in the app issues a new key with the same name, scopes, access level, clients and expiry. The old key keeps working for at most 24 more hours, so you can switch over without downtime. - up4 records when a key was last used. ## Send the key Put it in the `Authorization` header as a Bearer token. `GET /me` needs only `self.read` and shows what the key can do: ```bash export UP4_API_KEY="pls_…" curl -s https://app.upfour.io/v1/me \ -H "Authorization: Bearer $UP4_API_KEY" ``` ```json { "data": { "api_key_id": "…", "api_version": "2026-05-07", "auth_method": "api_key", "client_set": { "All": true, "CompanyLevel": true, "IDs": null }, "org_id": "…", "org_role": "owner", "scopes": ["self.read", "monitor.read", "monitor.write", …], "user_id": "…" } } ``` `scopes` comes in no fixed order. A team key also shows `team_id`. `client_set` is explained on the [client scope](https://upfour.io/docs/api/client-scope.md) page. ## Scopes A scope is `.read`, `.write` or, for monitors, `.delete`. Each reference page states the scope per operation; the table shows what the families cover. | Read scope | Write scopes | Covers | | --- | --- | --- | | `monitor.read` | `monitor.write, monitor.delete` | Monitors and monitor groups. Write covers create, update, pause, resume, check now, bulk and batch changes; delete is its own scope. | | `heartbeat.read` | `heartbeat.write` | Scheduled jobs (heartbeats) and their groups. Deleting needs write. | | `incident.read` | `incident.write` | Incidents, severities and severity groups. | | `status_page.read` | `status_page.write` | Status pages, update templates and design templates. Subscriber changes need status_page.subscriber.write. | | `notification_channel.read` | `notification_channel.write` | Alert channels. | | `escalation_policy.read` | `escalation_policy.write` | Escalation policies. | | `oncall.read` | `oncall.write` | On-call schedules, rotations and overrides. | | `runbook.read` | `runbook.write` | Playbooks (runbooks). | | `client.read` | `client.write` | Clients and client templates. | | `team.read` | `team.write` | Teams. | | `member.read` | `member.write` | Company members and invites, and the members of a team. | | `report.read` | `report.write` | Reports, report schedules and share links, SLA policies and the advisor. | | `webhook.read` | `webhook.write` | Webhook subscriptions and their deliveries. | | `metadata.read` | `metadata.write` | Metadata on monitors, scheduled jobs, incidents and clients. | | `organization.read` | `organization.write` | Company settings: branding, security, SSO. | | `audit_log.read` | `—` | The audit log. | | `self.read` | `—` | GET /me, the key introspecting itself. | You may also see `api_key.read` and `api_key.write` on a key. No public route needs them. A key that is read-only by access level is refused on every write route, even if its scope list contains a write scope. The role of the person who created the key applies as well: a viewer cannot write with a read and write key. ## When the key is refused | Status | error.code | error.message | Meaning | | --- | --- | --- | --- | | 401 | [unauthorized](https://upfour.io/docs/api/errors#code-unauthorized) | Invalid authorization header format | The header is present but is not Bearer followed by a token. The scheme is case-sensitive: write Bearer. | | 401 | [unauthorized](https://upfour.io/docs/api/errors#code-unauthorized) | API key required | The token does not start with pls\_. | | 401 | [unauthorized](https://upfour.io/docs/api/errors#code-unauthorized) | Invalid API key | The key is unknown, revoked or expired, or its Company was deleted. | | 401 | [unauthorized](https://upfour.io/docs/api/errors#code-unauthorized) | Session cookie required | There is no Authorization header at all, so the request is treated as a browser request. | | 401 | [unauthorized](https://upfour.io/docs/api/errors#code-unauthorized) | Invalid organization | The person who created the key is no longer a member of the Company. | | 403 | [insufficient_scope](https://upfour.io/docs/api/errors#code-insufficient_scope) | API token does not have the required scope | The key lacks the scope this route needs. details.required_scope names it. | | 403 | [insufficient_scope](https://upfour.io/docs/api/errors#code-insufficient_scope) | API token cannot access this route | The route is not part of the public API. | | 403 | [read_only_api_key](https://upfour.io/docs/api/errors#code-read_only_api_key) | Read-only API keys cannot use write permissions | The key has the read-only access level and the route needs a .write or .delete scope. | | 403 | [api_key_scopes_required](https://upfour.io/docs/api/errors#code-api_key_scopes_required) | Legacy API key has no explicit scopes and must be rotated | An old key without a scope list. Rotate it in the app. | | 403 | [session_required](https://upfour.io/docs/api/errors#code-session_required) | This route is available only to an authenticated browser session | The route belongs to the app: key management, billing, user settings, the dashboard. | The shape of these bodies is on the [responses](https://upfour.io/docs/api/responses.md) page; every code is in the [error catalogue](https://upfour.io/docs/api/errors.md), for example [insufficient_scope](https://upfour.io/docs/api/errors#code-insufficient_scope). Company-level routes such as `/organization` answer `404 Not found` to team keys and to keys that cannot read company-level resources. ## Common mistakes - Writing `bearer` in lowercase, or leaving out the word. Both give `401`. - Reading `403` as a bad key. A bad key is `401`; `403` means the key is valid but lacks the scope or the write access. - Creating a key as read-only and adding write scopes later in your head. Create a read and write key, or rotate to one. - Keeping keys in shared chat or in code. Revoke a leaked key in the app; it stops working at once. - Using a key tied to one person for an integration. When that person leaves the Company, the key answers `401`. --- # Responses and errors > The two response shapes of the up4 API: the data envelope on success and the error object on failure, with the status-code mapping and the request id to quote to support. Source: https://upfour.io/docs/api/responses A successful call returns `data`. A failed call returns `error`. You can tell them apart by the status code and by the one key at the top. ## Success A 2xx response with a JSON body wraps its payload as `{ "data": … }`. A list puts an array in `data` and adds `pagination`; what that object means depends on the endpoint, see [pagination](https://upfour.io/docs/api/pagination.md). ```bash curl -s https://app.upfour.io/v1/monitors/$MONITOR_ID \ -H "Authorization: Bearer $UP4_API_KEY" ``` ```json { "data": { "id": "…", "name": "Example", … } } { "data": [ … ], "pagination": { "cursor": null, "next_cursor": null, "prev_cursor": null, "limit": 50, "has_more": false } } ``` - The envelope applies to every call made with an API key. A response that already carries a top-level `data` or `error` is passed through unchanged. - `204 No Content` has no body. File downloads such as report exports are not JSON and are not wrapped. - New fields can appear in a response; the [changelog](https://upfour.io/docs/api/changelog.md) lists them as added. Ignore fields you do not know. ## Errors Every 4xx and 5xx response uses one object, with `Content-Type: application/json`. Errors are not wrapped in `data`. ```json { "error": { "code": "insufficient_scope", "message": "API token does not have the required scope", "request_id": "req_…", "details": { "required_scope": "monitor.write" } } } ``` | Field | Meaning | | --- | --- | | `error.code` | A stable identifier in snake_case. Branch on this. | | `error.message` | A sentence for people. It can change; do not match on it. | | `error.request_id` | The id of this request, the same as the `X-Request-Id` response header. | | `error.details` | Only present when there is more to say: `field` names the invalid field, `required_scope` the missing scope, `original_request_at` the first use of an idempotency key. | Every code, with its status and cause, is in the [error catalogue](https://upfour.io/docs/api/errors.md). ## Status codes A status without a more specific code gets the default code below. Many errors carry a specific code instead, such as `cursor_expired` or `idempotency_key_conflict`. | Status | Default error.code | Usually means | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or a field is invalid. A field error adds details.field. | | 401 | [unauthorized](https://upfour.io/docs/api/errors#code-unauthorized) | No valid key. See API keys and scopes. | | 402 | [payment_required](https://upfour.io/docs/api/errors#code-payment_required) | The Company’s plan or billing state does not allow the action. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | Refused. Scope errors use their own codes, see API keys and scopes. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The resource does not exist, or your key cannot see it. | | 409 | [conflict](https://upfour.io/docs/api/errors#code-conflict) | The request conflicts with the current state. Idempotency conflicts use their own codes. | | 429 | [rate_limit_exceeded](https://upfour.io/docs/api/errors#code-rate_limit_exceeded) | Too many requests. Wait for Retry-After. | | 503 | [service_unavailable](https://upfour.io/docs/api/errors#code-service_unavailable) | A deliberate notice, for example a feature that is not available right now. The message is meant to be read. | | other 5xx | [internal_error](https://upfour.io/docs/api/errors#code-internal_error) | Our fault. The message is always “Internal server error”; send us the request_id. | Any other status falls back to `request_failed`. ## Request ids Every response, success or failure, carries `X-Request-Id`. If you send the header, up4 uses your value; otherwise it generates `req_` followed by a UUID. Log it, and quote it when you contact support. ```bash curl -si https://app.upfour.io/v1/me \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "X-Request-Id: nightly-sync-2026-10-27" | grep -i '^x-request-id' ``` ## Common mistakes - Matching on `error.message`. Match on `error.code` and the status. - Reading `data` on an error response. There is none. - Expecting a body on `204`. - Treating every `403` as “not allowed for good”. A missing scope or a read-only key can be fixed with a new key. - Retrying a `5xx` or timed-out POST without an `Idempotency-Key`. With one, a retry returns the stored answer when the first request finished below 500; a `5xx` itself is not stored. See [idempotency](https://upfour.io/docs/api/idempotency.md). --- # Errors > The up4 API error envelope, how to retry, and every error code with its meaning, trigger, fix and the operations that can return it. Source: https://upfour.io/docs/api/errors Every failed request returns one JSON shape and a stable code. Branch on the code, retry only what is worth retrying, and keep the request id. ## The error envelope A 4xx or 5xx response from a `/v1` operation has this body, with one exception noted below: ```json { "error": { "code": "import_rows_out_of_range", "message": "Choose between 1 and 500 rows per import.", "request_id": "req_…", "details": { "min_rows": 1, "max_rows": 500 } } } ``` | Field | Meaning | | --- | --- | | `code` | A stable snake_case identifier. Branch on this, not on the message. | | `message` | A sentence for people. Its wording can change; do not match on it. The message of a 5xx is replaced by Internal server error, except for 503 and a few provider failures; the cause stays in our log. | | `request_id` | The id of this request. Quote it when you contact support; it finds the request in our logs. | | `details` | Optional. Extra data some codes define, such as `max_bytes` or `retry_at`. Absent when a code has none. | A code that no handler names comes from its HTTP status: `bad_request` for 400, `unauthorized` for 401, `payment_required` for 402, `permission_denied` for 403, `not_found` for 404, `conflict` for 409, `rate_limit_exceeded` for 429, `service_unavailable` for 503, `internal_error` for other 5xx and `request_failed` for any other status. The incident AI operations are an exception: a 402 for a used-up AI quota still has the older flat body `{"error": "…", "quota": {…}}`, where `error` is a string. Read the HTTP status there. ## Handling errors - Branch on `error.code`. The message and the details text are for people. - Retry only **429** and **5xx**. Any other 4xx fails the same way until you change the request. - Wait for the `Retry-After` header when there is one. It gives seconds and comes with `rate_limit_exceeded`, `confirmation_rate_limited` and the 429 form of `quota_limit_reached`. Without it, back off exponentially and add jitter. - Send an `Idempotency-Key` on every POST and PATCH you may retry. When the first request finished with a status from 200 to 499, a retry after a timeout gets that stored answer and creates nothing a second time. A **5xx is not stored**: a retry with the same key runs the request again, so after a 5xx look at the resource before you assume nothing was created. See [idempotency](https://upfour.io/docs/api/idempotency.md). - Log `request_id` with the code, and send it to support for any 5xx that persists. ## Errors on every operation Middleware, not a handler, answers these before a request reaches its operation, so they are listed here once and not on each operation. Each applies only under the condition shown. | Code | Status | When | | --- | --- | --- | | [api_key_scopes_required](https://upfour.io/docs/api/errors#code-api_key_scopes_required) | 403 | The request uses an API key created before scopes existed. | | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | 400 | A POST or PATCH carries an Idempotency-Key header and its body cannot be read. | | [forbidden](https://upfour.io/docs/api/errors#code-forbidden) | 403 | A POST or PATCH carries an Idempotency-Key header and the request has no verifiable current authorization. | | [idempotency_key_conflict](https://upfour.io/docs/api/errors#code-idempotency_key_conflict) | 409 | A POST or PATCH carries an Idempotency-Key header that was stored with a different body. | | [idempotency_replay_unavailable](https://upfour.io/docs/api/errors#code-idempotency_replay_unavailable) | 409 | A POST or PATCH carries an Idempotency-Key header whose stored response cannot be replayed to this caller. | | [insufficient_scope](https://upfour.io/docs/api/errors#code-insufficient_scope) | 403 | The request uses an API key that lacks the scope the operation requires. | | [internal_error](https://upfour.io/docs/api/errors#code-internal_error) | 500 · 502 | A POST or PATCH carries an Idempotency-Key header and the key store fails. | | [not_found](https://upfour.io/docs/api/errors#code-not_found) | 404 | A client-portal session requests a route the portal does not expose. | | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | 403 | A browser session sends a request from another origin; Bearer keys are not affected. | | [rate_limit_exceeded](https://upfour.io/docs/api/errors#code-rate_limit_exceeded) | 429 | The request exceeds the per-client rate limit, or the per-credential limit once authenticated; Retry-After gives the wait in seconds. | | [read_only_api_key](https://upfour.io/docs/api/errors#code-read_only_api_key) | 403 | A read-only API key calls an operation that writes. | | [session_required](https://upfour.io/docs/api/errors#code-session_required) | 403 | An API key calls a route that is limited to browser sessions. | | [unauthorized](https://upfour.io/docs/api/errors#code-unauthorized) | 401 | The credentials are missing, malformed, unknown, revoked or expired. | ## Error codes by status 67 codes can come back from a public operation. Each is listed under its first HTTP status; a code that can carry another status shows it next to the code. “Operations” are the ones whose handler can answer with the code, found by reading the source; the page of an operation in the [reference](https://upfour.io/docs/api.md) is authoritative for what it returns. [200](https://upfour.io/docs/api/errors#status-200)[400](https://upfour.io/docs/api/errors#status-400)[401](https://upfour.io/docs/api/errors#status-401)[402](https://upfour.io/docs/api/errors#status-402)[403](https://upfour.io/docs/api/errors#status-403)[404](https://upfour.io/docs/api/errors#status-404)[409](https://upfour.io/docs/api/errors#status-409)[413](https://upfour.io/docs/api/errors#status-413)[422](https://upfour.io/docs/api/errors#status-422)[429](https://upfour.io/docs/api/errors#status-429)[500](https://upfour.io/docs/api/errors#status-500)[502](https://upfour.io/docs/api/errors#status-502)[503](https://upfour.io/docs/api/errors#status-503)[504](https://upfour.io/docs/api/errors#status-504) ### 200 #### [request_failed](https://upfour.io/docs/api/errors#code-request_failed) 200 · 410 · 413 · 422 The request failed with a status that has no code of its own. - **Typical trigger:** A 410 for an expired invite, a 413 for a body over its size limit, or a 422 for a request that parses but that the rules reject. - **Fix:** Use the HTTP status and message; these are not retryable until the request changes. - **Operations:** - [POST /incidents/{incidentID}/attachments](https://upfour.io/docs/api/incidents#post_incidents_incidentID_attachments) - [POST /status-page-design-templates](https://upfour.io/docs/api/status-page-design-templates#post_status_page_design_templates) - [PUT /status-page-design-templates/{templateID}](https://upfour.io/docs/api/status-page-design-templates#put_status_page_design_templates_templateID) - [POST /status-pages/{pageID}/subscribers/upload](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers_upload) ### 400 Bad request #### [attachment_empty](https://upfour.io/docs/api/errors#code-attachment_empty) The attachment file is empty. - **Typical trigger:** Uploading an incident attachment of zero bytes. - **Fix:** Upload a file that has content. - **Operations:** - [POST /incidents/{incidentID}/attachments](https://upfour.io/docs/api/incidents#post_incidents_incidentID_attachments) #### [attachment_type_unsupported](https://upfour.io/docs/api/errors#code-attachment_type_unsupported) The attachment's file type is not accepted. - **Typical trigger:** Uploading an incident attachment whose MIME type is not on the allowed list. - **Fix:** Upload a file of an allowed type. - **Operations:** - [POST /incidents/{incidentID}/attachments](https://upfour.io/docs/api/incidents#post_incidents_incidentID_attachments) #### [bad_request](https://upfour.io/docs/api/errors#code-bad_request) The request is malformed or fails validation. - **Typical trigger:** A body that is not valid JSON, a missing or invalid field, or a path or query value that does not parse; the message names the problem. - **Fix:** Read message and details, correct the request and send it again; an unchanged retry fails the same way. - **Operations:** Under the condition in [Errors on every operation](https://upfour.io/docs/api/errors#every-operation), and from handlers on 267 of 271 operations. #### [cursor_expired](https://upfour.io/docs/api/errors#code-cursor_expired) The list cursor has expired. - **Typical trigger:** Paging a monitor list with a cursor that is too old. - **Fix:** Start again without a cursor. - **Operations:** - [GET /monitors](https://upfour.io/docs/api/monitors#get_monitors) #### [cursor_invalid](https://upfour.io/docs/api/errors#code-cursor_invalid) The list cursor is not valid. - **Typical trigger:** Paging a monitor list with a cursor that was not issued by the API or was altered. - **Fix:** Use the cursor from the previous response unchanged, or start without one. - **Operations:** - [GET /monitors](https://upfour.io/docs/api/monitors#get_monitors) #### [cursor_parameters_changed](https://upfour.io/docs/api/errors#code-cursor_parameters_changed) The filters changed since the cursor was issued. - **Typical trigger:** Paging a monitor list with a cursor while sort or filter parameters differ from the first request. - **Fix:** Keep the parameters the same for all pages, or start again without a cursor. - **Operations:** - [GET /monitors](https://upfour.io/docs/api/monitors#get_monitors) #### [file_empty](https://upfour.io/docs/api/errors#code-file_empty) The uploaded file is empty. - **Typical trigger:** Uploading a logo or image with no content. - **Fix:** Upload a file that has content. - **Operations:** - [POST /clients](https://upfour.io/docs/api/clients#post_clients) - [PUT /clients/{clientID}](https://upfour.io/docs/api/clients#put_clients_clientID) - [GET /organization/report-branding](https://upfour.io/docs/api/organization#get_organization_report_branding) - [PUT /organization/report-branding](https://upfour.io/docs/api/organization#put_organization_report_branding) - [PUT /status-pages/{pageID}/assets/{kind}](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_assets_kind) #### [file_type_unsupported](https://upfour.io/docs/api/errors#code-file_type_unsupported) The file's type is not accepted for this upload. - **Typical trigger:** Uploading a file whose type is not one of the formats the operation accepts; details.formats lists them. - **Fix:** Upload a file in one of details.formats. - **Operations:** - [POST /clients](https://upfour.io/docs/api/clients#post_clients) - [PUT /clients/{clientID}](https://upfour.io/docs/api/clients#put_clients_clientID) - [GET /organization/report-branding](https://upfour.io/docs/api/organization#get_organization_report_branding) - [PUT /organization/report-branding](https://upfour.io/docs/api/organization#put_organization_report_branding) - [PUT /status-pages/{pageID}/assets/{kind}](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_assets_kind) #### [image_dimensions_too_large](https://upfour.io/docs/api/errors#code-image_dimensions_too_large) The image has more pixels than allowed. - **Typical trigger:** Uploading an image with more pixels than details.max_pixels. - **Fix:** Scale the image down and upload it again. - **Operations:** - [POST /clients](https://upfour.io/docs/api/clients#post_clients) - [PUT /clients/{clientID}](https://upfour.io/docs/api/clients#put_clients_clientID) - [GET /organization/report-branding](https://upfour.io/docs/api/organization#get_organization_report_branding) - [PUT /organization/report-branding](https://upfour.io/docs/api/organization#put_organization_report_branding) - [PUT /status-pages/{pageID}/assets/{kind}](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_assets_kind) #### [image_invalid](https://upfour.io/docs/api/errors#code-image_invalid) The file is not a readable image. - **Typical trigger:** Uploading a file with an image type that cannot be decoded. - **Fix:** Export the image again and upload the new file. - **Operations:** - [POST /clients](https://upfour.io/docs/api/clients#post_clients) - [PUT /clients/{clientID}](https://upfour.io/docs/api/clients#put_clients_clientID) - [GET /organization/report-branding](https://upfour.io/docs/api/organization#get_organization_report_branding) - [PUT /organization/report-branding](https://upfour.io/docs/api/organization#put_organization_report_branding) - [PUT /status-pages/{pageID}/assets/{kind}](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_assets_kind) #### [import_invalid](https://upfour.io/docs/api/errors#code-import_invalid) The import body is not valid. - **Typical trigger:** A batch monitor import whose body cannot be parsed; the message names the problem. - **Fix:** Correct the body and send it again. - **Operations:** - [POST /monitors/batch](https://upfour.io/docs/api/monitors#post_monitors_batch) - [POST /monitors/batch/dry-run](https://upfour.io/docs/api/monitors#post_monitors_batch_dry_run) #### [import_rows_out_of_range](https://upfour.io/docs/api/errors#code-import_rows_out_of_range) The import has too few or too many rows. - **Typical trigger:** A batch monitor import with fewer than min_rows or more than max_rows rows, both in details. - **Fix:** Send between min_rows and max_rows rows per request. - **Operations:** - [POST /monitors/batch](https://upfour.io/docs/api/monitors#post_monitors_batch) - [POST /monitors/batch/dry-run](https://upfour.io/docs/api/monitors#post_monitors_batch_dry_run) #### [invalid_move_source](https://upfour.io/docs/api/errors#code-invalid_move_source) The page to move the domain from is not valid. - **Typical trigger:** Setting a domain with a move-from page id that is not a status page the caller can use. - **Fix:** Send the id of the status page that holds the domain. - **Operations:** - [PUT /status-pages/{pageID}/custom-domain](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_custom_domain) #### [logo_fetch_failed](https://upfour.io/docs/api/errors#code-logo_fetch_failed) The logo could not be fetched from the address given. - **Typical trigger:** Importing a client logo from a URL that cannot be reached or that does not return an acceptable image. - **Fix:** Check the URL, or upload the logo file instead. - **Operations:** - [POST /clients](https://upfour.io/docs/api/clients#post_clients) - [POST /clients/logo-import](https://upfour.io/docs/api/clients#post_clients_logo_import) - [PUT /clients/{clientID}](https://upfour.io/docs/api/clients#put_clients_clientID) - [GET /organization/report-branding](https://upfour.io/docs/api/organization#get_organization_report_branding) - [PUT /organization/report-branding](https://upfour.io/docs/api/organization#put_organization_report_branding) - [PUT /status-pages/{pageID}/assets/{kind}](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_assets_kind) #### [no_channels_linked](https://upfour.io/docs/api/errors#code-no_channels_linked) The monitor has no alert channel to send a test to. - **Typical trigger:** A test alert for a monitor that has no alert channel linked. - **Fix:** Link an alert channel to the monitor and test again. - **Operations:** - [POST /monitors/{monitorID}/test-alert](https://upfour.io/docs/api/monitors#post_monitors_monitorID_test_alert) #### [svg_unsafe](https://upfour.io/docs/api/errors#code-svg_unsafe) The SVG contains content that is not allowed. - **Typical trigger:** Uploading an SVG that contains a script, a foreignObject, a javascript: URL, an event-handler attribute or an external href. - **Fix:** Remove those parts from the SVG, or upload a PNG or JPEG. - **Operations:** - [POST /clients](https://upfour.io/docs/api/clients#post_clients) - [PUT /clients/{clientID}](https://upfour.io/docs/api/clients#put_clients_clientID) - [GET /organization/report-branding](https://upfour.io/docs/api/organization#get_organization_report_branding) - [PUT /organization/report-branding](https://upfour.io/docs/api/organization#put_organization_report_branding) - [PUT /status-pages/{pageID}/assets/{kind}](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_assets_kind) #### [upload_invalid](https://upfour.io/docs/api/errors#code-upload_invalid) The upload could not be read as a multipart form. - **Typical trigger:** A body that is not a valid multipart form, or whose file cannot be parsed. - **Fix:** Send a well-formed multipart/form-data request. - **Operations:** 7 operations - [POST /clients](https://upfour.io/docs/api/clients#post_clients) - [PUT /clients/{clientID}](https://upfour.io/docs/api/clients#put_clients_clientID) - [POST /incidents/{incidentID}/attachments](https://upfour.io/docs/api/incidents#post_incidents_incidentID_attachments) - [GET /organization/report-branding](https://upfour.io/docs/api/organization#get_organization_report_branding) - [PUT /organization/report-branding](https://upfour.io/docs/api/organization#put_organization_report_branding) - [PUT /status-pages/{pageID}/assets/{kind}](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_assets_kind) - [POST /status-pages/{pageID}/subscribers/upload](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers_upload) #### [upload_no_file](https://upfour.io/docs/api/errors#code-upload_no_file) The multipart request has no file part. - **Typical trigger:** An upload request without a file field. - **Fix:** Send the file as multipart/form-data in the field the operation names. - **Operations:** 7 operations - [POST /clients](https://upfour.io/docs/api/clients#post_clients) - [PUT /clients/{clientID}](https://upfour.io/docs/api/clients#put_clients_clientID) - [POST /incidents/{incidentID}/attachments](https://upfour.io/docs/api/incidents#post_incidents_incidentID_attachments) - [GET /organization/report-branding](https://upfour.io/docs/api/organization#get_organization_report_branding) - [PUT /organization/report-branding](https://upfour.io/docs/api/organization#put_organization_report_branding) - [PUT /status-pages/{pageID}/assets/{kind}](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_assets_kind) - [POST /status-pages/{pageID}/subscribers/upload](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers_upload) ### 401 Unauthorized #### [unauthorized](https://upfour.io/docs/api/errors#code-unauthorized) The request carries no valid credentials. - **Typical trigger:** The Authorization header is missing or malformed, the API key is unknown or revoked, or a browser session has expired. - **Fix:** Send a valid key as Authorization: Bearer pls\_..., and create a new key if the old one was revoked. - **Operations:** Every operation, under the condition in [Errors on every operation](https://upfour.io/docs/api/errors#every-operation). Handlers also return it on: - [GET /incidents/{incidentID}/ai](https://upfour.io/docs/api/incidents#get_incidents_incidentID_ai) - [POST /incidents/{incidentID}/ai/postmortem](https://upfour.io/docs/api/incidents#post_incidents_incidentID_ai_postmortem) - [POST /incidents/{incidentID}/ai/summary](https://upfour.io/docs/api/incidents#post_incidents_incidentID_ai_summary) - [PUT /incidents/{incidentID}/ai/{kind}/rating](https://upfour.io/docs/api/incidents#put_incidents_incidentID_ai_kind_rating) ### 402 Payment required #### [ai_billing_blocked](https://upfour.io/docs/api/errors#code-ai_billing_blocked) AI features are switched off because the Company has no active credit. - **Typical trigger:** An AI summary or postmortem request while the Company has no active credit. - **Fix:** Top up the credit, then retry. - **Operations:** - [GET /incidents/{incidentID}/ai](https://upfour.io/docs/api/incidents#get_incidents_incidentID_ai) - [POST /incidents/{incidentID}/ai/postmortem](https://upfour.io/docs/api/incidents#post_incidents_incidentID_ai_postmortem) - [POST /incidents/{incidentID}/ai/summary](https://upfour.io/docs/api/incidents#post_incidents_incidentID_ai_summary) - [PUT /incidents/{incidentID}/ai/{kind}/rating](https://upfour.io/docs/api/incidents#put_incidents_incidentID_ai_kind_rating) - [PUT /organization/ai-quota](https://upfour.io/docs/api/organization#put_organization_ai_quota) - [GET /organization/ai-usage](https://upfour.io/docs/api/organization#get_organization_ai_usage) #### [billing_action_unavailable](https://upfour.io/docs/api/errors#code-billing_action_unavailable) The Company's billing state does not allow this action. - **Typical trigger:** A metered or limited action while the billing state, such as paused or without credit, blocks it; details holds the action and billing_state. - **Fix:** Top up the credit or change the plan, then retry. - **Operations:** 21 operations - [POST /escalation-policies](https://upfour.io/docs/api/escalation-policies#post_escalation_policies) - [PUT /escalation-policies/{policyID}](https://upfour.io/docs/api/escalation-policies#put_escalation_policies_policyID) - [POST /incidents/{incidentID}/attachments](https://upfour.io/docs/api/incidents#post_incidents_incidentID_attachments) - [POST /invites](https://upfour.io/docs/api/invites#post_invites) - [POST /invites/{inviteID}/resend](https://upfour.io/docs/api/invites#post_invites_inviteID_resend) - [POST /monitors/{monitorID}/test-alert](https://upfour.io/docs/api/monitors#post_monitors_monitorID_test_alert) - [POST /notification_channels/{channelID}/test](https://upfour.io/docs/api/notification-channels#post_notification_channels_channelID_test) - [POST /reports/schedules](https://upfour.io/docs/api/reports#post_reports_schedules) - [PUT /reports/schedules/{scheduleID}](https://upfour.io/docs/api/reports#put_reports_schedules_scheduleID) - [POST /reports/schedules/{scheduleID}/run-now](https://upfour.io/docs/api/reports#post_reports_schedules_scheduleID_run_now) - [POST /reports/{runID}/share](https://upfour.io/docs/api/reports#post_reports_runID_share) - [POST /status-pages](https://upfour.io/docs/api/status-pages#post_status_pages) - [POST /status-pages/{pageID}/sections/{sectionID}/resources](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_sections_sectionID_resources) - [PUT /status-pages/{pageID}/sections/{sectionID}/resources/{resourceID}](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_sections_sectionID_resources_resourceID) - [PUT /status-pages/{pageID}/structure](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_structure) - [POST /status-pages/{pageID}/subscribers](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers) - [POST /status-pages/{pageID}/subscribers/upload](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers_upload) - [POST /status-pages/{pageID}/subscribers/{subscriberID}/resend](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers_subscriberID_resend) - [POST /webhook-subscriptions](https://upfour.io/docs/api/webhook-subscriptions#post_webhook_subscriptions) - [POST /webhook-subscriptions/{subscriptionID}/redeliver/{deliveryID}](https://upfour.io/docs/api/webhook-subscriptions#post_webhook_subscriptions_subscriptionID_redeliver_deliveryID) - [POST /webhook-subscriptions/{subscriptionID}/test](https://upfour.io/docs/api/webhook-subscriptions#post_webhook_subscriptions_subscriptionID_test) #### [payment_required](https://upfour.io/docs/api/errors#code-payment_required) The Company's billing state does not allow the action. - **Typical trigger:** Creating a monitor, scheduled job or alert channel when the credit is used up or the plan does not cover it; the message names the reason. - **Fix:** Top up the credit or change the plan in billing, then retry. - **Operations:** 9 operations - [POST /heartbeats](https://upfour.io/docs/api/heartbeats#post_heartbeats) - [POST /monitors](https://upfour.io/docs/api/monitors#post_monitors) - [PUT /monitors/{monitorID}](https://upfour.io/docs/api/monitors#put_monitors_monitorID) - [PATCH /monitors/{monitorID}](https://upfour.io/docs/api/monitors#patch_monitors_monitorID) - [POST /monitors/{monitorID}/resume](https://upfour.io/docs/api/monitors#post_monitors_monitorID_resume) - [POST /notification_channels](https://upfour.io/docs/api/notification-channels#post_notification_channels) - [POST /status-pages](https://upfour.io/docs/api/status-pages#post_status_pages) - [PUT /status-pages/{pageID}/custom-domain](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_custom_domain) - [POST /status-pages/{pageID}/duplicate](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_duplicate) #### [subscriber_import_requires_payment](https://upfour.io/docs/api/errors#code-subscriber_import_requires_payment) Subscriber import is locked while the Company is on its starting credit. - **Typical trigger:** Importing subscribers before the first payment. - **Fix:** Top up the credit, then import. - **Operations:** - [POST /status-pages/{pageID}/subscribers](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers) - [POST /status-pages/{pageID}/subscribers/upload](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers_upload) - [POST /status-pages/{pageID}/subscribers/{subscriberID}/resend](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers_subscriberID_resend) ### 403 Forbidden #### [api_key_scopes_required](https://upfour.io/docs/api/errors#code-api_key_scopes_required) The API key predates scopes and has none. - **Typical trigger:** Only for API keys created before scopes existed, on any public operation. - **Fix:** Rotate the key in the app to get one with explicit scopes. - **Operations:** Every operation, under the condition in [Errors on every operation](https://upfour.io/docs/api/errors#every-operation). #### [check_capacity_exceeded](https://upfour.io/docs/api/errors#code-check_capacity_exceeded) The change would push the subscription's projected monthly check load over its included budget. - **Typical trigger:** Creating a monitor, directly or through a client template, or changing one, when the projected cost including the change exceeds the budget; running monitors are not affected, and details carries projected_cents, included_cents and change_cents. - **Fix:** Use a longer interval, fewer regions or fewer monitors, or raise the included budget. - **Operations:** 7 operations - [POST /clients](https://upfour.io/docs/api/clients#post_clients) - [POST /monitors](https://upfour.io/docs/api/monitors#post_monitors) - [PUT /monitors/{monitorID}](https://upfour.io/docs/api/monitors#put_monitors_monitorID) - [PATCH /monitors/{monitorID}](https://upfour.io/docs/api/monitors#patch_monitors_monitorID) - [POST /monitors/{monitorID}/check](https://upfour.io/docs/api/monitors#post_monitors_monitorID_check) - [POST /monitors/{monitorID}/pause](https://upfour.io/docs/api/monitors#post_monitors_monitorID_pause) - [POST /monitors/{monitorID}/resume](https://upfour.io/docs/api/monitors#post_monitors_monitorID_resume) #### [feature_disabled](https://upfour.io/docs/api/errors#code-feature_disabled) The monitor uses a feature that is currently switched off. - **Typical trigger:** Saving a monitor whose configuration needs a feature the platform has disabled; the saved configuration stays as it was. - **Fix:** Remove that feature from the monitor, or try again once the feature is back. - **Operations:** - [POST /monitors](https://upfour.io/docs/api/monitors#post_monitors) - [PUT /monitors/{monitorID}](https://upfour.io/docs/api/monitors#put_monitors_monitorID) - [PATCH /monitors/{monitorID}](https://upfour.io/docs/api/monitors#patch_monitors_monitorID) - [POST /monitors/{monitorID}/check](https://upfour.io/docs/api/monitors#post_monitors_monitorID_check) - [POST /monitors/{monitorID}/pause](https://upfour.io/docs/api/monitors#post_monitors_monitorID_pause) - [POST /monitors/{monitorID}/resume](https://upfour.io/docs/api/monitors#post_monitors_monitorID_resume) #### [forbidden](https://upfour.io/docs/api/errors#code-forbidden) The idempotency layer has no current authorization to bind the key to. - **Typical trigger:** Only on POST and PATCH requests that send an Idempotency-Key header and whose authorization cannot be verified. - **Fix:** Authenticate the request with a valid key before sending Idempotency-Key. - **Operations:** Every operation, under the condition in [Errors on every operation](https://upfour.io/docs/api/errors#every-operation). #### [insufficient_scope](https://upfour.io/docs/api/errors#code-insufficient_scope) The API key lacks the scope this operation needs. - **Typical trigger:** Only for API keys, when the key's scopes do not include the one the operation requires; details.required_scope names it. - **Fix:** Create a key with the needed scope; a missing scope never resolves by retrying. - **Operations:** Every operation, under the condition in [Errors on every operation](https://upfour.io/docs/api/errors#every-operation). #### [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) The credentials are valid but may not do this. - **Typical trigger:** The key's role or client scope does not allow the action, for example a write on a client the key cannot write to. - **Fix:** Use a key with the needed role and client scope, or ask a Company admin to grant it. - **Operations:** Under the condition in [Errors on every operation](https://upfour.io/docs/api/errors#every-operation), and from handlers on 182 of 271 operations. #### [quota_limit_reached](https://upfour.io/docs/api/errors#code-quota_limit_reached) 403 · 429 A usage or inventory limit of the Company was reached. - **Typical trigger:** Counters and concurrency limits answer 429 with Retry-After, inventory and capacity limits answer 403; details holds purpose, limit and window. - **Fix:** On 429 wait Retry-After seconds; on 403 free something up, request a higher limit where details.raise_request is true, or buy an extension. - **Operations:** 21 operations - [POST /escalation-policies](https://upfour.io/docs/api/escalation-policies#post_escalation_policies) - [PUT /escalation-policies/{policyID}](https://upfour.io/docs/api/escalation-policies#put_escalation_policies_policyID) - [POST /incidents/{incidentID}/attachments](https://upfour.io/docs/api/incidents#post_incidents_incidentID_attachments) - [POST /invites](https://upfour.io/docs/api/invites#post_invites) - [POST /invites/{inviteID}/resend](https://upfour.io/docs/api/invites#post_invites_inviteID_resend) - [POST /monitors/{monitorID}/test-alert](https://upfour.io/docs/api/monitors#post_monitors_monitorID_test_alert) - [POST /notification_channels/{channelID}/test](https://upfour.io/docs/api/notification-channels#post_notification_channels_channelID_test) - [POST /reports/schedules](https://upfour.io/docs/api/reports#post_reports_schedules) - [PUT /reports/schedules/{scheduleID}](https://upfour.io/docs/api/reports#put_reports_schedules_scheduleID) - [POST /reports/schedules/{scheduleID}/run-now](https://upfour.io/docs/api/reports#post_reports_schedules_scheduleID_run_now) - [POST /reports/{runID}/share](https://upfour.io/docs/api/reports#post_reports_runID_share) - [POST /status-pages](https://upfour.io/docs/api/status-pages#post_status_pages) - [POST /status-pages/{pageID}/sections/{sectionID}/resources](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_sections_sectionID_resources) - [PUT /status-pages/{pageID}/sections/{sectionID}/resources/{resourceID}](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_sections_sectionID_resources_resourceID) - [PUT /status-pages/{pageID}/structure](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_structure) - [POST /status-pages/{pageID}/subscribers](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers) - [POST /status-pages/{pageID}/subscribers/upload](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers_upload) - [POST /status-pages/{pageID}/subscribers/{subscriberID}/resend](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers_subscriberID_resend) - [POST /webhook-subscriptions](https://upfour.io/docs/api/webhook-subscriptions#post_webhook_subscriptions) - [POST /webhook-subscriptions/{subscriptionID}/redeliver/{deliveryID}](https://upfour.io/docs/api/webhook-subscriptions#post_webhook_subscriptions_subscriptionID_redeliver_deliveryID) - [POST /webhook-subscriptions/{subscriptionID}/test](https://upfour.io/docs/api/webhook-subscriptions#post_webhook_subscriptions_subscriptionID_test) #### [read_only_api_key](https://upfour.io/docs/api/errors#code-read_only_api_key) A read-only API key tried to write. - **Typical trigger:** Only for read-only API keys, on any POST, PUT, PATCH or DELETE operation. - **Fix:** Use a key with write permission for this call. - **Operations:** Every operation, under the condition in [Errors on every operation](https://upfour.io/docs/api/errors#every-operation). #### [session_required](https://upfour.io/docs/api/errors#code-session_required) The operation is available only to a signed-in browser session, not to an API key. - **Typical trigger:** Only for API keys, on routes that manage keys or are limited to the web app. - **Fix:** Do this in the app while signed in; API keys cannot call it. - **Operations:** Every operation, under the condition in [Errors on every operation](https://upfour.io/docs/api/errors#every-operation). ### 404 Not found #### [design_template_not_found](https://upfour.io/docs/api/errors#code-design_template_not_found) The design template does not exist or is not visible to the caller. - **Typical trigger:** A design template id in the path or body that is unknown or not visible to the caller. - **Fix:** List design templates and use an id from the list. - **Operations:** 12 operations - [POST /client-templates](https://upfour.io/docs/api/client-templates#post_client_templates) - [PUT /client-templates/{templateID}](https://upfour.io/docs/api/client-templates#put_client_templates_templateID) - [POST /client-templates/{templateID}/dry-run](https://upfour.io/docs/api/client-templates#post_client_templates_templateID_dry_run) - [POST /clients](https://upfour.io/docs/api/clients#post_clients) - [GET /status-page-design-templates](https://upfour.io/docs/api/status-page-design-templates#get_status_page_design_templates) - [POST /status-page-design-templates](https://upfour.io/docs/api/status-page-design-templates#post_status_page_design_templates) - [GET /status-page-design-templates/{templateID}](https://upfour.io/docs/api/status-page-design-templates#get_status_page_design_templates_templateID) - [PUT /status-page-design-templates/{templateID}](https://upfour.io/docs/api/status-page-design-templates#put_status_page_design_templates_templateID) - [DELETE /status-page-design-templates/{templateID}](https://upfour.io/docs/api/status-page-design-templates#delete_status_page_design_templates_templateID) - [GET /status-page-design-templates/{templateID}/assets/{kind}](https://upfour.io/docs/api/status-page-design-templates#get_status_page_design_templates_templateID_assets_kind) - [POST /status-pages](https://upfour.io/docs/api/status-pages#post_status_pages) - [PUT /status-pages/{pageID}](https://upfour.io/docs/api/status-pages#put_status_pages_pageID) #### [not_found](https://upfour.io/docs/api/errors#code-not_found) The resource does not exist or is outside the caller's scope. - **Typical trigger:** An id in the path that is unknown, belongs to another Company, or lies outside the key's client scope, and a client-portal session asking for a route the portal does not expose. - **Fix:** Check the id and that the key's client scope covers it; do not retry an unchanged request. - **Operations:** Under the condition in [Errors on every operation](https://upfour.io/docs/api/errors#every-operation), and from handlers on 196 of 271 operations. ### 409 Conflict #### [channel_in_use](https://upfour.io/docs/api/errors#code-channel_in_use) The alert channel is still used by monitors or alert plans, so it was not deleted. - **Typical trigger:** Deleting an alert channel that a monitor notifies or an alert plan step targets. \`details\` carries the counts \`monitors\` and \`alert_plans\`. - **Fix:** Remove the channel from those monitors and alert plan steps first, then delete it. - **Operations:** - [DELETE /notification_channels/{channelID}](https://upfour.io/docs/api/notification-channels#delete_notification_channels_channelID) #### [conflict](https://upfour.io/docs/api/errors#code-conflict) The request conflicts with the current state of the resource. - **Typical trigger:** Creating something that already exists, or acting on a resource whose state no longer allows it, such as a postmortem for an incident that is not resolved. - **Fix:** Fetch the current state, resolve the difference and send the request again. - **Operations:** 11 operations - [GET /incidents/{incidentID}/ai](https://upfour.io/docs/api/incidents#get_incidents_incidentID_ai) - [POST /incidents/{incidentID}/ai/postmortem](https://upfour.io/docs/api/incidents#post_incidents_incidentID_ai_postmortem) - [POST /incidents/{incidentID}/ai/summary](https://upfour.io/docs/api/incidents#post_incidents_incidentID_ai_summary) - [PUT /incidents/{incidentID}/ai/{kind}/rating](https://upfour.io/docs/api/incidents#put_incidents_incidentID_ai_kind_rating) - [POST /invites](https://upfour.io/docs/api/invites#post_invites) - [POST /monitors/batch](https://upfour.io/docs/api/monitors#post_monitors_batch) - [POST /organization](https://upfour.io/docs/api/organization#post_organization) - [PUT /organization/ai-quota](https://upfour.io/docs/api/organization#put_organization_ai_quota) - [GET /organization/ai-usage](https://upfour.io/docs/api/organization#get_organization_ai_usage) - [POST /reports/{runID}/share](https://upfour.io/docs/api/reports#post_reports_runID_share) - [DELETE /status-pages/{pageID}/updates/{updateID}](https://upfour.io/docs/api/status-pages#delete_status_pages_pageID_updates_updateID) #### [design_template_conflict](https://upfour.io/docs/api/errors#code-design_template_conflict) The design template was changed after the caller read it. - **Typical trigger:** Updating or applying a design template that was changed after the caller read it. - **Fix:** Reload the template and apply the change again. - **Operations:** 10 operations - [POST /client-templates/{templateID}/dry-run](https://upfour.io/docs/api/client-templates#post_client_templates_templateID_dry_run) - [POST /clients](https://upfour.io/docs/api/clients#post_clients) - [GET /status-page-design-templates](https://upfour.io/docs/api/status-page-design-templates#get_status_page_design_templates) - [POST /status-page-design-templates](https://upfour.io/docs/api/status-page-design-templates#post_status_page_design_templates) - [GET /status-page-design-templates/{templateID}](https://upfour.io/docs/api/status-page-design-templates#get_status_page_design_templates_templateID) - [PUT /status-page-design-templates/{templateID}](https://upfour.io/docs/api/status-page-design-templates#put_status_page_design_templates_templateID) - [DELETE /status-page-design-templates/{templateID}](https://upfour.io/docs/api/status-page-design-templates#delete_status_page_design_templates_templateID) - [GET /status-page-design-templates/{templateID}/assets/{kind}](https://upfour.io/docs/api/status-page-design-templates#get_status_page_design_templates_templateID_assets_kind) - [POST /status-pages](https://upfour.io/docs/api/status-pages#post_status_pages) - [PUT /status-pages/{pageID}](https://upfour.io/docs/api/status-pages#put_status_pages_pageID) #### [domain_claimed](https://upfour.io/docs/api/errors#code-domain_claimed) The domain is in use by another Company. - **Typical trigger:** Setting a custom domain on a status page when another Company already holds it; details.domain names it. - **Fix:** Prove control of the domain with the TXT record shown for the page, or use a different domain. - **Operations:** - [PUT /status-pages/{pageID}/custom-domain](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_custom_domain) #### [domain_move_stale](https://upfour.io/docs/api/errors#code-domain_move_stale) The page named as the domain's current holder is not the holder any more. - **Typical trigger:** Moving a domain with move_from_page_id set to a page that does not hold it; details.page names the real holder. - **Fix:** Read the domain's current holder and send the move again with that page. - **Operations:** - [PUT /status-pages/{pageID}/custom-domain](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_custom_domain) #### [domain_on_other_page](https://upfour.io/docs/api/errors#code-domain_on_other_page) The domain is used by another status page in the same Company. - **Typical trigger:** Setting a custom domain that another page of the Company already holds; details.page names that page. - **Fix:** Move the domain on purpose by sending the holder's page id as the page to move from, or choose another domain. - **Operations:** - [PUT /status-pages/{pageID}/custom-domain](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_custom_domain) #### [domain_removal_pending](https://upfour.io/docs/api/errors#code-domain_removal_pending) The domain is still being disconnected from its previous status page. - **Typical trigger:** Setting a domain right after it was removed from another page, before the disconnect finished. - **Fix:** Try again in a few minutes. - **Operations:** - [PUT /status-pages/{pageID}/custom-domain](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_custom_domain) #### [domain_used_in_company](https://upfour.io/docs/api/errors#code-domain_used_in_company) The domain is used by a status page in the Company that the caller cannot see. - **Typical trigger:** Setting a custom domain held by a page outside the caller's client scope; the page is not named. - **Fix:** Ask someone with access to that page to move or remove the domain, or choose another domain. - **Operations:** - [PUT /status-pages/{pageID}/custom-domain](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_custom_domain) #### [idempotency_key_conflict](https://upfour.io/docs/api/errors#code-idempotency_key_conflict) The Idempotency-Key was already used with a different request body. - **Typical trigger:** Only on POST and PATCH requests with an Idempotency-Key header that repeat a stored key with a changed body; details.original_request_at gives the first request's time. - **Fix:** Send the same body to replay the first answer, or use a new key for a new request. - **Operations:** Every operation, under the condition in [Errors on every operation](https://upfour.io/docs/api/errors#every-operation). #### [idempotency_replay_unavailable](https://upfour.io/docs/api/errors#code-idempotency_replay_unavailable) The request was already processed, but its stored answer cannot be replayed. - **Typical trigger:** Only on POST and PATCH requests with an Idempotency-Key header whose stored response was made with a different API key, or under different scopes, access level or client access, or is no longer visible to the caller. - **Fix:** Read the resource's current state instead of replaying, and use a new key for any new attempt. - **Operations:** Every operation, under the condition in [Errors on every operation](https://upfour.io/docs/api/errors#every-operation). #### [oncall_schedule_in_use](https://upfour.io/docs/api/errors#code-oncall_schedule_in_use) The on-call rotation is still a target of an alert plan step, so it was not deleted. - **Typical trigger:** Deleting an on-call rotation that an alert plan step targets as \`oncall:\`. \`details.alert_plans\` lists those plans. - **Fix:** Remove the rotation from the steps of those alert plans first, then delete it. - **Operations:** - [DELETE /oncall-schedules/{scheduleID}](https://upfour.io/docs/api/oncall-schedules#delete_oncall_schedules_scheduleID) #### [subscriber_import_running](https://upfour.io/docs/api/errors#code-subscriber_import_running) Another subscriber import of this Company is still running. - **Typical trigger:** Starting an import while a previous one has not finished. - **Fix:** Wait for the running import to finish, then start the next. - **Operations:** - [POST /status-pages/{pageID}/subscribers](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers) - [POST /status-pages/{pageID}/subscribers/upload](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers_upload) - [POST /status-pages/{pageID}/subscribers/{subscriberID}/resend](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers_subscriberID_resend) ### 413 Content too large #### [attachment_too_large](https://upfour.io/docs/api/errors#code-attachment_too_large) The attachment is over the size limit. - **Typical trigger:** Uploading an incident attachment larger than the limit; details.max_bytes gives it. - **Fix:** Compress or split the file below details.max_bytes. - **Operations:** - [POST /incidents/{incidentID}/attachments](https://upfour.io/docs/api/incidents#post_incidents_incidentID_attachments) #### [file_too_large](https://upfour.io/docs/api/errors#code-file_too_large) The uploaded file is over the size limit. - **Typical trigger:** Uploading a logo or image larger than details.max_bytes. - **Fix:** Reduce the file size and upload again. - **Operations:** - [POST /clients](https://upfour.io/docs/api/clients#post_clients) - [PUT /clients/{clientID}](https://upfour.io/docs/api/clients#put_clients_clientID) - [GET /organization/report-branding](https://upfour.io/docs/api/organization#get_organization_report_branding) - [PUT /organization/report-branding](https://upfour.io/docs/api/organization#put_organization_report_branding) - [PUT /status-pages/{pageID}/assets/{kind}](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_assets_kind) #### [import_too_large](https://upfour.io/docs/api/errors#code-import_too_large) The import body is over the size limit. - **Typical trigger:** A batch monitor import larger than details.max_bytes. - **Fix:** Split the import into several requests. - **Operations:** - [POST /monitors/batch](https://upfour.io/docs/api/monitors#post_monitors_batch) - [POST /monitors/batch/dry-run](https://upfour.io/docs/api/monitors#post_monitors_batch_dry_run) #### [subscriber_import_too_large](https://upfour.io/docs/api/errors#code-subscriber_import_too_large) The subscriber file is over the size limit. - **Typical trigger:** Uploading a subscriber file larger than details.max_bytes. - **Fix:** Split the file into smaller ones. - **Operations:** - [POST /status-pages/{pageID}/subscribers](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers) - [POST /status-pages/{pageID}/subscribers/upload](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers_upload) - [POST /status-pages/{pageID}/subscribers/{subscriberID}/resend](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers_subscriberID_resend) ### 422 Unprocessable content #### [ai_budget_above_platform_limit](https://upfour.io/docs/api/errors#code-ai_budget_above_platform_limit) The monthly AI budget is above the platform limit. - **Typical trigger:** Saving an AI budget larger than the platform allows; details.limit gives the maximum. - **Fix:** Set a budget at or below details.limit. - **Operations:** - [GET /incidents/{incidentID}/ai](https://upfour.io/docs/api/incidents#get_incidents_incidentID_ai) - [POST /incidents/{incidentID}/ai/postmortem](https://upfour.io/docs/api/incidents#post_incidents_incidentID_ai_postmortem) - [POST /incidents/{incidentID}/ai/summary](https://upfour.io/docs/api/incidents#post_incidents_incidentID_ai_summary) - [PUT /incidents/{incidentID}/ai/{kind}/rating](https://upfour.io/docs/api/incidents#put_incidents_incidentID_ai_kind_rating) - [PUT /organization/ai-quota](https://upfour.io/docs/api/organization#put_organization_ai_quota) - [GET /organization/ai-usage](https://upfour.io/docs/api/organization#get_organization_ai_usage) #### [ai_call_cap_above_platform_limit](https://upfour.io/docs/api/errors#code-ai_call_cap_above_platform_limit) The monthly AI call cap is above the platform limit. - **Typical trigger:** Saving an AI call cap larger than the platform allows; details.limit gives the maximum. - **Fix:** Set a cap at or below details.limit. - **Operations:** - [GET /incidents/{incidentID}/ai](https://upfour.io/docs/api/incidents#get_incidents_incidentID_ai) - [POST /incidents/{incidentID}/ai/postmortem](https://upfour.io/docs/api/incidents#post_incidents_incidentID_ai_postmortem) - [POST /incidents/{incidentID}/ai/summary](https://upfour.io/docs/api/incidents#post_incidents_incidentID_ai_summary) - [PUT /incidents/{incidentID}/ai/{kind}/rating](https://upfour.io/docs/api/incidents#put_incidents_incidentID_ai_kind_rating) - [PUT /organization/ai-quota](https://upfour.io/docs/api/organization#put_organization_ai_quota) - [GET /organization/ai-usage](https://upfour.io/docs/api/organization#get_organization_ai_usage) #### [provider_auth_rejected](https://upfour.io/docs/api/errors#code-provider_auth_rejected) The alert channel's provider rejected the credentials. - **Typical trigger:** A test alert whose provider answered 401 or 403. - **Fix:** Check the token, secret or account details of the channel. - **Operations:** - [POST /monitors/{monitorID}/test-alert](https://upfour.io/docs/api/monitors#post_monitors_monitorID_test_alert) - [POST /notification_channels/{channelID}/test](https://upfour.io/docs/api/notification-channels#post_notification_channels_channelID_test) #### [provider_rejected](https://upfour.io/docs/api/errors#code-provider_rejected) The alert channel's provider rejected the test. - **Typical trigger:** A test alert whose provider answered 400, 404, 405, 410 or 422. - **Fix:** Check the channel's address and settings. - **Operations:** - [POST /monitors/{monitorID}/test-alert](https://upfour.io/docs/api/monitors#post_monitors_monitorID_test_alert) - [POST /notification_channels/{channelID}/test](https://upfour.io/docs/api/notification-channels#post_notification_channels_channelID_test) #### [provider_unreachable](https://upfour.io/docs/api/errors#code-provider_unreachable) The alert channel's address cannot be reached. - **Typical trigger:** A test alert to a host that does not resolve, refuses the connection, has an invalid URL or an invalid HTTPS certificate. - **Fix:** Check the URL and its HTTPS certificate. - **Operations:** - [POST /monitors/{monitorID}/test-alert](https://upfour.io/docs/api/monitors#post_monitors_monitorID_test_alert) - [POST /notification_channels/{channelID}/test](https://upfour.io/docs/api/notification-channels#post_notification_channels_channelID_test) #### [subscriber_confirmations_full](https://upfour.io/docs/api/errors#code-subscriber_confirmations_full) Too many addresses are waiting for confirmation. - **Typical trigger:** An import or sign-up that would exceed the number of unconfirmed addresses allowed; nothing was added, details gives new_addresses and free_places. - **Fix:** Wait for confirmations or remove unconfirmed addresses, then add fewer addresses. - **Operations:** - [POST /status-pages/{pageID}/subscribers](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers) - [POST /status-pages/{pageID}/subscribers/upload](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers_upload) - [POST /status-pages/{pageID}/subscribers/{subscriberID}/resend](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers_subscriberID_resend) #### [subscriber_import_too_many_rows](https://upfour.io/docs/api/errors#code-subscriber_import_too_many_rows) The subscriber import has too many rows. - **Typical trigger:** An import with more rows than details.max_rows; nothing was imported. - **Fix:** Split the import into several files. - **Operations:** - [POST /status-pages/{pageID}/subscribers](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers) - [POST /status-pages/{pageID}/subscribers/upload](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers_upload) - [POST /status-pages/{pageID}/subscribers/{subscriberID}/resend](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers_subscriberID_resend) ### 429 Too many requests #### [confirmation_rate_limited](https://upfour.io/docs/api/errors#code-confirmation_rate_limited) Subscriber confirmation emails are being sent faster than the limits allow. - **Typical trigger:** Adding subscribers faster than the confirmation limits allow; details.reason says which limit, and details.retry_at and Retry-After say when to try again. - **Fix:** Wait until details.retry_at, then add the remaining addresses. - **Operations:** - [POST /status-pages/{pageID}/subscribers](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers) - [POST /status-pages/{pageID}/subscribers/upload](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers_upload) - [POST /status-pages/{pageID}/subscribers/{subscriberID}/resend](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers_subscriberID_resend) #### [rate_limit_exceeded](https://upfour.io/docs/api/errors#code-rate_limit_exceeded) Too many requests in a short time. - **Typical trigger:** The per-client or per-credential request limit was reached, or an endpoint with its own limit, such as test checks or AI calls, refused the call; Retry-After carries the wait in seconds where the code sets it. - **Fix:** Wait for Retry-After seconds, then retry with backoff and spread bursts over time. - **Operations:** Every operation, under the condition in [Errors on every operation](https://upfour.io/docs/api/errors#every-operation). Handlers also return it on: - [GET /incidents/{incidentID}/ai](https://upfour.io/docs/api/incidents#get_incidents_incidentID_ai) - [POST /incidents/{incidentID}/ai/postmortem](https://upfour.io/docs/api/incidents#post_incidents_incidentID_ai_postmortem) - [POST /incidents/{incidentID}/ai/summary](https://upfour.io/docs/api/incidents#post_incidents_incidentID_ai_summary) - [PUT /incidents/{incidentID}/ai/{kind}/rating](https://upfour.io/docs/api/incidents#put_incidents_incidentID_ai_kind_rating) - [PUT /organization/ai-quota](https://upfour.io/docs/api/organization#put_organization_ai_quota) - [GET /organization/ai-usage](https://upfour.io/docs/api/organization#get_organization_ai_usage) ### 500 Internal server error #### [internal_error](https://upfour.io/docs/api/errors#code-internal_error) 500 · 502 The server failed while handling the request. - **Typical trigger:** An unexpected failure or a failed upstream call; the message of a 5xx is replaced by "Internal server error" except for 503 and a few provider failures, and the cause stays in the server log. - **Fix:** Retry with backoff, and send request_id to support if it keeps failing. - **Operations:** Under the condition in [Errors on every operation](https://upfour.io/docs/api/errors#every-operation), and from handlers on 118 of 271 operations. ### 502 Bad gateway #### [channel_test_failed](https://upfour.io/docs/api/errors#code-channel_test_failed) The alert channel's provider could not deliver the test. - **Typical trigger:** Sending a test alert to a channel when the provider fails in a way that fits none of the other provider codes. - **Fix:** Check the channel's address and settings, then test again. - **Operations:** - [POST /monitors/{monitorID}/test-alert](https://upfour.io/docs/api/monitors#post_monitors_monitorID_test_alert) - [POST /notification_channels/{channelID}/test](https://upfour.io/docs/api/notification-channels#post_notification_channels_channelID_test) ### 503 Service unavailable #### [notification_setup_incomplete](https://upfour.io/docs/api/errors#code-notification_setup_incomplete) The Company's setup is incomplete, so the channel cannot be created. - **Typical trigger:** Creating an alert channel in a Company that has no default team. - **Fix:** Contact support with the request_id so the setup can be completed. - **Operations:** - [POST /notification_channels](https://upfour.io/docs/api/notification-channels#post_notification_channels) #### [quota_gate_unavailable](https://upfour.io/docs/api/errors#code-quota_gate_unavailable) The limit check itself failed, so the action was not carried out. - **Typical trigger:** The quota service could not decide while the request ran. - **Fix:** Retry in a moment; nothing was changed. - **Operations:** 21 operations - [POST /escalation-policies](https://upfour.io/docs/api/escalation-policies#post_escalation_policies) - [PUT /escalation-policies/{policyID}](https://upfour.io/docs/api/escalation-policies#put_escalation_policies_policyID) - [POST /incidents/{incidentID}/attachments](https://upfour.io/docs/api/incidents#post_incidents_incidentID_attachments) - [POST /invites](https://upfour.io/docs/api/invites#post_invites) - [POST /invites/{inviteID}/resend](https://upfour.io/docs/api/invites#post_invites_inviteID_resend) - [POST /monitors/{monitorID}/test-alert](https://upfour.io/docs/api/monitors#post_monitors_monitorID_test_alert) - [POST /notification_channels/{channelID}/test](https://upfour.io/docs/api/notification-channels#post_notification_channels_channelID_test) - [POST /reports/schedules](https://upfour.io/docs/api/reports#post_reports_schedules) - [PUT /reports/schedules/{scheduleID}](https://upfour.io/docs/api/reports#put_reports_schedules_scheduleID) - [POST /reports/schedules/{scheduleID}/run-now](https://upfour.io/docs/api/reports#post_reports_schedules_scheduleID_run_now) - [POST /reports/{runID}/share](https://upfour.io/docs/api/reports#post_reports_runID_share) - [POST /status-pages](https://upfour.io/docs/api/status-pages#post_status_pages) - [POST /status-pages/{pageID}/sections/{sectionID}/resources](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_sections_sectionID_resources) - [PUT /status-pages/{pageID}/sections/{sectionID}/resources/{resourceID}](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_sections_sectionID_resources_resourceID) - [PUT /status-pages/{pageID}/structure](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_structure) - [POST /status-pages/{pageID}/subscribers](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers) - [POST /status-pages/{pageID}/subscribers/upload](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers_upload) - [POST /status-pages/{pageID}/subscribers/{subscriberID}/resend](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers_subscriberID_resend) - [POST /webhook-subscriptions](https://upfour.io/docs/api/webhook-subscriptions#post_webhook_subscriptions) - [POST /webhook-subscriptions/{subscriptionID}/redeliver/{deliveryID}](https://upfour.io/docs/api/webhook-subscriptions#post_webhook_subscriptions_subscriptionID_redeliver_deliveryID) - [POST /webhook-subscriptions/{subscriptionID}/test](https://upfour.io/docs/api/webhook-subscriptions#post_webhook_subscriptions_subscriptionID_test) #### [service_unavailable](https://upfour.io/docs/api/errors#code-service_unavailable) The service or a dependency the request needs cannot take it right now. - **Typical trigger:** A dependency such as the AI provider, the checkout or the report runner is not available; the action was not carried out. - **Fix:** Retry with backoff; nothing was changed by the failed request. - **Operations:** 10 operations - [GET /incidents/{incidentID}/ai](https://upfour.io/docs/api/incidents#get_incidents_incidentID_ai) - [POST /incidents/{incidentID}/ai/postmortem](https://upfour.io/docs/api/incidents#post_incidents_incidentID_ai_postmortem) - [POST /incidents/{incidentID}/ai/summary](https://upfour.io/docs/api/incidents#post_incidents_incidentID_ai_summary) - [PUT /incidents/{incidentID}/ai/{kind}/rating](https://upfour.io/docs/api/incidents#put_incidents_incidentID_ai_kind_rating) - [POST /incidents/{incidentID}/attachments](https://upfour.io/docs/api/incidents#post_incidents_incidentID_attachments) - [POST /monitors/batch](https://upfour.io/docs/api/monitors#post_monitors_batch) - [PUT /organization/ai-quota](https://upfour.io/docs/api/organization#put_organization_ai_quota) - [GET /organization/ai-usage](https://upfour.io/docs/api/organization#get_organization_ai_usage) - [POST /reports/schedules/{scheduleID}/run-now](https://upfour.io/docs/api/reports#post_reports_schedules_scheduleID_run_now) - [POST /reports/{runID}/share](https://upfour.io/docs/api/reports#post_reports_runID_share) #### [test_unavailable](https://upfour.io/docs/api/errors#code-test_unavailable) Sending a test is not possible right now. - **Typical trigger:** A test alert request while the test sender cannot take it. - **Fix:** Try again in a moment. - **Operations:** - [POST /monitors/{monitorID}/test-alert](https://upfour.io/docs/api/monitors#post_monitors_monitorID_test_alert) - [POST /notification_channels/{channelID}/test](https://upfour.io/docs/api/notification-channels#post_notification_channels_channelID_test) ### 504 Gateway timeout #### [provider_timeout](https://upfour.io/docs/api/errors#code-provider_timeout) The alert channel's provider did not answer in time. - **Typical trigger:** A test alert whose provider took too long to respond. - **Fix:** Check the address and try again later. - **Operations:** - [POST /monitors/{monitorID}/test-alert](https://upfour.io/docs/api/monitors#post_monitors_monitorID_test_alert) - [POST /notification_channels/{channelID}/test](https://upfour.io/docs/api/notification-channels#post_notification_channels_channelID_test) ## Codes of the web app The routes behind these 40 codes are not part of the public API: sign-in, billing, add-ons, support messages and test checks. API keys do not reach them, and nothing here is promised to stay. They are listed so that a code you find in a log or in the app has an explanation. #### [account_deletion_invalid_password](https://upfour.io/docs/api/errors#code-account_deletion_invalid_password) 400 The password given to confirm the account deletion is wrong. - **Typical trigger:** Deleting the signed-in account with a current password that does not match. - **Fix:** Send the account's current password, or reset it first. #### [account_deletion_owner_transfer_required](https://upfour.io/docs/api/errors#code-account_deletion_owner_transfer_required) 409 The account owns a Company and must hand ownership over before it can be deleted. - **Typical trigger:** Deleting the account of a Company owner. - **Fix:** Transfer ownership to another member, or ask the up4 team to delete the whole Company. #### [account_deletion_unavailable](https://upfour.io/docs/api/errors#code-account_deletion_unavailable) 503 Account deletion cannot be carried out right now. - **Typical trigger:** A server-side failure while processing the deletion. - **Fix:** Email privacy@upfour.io so the up4 team can process it. #### [addon_change_open](https://upfour.io/docs/api/errors#code-addon_change_open) 409 An add-on purchase is still waiting for its payment, so the plan cannot change. - **Typical trigger:** Changing the plan while an earlier add-on purchase is open. - **Fix:** Finish or cancel the open add-on purchase, then change the plan. #### [addons_change_failed](https://upfour.io/docs/api/errors#code-addons_change_failed) 502 Stripe could not be reached for an add-on change; nothing was changed. - **Typical trigger:** Stripe failed or timed out while the add-on change was applied. - **Fix:** Retry in a few minutes; the change applies only when a request succeeds. #### [below_reduction_floor](https://upfour.io/docs/api/errors#code-below_reduction_floor) 422 The new pack quantity is lower than current use needs. - **Typical trigger:** Reducing an add-on to fewer packs than what the Company uses. - **Fix:** Choose a quantity at or above what is in use, or free up usage first. #### [billing_portal_failed](https://upfour.io/docs/api/errors#code-billing_portal_failed) 502 The billing portal could not be opened. - **Typical trigger:** Stripe failed or timed out while the portal session was created; the plan and payment details are unchanged. - **Fix:** Retry in a few minutes, or message the up4 team. #### [checker_busy](https://upfour.io/docs/api/errors#code-checker_busy) 503 The checkers are too busy for a test check. - **Typical trigger:** A test check while the checker pool or the target address is at its test limit. - **Fix:** Try again in a few seconds. #### [checker_unavailable](https://upfour.io/docs/api/errors#code-checker_unavailable) 503 The test check could not be started. - **Typical trigger:** A test check when the request cannot be sent to a checker. - **Fix:** Try again later. #### [checkout_failed](https://upfour.io/docs/api/errors#code-checkout_failed) 502 The checkout for the plan could not be opened. - **Typical trigger:** Stripe failed or timed out while the checkout session was created; nothing was charged. - **Fix:** Retry in a few minutes, or message the up4 team. #### [custom_domain_slots_retired](https://upfour.io/docs/api/errors#code-custom_domain_slots_retired) 410 Custom domain slots are no longer sold. - **Typical trigger:** Buying or changing a custom domain slot; a connected custom domain is billed per day from the credit. - **Fix:** Connect the domain to a status page directly. #### [idempotency_key_required](https://upfour.io/docs/api/errors#code-idempotency_key_required) 400 The request needs an Idempotency-Key header. - **Typical trigger:** An add-on or support request sent without Idempotency-Key. - **Fix:** Send a unique Idempotency-Key header and keep it for retries. #### [invalid_code](https://upfour.io/docs/api/errors#code-invalid_code) 401 The two-factor code did not match. - **Typical trigger:** Confirming two-factor setup at sign-in with a wrong or expired code. - **Fix:** Check the time on the phone and enter the current code. #### [invalid_quantities](https://upfour.io/docs/api/errors#code-invalid_quantities) 400 The add-on quantities are not valid. - **Typical trigger:** Changing add-ons with no quantity, an unknown add-on, a quantity outside what can be booked, or a change that alters nothing; details names the reason. - **Fix:** Send quantities within the allowed range and at least one that differs from the current one. #### [invalid_signature](https://upfour.io/docs/api/errors#code-invalid_signature) 401 The heartbeat ping's signature is not valid. - **Typical trigger:** Pinging a scheduled job that requires signed pings with a missing or wrong X-Up4-Signature header. - **Fix:** Sign the ping as documented for the job. #### [invite_organization_conflict](https://upfour.io/docs/api/errors#code-invite_organization_conflict) 409 The account already belongs to another Company. - **Typical trigger:** Accepting an invite, signing in through SSO or verifying a magic link with an account that is a member of a different Company. - **Fix:** Use a different email address, or leave the other Company first. #### [no_live_region](https://upfour.io/docs/api/errors#code-no_live_region) 503 No checker is online in the selected region. - **Typical trigger:** A test check for a region with no live checker. - **Fix:** Try again in a minute or select another region. #### [no_pending](https://upfour.io/docs/api/errors#code-no_pending) 404 No add-on purchase is waiting for its payment. - **Typical trigger:** Cancelling a pending purchase when none is open. - **Fix:** Refresh the add-on state; there is nothing to cancel. #### [not_purchasable](https://upfour.io/docs/api/errors#code-not_purchasable) 409 The add-on cannot be bought or changed in the current state. - **Typical trigger:** A purchase or plan change when an add-on has no price, the Company is read-only or another condition applies; details.reason names it. - **Fix:** Act on details.reason, or message the up4 team. #### [nothing_scheduled](https://upfour.io/docs/api/errors#code-nothing_scheduled) 404 No reduction is scheduled for the add-on. - **Typical trigger:** Withdrawing a scheduled reduction when none exists for that add-on. - **Fix:** Refresh the add-on state; there is nothing to withdraw. #### [password_required](https://upfour.io/docs/api/errors#code-password_required) 409 The account has no password yet. - **Typical trigger:** Starting two-factor setup at sign-in for an account without a password. - **Fix:** Use Forgot password to set one, sign in again and then set up two-factor. #### [payment_pending](https://upfour.io/docs/api/errors#code-payment_pending) 409 A change to the subscription is still waiting for its payment. - **Typical trigger:** Changing the plan while the subscription has an unpaid pending update. - **Fix:** Finish the pending payment, then change the plan. #### [pending_lapses](https://upfour.io/docs/api/errors#code-pending_lapses) 409 The pending purchase cannot be cancelled and lapses on its own. - **Typical trigger:** Cancelling a purchase that is not paid yet. - **Fix:** Wait for it to lapse if it is not paid. #### [pending_open](https://upfour.io/docs/api/errors#code-pending_open) 409 An add-on purchase is still waiting for its payment. - **Typical trigger:** Starting, confirming or changing an add-on purchase while an earlier one is open. - **Fix:** Finish or let the open purchase lapse before starting another. #### [price_unavailable](https://upfour.io/docs/api/errors#code-price_unavailable) 503 The price for the monitor is currently unavailable. - **Typical trigger:** Reading a monitor's month-to-date cost while a meter price is missing; the API never answers 0 for a missing price. - **Fix:** Try again later. #### [probe_timeout](https://upfour.io/docs/api/errors#code-probe_timeout) 504 No checker answered the test check in time. - **Typical trigger:** A test check that got no reply before its deadline. - **Fix:** Try again; if it keeps failing, check the target and region. #### [quote_expired](https://upfour.io/docs/api/errors#code-quote_expired) 409 The add-on price is no longer valid. - **Typical trigger:** Confirming a quote after its price expired. - **Fix:** Review the change again to get a new quote. #### [quote_not_found](https://upfour.io/docs/api/errors#code-quote_not_found) 404 The add-on quote does not exist. - **Typical trigger:** Confirming a quote id that is unknown. - **Fix:** Request a new quote and confirm that one. #### [subscription_change_failed](https://upfour.io/docs/api/errors#code-subscription_change_failed) 502 Stripe could not be reached for the subscription change; nothing was applied. - **Typical trigger:** Stripe failed or timed out while the plan change was applied. - **Fix:** Retry in a few minutes, or message the up4 team. #### [subscriptions_unavailable](https://upfour.io/docs/api/errors#code-subscriptions_unavailable) 409 New status page subscriptions are not possible right now. - **Typical trigger:** A visitor signing up or confirming while the Company's subscriber pool is full. - **Fix:** Try again later. #### [support_category_not_available](https://upfour.io/docs/api/errors#code-support_category_not_available) 403 The message category is not available to this user. - **Typical trigger:** A client-portal user sending a message other than a problem report. - **Fix:** Choose the problem report category. #### [support_delivery_failed](https://upfour.io/docs/api/errors#code-support_delivery_failed) 503 The support message could not be delivered. - **Typical trigger:** The mail send failed after the request was accepted. - **Fix:** Email the support address named in the message directly. #### [support_messages_disabled](https://upfour.io/docs/api/errors#code-support_messages_disabled) 503 In-app support messages are switched off. - **Typical trigger:** Sending a support message while the feature is disabled. - **Fix:** Email the support address named in the message directly. #### [support_scope_invalid](https://upfour.io/docs/api/errors#code-support_scope_invalid) 400 The message scope is not valid. - **Typical trigger:** Sending a support message that refers to a scope the sender cannot use. - **Fix:** Pick a scope the sender can access, or send the message without one. #### [terms_not_accepted](https://upfour.io/docs/api/errors#code-terms_not_accepted) 400 The request does not accept the current terms of service. - **Typical trigger:** Signing up, starting a plan checkout or topping up the wallet without \`accept_terms: true\` and a \`terms_version\`. - **Fix:** Show the terms to the person, then send \`accept_terms: true\` with the current \`terms_version\`. #### [terms_notice_not_due](https://upfour.io/docs/api/errors#code-terms_notice_not_due) 409 The current terms version cannot be announced any more. - **Typical trigger:** An operator sends the change notice for a version whose effective date is today or in the past. - **Fix:** Publish a new version with a future effective date first (docs/ops/terms-versions.md). #### [terms_version_outdated](https://upfour.io/docs/api/errors#code-terms_version_outdated) 409 The terms version the request accepts is not the current one. - **Typical trigger:** A \`terms_version\` from a page loaded before a new version of the terms of service was published. - **Fix:** Reload the page, show the current terms and send their version. #### [topup_failed](https://upfour.io/docs/api/errors#code-topup_failed) 502 The payment page for the top-up could not be opened. - **Typical trigger:** Stripe failed or timed out while the top-up was prepared; nothing was charged. - **Fix:** Retry in a few minutes, or message the up4 team. #### [two_factor_already_enabled](https://upfour.io/docs/api/errors#code-two_factor_already_enabled) 409 Two-factor authentication is already on for the account. - **Typical trigger:** Starting two-factor setup for an account that already has it. - **Fix:** Sign in again. #### [two_factor_setup_expired](https://upfour.io/docs/api/errors#code-two_factor_setup_expired) 401 The two-factor setup step has expired. - **Typical trigger:** Continuing two-factor setup after its short window ended. - **Fix:** Sign in again to restart the setup. --- # Pagination > How list endpoints page: signed cursors on GET /monitors, page and per_page on incidents, and a single full array everywhere else, with the exact parameters and errors. Source: https://upfour.io/docs/api/pagination List endpoints do not all page the same way. `GET /monitors` uses signed cursors, the incident lists use `page` and `per_page`, and most other lists return everything in one array. | Endpoint | How it pages | Response | | --- | --- | --- | | `GET /monitors` | `limit` and `cursor` | `data` array and a real `pagination` object | | `GET /incidents`, `GET /monitors/{id}/incidents` | `page` and `per_page` | `data` object with `items`, `page`, `per_page`, `total`, `counts`; no `pagination` | | Most other lists | None. The whole list comes back. | `data` array and a placeholder `pagination` object | Some reference pages list `limit` and `cursor` on every list operation. Only `GET /monitors` reads `cursor`; the others ignore it. ## Cursor pages: GET /monitors ```bash curl -s -G https://app.upfour.io/v1/monitors \ -H "Authorization: Bearer $UP4_API_KEY" \ --data-urlencode "limit=100" \ --data-urlencode "sort=name" \ --data-urlencode "status=DOWN" ``` ```json { "data": [ { "id": "…", "name": "Lakeside Bakery site", … } ], "pagination": { "cursor": null, "next_cursor": "eyJ2IjoxLC…", "prev_cursor": null, "limit": 100, "has_more": true } } ``` Repeat with `cursor` set to `next_cursor` until `has_more` is false: ``` cursor="" while true; do page=$(curl -s -G https://app.upfour.io/v1/monitors \ -H "Authorization: Bearer $UP4_API_KEY" \ --data-urlencode "limit=100" \ --data-urlencode "cursor=$cursor") echo "$page" | jq -r '.data[].name' cursor=$(echo "$page" | jq -r '.pagination.next_cursor // empty') [ -z "$cursor" ] && break done ``` ### Parameters | Parameter | Meaning | | --- | --- | | `limit` | Page size. Default 50, at most 250; a larger value is lowered to 250, and a missing, zero or non-numeric value means 50. | | `cursor` | The next_cursor of the previous page. Leave it out, or empty, for the first page. | | `sort` | created_at, name or next_check_at; a leading - sorts descending. Default -created_at. Anything else is 400. | | `status` | UP, DOWN or PAUSED, not case-sensitive. state works as an alias. Anything else is 400. | | `monitor_type` | http, tcp, ping or dns. Anything else is 400. | | `region` | Monitors that run in this region. | | `q` | Case-insensitive text search over name, URL, type, TCP host, DNS query and region. | | `monitor_group_id` | A monitor group id. A value that is not a UUID is 400. | | `severity, severity_group` | Monitors with an open incident of this severity or severity group. | | `client_id, all_clients` | Which clients to list; see client scope. Not part of the cursor check. | ### Response fields | Field | Meaning | | --- | --- | | `data` | The page of monitors. | | `pagination.limit` | The page size that was applied, after the clamp. | | `pagination.has_more` | True when another page exists. | | `pagination.next_cursor` | Pass it as cursor for the next page. null when has_more is false. | | `pagination.cursor` | The cursor you sent, or null on the first page. | | `pagination.prev_cursor` | Always null. Paging goes forward only. | There is no total count. Rows are ordered by the sort field and then by id, so the order is stable. ### The cursor A cursor is an opaque, signed string. It is valid for 24 hours and is tied to the sort and to the filters of the request that produced it. Treat it as a value to hand back, not as something to build or read. | error.code | Status | Cause | | --- | --- | --- | | [cursor_invalid](https://upfour.io/docs/api/errors#code-cursor_invalid) | 400 | The cursor was not issued by up4, was changed, or is malformed. | | [cursor_expired](https://upfour.io/docs/api/errors#code-cursor_expired) | 400 | The cursor is older than 24 hours. | | [cursor_parameters_changed](https://upfour.io/docs/api/errors#code-cursor_parameters_changed) | 400 | sort, status, monitor_type, region, q, monitor_group_id, severity or severity_group differ from the request that produced the cursor. | After any of the three, start again without `cursor`. ## Page numbers: incidents `GET /incidents` and `GET /monitors/{id}/incidents` take `page` (from 1) and `per_page` (default 25, at most 100). The result is one object: the page of incidents in `items`, the position in `page` and `per_page`, the number of incidents that match your filters in `total`, and `counts`, summary counters that ignore the page and the list filters. ```bash curl -s -G https://app.upfour.io/v1/incidents \ -H "Authorization: Bearer $UP4_API_KEY" \ --data-urlencode "per_page=50" \ --data-urlencode "page=2" ``` ## Everything at once: the other lists The remaining lists return every row in `data`. up4 still adds a `pagination` block so every list has the same outline, but it is a placeholder: `limit` is always 50, `has_more` is false and the cursors are null, whatever the length of the array. A few lists take their own `limit`, for example `GET /heartbeats/{id}/pings`, which returns 25 pings by default and at most 10,000; their reference pages say so. ## Common mistakes - **Sending `page` or `per_page` to `/monitors`.** That is `400 bad_request`: the public API uses `cursor` and `limit` there. - **Changing sort or a filter between pages.** The cursor check fails with `cursor_parameters_changed`. Changing `limit` is fine. - **Changing `client_id` or `all_clients` between pages.** They are not part of the cursor check, so up4 does not warn you; keep them the same. - **Stopping when a page is shorter than `limit`.** Stop on `has_more` being false or `next_cursor` being null. - **Believing the placeholder.** `has_more: false` on a list outside the table above says nothing about its size. - **Expecting a snapshot.** Each page starts after the last row of the previous one. With the default sort, a monitor created while you page sorts before your position and is not in this run. - **Keeping a cursor for later.** It expires after 24 hours. --- # Rate limits > The up4 API rate limits: the three buckets, what X-RateLimit and Retry-After actually report, and how to back off after a 429. Source: https://upfour.io/docs/api/rate-limits The API limits requests with token buckets. A bucket holds a burst, refills at a steady rate and answers [429 rate_limit_exceeded](https://upfour.io/docs/api/errors#code-rate_limit_exceeded) when it is empty. ## The buckets A bucket starts full. Each request takes one token and tokens come back at the rate in the table. At 1,200 per minute that is 20 requests per second, and a burst of up to 300 at once is fine. | Bucket | Applies to | Refill | Burst | | --- | --- | --- | --- | | Per IP address | All requests from one address except the check-ins below, authenticated or not. Not counted: OPTIONS, /v1/health, /v1/readyz and /v1/password/forgot. | 3,000 per minute | 300 | | Per API key | Every authenticated request, counted per key. | 1,200 per minute | 300 | | Per heartbeat slug and IP | Check-ins to /v1/heartbeat/{slug}. These calls need no key. | 120 per minute | 20 | An API call takes a token from the IP bucket first and then from the key bucket, and fails if either is empty. Idempotent replays count like any other request. Login, registration, contact and the public status page forms have their own, tighter buckets per IP address. ## Headers | Header | Value | | --- | --- | | `X-RateLimit-Limit` | The refill rate of the IP bucket in requests per minute. | | `X-RateLimit-Remaining` | Whole requests left in the IP bucket after this one. | | `X-RateLimit-Reset` | A Unix timestamp in seconds, the moment the IP bucket has refilled one request. The bucket refills continuously, so this is about now, not the end of a window. | | `Retry-After` | Only on 429: whole seconds to wait, at least 1. | The `X-RateLimit-*` headers describe the IP bucket, not the key bucket. A key that has used up its own 300-request burst gets `429` and `Retry-After: 1` while the headers still show a healthy IP bucket. Treat `429` and `Retry-After` as the signal, not `Remaining`. ```bash curl -si https://app.upfour.io/v1/monitors \ -H "Authorization: Bearer $UP4_API_KEY" | grep -i -E '^(HTTP|x-ratelimit|retry-after)' ``` ``` HTTP/2 429 retry-after: 1 x-ratelimit-limit: 3000 x-ratelimit-remaining: 299 x-ratelimit-reset: 1793102400 { "error": { "code": "rate_limit_exceeded", "message": "Rate limit exceeded", "request_id": "req_…" } } ``` ## After a 429 1. Stop sending for the number of seconds in `Retry-After`. 2. Retry the same request. A `POST` that carries an `Idempotency-Key` is safe to repeat. 3. If the next answer is again 429, wait longer each time and add a random delay, so parallel workers do not retry together. Spread bulk work instead of racing it: `POST /monitors/batch` and `POST /monitors/bulk` create or change many monitors in one request. ## How the counts are kept When up4 runs its shared counter store, all API servers count together. If that store is not available, each server counts for itself, so the ceiling you can reach during such a period can be higher, never unlimited. ## Common mistakes - Waiting until `X-RateLimit-Reset`. It is almost now; use `Retry-After`. - Reading `Remaining` as your key’s budget. It is the IP bucket’s. - Running many workers behind one address and one key. They share both buckets. - Retrying at once in a tight loop. Every retry takes a token too. - Polling for state changes. A [webhook](https://upfour.io/docs/api/webhooks.md) sends them to you. --- # Idempotency > How the Idempotency-Key header makes POST and PATCH retries safe: what is stored, for how long, and which 409 you get when a key is reused with another body. Source: https://upfour.io/docs/api/idempotency Send an `Idempotency-Key` with a `POST` or `PATCH`. If the connection drops and you send the same request again, up4 returns the first answer instead of creating a second monitor. ## Use the header ``` KEY=$(uuidgen) curl -si -X POST https://app.upfour.io/v1/monitors \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $KEY" \ -d '{"name":"Example","url":"https://example.com","monitor_type":"http"}' \ | grep -i -E '^(HTTP|idempotency-status)' ``` ``` HTTP/2 201 idempotency-status: stored ``` Run the same command again with the same `KEY`: ``` HTTP/2 201 idempotency-status: replayed ``` ## The rules | Topic | Behavior | | --- | --- | | **Header** | `Idempotency-Key`, any non-empty text. A UUID is a good choice. An empty or missing header turns the feature off for that request. | | **Methods** | `POST` and `PATCH`. `PUT`, `DELETE` and `GET` ignore the header. | | **Not covered** | `POST /monitors/batch` ignores this layer; it requires the header and keeps its own receipt (same key and body: the stored receipt, a different body: a refusal). | | **Required** | `POST /incidents/{incidentID}/escalate` and `POST /escalation-policies/{policyID}/test` answer `400` without the header and accept at most 128 characters. Everywhere else it is optional and has no length limit. | | **A key belongs to** | Your Company, your user, the method and path of the request (not the query string), and the key text. The same key on another path is a different key. | | **Body comparison** | JSON bodies are compared as JSON, so whitespace and key order do not matter. Other bodies are compared byte for byte. | | **Stored for** | 24 hours after the first response, then deleted. After that the key is free again: the next request with it runs and its response is stored anew. | | **What is stored** | Responses with a status from 200 to 499 and a JSON body, including validation errors. Not stored: 5xx and responses without a body. | | **Secrets** | The response is stored as sent. A replay of `POST /webhook-subscriptions` or `POST /webhook-subscriptions/{id}/rotate-secret` therefore carries the same one-time `secret` again within those 24 hours, also after a later rotation made it obsolete. See [Get the signing secret](https://upfour.io/docs/api/webhooks#secret). | | **Replays** | Return the stored status, content type and body with `Idempotency-Status: replayed`. | | **Rate limits** | A replay counts as a request. | ## What you get | Situation | Result | | --- | --- | | No receipt for the key | The request runs. A response with status 200 to 499 and a JSON body is stored and carries Idempotency-Status: stored. | | Receipt, same body | The stored status and body come back with Idempotency-Status: replayed. The request does not run again. | | Receipt, different body | 409 idempotency_key_conflict. details.original_request_at holds the time of the first request. | | Receipt, but made with another API key or under changed access | 409 idempotency_replay_unavailable. The request does not run again. | | Response was 5xx, 204 or had no JSON body | Nothing is stored. A retry with the same key runs the request again. | ```json { "error": { "code": "idempotency_key_conflict", "message": "Idempotency key was already used with a different request body", "request_id": "req_…", "details": { "original_request_at": "2026-10-27T09:14:02Z" } } } ``` The receipt remembers who sent the request: the API key, its scopes and access level, and its client access. A retry with another key, or after one of those changed, gets [idempotency_replay_unavailable](https://upfour.io/docs/api/errors#code-idempotency_replay_unavailable) instead of a replay, and the request is not run again. Look at the resource to see what happened. ## Common mistakes - **A new key on every retry.** Generate the key once per intended operation and reuse it for every attempt of that operation. - **One key for different operations.** The second body gets [409 idempotency_key_conflict](https://upfour.io/docs/api/errors#code-idempotency_key_conflict). Use one key per operation. - **Fixing a rejected request under the same key.** A `400` is stored too. Resending the same body replays the `400`; resending a corrected body is a conflict. Use a new key for the corrected request. - **Retrying while the first request is still running.** The receipt is written when the first request finishes, so an overlapping retry is not guaranteed to be caught. Wait for the response or your timeout. - **Switching to another API key for the retry.** See `idempotency_replay_unavailable` above. - **Relying on it for `DELETE` or `PUT`.** The header is ignored on those methods. --- # Versioning > What versioning means for the up4 API today: one URL version, what X-API-Version and a key’s api_version do, and where breaking changes are announced. Source: https://upfour.io/docs/api/versioning The API has one version, `/v1`. Changes land inside it, and the [changelog](https://upfour.io/docs/api/changelog.md) records each one, including the ones that can break a client. ## What the API does today | Topic | Behavior | | --- | --- | | **URL version** | Every operation is served under `/v1`. There is no other version and no `/v2`. | | **`X-API-Version` request header** | Accepted. Any non-empty value marks the request as a public API request, which gives it the `data` envelope and, on `GET /monitors`, cursor pagination. The value itself is not read. | | **`api_version` of a key** | Each key stores one, `2026-05-07` by default. `GET /me` returns it. No behavior changes with it. | | **`Deprecation` and `Sunset` headers** | The API sends neither. | ```bash curl -s https://app.upfour.io/v1/me \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "X-API-Version: 2026-05-07" ``` The call above behaves exactly like one without the header, because a request made with an API key already gets the public API behavior. ## How changes reach you The changelog labels every change to the contract as **breaking**, **added**, **changed**, **deprecated** or **documented**, and has a “Breaking only” filter. It is generated from the history of the OpenAPI file. Watch it, or the [OpenAPI file](https://upfour.io/api/openapi.json) itself, for the operations you use. The stated intention is that a breaking change gets its own version, `/v2`, with 12 months of parallel support and `Deprecation` and `Sunset` headers. That is an intention, not behavior you can test: there is no `/v2` and no header today, and the changelog shows breaking changes that were made inside `/v1`. Plan for the changelog, not for a header. ## Write a tolerant client - Ignore response fields you do not know; new ones appear as **added**. - Do not depend on the order of keys, or of `scopes` in `GET /me`. - Branch on `error.code`, not on `error.message`. See [responses](https://upfour.io/docs/api/responses.md). - Cover the operations you use with tests. A changed required scope is labeled breaking in the changelog. ## Common mistakes - Sending `X-API-Version` to pin an old behavior. It pins nothing. - Changing a key’s `api_version` to get a different response. There is no such effect. - Waiting for a `Sunset` header before checking for removals. Read the changelog. - Building against `/v2` paths. They do not exist. --- # Client scope > How an API call selects a client in agency mode: the client_id and all_clients query parameters, client_id in request bodies, keys limited to clients, and what happens when agency mode is off. Source: https://upfour.io/docs/api/client-scope In agency mode, monitors and other resources can belong to a client. The API has no “current client”: each request says which clients it means, and the key decides which ones it may see. ## Agency mode must be on Clients are available when agency mode is switched on for the Company. While it is off: - Every client endpoint, such as `GET /clients`, answers `404 Not found`. - List endpoints ignore `client_id` and `all_clients` and return company-level resources only. ## Choose clients when you read Resources that can belong to a client carry an optional `client_id`: monitors, scheduled jobs, status pages, incidents, escalation policies, on-call schedules and playbooks. A resource without one is company-level. List endpoints take two query parameters: | You send | You get | | --- | --- | | nothing | Company-level resources only. | | `client_id=` | The resources of that one client. | | `all_clients=true` | Everything the key may see: all clients and company-level resources. It wins over `client_id`. | ```bash export CLIENT_ID="…" # from GET /clients curl -s -G https://app.upfour.io/v1/monitors \ -H "Authorization: Bearer $UP4_API_KEY" \ --data-urlencode "client_id=$CLIENT_ID" curl -s -G https://app.upfour.io/v1/monitors \ -H "Authorization: Bearer $UP4_API_KEY" \ --data-urlencode "all_clients=true" ``` - `all_clients` counts only when it is exactly `true`. - A `client_id` that is not a UUID does not fail. The call falls back to company-level resources. - There is no header for this. The choice belongs to each request, not to the key. ## Choose a client when you write Create and update calls take `client_id` in the JSON body. Leave it out and the resource is company-level. ```bash curl -s -X POST https://app.upfour.io/v1/monitors \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ "name": "Lakeside Bakery site", "url": "https://example.com", "monitor_type": "http", "client_id": "'"$CLIENT_ID"'" }' ``` - Creating a monitor with a `client_id` that is not a client of your Company, or that belongs to an archived client, is `400 bad_request`. - A key that may not write to that client gets `403` with the message “You do not have write access to this client”. ## Keys limited to clients A key can be limited to chosen clients when it is created in the app. What a key may see is its client set: for a personal key, what its owner may see, narrowed to the chosen clients; for a team key, the clients of its team plus company-level resources. `GET /me` shows it: ``` "client_set": { "All": false, "CompanyLevel": true, "IDs": ["…", "…"] } ``` | Field | Meaning | | --- | --- | | `All` | The key may see every client. | | `CompanyLevel` | The key may see company-level resources. | | `IDs` | The clients the key may see when it is limited, otherwise `null`. | - Lists never contain clients outside the set, whatever you pass in `client_id`. - A single resource outside the set, such as a monitor, answers `404` and not `403`, so ids of other clients cannot be probed. - Company-level routes such as `/organization` answer `404` to team keys and to keys that cannot read company-level resources. ## Common mistakes - **Creating a monitor without `client_id` and then listing with `client_id`.** The monitor is company-level and does not appear. - **Passing `all_clients=True` or `all_clients=1`.** Only `true` works; the rest means company-level. - **Expecting an error when agency mode is off.** The parameters are ignored without a message. - **Sending `client_id` as a header.** No header exists. - **Changing the client between pages.** The [cursor check](https://upfour.io/docs/api/pagination.md) does not cover it; keep it the same for every page. --- # Test without paging anyone > up4 has no sandbox: every API key acts on your real Company. Narrow keys, notify: false, dry-runs and notify_subscribers keep an experiment from alerting anyone. Source: https://upfour.io/docs/api/testing up4 has no separate test environment. Every API key works on your real Company, and every check it creates runs for real and is billed like any other (€9 per million executions). You keep an experiment quiet and small with a narrow key, the silent switches and the dry-runs below. ## One kind of key API keys start with `pls_`; keys created earlier with the `pls_live_` prefix keep working. Create keys in the app under **Settings → API tokens**. API keys cannot create other keys, so this step always happens in the signed-in app. For an experiment, hand out as little as possible: - **Read-only** access level, or only the scopes the experiment needs (`monitor.read`, `incident.write`, …). - **A dedicated Client or Team.** With agency mode, limit the key to one Client through `allowed_clients` and archive that Client when you are done. - **A short expiry**, 30 days or less. ```bash export UP4_API_KEY="pls_…" curl -s https://app.upfour.io/v1/me \ -H "Authorization: Bearer $UP4_API_KEY" ``` ![API token form with the name CI integration, access level Read-only, and monitor.read and monitor.write checked.](https://upfour.io/docs/images/api-tokens.webp) **Create a scoped token.** Name, access level and permissions in the token form. 1. **Token name** Choose a name that identifies the integration, such as CI integration. 2. **Access level** The default for a token without explicit scopes. Read-only is the safe start. The screenshot shows the form before a token is created. 3. **Scopes** Select only the operations the integration needs. The example allows reading and writing monitors. ## Stay quiet: `notify: false` The calls that notify people on the side take an optional JSON field `notify` (boolean, default `true`): | `POST /incidents` | The alert channels and the alert plan the new incident would trigger. | | --- | --- | | `POST` or `PUT /incidents/{id}/acknowledge` | The notices for the acknowledgement. | | `POST` or `PUT /incidents/{id}/resolve` | The notices for the resolution. | | `POST` or `PUT /incidents/{id}/reopen` | The notices for the reopening. | | `POST /incidents/{id}/comments` | The notices for the comment. | | `POST /incidents/{id}/updates` | The notices for the update. | With `notify: false` no alert channel (email, SMS, voice, Slack, Teams, …) and no alert plan (escalation policy) is triggered. The live view in the app and outbound [API webhooks](https://upfour.io/docs/api/webhooks.md) still fire: webhooks are for systems, not people. Acknowledge, resolve and reopen take no body otherwise; send `{"notify": false}` for the quiet version. ```bash curl -s -X POST https://app.upfour.io/v1/incidents \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{"title": "Failover rehearsal", "notify": false}' ``` A request that contradicts itself is rejected with `400`: `POST /incidents` with `notify: false` plus an `escalation_policy_id` or `notification_channel_ids` asks for a quiet incident and for notifications at once. ## Preview before you apply | `POST /monitors/batch/dry-run` | Checks an import and returns a `dry_run_id`. Send the same body with that `dry_run_id` to `POST /monitors/batch` to apply it; if the resolved settings or the existing monitors changed in between, the apply answers `409` and creates nothing. With `create_paused: true` the monitors start paused, so no check runs until you resume them. Scope `monitor.write`. | | --- | --- | | `POST /client-templates/{templateID}/dry-run` | Shows what applying a template to a Client would resolve and creates nothing. Scope `client.write`. | | `GET /status-pages/{pageID}/mail-preview` | Forecasts who would get mail for a status page entry. Scope `status_page.read`. | ## Status pages: `notify_subscribers` `POST /status-pages/{pageID}/updates`, its `/entries` and `POST /status-pages/{pageID}/reports` mail the matching subscribers when `notify_subscribers` is `true`. Set it to `false` to publish without mailing anyone. ## Where there is no silent switch Calls whose purpose is to notify have no `notify` field: test alerts, channel and alert-plan tests, escalation, invitations, report sending and the webhook test. Point those at a channel that reaches only you. ## Use it everywhere docs say “Try it” The [reference pages](https://upfour.io/docs/api.md) run requests straight from your browser. Every request that is not a `GET` asks for confirmation first, because every key changes real data. When you are done, pause or delete what the experiment created: running checks cost money until you do. --- # Webhooks > Outbound, signed webhooks: get the signing secret, verify X-Up4-Signature in Node, Python, Go, PHP and Ruby with test vectors, delivery and retry rules, and every event with its payload. Source: https://upfour.io/docs/api/webhooks Subscribe once and the events you pick come to you, signed, so nobody can forge an “all clear”. This page is the whole contract: how you get the secret, how you verify a delivery, what up4 does when your endpoint fails, and what every event contains. Manage subscriptions under [/webhook-subscriptions](https://upfour.io/docs/api/webhook-subscriptions.md). ![A webhook-subscription request panel with an empty key field and example JSON containing a destination URL and three event types.](https://upfour.io/docs/images/webhook-subscription.webp) **Set the destination and event types.** An example subscription in the reference’s Try it panel. 1. **API key** Use a key with webhook.write and access to the intended organization. 2. **Destination and events** Replace the example URL with your receiver and choose the events you need. Keep the returned subscription secret for signature verification. 3. **Create subscription** Send request creates the subscription. The response is shown below the button; no request was sent for this screenshot. ## Get the signing secret Every subscription has its own secret, `whsec_` followed by 64 hex characters. `POST /v1/webhook-subscriptions` returns it as `data.secret`, **once**. List, update and every other read leave it out, and up4 cannot show it again, so store it with your other secrets right away. Creating a subscription sends nothing to the URL: up4 only checks the address (below) and stores the subscription. Use `POST /v1/webhook-subscriptions/{id}/test` to see a delivery arrive. A URL that is not `http` or `https`, whose host does not resolve, or that resolves to a loopback, private or otherwise blocked address is refused with `400 bad_request`; the message says which (`webhook url resolves to a blocked address range` for a private or loopback address). Nothing is created. With an [Idempotency-Key](https://upfour.io/docs/api/idempotency.md), a retry of the same create returns the stored response, **secret included**, for 24 hours: a repeat does not make a second subscription and does not make a second secret. That also holds after you rotated the secret in between, when the replay shows the earlier secret, which no longer signs. Keep the secret from the first answer, and use a new key for every subscription you really want to create. ```bash curl -s -X POST https://app.upfour.io/v1/webhook-subscriptions \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://ops.example.com/hooks/up4", "events": ["incident.opened", "incident.resolved"] }' # 201 { "data": { "id": "…", "url": "https://ops.example.com/hooks/up4", "events": ["incident.opened", "incident.resolved"], "active": true, "failure_count": 0, "secret": "whsec_9f2c…", … } } ``` Lost it, or leaked it? `POST /v1/webhook-subscriptions/{id}/rotate-secret` (same permission as updating a subscription, `webhook.write`) makes a new secret and returns it the same way, once. The URL, events and history stay. **The old secret stops signing at once, there is no grace period.** The secret is read for every attempt, so a retry that was queued before the rotation is signed with the new one as well. To rotate without losing a delivery, first deploy a receiver that accepts either secret (call the verifier below with the new secret, then with the old one), then rotate, then remove the old one. Do not answer an unverifiable delivery with a 4xx while you switch: it ends that delivery for good (see [delivery and retries](https://upfour.io/docs/api/webhooks#delivery)). A `5xx` is retried. ## Verify a delivery Every subscription delivery carries `X-Up4-Signature: t=,v1=`. `v1` is the HMAC-SHA256, in lower-case hex, of the decimal `t`, a dot and the raw request body, keyed with the whole secret string, `whsec_` prefix included (do not hex-decode it). This is the format of webhook **subscriptions**; the [Webhook alert channel](https://upfour.io/docs/api/webhooks#alert-channel-webhooks) signs differently (`sha256=` over the body). A receiver has to: 1. Read the **raw body** exactly as it arrived. A body that was parsed and serialised again has other bytes and never verifies. 2. Rebuild the signature from `t` and the raw body and compare it in constant time with the `v1` value. A header with several `v1` values is valid if any of them matches. up4 sends one today; the snippets accept any so a change would not break you. 3. Reject a `t` more than 300 seconds away from your clock, in either direction. up4 does not enforce the window, it is your protection against replays. `t` is the time of that attempt, so a retry is signed anew and passes. The snippets below return true or false and take the secret, the header, the raw body and, optionally, your clock. Each of them is run against every test vector (below) with the real interpreter before this page is published. ### Node verify.mjs ```javascript import { createHmac, timingSafeEqual } from "node:crypto"; // secret: the whsec_… string, whole, as the create or rotate call returned it // header: the X-Up4-Signature header, "t=,v1=" // rawBody: the request body exactly as received (a Buffer), before JSON.parse export function verifyUp4Signature(secret, header, rawBody, now = Math.floor(Date.now() / 1000), tolerance = 300) { let timestamp = null; const signatures = []; for (const part of String(header ?? "").split(",")) { const at = part.indexOf("="); const key = at < 0 ? part : part.slice(0, at); const value = at < 0 ? "" : part.slice(at + 1); if (key === "t") timestamp = value; if (key === "v1") signatures.push(value); } if (timestamp === null || !/^[0-9]+$/.test(timestamp) || signatures.length === 0) return false; if (Math.abs(now - Number(timestamp)) > tolerance) return false; const expected = createHmac("sha256", secret).update(`${timestamp}.`).update(rawBody).digest(); return signatures.some( (signature) => /^[0-9a-f]{64}$/i.test(signature) && timingSafeEqual(expected, Buffer.from(signature, "hex")), ); } ``` ### Python verify.py ```python import hashlib import hmac import re import time def verify_up4_signature(secret, header, raw_body, now=None, tolerance=300): """Check X-Up4-Signature. secret: the whsec_... string, whole, as the create or rotate call returned it header: the X-Up4-Signature header, "t=,v1=" raw_body: the request body exactly as received (bytes), before json.loads """ timestamp, signatures = None, [] for part in (header or "").split(","): key, _, value = part.partition("=") if key == "t": timestamp = value elif key == "v1": signatures.append(value) if timestamp is None or not re.fullmatch(r"[0-9]+", timestamp) or not signatures: return False if abs((time.time() if now is None else now) - int(timestamp)) > tolerance: return False message = timestamp.encode() + b"." + raw_body expected = hmac.new(secret.encode(), message, hashlib.sha256).hexdigest().encode() return any(hmac.compare_digest(expected, signature.lower().encode()) for signature in signatures) ``` ### Go verify.go ```go package up4webhook import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "strings" "time" ) // Verify reports whether header, the X-Up4-Signature value "t=,v1=", // matches rawBody. secret is the whole whsec_... string as the create or rotate // call returned it; rawBody is the request body exactly as received, before any // JSON decoding. func Verify(secret, header string, rawBody []byte, now time.Time) bool { const tolerance = 300 * time.Second var timestamp string var signatures []string for _, part := range strings.Split(header, ",") { key, value, _ := strings.Cut(part, "=") switch key { case "t": timestamp = value case "v1": signatures = append(signatures, value) } } if timestamp == "" || strings.Trim(timestamp, "0123456789") != "" || len(signatures) == 0 { return false } var seconds int64 for _, digit := range timestamp { if seconds > (1<<62)/10 { return false } seconds = seconds*10 + int64(digit-'0') } if age := now.Sub(time.Unix(seconds, 0)); age > tolerance || age < -tolerance { return false } mac := hmac.New(sha256.New, []byte(secret)) mac.Write([]byte(timestamp + ".")) mac.Write(rawBody) expected := mac.Sum(nil) for _, signature := range signatures { if got, err := hex.DecodeString(signature); err == nil && hmac.Equal(got, expected) { return true } } return false } ``` ### PHP verify.php ```php ,v1=" * $rawBody: the request body exactly as received: file_get_contents('php://input') */ function verify_up4_signature(string $secret, string $header, string $rawBody, ?int $now = null, int $tolerance = 300): bool { $timestamp = null; $signatures = []; foreach (explode(',', $header) as $part) { [$key, $value] = array_pad(explode('=', $part, 2), 2, ''); if ($key === 't') { $timestamp = $value; } elseif ($key === 'v1') { $signatures[] = $value; } } if ($timestamp === null || !ctype_digit($timestamp) || $signatures === []) { return false; } if (abs(($now ?? time()) - (int) $timestamp) > $tolerance) { return false; } $expected = hash_hmac('sha256', $timestamp . '.' . $rawBody, $secret); foreach ($signatures as $signature) { if (hash_equals($expected, strtolower($signature))) { return true; } } return false; } ``` ### Ruby verify.rb ```ruby require "openssl" # secret: the whsec_... string, whole, as the create or rotate call returned it # header: the X-Up4-Signature header, "t=,v1=" # raw_body: the request body exactly as received (request.body.read), before JSON.parse def verify_up4_signature(secret, header, raw_body, now = Time.now.to_i, tolerance = 300) timestamp = nil signatures = [] header.to_s.split(",").each do |part| key, value = part.split("=", 2) timestamp = value.to_s if key == "t" signatures << value.to_s if key == "v1" end return false if timestamp.nil? || timestamp !~ /\A[0-9]+\z/ || signatures.empty? return false if (now - timestamp.to_i).abs > tolerance expected = OpenSSL::HMAC.hexdigest("SHA256", secret, "#{timestamp}.".b + raw_body.b) signatures.any? { |signature| secure_compare(expected, signature.downcase) } end # Constant-time comparison (OpenSSL.fixed_length_secure_compare on Ruby 3.0+). def secure_compare(a, b) return false unless a.bytesize == b.bytesize a.bytes.zip(b.bytes).reduce(0) { |acc, (x, y)| acc | (x ^ y) }.zero? end ``` ### Test vectors [webhook-test-vectors.json](https://upfour.io/api/webhook-test-vectors.json) holds a fixed secret, timestamp and raw body, the header up4 would send, and cases that a correct verifier answers as listed: valid (also with non-ASCII bytes and with a second `v1` value), wrong secret, the secret without its prefix, a body changed by one bit, the same JSON re-serialised, a timestamp 301 seconds old or ahead, a replaced timestamp, and malformed headers. Each case has its own `secret`, `header`, `body`, `now` (the receiver's clock, Unix seconds) and the expected `valid`. The signatures come from the code that signs real deliveries. Run your verifier over every case before you point it at a live endpoint. The file is one JSON object. At the top level, `description` says how it was made and `algorithm` repeats the signing rule in one sentence. `tolerance_seconds` is the replay window (300). `secret`, `timestamp` (Unix seconds), `body` (the raw body as one JSON string), `signature` (the lower-case hex `v1`) and `header` (the complete `X-Up4-Signature` value) are one signed delivery you can check by hand. `cases` is an array of objects with `name`, `description`, `secret`, `header`, `body`, `now` and `valid`. A case's `body` is a JSON string that holds the request body exactly as it would arrive: the string value, UTF-8 encoded, is the byte sequence to hash. Do not parse it as JSON and serialise it again; that is what the case `invalid_body_reserialised` tests. Run `verify(secret, header, body, now)` for each case and expect `valid`. ## What up4 sends Each delivery is a `POST` with a JSON body (payload version 2) and these headers: | `Content-Type` | Always application/json. | | --- | --- | | `User-Agent` | up4-webhooks/1.0. | | `X-Up4-Event` | The event name, the same as type in the body. | | `X-Up4-Delivery` | ID of the delivery (a UUID). The same on every retry of one delivery; a manual redelivery is a new delivery with a new ID. | | `X-Up4-Signature` | t=,v1=." followed by the raw body, keyed with the subscription secret>. Built again for every attempt, so t is the time of that attempt. | The body is a short summary, never the whole resource. Around `data` it has these fields. `payload_version`, `type` and `app_url` are in every delivery; `id` and `created_at` are set on every event up4 publishes today and are left out only when the event it was built from carried none: | Field | Type | Description | | --- | --- | --- | | `app_url` | string (uri) | Link to the resource in the up4 app (signing in is required to open it). Just the application's base URL when the event names no resource. | | `created_at` | string (date-time) | When up4 published the event, RFC 3339 in UTC. Left out when empty. | | `id` | string (uuid) | ID of the event, a UUID. It is the same on every attempt, retry and manual redelivery of an event, so it is the key for dropping duplicates. The delivery ID is the X-Up4-Delivery header. Left out when empty. | | `payload_version` | integer | Version of the body layout, always 2. | | `type` | string | The event name, the same as the X-Up4-Event header. | | `data` | object | The fields of the event, listed per event below. An allowlist: resource ids, names and titles up to 256 characters, states, timestamps, a safe cause, your own metadata, and on status-page events the text you published. Nothing else is sent. | Use `app_url` or the API for anything the event does not carry, such as a monitor's name. Your [custom metadata](https://upfour.io/docs/watch-a-website.md) travels with `incident.*`, `heartbeat.*` and `monitor.status_changed` as the string object `data.metadata`. An incident's metadata is copied from its monitor or job when it opens. ## Delivery and retries | Timeout | 10 seconds for one attempt in total, redirects included. Answer fast and do the work afterwards. | | --- | --- | | Success | Any `2xx` from the final URL. The response body is discarded; only the status is kept. | | Redirects | Followed, up to 4, and every target is checked like the subscription URL. A `301`, `302` or `303` turns the POST into a GET without body, so answer at the registered URL. | | Retried | Timeouts, network errors, `5xx`, `408`, `429` and any other status that is not `2xx` or `4xx`. For `429` and `503` a `Retry-After` header (seconds or HTTP date) postpones the next attempt, never earlier than the schedule, at most 24 hours. | | Not retried | Every other `4xx`: the delivery ends as `failed`. A destination that resolves to a blocked address also fails at once. | | Schedule | At most 7 attempts per delivery: the first one right away, then after 30 seconds, 5 minutes, 30 minutes, 2 hours, 12 hours, 24 hours, each counted from the previous attempt. That is about 39 hours from first to last. A retry starts at its time or up to a few seconds later; up4 checks for due retries every 15 seconds. An attempt is counted before it is sent, so a delivery cut short by a restart is retried after about two minutes and still never goes past 7 attempts. | | Switched off | A subscription gets `active: false` and `disabled_at` when a delivery fails its last attempt, when your endpoint answers `410 Gone`, when the URL resolves to a blocked address, or when a `4xx` arrives while `failure_count` is already 6 or more. `failure_count` counts failed attempts since the last successful delivery and goes back to 0 on the next `2xx`. | | Switched on again | `PUT /v1/webhook-subscriptions/{id}` with `active: true`, which is also the default when the body leaves `active` out. That clears `disabled_at`; it does not reset `failure_count`. Retries queued while it was off run then, up to their attempt limit. **Events that happened while it was off are not sent afterwards.** | | Order | No guarantee. Deliveries are independent, and a retry of an older event can arrive after a newer event. Use `created_at` in the body to order them. | | Duplicates | At least once. `X-Up4-Delivery` is the same on every retry of one delivery. A manual redelivery is a new delivery with a new `X-Up4-Delivery` but the same body `id`, and an event can also reach you twice if up4 has to hand it out again, so deduplicate on the body's `id`. | | Test | `POST /v1/webhook-subscriptions/{id}/test` sends one `webhook.test` to that subscription, whatever its `events` say. It must be active. The call answers `202` with the delivery record; limited to 100 per Company and day. | | Redelivery | `POST /v1/webhook-subscriptions/{id}/redeliver/{deliveryID}` sends the stored body of an earlier delivery again, right away, as a new delivery with the usual retries. The subscription must be active. 100 per Company and day. | | History | `GET /v1/webhook-subscriptions/{id}/deliveries`, newest first, `limit` up to 250 (default 50). Each record has `status` (`pending`, `retrying`, `delivered`, `failed`), the `attempt` number, `response_code` and `next_retry_at`. Delivered and failed records are deleted after 30 days. | | Destinations | Loopback, link-local, multicast, unspecified and private (RFC 1918) addresses are refused when you save the URL, before every attempt and when connecting. A URL needs `http` or `https`. Use `POST …/test` to try a receiver end to end. | ## Choose events A subscription's `events` is a list of event names from the catalogue below, at least one. The service does not check the names in events. A name that no event carries is stored as given and never matches, and the request still succeeds. Names are compared exactly (case-sensitive, no wildcards); blanks and duplicates are dropped. A typo therefore creates a subscription that never fires, so check the names against this list: `monitor.created` `monitor.updated` `monitor.deleted` `monitor.status_changed` `monitor.degraded_changed` `monitor.unstable` `monitors.bulk_updated` `incident.opened` `incident.acknowledged` `incident.resolved` `incident.reopened` `incident.escalated` `incident.propagated` `incident.partially_recovered` `heartbeat.missed` `heartbeat.recovered` `status_page.update_published` `status_page.report_published` `webhook.test` `webhook.test` is never published on its own; it exists for the test call. A draft status-page post sends nothing; the event goes out when you publish it. ## Event catalogue 19 events. Every example is the body up4 sends for a representative event, produced by the same code that builds real deliveries. **always** marks a field that is in every delivery of the event; **when set** fields are left out when there is nothing to send. The shared fields (`payload_version`, `type`, `app_url`, `id`, `created_at`) are not repeated. ### `monitor.created` A monitor is created with POST /v1/monitors or in the app. Monitors made by a batch import send no event. `data` is an empty object. The event says that something happened and not to which monitor; read the monitor list with the API if you need it. ```json { "payload_version": 2, "type": "monitor.created", "app_url": "https://app.upfour.io", "id": "9d1b2c3e-4f5a-4b6c-8d7e-0a1b2c3d4e5f", "created_at": "2026-10-24T09:00:00Z", "data": {} } ``` ### `monitor.updated` A monitor is changed with PUT or PATCH, or paused or resumed. `data` is an empty object. The event says that something happened and not to which monitor; read the monitor list with the API if you need it. ```json { "payload_version": 2, "type": "monitor.updated", "app_url": "https://app.upfour.io", "id": "9d1b2c3e-4f5a-4b6c-8d7e-0a1b2c3d4e5f", "created_at": "2026-10-24T09:00:00Z", "data": {} } ``` ### `monitor.deleted` A monitor is deleted. | data field | Type | Description | | --- | --- | --- | | `monitor_id` | string (uuid) always | The monitor the event is about. | ```json { "payload_version": 2, "type": "monitor.deleted", "app_url": "https://app.upfour.io/monitors/11111111-1111-4111-8111-111111111111", "id": "9d1b2c3e-4f5a-4b6c-8d7e-0a1b2c3d4e5f", "created_at": "2026-10-24T09:00:00Z", "data": { "monitor_id": "11111111-1111-4111-8111-111111111111" } } ``` ### `monitor.status_changed` A monitor changes between UP and DOWN once three consistent observations confirm it (the first differing check plus two instant re-checks, and the confirmation or recovery period if one is set). | data field | Type | Description | | --- | --- | --- | | `metadata` | object of strings when set | Custom key/value pairs of the monitor, scheduled job or incident, as set with the metadata field or PUT /v1/metadata. Left out when there are none. | | `monitor_id` | string (uuid) always | The monitor the event is about. | | `state` | string always | The state the monitor changed to. One of `UP`, `DOWN`. | ```json { "payload_version": 2, "type": "monitor.status_changed", "app_url": "https://app.upfour.io/monitors/11111111-1111-4111-8111-111111111111", "id": "9d1b2c3e-4f5a-4b6c-8d7e-0a1b2c3d4e5f", "created_at": "2026-10-24T09:00:00Z", "data": { "metadata": { "customer_no": "K-4711" }, "monitor_id": "11111111-1111-4111-8111-111111111111", "state": "DOWN" } } ``` ### `monitor.degraded_changed` A monitor with a slow-response alert starts answering slowly (is_degraded true) or is fast again (false), after three consecutive slow or fast checks. | data field | Type | Description | | --- | --- | --- | | `is_degraded` | boolean always | true when the monitor now answers slowly, false when it is fast again. | | `monitor_id` | string (uuid) always | The monitor the event is about. | ```json { "payload_version": 2, "type": "monitor.degraded_changed", "app_url": "https://app.upfour.io/monitors/11111111-1111-4111-8111-111111111111", "id": "9d1b2c3e-4f5a-4b6c-8d7e-0a1b2c3d4e5f", "created_at": "2026-10-24T09:00:00Z", "data": { "is_degraded": true, "monitor_id": "11111111-1111-4111-8111-111111111111" } } ``` ### `monitor.unstable` A monitor flaps often enough that up4 stops alerting on every change and treats it as unstable until it has stayed healthy for the recovery window. | data field | Type | Description | | --- | --- | --- | | `monitor_id` | string (uuid) always | The monitor the event is about. | ```json { "payload_version": 2, "type": "monitor.unstable", "app_url": "https://app.upfour.io/monitors/11111111-1111-4111-8111-111111111111", "id": "9d1b2c3e-4f5a-4b6c-8d7e-0a1b2c3d4e5f", "created_at": "2026-10-24T09:00:00Z", "data": { "monitor_id": "11111111-1111-4111-8111-111111111111" } } ``` ### `monitors.bulk_updated` A bulk change (POST /v1/monitors/bulk) changed at least one monitor. One event for the whole call, with the number of monitors changed. | data field | Type | Description | | --- | --- | --- | | `updated` | integer always | Number of monitors the call changed. | ```json { "payload_version": 2, "type": "monitors.bulk_updated", "app_url": "https://app.upfour.io", "id": "9d1b2c3e-4f5a-4b6c-8d7e-0a1b2c3d4e5f", "created_at": "2026-10-24T09:00:00Z", "data": { "updated": 12 } } ``` ### `incident.opened` An incident opens: a monitor goes DOWN, a scheduled job misses a check-in or reports a failed run, or someone creates one with POST /v1/incidents. | data field | Type | Description | | --- | --- | --- | | `cause` | string always | Why the incident opened, a short diagnostic such as HTTP 503\. May be an empty string. | | `incident_id` | string (uuid) always | The incident the event is about. | | `metadata` | object of strings when set | Custom key/value pairs of the monitor, scheduled job or incident, as set with the metadata field or PUT /v1/metadata. Left out when there are none. | | `severity` | string when set | Severity of the incident. Left out for a warning incident: warning is not part of the allowlist, so a receiver cannot tell it from an incident without a severity. One of `critical`, `info`. | | `status` | string always | Status of the incident after the event. One of `open`, `acknowledged`, `resolved`. | | `title` | string always | Title of the incident, at most 256 characters. | ```json { "payload_version": 2, "type": "incident.opened", "app_url": "https://app.upfour.io/incidents/33333333-3333-4333-8333-333333333333", "id": "9d1b2c3e-4f5a-4b6c-8d7e-0a1b2c3d4e5f", "created_at": "2026-10-24T09:00:00Z", "data": { "cause": "HTTP 503", "incident_id": "33333333-3333-4333-8333-333333333333", "metadata": { "customer_no": "K-4711" }, "severity": "critical", "status": "open", "title": "Checkout is down" } } ``` ### `incident.acknowledged` Someone acknowledges an incident (POST /v1/incidents/{id}/acknowledge). | data field | Type | Description | | --- | --- | --- | | `cause` | string always | Why the incident opened, a short diagnostic such as HTTP 503\. May be an empty string. | | `incident_id` | string (uuid) always | The incident the event is about. | | `metadata` | object of strings when set | Custom key/value pairs of the monitor, scheduled job or incident, as set with the metadata field or PUT /v1/metadata. Left out when there are none. | | `severity` | string when set | Severity of the incident. Left out for a warning incident: warning is not part of the allowlist, so a receiver cannot tell it from an incident without a severity. One of `critical`, `info`. | | `status` | string always | Status of the incident after the event. One of `open`, `acknowledged`, `resolved`. | | `title` | string always | Title of the incident, at most 256 characters. | ```json { "payload_version": 2, "type": "incident.acknowledged", "app_url": "https://app.upfour.io/incidents/33333333-3333-4333-8333-333333333333", "id": "9d1b2c3e-4f5a-4b6c-8d7e-0a1b2c3d4e5f", "created_at": "2026-10-24T09:00:00Z", "data": { "cause": "HTTP 503", "incident_id": "33333333-3333-4333-8333-333333333333", "severity": "critical", "status": "acknowledged", "title": "Checkout is down" } } ``` ### `incident.resolved` An incident is closed: its monitor or scheduled job recovered, someone resolved it with POST /v1/incidents/{id}/resolve, or monitoring resumed after a billing pause (then recovered is false). | data field | Type | Description | | --- | --- | --- | | `cause` | string when set | Why the incident opened. Only on a resolve that sends the incident's own fields. | | `heartbeat_id` | string (uuid) when set | The scheduled job whose check-in closed the incident, or whose incident was closed when monitoring resumed. | | `incident_id` | string (uuid) always | The incident the event is about. | | `metadata` | object of strings when set | Custom key/value pairs of the monitor, scheduled job or incident, as set with the metadata field or PUT /v1/metadata. Left out when there are none. | | `monitor_id` | string (uuid) when set | The monitor whose recovery closed the incident, or whose incident was closed when monitoring resumed. | | `recovered` | boolean when set | false when monitoring resumed after a billing pause closed the incident: nothing was measured in between, so the end of the outage is unknown. Absent on every other resolve. | | `resolution` | string when set | Only when monitoring resumed after a billing pause closed the incident. One of `monitoring_resumed`. | | `resolved_at` | string (date-time) when set | When the incident was closed, RFC 3339 in UTC. Only on a resolve that sends the incident's own fields. | | `resolved_by_type` | string when set | What closed the incident: monitor or heartbeat (the source recovered), user (someone resolved it), billing_resume (monitoring resumed after a billing pause) or system. Only on a resolve that sends the incident's own fields. One of `billing_resume`, `monitor`, `heartbeat`, `user`, `system`. | | `severity` | string when set | Severity of the incident. Left out for a warning incident: warning is not part of the allowlist, so a receiver cannot tell it from an incident without a severity. One of `critical`, `info`. | | `status` | string when set | Status of the incident, resolved. Only on a resolve that sends the incident's own fields. One of `open`, `acknowledged`, `resolved`. | | `title` | string when set | Title of the incident, at most 256 characters. Only on a resolve that sends the incident's own fields. | **Monitor recovered** ```json { "payload_version": 2, "type": "incident.resolved", "app_url": "https://app.upfour.io/incidents/33333333-3333-4333-8333-333333333333", "id": "9d1b2c3e-4f5a-4b6c-8d7e-0a1b2c3d4e5f", "created_at": "2026-10-24T09:00:00Z", "data": { "incident_id": "33333333-3333-4333-8333-333333333333", "monitor_id": "11111111-1111-4111-8111-111111111111" } } ``` **Resolved through the API** ```json { "payload_version": 2, "type": "incident.resolved", "app_url": "https://app.upfour.io/incidents/33333333-3333-4333-8333-333333333333", "id": "9d1b2c3e-4f5a-4b6c-8d7e-0a1b2c3d4e5f", "created_at": "2026-10-24T09:00:00Z", "data": { "cause": "HTTP 503", "incident_id": "33333333-3333-4333-8333-333333333333", "resolved_at": "2026-10-24T09:00:00Z", "resolved_by_type": "user", "severity": "critical", "status": "resolved", "title": "Checkout is down" } } ``` **Closed when monitoring resumed after a billing pause** ```json { "payload_version": 2, "type": "incident.resolved", "app_url": "https://app.upfour.io/incidents/33333333-3333-4333-8333-333333333333", "id": "9d1b2c3e-4f5a-4b6c-8d7e-0a1b2c3d4e5f", "created_at": "2026-10-24T09:00:00Z", "data": { "cause": "HTTP 503", "incident_id": "33333333-3333-4333-8333-333333333333", "monitor_id": "11111111-1111-4111-8111-111111111111", "recovered": false, "resolution": "monitoring_resumed", "resolved_at": "2026-10-24T09:00:00Z", "resolved_by_type": "billing_resume", "severity": "critical", "status": "resolved", "title": "Checkout is down" } } ``` ### `incident.reopened` Someone reopens a resolved incident (POST /v1/incidents/{id}/reopen). | data field | Type | Description | | --- | --- | --- | | `cause` | string always | Why the incident opened, a short diagnostic such as HTTP 503\. May be an empty string. | | `incident_id` | string (uuid) always | The incident the event is about. | | `metadata` | object of strings when set | Custom key/value pairs of the monitor, scheduled job or incident, as set with the metadata field or PUT /v1/metadata. Left out when there are none. | | `severity` | string when set | Severity of the incident. Left out for a warning incident: warning is not part of the allowlist, so a receiver cannot tell it from an incident without a severity. One of `critical`, `info`. | | `status` | string always | Status of the incident after the event. One of `open`, `acknowledged`, `resolved`. | | `title` | string always | Title of the incident, at most 256 characters. | ```json { "payload_version": 2, "type": "incident.reopened", "app_url": "https://app.upfour.io/incidents/33333333-3333-4333-8333-333333333333", "id": "9d1b2c3e-4f5a-4b6c-8d7e-0a1b2c3d4e5f", "created_at": "2026-10-24T09:00:00Z", "data": { "cause": "HTTP 503", "incident_id": "33333333-3333-4333-8333-333333333333", "severity": "critical", "status": "open", "title": "Checkout is down" } } ``` ### `incident.escalated` Someone escalates an incident by hand (POST /v1/incidents/{id}/escalate). | data field | Type | Description | | --- | --- | --- | | `cause` | string always | Why the incident opened, a short diagnostic such as HTTP 503\. May be an empty string. | | `incident_id` | string (uuid) always | The incident the event is about. | | `metadata` | object of strings when set | Custom key/value pairs of the monitor, scheduled job or incident, as set with the metadata field or PUT /v1/metadata. Left out when there are none. | | `severity` | string when set | Severity of the incident. Left out for a warning incident: warning is not part of the allowlist, so a receiver cannot tell it from an incident without a severity. One of `critical`, `info`. | | `status` | string always | Status of the incident after the event. One of `open`, `acknowledged`, `resolved`. | | `title` | string always | Title of the incident, at most 256 characters. | ```json { "payload_version": 2, "type": "incident.escalated", "app_url": "https://app.upfour.io/incidents/33333333-3333-4333-8333-333333333333", "id": "9d1b2c3e-4f5a-4b6c-8d7e-0a1b2c3d4e5f", "created_at": "2026-10-24T09:00:00Z", "data": { "cause": "HTTP 503", "incident_id": "33333333-3333-4333-8333-333333333333", "severity": "critical", "status": "open", "title": "Checkout is down" } } ``` ### `incident.propagated` The incident correlation engine adds an incident to a group of correlated incidents, so two or more incidents are now handled as one wider outage. Sent for the incident that joins. | data field | Type | Description | | --- | --- | --- | | `incident_id` | string (uuid) always | The incident the event is about. | | `metadata` | object of strings when set | Custom key/value pairs of the monitor, scheduled job or incident, as set with the metadata field or PUT /v1/metadata. Left out when there are none. | ```json { "payload_version": 2, "type": "incident.propagated", "app_url": "https://app.upfour.io/incidents/33333333-3333-4333-8333-333333333333", "id": "9d1b2c3e-4f5a-4b6c-8d7e-0a1b2c3d4e5f", "created_at": "2026-10-24T09:00:00Z", "data": { "incident_id": "33333333-3333-4333-8333-333333333333" } } ``` ### `incident.partially_recovered` Some, but not all, incidents of a correlated incident group have recovered. | data field | Type | Description | | --- | --- | --- | | `incident_id` | string (uuid) always | The incident the event is about. | | `metadata` | object of strings when set | Custom key/value pairs of the monitor, scheduled job or incident, as set with the metadata field or PUT /v1/metadata. Left out when there are none. | ```json { "payload_version": 2, "type": "incident.partially_recovered", "app_url": "https://app.upfour.io/incidents/33333333-3333-4333-8333-333333333333", "id": "9d1b2c3e-4f5a-4b6c-8d7e-0a1b2c3d4e5f", "created_at": "2026-10-24T09:00:00Z", "data": { "incident_id": "33333333-3333-4333-8333-333333333333" } } ``` ### `heartbeat.missed` A scheduled job misses its check-in (expected interval plus grace period) or reports a failed run. Sent once when the job turns down, not for every missed interval. | data field | Type | Description | | --- | --- | --- | | `heartbeat_id` | string (uuid) always | The scheduled job the event is about. | | `metadata` | object of strings when set | Custom key/value pairs of the monitor, scheduled job or incident, as set with the metadata field or PUT /v1/metadata. Left out when there are none. | | `name` | string always | Name of the scheduled job, at most 256 characters. | | `status` | string always | down for heartbeat.missed, up for heartbeat.recovered. One of `down`, `up`. | ```json { "payload_version": 2, "type": "heartbeat.missed", "app_url": "https://app.upfour.io/heartbeats/22222222-2222-4222-8222-222222222222", "id": "9d1b2c3e-4f5a-4b6c-8d7e-0a1b2c3d4e5f", "created_at": "2026-10-24T09:00:00Z", "data": { "heartbeat_id": "22222222-2222-4222-8222-222222222222", "metadata": { "customer_no": "K-4711" }, "name": "Nightly backup", "status": "down" } } ``` ### `heartbeat.recovered` A scheduled job that was down checks in successfully again. | data field | Type | Description | | --- | --- | --- | | `heartbeat_id` | string (uuid) always | The scheduled job the event is about. | | `metadata` | object of strings when set | Custom key/value pairs of the monitor, scheduled job or incident, as set with the metadata field or PUT /v1/metadata. Left out when there are none. | | `name` | string always | Name of the scheduled job, at most 256 characters. | | `status` | string always | down for heartbeat.missed, up for heartbeat.recovered. One of `down`, `up`. | ```json { "payload_version": 2, "type": "heartbeat.recovered", "app_url": "https://app.upfour.io/heartbeats/22222222-2222-4222-8222-222222222222", "id": "9d1b2c3e-4f5a-4b6c-8d7e-0a1b2c3d4e5f", "created_at": "2026-10-24T09:00:00Z", "data": { "heartbeat_id": "22222222-2222-4222-8222-222222222222", "name": "Nightly backup", "status": "up" } } ``` ### `status_page.update_published` An incident or maintenance thread goes public on a status page, or a follow-up entry is added to a public thread. A draft sends nothing until it is published. | data field | Type | Description | | --- | --- | --- | | `ends_at` | string (date-time) when set | End of the maintenance window. Only on a maintenance thread that just went public. | | `entry_id` | string (uuid) when set | The entry this event is about: the latest one of a thread that just went public, or the one just added. Left out when a thread has no entry. | | `message` | string always | The text as visitors read it on the status page. It is what you wrote and can contain anything you typed. | | `published_at` | string (date-time) always | When the post went public, RFC 3339 in UTC. | | `starts_at` | string (date-time) when set | Start of the maintenance window. Only on a maintenance thread that just went public. | | `status` | string when set | Status of that entry, such as investigating, identified, monitoring or resolved for an incident, or scheduled, in_progress or completed for a maintenance. Left out when the status is none of the known ones. | | `status_page_id` | string (uuid) always | The status page the post was published on. | | `title` | string always | Title of the thread or report, at most 256 characters. | | `update_id` | string (uuid) always | The incident or maintenance thread. | | `update_type` | string always | Whether the thread is an incident or a maintenance. One of `incident`, `maintenance`. | **New maintenance thread** ```json { "payload_version": 2, "type": "status_page.update_published", "app_url": "https://app.upfour.io/status-pages/44444444-4444-4444-8444-444444444444", "id": "9d1b2c3e-4f5a-4b6c-8d7e-0a1b2c3d4e5f", "created_at": "2026-10-24T09:00:00Z", "data": { "ends_at": "2026-10-25T23:00:00Z", "entry_id": "66666666-6666-4666-8666-666666666666", "message": "We upgrade the database.", "published_at": "2026-10-24T09:00:00Z", "starts_at": "2026-10-25T22:00:00Z", "status": "scheduled", "status_page_id": "44444444-4444-4444-8444-444444444444", "title": "Database maintenance", "update_id": "55555555-5555-4555-8555-555555555555", "update_type": "maintenance" } } ``` **Follow-up entry** ```json { "payload_version": 2, "type": "status_page.update_published", "app_url": "https://app.upfour.io/status-pages/44444444-4444-4444-8444-444444444444", "id": "9d1b2c3e-4f5a-4b6c-8d7e-0a1b2c3d4e5f", "created_at": "2026-10-24T09:00:00Z", "data": { "entry_id": "66666666-6666-4666-8666-666666666666", "message": "A fix is deployed, we are watching.", "published_at": "2026-10-24T09:30:00Z", "status": "monitoring", "status_page_id": "44444444-4444-4444-8444-444444444444", "title": "Checkout is down", "update_id": "55555555-5555-4555-8555-555555555555", "update_type": "incident" } } ``` ### `status_page.report_published` A report goes public on a status page, when it is created as public or when a draft is published. | data field | Type | Description | | --- | --- | --- | | `message` | string always | The text as visitors read it on the status page. It is what you wrote and can contain anything you typed. | | `published_at` | string (date-time) always | When the post went public, RFC 3339 in UTC. | | `report_id` | string (uuid) always | The report. | | `status` | string when set | Status of the report, published unless it was created with another one. | | `status_page_id` | string (uuid) always | The status page the post was published on. | | `title` | string always | Title of the thread or report, at most 256 characters. | | `update_type` | string always | Always report. One of `report`. | ```json { "payload_version": 2, "type": "status_page.report_published", "app_url": "https://app.upfour.io/status-pages/44444444-4444-4444-8444-444444444444", "id": "9d1b2c3e-4f5a-4b6c-8d7e-0a1b2c3d4e5f", "created_at": "2026-10-24T09:00:00Z", "data": { "message": "99.98 percent across all services.", "published_at": "2026-10-01T08:00:00Z", "report_id": "77777777-7777-4777-8777-777777777777", "status": "published", "status_page_id": "44444444-4444-4444-8444-444444444444", "title": "September uptime", "update_type": "report" } } ``` ### `webhook.test` You call POST /v1/webhook-subscriptions/{id}/test. It goes to that subscription whatever its events list says, and is never published otherwise. | data field | Type | Description | | --- | --- | --- | | `message` | string always | Always Webhook test. | ```json { "payload_version": 2, "type": "webhook.test", "app_url": "https://app.upfour.io", "id": "9d1b2c3e-4f5a-4b6c-8d7e-0a1b2c3d4e5f", "created_at": "2026-10-24T09:00:00Z", "data": { "message": "Webhook test" } } ``` ### Incidents closed when monitoring resumes `incident.resolved` normally means the monitor or scheduled job recovered. When a Company's billing pause ends while a source is still failing, up4 closes its open automatic incident and starts the source afresh: `recovered` is `false`, `resolution` is `monitoring_resumed`, `resolved_by_type` is `billing_resume`. Nothing was measured during the pause, so treat the incident as closed, not as recovered. No `monitor.status_changed` or `heartbeat.recovered` follows; if the source is still failing, the first check after the resume opens a new incident. ## Alert-channel webhooks A **webhook alert channel** is a different thing from a subscription: it receives the alerts of the monitors it is attached to, its URL must be `https`, and it signs only if you set its optional secret. The header is `X-Up4-Signature: sha256=`, an HMAC-SHA256 over the raw body with the channel secret and no timestamp. Without a secret there is no header. The body is the alert notification (`event_type`, `monitor`, `details` and so on, with `is_test: true` on a channel test), not one of the events above. A `2xx` is delivered, any other `4xx` is permanent, everything else is treated as transient. Do not reuse the subscription verifier for it: ```javascript import { createHmac, timingSafeEqual } from "node:crypto"; export function verifyChannel(secret, header, rawBody) { if (!header?.startsWith("sha256=")) return false; const supplied = header.slice(7); if (!/^[0-9a-f]{64}$/i.test(supplied)) return false; const expected = createHmac("sha256", secret).update(rawBody).digest(); return timingSafeEqual(expected, Buffer.from(supplied, "hex")); } ``` --- # API changelog > Every change to the public API contract by date: new and removed operations, scopes, parameters, fields and status codes, generated from the OpenAPI history. Source: https://upfour.io/docs/api/changelog Every change to the public contract of `https://app.upfour.io/v1` since 2026-05-05, one line per operation and date. The lines are generated from the history of the [OpenAPI file](https://upfour.io/api/openapi.yaml), so they cannot drift from what the reference pages show. Also available as [JSON](https://upfour.io/api/changelog.json). ## What the labels mean | breaking | Code written against the old contract can stop working. | | --- | --- | | added | Something new that nothing existing depends on. | | changed | Behavior or contract changed in a way that is unlikely to break a client. | | deprecated | Still works, marked for removal. | | documented | The reference now describes something that already worked. Not counted as a change. | ## What counts as breaking A change is labeled breaking when a request that was valid before can fail or mean something else, or a response loses something a client could rely on: - An operation is removed. - The required scope changes to a different one or a higher level (`report.read` to `report.write`). Keys that do not hold the new scope are refused. Moving to a lower level of the same scope is labeled changed. - A request gains a required field, query parameter or body. - A request field or parameter is removed, renamed, made required, changes type or stops accepting a value. - A response field is removed, changes type or is no longer always present. - The success status of an operation changes. These are not breaking: new operations, new optional parameters and request fields, new response fields, new accepted values, and new error statuses. Clients should ignore response fields they do not know and treat the values of a response enum as an open set. ## Versioning The contract states one policy. The right column is what the API does today, checked against the code by this site's tests. | Topic | The contract says | The API does today | | --- | --- | --- | | **URL version** | The version is `/v1`. Additive changes land there. | Every operation is served under `/v1`. There is no other version. | | **Breaking changes** | A breaking change needs `/v2` and 12 months of parallel support. | There is no `/v2`. 5 entries below are labeled breaking, the latest on 2026-10-30; all of them were made inside `/v1`. | | **Deprecation** | Deprecations are announced with `Deprecation` and `Sunset` headers. | The API sends neither header. No operation is marked deprecated. A deprecation would show up here as a deprecated entry. | | **`X-API-Version`** | The header pins compatible behavior; keys remember a default version. | The header is accepted, and a request that carries it is treated like an API-key request (cursor pagination, `{ "data": … }` envelope). Its value is not read. Each key stores an `api_version`, which [GET /me](https://upfour.io/docs/api/me#get_me) reports; no behavior varies with it. | ## History 65 entries on 23 dates, newest first. ### [2026-10-30](https://upfour.io/docs/api/changelog#d-2026-10-30) - breaking [GET /me](https://upfour.io/docs/api/me#get_me) Response field `key_version` removed. - breaking Response field `sandbox_parent_id` removed. 3 operations - [GET /organization](https://upfour.io/docs/api/organization#get_organization) - [POST /organization](https://upfour.io/docs/api/organization#post_organization) - [PUT /organization](https://upfour.io/docs/api/organization#put_organization) - added [GET /monitors/{monitorID}/domain-expiry](https://upfour.io/docs/api/monitors#get_monitors_monitorID_domain_expiry) Added. Requires the `monitor.read` scope. - added New optional request field `notify`. 3 operations - [POST /incidents](https://upfour.io/docs/api/incidents#post_incidents) - [POST /incidents/{incidentID}/comments](https://upfour.io/docs/api/incidents#post_incidents_incidentID_comments) - [POST /incidents/{incidentID}/updates](https://upfour.io/docs/api/incidents#post_incidents_incidentID_updates) - added Now accepts an optional request body. 6 operations - [POST /incidents/{incidentID}/acknowledge](https://upfour.io/docs/api/incidents#post_incidents_incidentID_acknowledge) - [POST /incidents/{incidentID}/reopen](https://upfour.io/docs/api/incidents#post_incidents_incidentID_reopen) - [POST /incidents/{incidentID}/resolve](https://upfour.io/docs/api/incidents#post_incidents_incidentID_resolve) - [PUT /incidents/{incidentID}/acknowledge](https://upfour.io/docs/api/incidents#put_incidents_incidentID_acknowledge) - [PUT /incidents/{incidentID}/reopen](https://upfour.io/docs/api/incidents#put_incidents_incidentID_reopen) - [PUT /incidents/{incidentID}/resolve](https://upfour.io/docs/api/incidents#put_incidents_incidentID_resolve) - changed Now documents the status `400`. 7 operations - [POST /incidents](https://upfour.io/docs/api/incidents#post_incidents) - [POST /incidents/{incidentID}/acknowledge](https://upfour.io/docs/api/incidents#post_incidents_incidentID_acknowledge) - [POST /incidents/{incidentID}/reopen](https://upfour.io/docs/api/incidents#post_incidents_incidentID_reopen) - [POST /incidents/{incidentID}/resolve](https://upfour.io/docs/api/incidents#post_incidents_incidentID_resolve) - [PUT /incidents/{incidentID}/acknowledge](https://upfour.io/docs/api/incidents#put_incidents_incidentID_acknowledge) - [PUT /incidents/{incidentID}/reopen](https://upfour.io/docs/api/incidents#put_incidents_incidentID_reopen) - [PUT /incidents/{incidentID}/resolve](https://upfour.io/docs/api/incidents#put_incidents_incidentID_resolve) - changed Response field `notification_deliveries[].reason_code` no longer returns `sandbox`. 11 operations - [GET /incidents/{incidentID}](https://upfour.io/docs/api/incidents#get_incidents_incidentID) - [POST /incidents](https://upfour.io/docs/api/incidents#post_incidents) - [POST /incidents/{incidentID}/acknowledge](https://upfour.io/docs/api/incidents#post_incidents_incidentID_acknowledge) - [POST /incidents/{incidentID}/comments](https://upfour.io/docs/api/incidents#post_incidents_incidentID_comments) - [POST /incidents/{incidentID}/escalate](https://upfour.io/docs/api/incidents#post_incidents_incidentID_escalate) - [POST /incidents/{incidentID}/reopen](https://upfour.io/docs/api/incidents#post_incidents_incidentID_reopen) - [POST /incidents/{incidentID}/resolve](https://upfour.io/docs/api/incidents#post_incidents_incidentID_resolve) - [POST /incidents/{incidentID}/updates](https://upfour.io/docs/api/incidents#post_incidents_incidentID_updates) - [PUT /incidents/{incidentID}/acknowledge](https://upfour.io/docs/api/incidents#put_incidents_incidentID_acknowledge) - [PUT /incidents/{incidentID}/reopen](https://upfour.io/docs/api/incidents#put_incidents_incidentID_reopen) - [PUT /incidents/{incidentID}/resolve](https://upfour.io/docs/api/incidents#put_incidents_incidentID_resolve) - documented Request and response fields are now documented for 6 operations. This describes behavior that already existed and is not counted as a change. Areas: [Incidents](https://upfour.io/docs/api/incidents.md) ### [2026-10-27](https://upfour.io/docs/api/changelog#d-2026-10-27) - added [POST /webhook-subscriptions/{subscriptionID}/rotate-secret](https://upfour.io/docs/api/webhook-subscriptions#post_webhook_subscriptions_subscriptionID_rotate_secret) Added (beta). Requires the `webhook.write` scope. - documented Request and response fields are now documented for 230 operations. This describes behavior that already existed and is not counted as a change. Areas: [Health advisor](https://upfour.io/docs/api/advisor.md), [Client templates](https://upfour.io/docs/api/client-templates.md), [Clients (agency mode)](https://upfour.io/docs/api/clients.md), [Alert plans (escalation policies)](https://upfour.io/docs/api/escalation-policies.md), [Scheduled job groups](https://upfour.io/docs/api/heartbeat-groups.md), [Scheduled jobs (heartbeats)](https://upfour.io/docs/api/heartbeats.md), [Incidents](https://upfour.io/docs/api/incidents.md), [Invites](https://upfour.io/docs/api/invites.md), [Token introspection](https://upfour.io/docs/api/me.md), [Resource metadata](https://upfour.io/docs/api/metadata.md), [Monitor groups](https://upfour.io/docs/api/monitor-groups.md), [Monitors](https://upfour.io/docs/api/monitors.md), [Alert channels](https://upfour.io/docs/api/notification-channels.md), [On-call rotations](https://upfour.io/docs/api/oncall-schedules.md), [Organization](https://upfour.io/docs/api/organization.md), [Reports](https://upfour.io/docs/api/reports.md), [Playbooks (runbooks)](https://upfour.io/docs/api/runbooks.md), [Severities](https://upfour.io/docs/api/severities.md), [Severity groups](https://upfour.io/docs/api/severity-groups.md), [SLA policies](https://upfour.io/docs/api/sla-policies.md), [Status page design templates](https://upfour.io/docs/api/status-page-design-templates.md), [Status page update templates](https://upfour.io/docs/api/status-page-templates.md), [Status pages](https://upfour.io/docs/api/status-pages.md), [Teams](https://upfour.io/docs/api/teams.md), [Webhook subscriptions](https://upfour.io/docs/api/webhook-subscriptions.md) ### [2026-10-26](https://upfour.io/docs/api/changelog#d-2026-10-26) - documented Request and response fields are now documented for 5 operations. This describes behavior that already existed and is not counted as a change. Areas: [Incidents](https://upfour.io/docs/api/incidents.md), [Status page design templates](https://upfour.io/docs/api/status-page-design-templates.md), [Status pages](https://upfour.io/docs/api/status-pages.md) ### [2026-10-22](https://upfour.io/docs/api/changelog#d-2026-10-22) - added [GET /status-pages/{pageID}/mail-preview](https://upfour.io/docs/api/status-pages#get_status_pages_pageID_mail_preview) Added. Requires the `status_page.read` scope. ### [2026-10-18](https://upfour.io/docs/api/changelog#d-2026-10-18) - added [POST /monitors/export-details](https://upfour.io/docs/api/monitors#post_monitors_export_details) Added. Requires the `monitor.read` scope. ### [2026-10-16](https://upfour.io/docs/api/changelog#d-2026-10-16) - added [GET /incidents/{incidentID}/domain-case](https://upfour.io/docs/api/incidents#get_incidents_incidentID_domain_case) Added. Requires the `incident.read` scope. ### [2026-10-14](https://upfour.io/docs/api/changelog#d-2026-10-14) - added [GET /status-page-design-templates](https://upfour.io/docs/api/status-page-design-templates#get_status_page_design_templates) Added. Requires the `status_page.read` scope. - added [POST /status-page-design-templates](https://upfour.io/docs/api/status-page-design-templates#post_status_page_design_templates) Added. Requires the `status_page.write` scope. - added [GET /status-page-design-templates/{templateID}](https://upfour.io/docs/api/status-page-design-templates#get_status_page_design_templates_templateID) Added. Requires the `status_page.read` scope. - added [PUT /status-page-design-templates/{templateID}](https://upfour.io/docs/api/status-page-design-templates#put_status_page_design_templates_templateID) Added. Requires the `status_page.write` scope. - added [DELETE /status-page-design-templates/{templateID}](https://upfour.io/docs/api/status-page-design-templates#delete_status_page_design_templates_templateID) Added. Requires the `status_page.write` scope. - added [GET /status-page-design-templates/{templateID}/assets/{kind}](https://upfour.io/docs/api/status-page-design-templates#get_status_page_design_templates_templateID_assets_kind) Added. Requires the `status_page.read` scope. ### [2026-09-22](https://upfour.io/docs/api/changelog#d-2026-09-22) - added [POST /clients/logo-import](https://upfour.io/docs/api/clients#post_clients_logo_import) Added. Requires the `client.write` scope. ### [2026-09-21](https://upfour.io/docs/api/changelog#d-2026-09-21) - added [GET /escalation-policies/{policyID}/test-runs/latest](https://upfour.io/docs/api/escalation-policies#get_escalation_policies_policyID_test_runs_latest) Added. Requires the `escalation_policy.read` scope. - added [GET /escalation-policies/{policyID}/test-runs/{runID}](https://upfour.io/docs/api/escalation-policies#get_escalation_policies_policyID_test_runs_runID) Added. Requires the `escalation_policy.read` scope. - added [POST /status-pages/{pageID}/subscribers/resend-pending](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers_resend_pending) Added. Requires the `status_page.subscriber.write` scope. ### [2026-09-19](https://upfour.io/docs/api/changelog#d-2026-09-19) - added [POST /status-pages/feed-preview](https://upfour.io/docs/api/status-pages#post_status_pages_feed_preview) Added. Requires the `status_page.write` scope. - added [PUT /status-pages/{pageID}/assets/{kind}](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_assets_kind) Added. Requires the `status_page.write` scope. - added [DELETE /status-pages/{pageID}/assets/{kind}](https://upfour.io/docs/api/status-pages#delete_status_pages_pageID_assets_kind) Added. Requires the `status_page.write` scope. - added [POST /status-pages/{pageID}/duplicate](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_duplicate) Added. Requires the `status_page.write` scope. - added [GET /status-pages/{pageID}/subscribers/count](https://upfour.io/docs/api/status-pages#get_status_pages_pageID_subscribers_count) Added. Requires the `status_page.read` scope. - added [GET /status-pages/{pageID}/updates/{updateID}/deliveries](https://upfour.io/docs/api/status-pages#get_status_pages_pageID_updates_updateID_deliveries) Added. Requires the `status_page.read` scope. ### [2026-09-18](https://upfour.io/docs/api/changelog#d-2026-09-18) - added [GET /status-page-templates](https://upfour.io/docs/api/status-page-templates#get_status_page_templates) Added. Requires the `status_page.read` scope. - added [POST /status-page-templates](https://upfour.io/docs/api/status-page-templates#post_status_page_templates) Added. Requires the `status_page.write` scope. - added [PUT /status-page-templates/{templateID}](https://upfour.io/docs/api/status-page-templates#put_status_page_templates_templateID) Added. Requires the `status_page.write` scope. - added [DELETE /status-page-templates/{templateID}](https://upfour.io/docs/api/status-page-templates#delete_status_page_templates_templateID) Added. Requires the `status_page.write` scope. - added [GET /status-pages/{pageID}/updates/{updateID}](https://upfour.io/docs/api/status-pages#get_status_pages_pageID_updates_updateID) Added. Requires the `status_page.read` scope. - added [PUT /status-pages/{pageID}/updates/{updateID}](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_updates_updateID) Added. Requires the `status_page.write` scope. - added [POST /status-pages/{pageID}/updates/{updateID}/entries](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_updates_updateID_entries) Added. Requires the `status_page.write` scope. - added [PUT /status-pages/{pageID}/updates/{updateID}/entries/{entryID}](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_updates_updateID_entries_entryID) Added. Requires the `status_page.write` scope. - added [DELETE /status-pages/{pageID}/updates/{updateID}/entries/{entryID}](https://upfour.io/docs/api/status-pages#delete_status_pages_pageID_updates_updateID_entries_entryID) Added. Requires the `status_page.write` scope. ### [2026-09-08](https://upfour.io/docs/api/changelog#d-2026-09-08) - added [GET /monitors/batch/history](https://upfour.io/docs/api/monitors#get_monitors_batch_history) Added. Requires the `monitor.write` scope. - added [POST /monitors/bulk](https://upfour.io/docs/api/monitors#post_monitors_bulk) Added. Requires the `monitor.write` scope. - added [GET /monitors/summary](https://upfour.io/docs/api/monitors#get_monitors_summary) Added. Requires the `monitor.read` scope. - added [GET /organization/report-branding](https://upfour.io/docs/api/organization#get_organization_report_branding) Added. Requires the `organization.read` scope. - added [PUT /organization/report-branding](https://upfour.io/docs/api/organization#put_organization_report_branding) Added. Requires the `organization.write` scope. - added [POST /reports/schedules/preview](https://upfour.io/docs/api/reports#post_reports_schedules_preview) Added. Requires the `report.read` scope. ### [2026-09-06](https://upfour.io/docs/api/changelog#d-2026-09-06) - added [GET /status-pages/platform](https://upfour.io/docs/api/status-pages#get_status_pages_platform) Added. Requires the `status_page.read` scope. ### [2026-09-02](https://upfour.io/docs/api/changelog#d-2026-09-02) - breaking Required scope changed. Keys that do not hold the new scope are refused. 7 operations - [DELETE /reports/schedules/{scheduleID}](https://upfour.io/docs/api/reports#delete_reports_schedules_scheduleID) `report.read` to `report.write` - [DELETE /reports/share-links/{linkID}](https://upfour.io/docs/api/reports#delete_reports_share_links_linkID) `report.read` to `report.write` - [GET /status-pages/{pageID}/subscribers](https://upfour.io/docs/api/status-pages#get_status_pages_pageID_subscribers) `status_page.subscriber.write` to `status_page.read` - [POST /reports/schedules](https://upfour.io/docs/api/reports#post_reports_schedules) `report.read` to `report.write` - [POST /reports/schedules/{scheduleID}/run-now](https://upfour.io/docs/api/reports#post_reports_schedules_scheduleID_run_now) `report.read` to `report.write` - [POST /reports/{runID}/share](https://upfour.io/docs/api/reports#post_reports_runID_share) `report.read` to `report.write` - [PUT /reports/schedules/{scheduleID}](https://upfour.io/docs/api/reports#put_reports_schedules_scheduleID) `report.read` to `report.write` - breaking Removed from the public API reference. 3 operations - `GET /incidents/{incidentID}/escalations/{step}/acknowledge` - `GET /reports/verify/{hash}` - `GET /share/reports/{token}` - changed Required scope relaxed. 50 operations - [GET /clients](https://upfour.io/docs/api/clients#get_clients) `client.write` to `client.read` - [GET /clients/{clientID}](https://upfour.io/docs/api/clients#get_clients_clientID) `client.write` to `client.read` - [GET /clients/{clientID}/portal-settings](https://upfour.io/docs/api/clients#get_clients_clientID_portal_settings) `client.write` to `client.read` - [GET /clients/{clientID}/share-links](https://upfour.io/docs/api/clients#get_clients_clientID_share_links) `client.write` to `client.read` - [GET /escalation-policies](https://upfour.io/docs/api/escalation-policies#get_escalation_policies) `escalation_policy.write` to `escalation_policy.read` - [GET /escalation-policies/{policyID}](https://upfour.io/docs/api/escalation-policies#get_escalation_policies_policyID) `escalation_policy.write` to `escalation_policy.read` - [GET /heartbeat-groups](https://upfour.io/docs/api/heartbeat-groups#get_heartbeat_groups) `heartbeat.write` to `heartbeat.read` - [GET /heartbeat-groups/{groupID}](https://upfour.io/docs/api/heartbeat-groups#get_heartbeat_groups_groupID) `heartbeat.write` to `heartbeat.read` - [GET /heartbeats](https://upfour.io/docs/api/heartbeats#get_heartbeats) `heartbeat.write` to `heartbeat.read` - [GET /heartbeats/{heartbeatID}](https://upfour.io/docs/api/heartbeats#get_heartbeats_heartbeatID) `heartbeat.write` to `heartbeat.read` - [GET /incidents](https://upfour.io/docs/api/incidents#get_incidents) `incident.write` to `incident.read` - [GET /incidents/{incidentID}/attachments](https://upfour.io/docs/api/incidents#get_incidents_incidentID_attachments) `incident.write` to `incident.read` - [GET /incidents/{incidentID}/attachments/{attachmentID}](https://upfour.io/docs/api/incidents#get_incidents_incidentID_attachments_attachmentID) `incident.write` to `incident.read` - [GET /incidents/{incidentID}/comments](https://upfour.io/docs/api/incidents#get_incidents_incidentID_comments) `incident.write` to `incident.read` - [GET /invites](https://upfour.io/docs/api/invites#get_invites) `member.write` to `member.read` - [GET /metadata](https://upfour.io/docs/api/metadata#get_metadata) `metadata.write` to `metadata.read` - [GET /monitor-groups](https://upfour.io/docs/api/monitor-groups#get_monitor_groups) `monitor.write` to `monitor.read` - [GET /monitor-groups/{groupID}](https://upfour.io/docs/api/monitor-groups#get_monitor_groups_groupID) `monitor.delete` to `monitor.read` - [GET /monitors](https://upfour.io/docs/api/monitors#get_monitors) `monitor.write` to `monitor.read` - [GET /monitors/{monitorID}](https://upfour.io/docs/api/monitors#get_monitors_monitorID) `monitor.delete` to `monitor.read` - [GET /notification_channels](https://upfour.io/docs/api/notification-channels#get_notification_channels) `notification_channel.write` to `notification_channel.read` - [GET /notification_channels/{channelID}](https://upfour.io/docs/api/notification-channels#get_notification_channels_channelID) `notification_channel.write` to `notification_channel.read` - [GET /oncall-schedules](https://upfour.io/docs/api/oncall-schedules#get_oncall_schedules) `oncall.write` to `oncall.read` - [GET /oncall-schedules/{scheduleID}](https://upfour.io/docs/api/oncall-schedules#get_oncall_schedules_scheduleID) `oncall.write` to `oncall.read` - [GET /oncall-schedules/{scheduleID}/overrides](https://upfour.io/docs/api/oncall-schedules#get_oncall_schedules_scheduleID_overrides) `oncall.write` to `oncall.read` - [GET /organization](https://upfour.io/docs/api/organization#get_organization) `organization.write` to `organization.read` - [GET /organization/members/email/{email}](https://upfour.io/docs/api/organization#get_organization_members_email_email) `member.write` to `member.read` - [GET /organization/security](https://upfour.io/docs/api/organization#get_organization_security) `organization.write` to `organization.read` - [GET /runbooks](https://upfour.io/docs/api/runbooks#get_runbooks) `runbook.write` to `runbook.read` - [GET /runbooks/{runbookID}](https://upfour.io/docs/api/runbooks#get_runbooks_runbookID) `runbook.write` to `runbook.read` - [GET /status-pages](https://upfour.io/docs/api/status-pages#get_status_pages) `status_page.write` to `status_page.read` - [GET /status-pages/{pageID}](https://upfour.io/docs/api/status-pages#get_status_pages_pageID) `status_page.write` to `status_page.read` - [GET /status-pages/{pageID}/access](https://upfour.io/docs/api/status-pages#get_status_pages_pageID_access) `status_page.write` to `status_page.read` - [GET /status-pages/{pageID}/reports](https://upfour.io/docs/api/status-pages#get_status_pages_pageID_reports) `status_page.write` to `status_page.read` - [GET /status-pages/{pageID}/sections](https://upfour.io/docs/api/status-pages#get_status_pages_pageID_sections) `status_page.write` to `status_page.read` - [GET /status-pages/{pageID}/sections/{sectionID}/resources](https://upfour.io/docs/api/status-pages#get_status_pages_pageID_sections_sectionID_resources) `status_page.write` to `status_page.read` - [GET /status-pages/{pageID}/structure](https://upfour.io/docs/api/status-pages#get_status_pages_pageID_structure) `status_page.write` to `status_page.read` - [GET /status-pages/{pageID}/translations](https://upfour.io/docs/api/status-pages#get_status_pages_pageID_translations) `status_page.write` to `status_page.read` - [GET /status-pages/{pageID}/updates](https://upfour.io/docs/api/status-pages#get_status_pages_pageID_updates) `status_page.write` to `status_page.read` - [GET /teams](https://upfour.io/docs/api/teams#get_teams) `team.write` to `team.read` - [GET /teams/{teamID}](https://upfour.io/docs/api/teams#get_teams_teamID) `team.write` to `team.read` - [GET /teams/{teamID}/clients](https://upfour.io/docs/api/teams#get_teams_teamID_clients) `team.write` to `team.read` - [GET /teams/{teamID}/members](https://upfour.io/docs/api/teams#get_teams_teamID_members) `member.write` to `member.read` - [GET /teams/{teamID}/members/email/{email}](https://upfour.io/docs/api/teams#get_teams_teamID_members_email_email) `member.write` to `member.read` - [GET /webhook-subscriptions](https://upfour.io/docs/api/webhook-subscriptions#get_webhook_subscriptions) `webhook.write` to `webhook.read` - [PATCH /monitors/{monitorID}](https://upfour.io/docs/api/monitors#patch_monitors_monitorID) `monitor.delete` to `monitor.write` - [POST /monitors/{monitorID}/notification_channels/{channelID}](https://upfour.io/docs/api/monitors#post_monitors_monitorID_notification_channels_channelID) `monitor.delete` to `monitor.write` - [PUT /monitor-groups/{groupID}](https://upfour.io/docs/api/monitor-groups#put_monitor_groups_groupID) `monitor.delete` to `monitor.write` - [PUT /monitors/{monitorID}](https://upfour.io/docs/api/monitors#put_monitors_monitorID) `monitor.delete` to `monitor.write` - [PUT /monitors/{monitorID}/escalation-policy](https://upfour.io/docs/api/monitors#put_monitors_monitorID_escalation_policy) `monitor.delete` to `monitor.write` - documented The required scope is now declared for 13 operations. This describes behavior that already existed and is not counted as a change. Areas: [Health advisor](https://upfour.io/docs/api/advisor.md), [Client templates](https://upfour.io/docs/api/client-templates.md), [SLA policies](https://upfour.io/docs/api/sla-policies.md) ### [2026-08-27](https://upfour.io/docs/api/changelog#d-2026-08-27) - added [POST /client-templates/{templateID}/dry-run](https://upfour.io/docs/api/client-templates#post_client_templates_templateID_dry_run) Added. ### [2026-08-26](https://upfour.io/docs/api/changelog#d-2026-08-26) - added `GET /incidents/{incidentID}/escalations/{step}/acknowledge` Added (runtime). ### [2026-07-25](https://upfour.io/docs/api/changelog#d-2026-07-25) - added Added 19 operations. Areas: [Health advisor](https://upfour.io/docs/api/advisor.md), [Incidents](https://upfour.io/docs/api/incidents.md), [Monitors](https://upfour.io/docs/api/monitors.md), [Organization](https://upfour.io/docs/api/organization.md), [Status pages](https://upfour.io/docs/api/status-pages.md) 19 operations - [DELETE /status-pages/{pageID}/access-token](https://upfour.io/docs/api/status-pages#delete_status_pages_pageID_access_token) - [DELETE /status-pages/{pageID}/custom-domain](https://upfour.io/docs/api/status-pages#delete_status_pages_pageID_custom_domain) - [GET /advisor](https://upfour.io/docs/api/advisor#get_advisor) - [GET /incidents/{incidentID}/ai](https://upfour.io/docs/api/incidents#get_incidents_incidentID_ai) - [GET /incidents/{incidentID}/group](https://upfour.io/docs/api/incidents#get_incidents_incidentID_group) - [GET /monitors/{monitorID}/certificates](https://upfour.io/docs/api/monitors#get_monitors_monitorID_certificates) - [GET /organization/ai-usage](https://upfour.io/docs/api/organization#get_organization_ai_usage) - [GET /status-pages/{pageID}/access](https://upfour.io/docs/api/status-pages#get_status_pages_pageID_access) - [GET /status-pages/{pageID}/domain](https://upfour.io/docs/api/status-pages#get_status_pages_pageID_domain) - [GET /status-pages/{pageID}/domain/events](https://upfour.io/docs/api/status-pages#get_status_pages_pageID_domain_events) - [POST /advisor/apply](https://upfour.io/docs/api/advisor#post_advisor_apply) - [POST /incidents/{incidentID}/ai/postmortem](https://upfour.io/docs/api/incidents#post_incidents_incidentID_ai_postmortem) - [POST /incidents/{incidentID}/ai/summary](https://upfour.io/docs/api/incidents#post_incidents_incidentID_ai_summary) - [POST /status-pages/{pageID}/access-token](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_access_token) - [POST /status-pages/{pageID}/custom-domain/recheck](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_custom_domain_recheck) - [PUT /incidents/{incidentID}/ai/{kind}/rating](https://upfour.io/docs/api/incidents#put_incidents_incidentID_ai_kind_rating) - [PUT /organization/ai-quota](https://upfour.io/docs/api/organization#put_organization_ai_quota) - [PUT /status-pages/{pageID}/access](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_access) - [PUT /status-pages/{pageID}/custom-domain](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_custom_domain) ### [2026-07-21](https://upfour.io/docs/api/changelog#d-2026-07-21) - added [GET /clients/{clientID}/portal-settings](https://upfour.io/docs/api/clients#get_clients_clientID_portal_settings) Added. Requires the `client.write` scope. - added [PUT /clients/{clientID}/portal-settings](https://upfour.io/docs/api/clients#put_clients_clientID_portal_settings) Added. Requires the `client.write` scope. ### [2026-06-01](https://upfour.io/docs/api/changelog#d-2026-06-01) - added [GET /incidents/{incidentID}/attachments/{attachmentID}](https://upfour.io/docs/api/incidents#get_incidents_incidentID_attachments_attachmentID) Added. Requires the `incident.write` scope. ### [2026-05-12](https://upfour.io/docs/api/changelog#d-2026-05-12) - added Added 21 operations. Areas: [Reports](https://upfour.io/docs/api/reports.md), share, [SLA policies](https://upfour.io/docs/api/sla-policies.md) 21 operations - [DELETE /reports/schedules/{scheduleID}](https://upfour.io/docs/api/reports#delete_reports_schedules_scheduleID) - [DELETE /reports/share-links/{linkID}](https://upfour.io/docs/api/reports#delete_reports_share_links_linkID) - [DELETE /sla-policies/{policyID}](https://upfour.io/docs/api/sla-policies#delete_sla_policies_policyID) - [GET /reports/downtime-ledger](https://upfour.io/docs/api/reports#get_reports_downtime_ledger) - [GET /reports/incidents](https://upfour.io/docs/api/reports#get_reports_incidents) - [GET /reports/response-time](https://upfour.io/docs/api/reports#get_reports_response_time) - [GET /reports/runs](https://upfour.io/docs/api/reports#get_reports_runs) - [GET /reports/schedules](https://upfour.io/docs/api/reports#get_reports_schedules) - [GET /reports/schedules/{scheduleID}](https://upfour.io/docs/api/reports#get_reports_schedules_scheduleID) - [GET /reports/share-links](https://upfour.io/docs/api/reports#get_reports_share_links) - [GET /reports/sla](https://upfour.io/docs/api/reports#get_reports_sla) - `GET /reports/verify/{hash}` - `GET /share/reports/{token}` - [GET /sla-policies](https://upfour.io/docs/api/sla-policies#get_sla_policies) - [GET /sla-policies/{policyID}](https://upfour.io/docs/api/sla-policies#get_sla_policies_policyID) - [POST /reports/schedules](https://upfour.io/docs/api/reports#post_reports_schedules) - [POST /reports/schedules/{scheduleID}/run-now](https://upfour.io/docs/api/reports#post_reports_schedules_scheduleID_run_now) - [POST /reports/{runID}/share](https://upfour.io/docs/api/reports#post_reports_runID_share) - [POST /sla-policies](https://upfour.io/docs/api/sla-policies#post_sla_policies) - [PUT /reports/schedules/{scheduleID}](https://upfour.io/docs/api/reports#put_reports_schedules_scheduleID) - [PUT /sla-policies/{policyID}](https://upfour.io/docs/api/sla-policies#put_sla_policies_policyID) ### [2026-05-11](https://upfour.io/docs/api/changelog#d-2026-05-11) - added [POST /heartbeat-groups/assign](https://upfour.io/docs/api/heartbeat-groups#post_heartbeat_groups_assign) Added. Requires the `heartbeat.write` scope. - added [GET /incidents/{incidentID}/runbook-progress](https://upfour.io/docs/api/incidents#get_incidents_incidentID_runbook_progress) Added. Requires the `incident.read` scope. - added [PUT /incidents/{incidentID}/runbook-steps/{stepID}/completion](https://upfour.io/docs/api/incidents#put_incidents_incidentID_runbook_steps_stepID_completion) Added. Requires the `incident.write` scope. - added [POST /monitor-groups/assign](https://upfour.io/docs/api/monitor-groups#post_monitor_groups_assign) Added. Requires the `monitor.write` scope. ### [2026-05-07](https://upfour.io/docs/api/changelog#d-2026-05-07) - breaking Removed from the public API reference. 5 operations - `DELETE /user/api-keys/{keyID}` - `GET /user/api-keys` - `POST /login` - `POST /register` - `POST /user/api-keys` - added Added 171 operations. Areas: [Client templates](https://upfour.io/docs/api/client-templates.md), [Clients (agency mode)](https://upfour.io/docs/api/clients.md), [Alert plans (escalation policies)](https://upfour.io/docs/api/escalation-policies.md), [Scheduled job groups](https://upfour.io/docs/api/heartbeat-groups.md), [Scheduled jobs (heartbeats)](https://upfour.io/docs/api/heartbeats.md), [Incidents](https://upfour.io/docs/api/incidents.md), [Invites](https://upfour.io/docs/api/invites.md), [Token introspection](https://upfour.io/docs/api/me.md), [Resource metadata](https://upfour.io/docs/api/metadata.md), [Monitor groups](https://upfour.io/docs/api/monitor-groups.md), [Monitors](https://upfour.io/docs/api/monitors.md), [On-call rotations](https://upfour.io/docs/api/oncall-schedules.md), [Organization](https://upfour.io/docs/api/organization.md), [Reports](https://upfour.io/docs/api/reports.md), [Playbooks (runbooks)](https://upfour.io/docs/api/runbooks.md), [Severities](https://upfour.io/docs/api/severities.md), [Severity groups](https://upfour.io/docs/api/severity-groups.md), [Status pages](https://upfour.io/docs/api/status-pages.md), [Teams](https://upfour.io/docs/api/teams.md), [Webhook subscriptions](https://upfour.io/docs/api/webhook-subscriptions.md) 171 operations - [DELETE /client-templates/{templateID}](https://upfour.io/docs/api/client-templates#delete_client_templates_templateID) - [DELETE /clients/{clientID}/share-links/{linkID}](https://upfour.io/docs/api/clients#delete_clients_clientID_share_links_linkID) - [DELETE /escalation-policies/{policyID}](https://upfour.io/docs/api/escalation-policies#delete_escalation_policies_policyID) - [DELETE /heartbeat-groups/{groupID}](https://upfour.io/docs/api/heartbeat-groups#delete_heartbeat_groups_groupID) - [DELETE /heartbeats/{heartbeatID}](https://upfour.io/docs/api/heartbeats#delete_heartbeats_heartbeatID) - [DELETE /incidents/{incidentID}/attachments/{attachmentID}](https://upfour.io/docs/api/incidents#delete_incidents_incidentID_attachments_attachmentID) - [DELETE /incidents/{incidentID}/comments/{commentID}](https://upfour.io/docs/api/incidents#delete_incidents_incidentID_comments_commentID) - [DELETE /invites/{inviteID}](https://upfour.io/docs/api/invites#delete_invites_inviteID) - [DELETE /monitor-groups/{groupID}](https://upfour.io/docs/api/monitor-groups#delete_monitor_groups_groupID) - [DELETE /monitors/{monitorID}/escalation-policy](https://upfour.io/docs/api/monitors#delete_monitors_monitorID_escalation_policy) - [DELETE /monitors/{monitorID}/notification_channels/{channelID}](https://upfour.io/docs/api/monitors#delete_monitors_monitorID_notification_channels_channelID) - [DELETE /oncall-schedules/{scheduleID}](https://upfour.io/docs/api/oncall-schedules#delete_oncall_schedules_scheduleID) - [DELETE /oncall-schedules/{scheduleID}/overrides/{overrideID}](https://upfour.io/docs/api/oncall-schedules#delete_oncall_schedules_scheduleID_overrides_overrideID) - [DELETE /organization](https://upfour.io/docs/api/organization#delete_organization) - [DELETE /organization/members/email/{email}](https://upfour.io/docs/api/organization#delete_organization_members_email_email) - [DELETE /organization/members/{memberID}](https://upfour.io/docs/api/organization#delete_organization_members_memberID) - [DELETE /runbooks/{runbookID}](https://upfour.io/docs/api/runbooks#delete_runbooks_runbookID) - [DELETE /status-pages/{pageID}](https://upfour.io/docs/api/status-pages#delete_status_pages_pageID) - [DELETE /status-pages/{pageID}/sections/{sectionID}](https://upfour.io/docs/api/status-pages#delete_status_pages_pageID_sections_sectionID) - [DELETE /status-pages/{pageID}/sections/{sectionID}/resources/{resourceID}](https://upfour.io/docs/api/status-pages#delete_status_pages_pageID_sections_sectionID_resources_resourceID) - [DELETE /status-pages/{pageID}/subscribers/{subscriberID}](https://upfour.io/docs/api/status-pages#delete_status_pages_pageID_subscribers_subscriberID) - [DELETE /status-pages/{pageID}/translations/{translationID}](https://upfour.io/docs/api/status-pages#delete_status_pages_pageID_translations_translationID) - [DELETE /status-pages/{pageID}/updates/{updateID}](https://upfour.io/docs/api/status-pages#delete_status_pages_pageID_updates_updateID) - [DELETE /teams/{teamID}](https://upfour.io/docs/api/teams#delete_teams_teamID) - [DELETE /teams/{teamID}/clients/{clientID}](https://upfour.io/docs/api/teams#delete_teams_teamID_clients_clientID) - [DELETE /teams/{teamID}/members/email/{email}](https://upfour.io/docs/api/teams#delete_teams_teamID_members_email_email) - [DELETE /teams/{teamID}/members/{memberID}](https://upfour.io/docs/api/teams#delete_teams_teamID_members_memberID) - [DELETE /webhook-subscriptions/{subscriptionID}](https://upfour.io/docs/api/webhook-subscriptions#delete_webhook_subscriptions_subscriptionID) - [GET /client-templates](https://upfour.io/docs/api/client-templates#get_client_templates) - [GET /client-templates/{templateID}](https://upfour.io/docs/api/client-templates#get_client_templates_templateID) - [GET /clients](https://upfour.io/docs/api/clients#get_clients) - [GET /clients/{clientID}](https://upfour.io/docs/api/clients#get_clients_clientID) - [GET /clients/{clientID}/share-links](https://upfour.io/docs/api/clients#get_clients_clientID_share_links) - [GET /clients/{clientID}/teams](https://upfour.io/docs/api/clients#get_clients_clientID_teams) - [GET /escalation-policies](https://upfour.io/docs/api/escalation-policies#get_escalation_policies) - [GET /escalation-policies/{policyID}](https://upfour.io/docs/api/escalation-policies#get_escalation_policies_policyID) - [GET /heartbeat-groups](https://upfour.io/docs/api/heartbeat-groups#get_heartbeat_groups) - [GET /heartbeat-groups/{groupID}](https://upfour.io/docs/api/heartbeat-groups#get_heartbeat_groups_groupID) - [GET /heartbeats](https://upfour.io/docs/api/heartbeats#get_heartbeats) - [GET /heartbeats/{heartbeatID}](https://upfour.io/docs/api/heartbeats#get_heartbeats_heartbeatID) - [GET /heartbeats/{heartbeatID}/availability](https://upfour.io/docs/api/heartbeats#get_heartbeats_heartbeatID_availability) - [GET /heartbeats/{heartbeatID}/pings](https://upfour.io/docs/api/heartbeats#get_heartbeats_heartbeatID_pings) - [GET /incidents](https://upfour.io/docs/api/incidents#get_incidents) - [GET /incidents/{incidentID}](https://upfour.io/docs/api/incidents#get_incidents_incidentID) - [GET /incidents/{incidentID}/attachments](https://upfour.io/docs/api/incidents#get_incidents_incidentID_attachments) - [GET /incidents/{incidentID}/comments](https://upfour.io/docs/api/incidents#get_incidents_incidentID_comments) - [GET /incidents/{incidentID}/timeline](https://upfour.io/docs/api/incidents#get_incidents_incidentID_timeline) - [GET /invites](https://upfour.io/docs/api/invites#get_invites) - [GET /me](https://upfour.io/docs/api/me#get_me) - [GET /metadata](https://upfour.io/docs/api/metadata#get_metadata) - [GET /monitor-groups](https://upfour.io/docs/api/monitor-groups#get_monitor_groups) - [GET /monitor-groups/{groupID}](https://upfour.io/docs/api/monitor-groups#get_monitor_groups_groupID) - [GET /monitors/{monitorID}/availability](https://upfour.io/docs/api/monitors#get_monitors_monitorID_availability) - [GET /monitors/{monitorID}/incidents](https://upfour.io/docs/api/monitors#get_monitors_monitorID_incidents) - [GET /monitors/{monitorID}/notification_channels](https://upfour.io/docs/api/monitors#get_monitors_monitorID_notification_channels) - [GET /monitors/{monitorID}/regions](https://upfour.io/docs/api/monitors#get_monitors_monitorID_regions) - [GET /monitors/{monitorID}/response_times](https://upfour.io/docs/api/monitors#get_monitors_monitorID_response_times) - [GET /monitors/{monitorID}/results](https://upfour.io/docs/api/monitors#get_monitors_monitorID_results) - [GET /oncall-schedules](https://upfour.io/docs/api/oncall-schedules#get_oncall_schedules) - [GET /oncall-schedules/current](https://upfour.io/docs/api/oncall-schedules#get_oncall_schedules_current) - [GET /oncall-schedules/{scheduleID}](https://upfour.io/docs/api/oncall-schedules#get_oncall_schedules_scheduleID) - [GET /oncall-schedules/{scheduleID}/coverage](https://upfour.io/docs/api/oncall-schedules#get_oncall_schedules_scheduleID_coverage) - [GET /oncall-schedules/{scheduleID}/events](https://upfour.io/docs/api/oncall-schedules#get_oncall_schedules_scheduleID_events) - [GET /oncall-schedules/{scheduleID}/overrides](https://upfour.io/docs/api/oncall-schedules#get_oncall_schedules_scheduleID_overrides) - [GET /oncall-schedules/{scheduleID}/rotations](https://upfour.io/docs/api/oncall-schedules#get_oncall_schedules_scheduleID_rotations) - [GET /organization](https://upfour.io/docs/api/organization#get_organization) - [GET /organization/audit-logs](https://upfour.io/docs/api/organization#get_organization_audit_logs) - [GET /organization/members](https://upfour.io/docs/api/organization#get_organization_members) - [GET /organization/members/email/{email}](https://upfour.io/docs/api/organization#get_organization_members_email_email) - [GET /organization/security](https://upfour.io/docs/api/organization#get_organization_security) - [GET /organization/sso](https://upfour.io/docs/api/organization#get_organization_sso) - [GET /reports/agency-ribbon](https://upfour.io/docs/api/reports#get_reports_agency_ribbon) - [GET /reports/monitor-ribbon](https://upfour.io/docs/api/reports#get_reports_monitor_ribbon) - [GET /runbooks](https://upfour.io/docs/api/runbooks#get_runbooks) - [GET /runbooks/{runbookID}](https://upfour.io/docs/api/runbooks#get_runbooks_runbookID) - [GET /runbooks/{runbookID}/step-completions](https://upfour.io/docs/api/runbooks#get_runbooks_runbookID_step_completions) - [GET /severities](https://upfour.io/docs/api/severities#get_severities) - [GET /severity-groups](https://upfour.io/docs/api/severity-groups#get_severity_groups) - [GET /status-pages](https://upfour.io/docs/api/status-pages#get_status_pages) - [GET /status-pages/{pageID}](https://upfour.io/docs/api/status-pages#get_status_pages_pageID) - [GET /status-pages/{pageID}/report-updates](https://upfour.io/docs/api/status-pages#get_status_pages_pageID_report_updates) - [GET /status-pages/{pageID}/reports](https://upfour.io/docs/api/status-pages#get_status_pages_pageID_reports) - [GET /status-pages/{pageID}/sections](https://upfour.io/docs/api/status-pages#get_status_pages_pageID_sections) - [GET /status-pages/{pageID}/sections/{sectionID}/resources](https://upfour.io/docs/api/status-pages#get_status_pages_pageID_sections_sectionID_resources) - [GET /status-pages/{pageID}/structure](https://upfour.io/docs/api/status-pages#get_status_pages_pageID_structure) - [GET /status-pages/{pageID}/subscribers](https://upfour.io/docs/api/status-pages#get_status_pages_pageID_subscribers) - [GET /status-pages/{pageID}/translations](https://upfour.io/docs/api/status-pages#get_status_pages_pageID_translations) - [GET /status-pages/{pageID}/updates](https://upfour.io/docs/api/status-pages#get_status_pages_pageID_updates) - [GET /teams](https://upfour.io/docs/api/teams#get_teams) - [GET /teams/{teamID}](https://upfour.io/docs/api/teams#get_teams_teamID) - [GET /teams/{teamID}/clients](https://upfour.io/docs/api/teams#get_teams_teamID_clients) - [GET /teams/{teamID}/members](https://upfour.io/docs/api/teams#get_teams_teamID_members) - [GET /teams/{teamID}/members/email/{email}](https://upfour.io/docs/api/teams#get_teams_teamID_members_email_email) - [GET /webhook-subscriptions](https://upfour.io/docs/api/webhook-subscriptions#get_webhook_subscriptions) - [GET /webhook-subscriptions/{subscriptionID}/deliveries](https://upfour.io/docs/api/webhook-subscriptions#get_webhook_subscriptions_subscriptionID_deliveries) - [PATCH /heartbeats/{heartbeatID}](https://upfour.io/docs/api/heartbeats#patch_heartbeats_heartbeatID) - [PATCH /monitors/{monitorID}](https://upfour.io/docs/api/monitors#patch_monitors_monitorID) - [POST /client-templates](https://upfour.io/docs/api/client-templates#post_client_templates) - [POST /clients](https://upfour.io/docs/api/clients#post_clients) - [POST /clients/{clientID}/archive](https://upfour.io/docs/api/clients#post_clients_clientID_archive) - [POST /clients/{clientID}/share-links](https://upfour.io/docs/api/clients#post_clients_clientID_share_links) - [POST /clients/{clientID}/unarchive](https://upfour.io/docs/api/clients#post_clients_clientID_unarchive) - [POST /escalation-policies](https://upfour.io/docs/api/escalation-policies#post_escalation_policies) - [POST /escalation-policies/{policyID}/test](https://upfour.io/docs/api/escalation-policies#post_escalation_policies_policyID_test) - [POST /heartbeat-groups](https://upfour.io/docs/api/heartbeat-groups#post_heartbeat_groups) - [POST /heartbeats](https://upfour.io/docs/api/heartbeats#post_heartbeats) - [POST /heartbeats/{heartbeatID}/pause](https://upfour.io/docs/api/heartbeats#post_heartbeats_heartbeatID_pause) - [POST /heartbeats/{heartbeatID}/resume](https://upfour.io/docs/api/heartbeats#post_heartbeats_heartbeatID_resume) - [POST /incidents](https://upfour.io/docs/api/incidents#post_incidents) - [POST /incidents/{incidentID}/acknowledge](https://upfour.io/docs/api/incidents#post_incidents_incidentID_acknowledge) - [POST /incidents/{incidentID}/attachments](https://upfour.io/docs/api/incidents#post_incidents_incidentID_attachments) - [POST /incidents/{incidentID}/comments](https://upfour.io/docs/api/incidents#post_incidents_incidentID_comments) - [POST /incidents/{incidentID}/escalate](https://upfour.io/docs/api/incidents#post_incidents_incidentID_escalate) - [POST /incidents/{incidentID}/reopen](https://upfour.io/docs/api/incidents#post_incidents_incidentID_reopen) - [POST /incidents/{incidentID}/resolve](https://upfour.io/docs/api/incidents#post_incidents_incidentID_resolve) - [POST /incidents/{incidentID}/updates](https://upfour.io/docs/api/incidents#post_incidents_incidentID_updates) - [POST /invites](https://upfour.io/docs/api/invites#post_invites) - [POST /invites/{inviteID}/resend](https://upfour.io/docs/api/invites#post_invites_inviteID_resend) - [POST /monitor-groups](https://upfour.io/docs/api/monitor-groups#post_monitor_groups) - [POST /monitors/{monitorID}/check](https://upfour.io/docs/api/monitors#post_monitors_monitorID_check) - [POST /monitors/{monitorID}/notification_channels/{channelID}](https://upfour.io/docs/api/monitors#post_monitors_monitorID_notification_channels_channelID) - [POST /monitors/{monitorID}/pause](https://upfour.io/docs/api/monitors#post_monitors_monitorID_pause) - [POST /monitors/{monitorID}/resume](https://upfour.io/docs/api/monitors#post_monitors_monitorID_resume) - [POST /monitors/{monitorID}/test-alert](https://upfour.io/docs/api/monitors#post_monitors_monitorID_test_alert) - [POST /oncall-schedules](https://upfour.io/docs/api/oncall-schedules#post_oncall_schedules) - [POST /oncall-schedules/{scheduleID}/overrides](https://upfour.io/docs/api/oncall-schedules#post_oncall_schedules_scheduleID_overrides) - [POST /organization](https://upfour.io/docs/api/organization#post_organization) - [POST /runbooks](https://upfour.io/docs/api/runbooks#post_runbooks) - [POST /status-pages](https://upfour.io/docs/api/status-pages#post_status_pages) - [POST /status-pages/{pageID}/reports](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_reports) - [POST /status-pages/{pageID}/sections](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_sections) - [POST /status-pages/{pageID}/sections/{sectionID}/resources](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_sections_sectionID_resources) - [POST /status-pages/{pageID}/subscribers](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers) - [POST /status-pages/{pageID}/subscribers/upload](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers_upload) - [POST /status-pages/{pageID}/subscribers/{subscriberID}/resend](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_subscribers_subscriberID_resend) - [POST /status-pages/{pageID}/translations](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_translations) - [POST /status-pages/{pageID}/updates](https://upfour.io/docs/api/status-pages#post_status_pages_pageID_updates) - [POST /teams](https://upfour.io/docs/api/teams#post_teams) - [POST /teams/{teamID}/clients](https://upfour.io/docs/api/teams#post_teams_teamID_clients) - [POST /teams/{teamID}/members](https://upfour.io/docs/api/teams#post_teams_teamID_members) - [POST /webhook-subscriptions](https://upfour.io/docs/api/webhook-subscriptions#post_webhook_subscriptions) - [POST /webhook-subscriptions/{subscriptionID}/redeliver/{deliveryID}](https://upfour.io/docs/api/webhook-subscriptions#post_webhook_subscriptions_subscriptionID_redeliver_deliveryID) - [POST /webhook-subscriptions/{subscriptionID}/test](https://upfour.io/docs/api/webhook-subscriptions#post_webhook_subscriptions_subscriptionID_test) - [PUT /client-templates/{templateID}](https://upfour.io/docs/api/client-templates#put_client_templates_templateID) - [PUT /clients/{clientID}](https://upfour.io/docs/api/clients#put_clients_clientID) - [PUT /escalation-policies/{policyID}](https://upfour.io/docs/api/escalation-policies#put_escalation_policies_policyID) - [PUT /heartbeat-groups/{groupID}](https://upfour.io/docs/api/heartbeat-groups#put_heartbeat_groups_groupID) - [PUT /heartbeats/{heartbeatID}](https://upfour.io/docs/api/heartbeats#put_heartbeats_heartbeatID) - [PUT /heartbeats/{heartbeatID}/pause](https://upfour.io/docs/api/heartbeats#put_heartbeats_heartbeatID_pause) - [PUT /heartbeats/{heartbeatID}/resume](https://upfour.io/docs/api/heartbeats#put_heartbeats_heartbeatID_resume) - [PUT /incidents/{incidentID}/acknowledge](https://upfour.io/docs/api/incidents#put_incidents_incidentID_acknowledge) - [PUT /incidents/{incidentID}/reopen](https://upfour.io/docs/api/incidents#put_incidents_incidentID_reopen) - [PUT /incidents/{incidentID}/resolve](https://upfour.io/docs/api/incidents#put_incidents_incidentID_resolve) - [PUT /metadata](https://upfour.io/docs/api/metadata#put_metadata) - [PUT /monitor-groups/{groupID}](https://upfour.io/docs/api/monitor-groups#put_monitor_groups_groupID) - [PUT /monitors/{monitorID}/escalation-policy](https://upfour.io/docs/api/monitors#put_monitors_monitorID_escalation_policy) - [PUT /oncall-schedules/{scheduleID}](https://upfour.io/docs/api/oncall-schedules#put_oncall_schedules_scheduleID) - [PUT /organization](https://upfour.io/docs/api/organization#put_organization) - [PUT /organization/members/{memberID}/role](https://upfour.io/docs/api/organization#put_organization_members_memberID_role) - [PUT /organization/security](https://upfour.io/docs/api/organization#put_organization_security) - [PUT /organization/sso/{provider}](https://upfour.io/docs/api/organization#put_organization_sso_provider) - [PUT /runbooks/{runbookID}](https://upfour.io/docs/api/runbooks#put_runbooks_runbookID) - [PUT /runbooks/{runbookID}/steps/{stepID}/completion](https://upfour.io/docs/api/runbooks#put_runbooks_runbookID_steps_stepID_completion) - [PUT /status-pages/{pageID}](https://upfour.io/docs/api/status-pages#put_status_pages_pageID) - [PUT /status-pages/{pageID}/sections/{sectionID}](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_sections_sectionID) - [PUT /status-pages/{pageID}/sections/{sectionID}/resources/{resourceID}](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_sections_sectionID_resources_resourceID) - [PUT /status-pages/{pageID}/structure](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_structure) - [PUT /status-pages/{pageID}/translations/{translationID}](https://upfour.io/docs/api/status-pages#put_status_pages_pageID_translations_translationID) - [PUT /teams/{teamID}](https://upfour.io/docs/api/teams#put_teams_teamID) - [PUT /teams/{teamID}/members/{memberID}/role](https://upfour.io/docs/api/teams#put_teams_teamID_members_memberID_role) - [PUT /webhook-subscriptions/{subscriptionID}](https://upfour.io/docs/api/webhook-subscriptions#put_webhook_subscriptions_subscriptionID) - documented Required scope and stability are now declared for 17 operations. This describes behavior that already existed and is not counted as a change. Areas: [Monitors](https://upfour.io/docs/api/monitors.md), [Alert channels](https://upfour.io/docs/api/notification-channels.md), [Reports](https://upfour.io/docs/api/reports.md) ### [2026-05-05](https://upfour.io/docs/api/changelog#d-2026-05-05) - added First version of the API reference, with 22 operations. ## How this log is made A script reads every version of the OpenAPI file, groups the versions by the date they were committed and compares the end of each date with the end of the one before. A route that disappears and returns on the same date leaves no entry. Operator routes are not part of the public API and are not listed. The reference was a route inventory until 2026-10-27: it listed operations, scopes and stability, but most request and response bodies were open objects and success statuses were estimates. For the time before that date the log reports added and removed operations, scopes and stability only. On 2026-10-27 the fields of the operations were documented; that is one documented entry, not a change. From the day after, parameters, status codes and fields are compared too. --- # Recipe: deploy hooks > Pause monitors during deploys, resume after — a GitHub Actions example that keeps restart blips out of your uptime history. Source: https://upfour.io/docs/api/recipes/deploy-hooks A rolling restart that drops requests for eight seconds is not an outage — but a 10-second monitor can notice it. Pause around the deploy and the planned restart stays out of your alert history and your uptime numbers. Paused monitors also cost nothing. ## The two calls Set `UP4_API_KEY` and `MONITOR_ID` first. ```bash curl -fsS -X POST "https://app.upfour.io/v1/monitors/$MONITOR_ID/pause" \ -H "Authorization: Bearer $UP4_API_KEY" # … deploy … curl -fsS -X POST "https://app.upfour.io/v1/monitors/$MONITOR_ID/resume" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ## GitHub Actions Store the key as a repository secret. `if: always()` on the resume step matters — the monitor must come back even when the deploy fails (_especially_ then): ```yaml name: deploy on: { push: { branches: [main] } } jobs: deploy: runs-on: ubuntu-latest env: UP4_API_KEY: ${{ secrets.UP4_API_KEY }} MONITOR_ID: 42ded714-482c-4c3e-b7f2-bcd2e7ece4f1 steps: - uses: actions/checkout@v4 - name: Pause monitoring run: | curl -fsS -X POST "https://app.upfour.io/v1/monitors/$MONITOR_ID/pause" \ -H "Authorization: Bearer $UP4_API_KEY" - name: Deploy run: ./deploy.sh - name: Resume monitoring if: always() run: | curl -fsS -X POST "https://app.upfour.io/v1/monitors/$MONITOR_ID/resume" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ## Variants **A whole fleet:** list the affected monitors first (`GET /monitors?q=…`, following the cursor) and loop. **Scheduled maintenance instead:** for recurring windows (nightly restarts), a [maintenance window](https://upfour.io/docs/plan-maintenance.md) on the monitor is cleaner than pausing from CI. **Scoped key:** give CI a key with only `monitor.write` — nothing else is reachable if the secret leaks. ![API token form with the name CI integration, access level Read-only, and monitor.read and monitor.write checked.](https://upfour.io/docs/images/api-tokens.webp) **Create a scoped token.** Name, access level and permissions in the token form. 1. **Token name** Choose a name that identifies the integration, such as CI integration. 2. **Access level** The default for a token without explicit scopes. Read-only is the safe start. The screenshot shows the form before a token is created. 3. **Scopes** Select only the operations the integration needs. The example allows reading and writing monitors. --- # Recipe: webhook consumer > A webhook receiver in Node: raw-body handling, signature verification, replay window, store first and answer 2xx after, dedupe by delivery id. Source: https://upfour.io/docs/api/recipes/webhook-consumer Four things make a webhook receiver production-shaped: verify the signature over the **raw** body, reject stale timestamps, store the event under its `id`, and only then answer 2xx. The real work runs from what you stored. This is the whole thing in Node; the [webhooks guide](https://upfour.io/docs/api/webhooks#verify) has the same verification in Node, Python, Go, PHP and Ruby. ## Subscribe ```bash curl -s -X POST https://app.upfour.io/v1/webhook-subscriptions \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://ops.example.com/hooks/up4", "events": ["monitor.status_changed", "incident.opened", "incident.resolved"] }' ``` The response includes the **subscription secret** — store it next to your other secrets. Then send yourself a test delivery: `POST /webhook-subscriptions/{id}/test`. ![A webhook-subscription request panel with an empty key field and example JSON containing a destination URL and three event types.](https://upfour.io/docs/images/webhook-subscription.webp) **Set the destination and event types.** An example subscription in the reference’s Try it panel. 1. **API key** Use a key with webhook.write and access to the intended organization. 2. **Destination and events** Replace the example URL with your receiver and choose the events you need. Keep the returned subscription secret for signature verification. 3. **Create subscription** Send request creates the subscription. The response is shown below the button; no request was sent for this screenshot. ## The receiver (Node) ```javascript import express from "express"; import { createHmac, timingSafeEqual } from "node:crypto"; import pg from "pg"; const app = express(); // During a secret rotation both are set; see "Rotate the secret" below. const SECRETS = [process.env.UP4_WEBHOOK_SECRET, process.env.UP4_WEBHOOK_SECRET_PREVIOUS].filter(Boolean); const db = new pg.Pool(); // up4_events(event_id text primary key, delivery_id text, event text, body text) // secret: the whsec_… string, whole, as the create or rotate call returned it // header: the X-Up4-Signature header, "t=,v1=" // rawBody: the request body exactly as received (a Buffer), before JSON.parse function verifyUp4Signature(secret, header, rawBody, now = Math.floor(Date.now() / 1000), tolerance = 300) { let timestamp = null; const signatures = []; for (const part of String(header ?? "").split(",")) { const at = part.indexOf("="); const key = at < 0 ? part : part.slice(0, at); const value = at < 0 ? "" : part.slice(at + 1); if (key === "t") timestamp = value; if (key === "v1") signatures.push(value); } if (timestamp === null || !/^[0-9]+$/.test(timestamp) || signatures.length === 0) return false; if (Math.abs(now - Number(timestamp)) > tolerance) return false; const expected = createHmac("sha256", secret).update(`${timestamp}.`).update(rawBody).digest(); return signatures.some( (signature) => /^[0-9a-f]{64}$/i.test(signature) && timingSafeEqual(expected, Buffer.from(signature, "hex")), ); } // express.raw, NOT express.json: the signature covers the raw bytes. app.post("/hooks/up4", express.raw({ type: "application/json" }), async (req, res) => { const signature = req.header("X-Up4-Signature") ?? ""; if (!SECRETS.some((secret) => verifyUp4Signature(secret, signature, req.body))) { return res.status(401).end(); } let payload; try { payload = JSON.parse(req.body); } catch { return res.status(400).end(); } try { // Store first. The event id is the same on every retry and on a manual // redelivery, so the primary key makes a repeat a no-op. (The delivery id // changes on a redelivery.) await db.query( "INSERT INTO up4_events (event_id, delivery_id, event, body) VALUES ($1, $2, $3, $4) ON CONFLICT (event_id) DO NOTHING", [payload.id, req.header("X-Up4-Delivery"), req.header("X-Up4-Event"), req.body.toString("utf8")], ); } catch { return res.status(500).end(); // nothing stored: up4 retries } res.status(200).end(); // stored, safe to acknowledge }); // A worker reads stored rows and does the real work. async function handle({ event, body }) { const payload = JSON.parse(body); switch (event) { case "incident.opened": // page your own systems, open a ticket, dim the lights … break; case "monitor.status_changed": // payload.data.state is UP or DOWN break; } } app.listen(8787); ``` A 5xx, 408 or 429 (or a timeout) makes up4 try again, up to seven attempts with the same `X-Up4-Delivery` id. Any other 4xx counts as permanent and ends that delivery, so answer 4xx only for requests that should never be retried. Deduplicate on the body's `id`, not on `X-Up4-Delivery`: a manual redelivery is a new delivery with a new delivery id, but the event keeps its `id`. ## Rotate the secret The old secret stops signing the moment you rotate, with no grace period, and a retry that was queued earlier is signed with the new one. So deploy the receiver with `UP4_WEBHOOK_SECRET_PREVIOUS` first, rotate, then drop the old value. While both are set the receiver accepts a delivery signed with either, and never answers 4xx for one that is signed with the other: a 4xx ends that delivery for good. See [Get the signing secret](https://upfour.io/docs/api/webhooks#secret). ## Test it end to end — safely Webhooks **deliver for real** and there is no test environment, so a monitor pointed at a URL you control gives you the full loop: break the URL, watch `incident.opened` arrive, fix it, watch `incident.resolved`. Inspect and redeliver any delivery via `GET /webhook-subscriptions/{id}/deliveries`. ## Notes Destinations must be publicly reachable — deliveries refuse loopback, private ranges and metadata endpoints (SSRF protection). For local development, a tunnel (cloudflared, ngrok) works fine. --- # Recipe: backup heartbeat > Monitor a nightly backup with a heartbeat: the crontab line, && semantics, curl flags that fail loudly, and how to size the grace window. Source: https://upfour.io/docs/api/recipes/backup-heartbeat Nobody notices a broken backup until restore day. Give the job a heartbeat: if the ping does not arrive on time, the silence becomes an incident and notifies your matching alert channels. ## 1 · Create the heartbeat In the app (**Scheduled jobs → New**) or via API: ```bash curl -s -X POST https://app.upfour.io/v1/heartbeats \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "nightly pg_dump", "expected_interval_seconds": 86400, "grace_period_seconds": 3600 }' ``` The response carries the `slug` — that slug is the ping URL. The deadline starts with the first check-in: a job that never pinged never alerts, so run the script once by hand after step 3. ## 2 · Size the grace window The job runs daily (`86400`), but not to the second: the dump takes 20–40 minutes depending on load. The grace window absorbs that spread — **interval = the schedule, grace = the jitter**. One hour of grace on a daily backup means: alert only when the ping is more than an hour late. Too tight pages you for a slow night; too loose costs you reaction time. ![Scheduled-job form named Nightly database backup, expecting a check-in every one day with a one-hour grace period.](https://upfour.io/docs/images/heartbeat.webp) **Set the expected interval and grace.** A daily backup with an extra hour for normal delays. 1. **Job name** Give the job a name you will recognize in an alert. 2. **Expected interval** The example job runs once per day. Set this to the job’s actual schedule. 3. **Grace period** One extra hour allows for a late finish. Send the ping only after the job succeeds. ## 3 · Save the backup script, then schedule it Save this as `/usr/local/bin/up4-nightly-backup.sh`, replace the database, backup path and ping URL, and make the script executable. Run it with Bash: ```bash #!/usr/bin/env bash set -euo pipefail pg_dump mydb | gzip > /backups/nightly.sql.gz curl -fsS --max-time 10 --retry 3 \ https://app.upfour.io/v1/heartbeat/ ``` Then add this single line to the backup user’s crontab, after checking that user can run the script and write the backup file: ``` 30 3 * * * /usr/local/bin/up4-nightly-backup.sh ``` Three details carry the whole recipe: | `set -euo pipefail` | Bash stops on failure, including a failed dump inside the compression pipeline. Without pipefail, gzip can succeed after pg_dump fails and the script would send a false success ping. | | --- | --- | | `-f` | curl exits non-zero on HTTP errors instead of swallowing them. | | `--max-time 10 --retry 3` | A hanging ping must not block cron; a transient network blip should not look like a missed backup. | ## 4 · What happens when it breaks Backup fails → no ping → interval + grace passes → incident opens and the [alert channels](https://upfour.io/docs/receive-alerts.md) that match the job are notified (jobs have no alert plan, on-call rotation or phone call); `heartbeat.missed` goes out as a webhook. The next successful ping recovers it and sends the all-clear. The ping log keeps accepted, stored check-ins with timestamp and source IP — `GET /heartbeats/{id}/pings` answers “did it really run on the 14th?” for 90 days (the IP is cleared after 7). ## Variants Weekly jobs: `604800` with a day of grace. Multiple backup hosts: one heartbeat each — a shared heartbeat can't tell you _which_ host went quiet. Migration downtime: pause the heartbeat, resume after; paused heartbeats never alert. --- # Recipe: monitors as code > Keep your monitor fleet in a JSON file in git and sync it idempotently from CI — create what's missing, leave the rest alone. Source: https://upfour.io/docs/api/recipes/monitor-as-code Monitors that only exist in a dashboard drift: someone edits an interval, nobody knows why. Keep the fleet in a JSON file next to your infrastructure code and sync it from CI — reviewable, diffable, revertable. ## The fleet file Save it as `monitors.json`: ```json [ { "name": "api", "url": "https://api.example.com/health", "monitor_type": "http", "interval_seconds": 10, "regions": ["eu"] }, { "name": "website", "url": "https://example.com", "monitor_type": "http", "interval_seconds": 60, "regions": ["eu"] }, { "name": "shop", "url": "https://shop.example.com", "monitor_type": "http", "interval_seconds": 30, "regions": ["eu"], "alert_type": "keyword", "keyword": "Add to cart" } ] ``` ## The sync script Create-if-missing, keyed by name within one client (or at company level when an entry has no `client_id`). The lookup uses the search parameter (`q=`, a substring match, so the script compares names exactly), URL-encodes the name and reads every page. Creation carries an idempotency key built from client id and name, so parallel CI runs cannot double-create: ```bash #!/usr/bin/env bash set -euo pipefail : "${UP4_API_KEY:?set UP4_API_KEY first}" API=https://app.upfour.io/v1 # exists NAME [CLIENT_ID]: 0 = found, 1 = not found, 2 = lookup failed exists() { local name=$1 client=${2:-} cursor="" page found more while :; do page=$(curl -fsS -G "$API/monitors" \ -H "Authorization: Bearer $UP4_API_KEY" \ --data-urlencode "q=$name" --data-urlencode "limit=250" \ ${client:+--data-urlencode "client_id=$client"} \ ${cursor:+--data-urlencode "cursor=$cursor"}) || return 2 found=$(jq --arg n "$name" 'any((.data // [])[]; .name == $n)' <<<"$page") || return 2 [ "$found" = true ] && return 0 more=$(jq -r '.pagination.has_more // false' <<<"$page") || return 2 [ "$more" = true ] || return 1 cursor=$(jq -r '.pagination.next_cursor' <<<"$page") || return 2 done } jq -c '.[]' monitors.json | while read -r monitor; do name=$(jq -r .name <<<"$monitor") client=$(jq -r '.client_id // empty' <<<"$monitor") rc=0; exists "$name" "$client" || rc=$? case $rc in 0) echo "exists: $name" ;; 1) key=$(printf '%s|%s' "$client" "$name" | shasum | cut -c1-32) curl -fsS -X POST "$API/monitors" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: fleet-$key" \ -d "$monitor" > /dev/null echo "created: $name" ;; *) echo "lookup failed: $name" >&2; exit 1 ;; esac done ``` A failed lookup stops the run instead of creating. The client id belongs in the key: with the name alone, the same name in a second client would send a different body under the same key, and the create is answered with `409 idempotency_key_conflict`, not treated as a duplicate. Keys are kept for 24 hours. ## Updating and pruning This recipe deliberately stops at create-if-missing — it can run against a fleet people also touch by hand without ever overwriting their work. If the file should be the single source of truth, extend the else-branch with a `PUT /monitors/{id}` of the desired state, and treat monitors that exist remotely but not in the file as candidates for deletion — behind a `--prune` flag you have to mean. ## Notes There is no test environment: try the script on a dedicated Client or a few throwaway monitors first ([testing](https://upfour.io/docs/api/testing.md)). Give CI a key scoped to `monitor.read` \+ `monitor.write`. Agencies: add `client_id` to each entry; the script then searches inside that client only. One file per client keeps the diffs small. ![API token form with the name CI integration, access level Read-only, and monitor.read and monitor.write checked.](https://upfour.io/docs/images/api-tokens.webp) **Create a scoped token.** Name, access level and permissions in the token form. 1. **Token name** Choose a name that identifies the integration, such as CI integration. 2. **Access level** The default for a token without explicit scopes. Read-only is the safe start. The screenshot shows the form before a token is created. 3. **Scopes** Select only the operations the integration needs. The example allows reading and writing monitors. --- # Monitors — API reference > The heart of the API: create, list, pause, resume and delete the things we check, as often as every ten seconds. REST endpoints with request and response fields, examples in curl, JavaScript, Python, Go and PHP, and an in-browser Try it console. Source: https://upfour.io/docs/api/monitors [Developers](https://upfour.io/docs/api.md)/Monitoring The heart of the API: create, list, pause, resume and delete the things we check, as often as every ten seconds. [The Monitor object](https://upfour.io/docs/api/monitors/get-a-monitor#returns) [In the app: Watch a website →](https://upfour.io/docs/watch-a-website.md) Base URL `https://app.upfour.io/v1`. Send your key as `Authorization: Bearer `; every key acts on your real Company, so [test without paging anyone](https://upfour.io/docs/api/testing.md). New here? Start with the [quickstart](https://upfour.io/docs/api/quickstart.md). The examples read the key from `UP4_API_KEY`. ## Everyday [GET **List monitors** /monitors](https://upfour.io/docs/api/monitors/list-monitors.md)[POST **Create a monitor** /monitors](https://upfour.io/docs/api/monitors/create-a-monitor.md)[GET **Get a monitor** /monitors/{monitorID}](https://upfour.io/docs/api/monitors/get-a-monitor.md)[PATCH **Update a monitor** /monitors/{monitorID}](https://upfour.io/docs/api/monitors/update-a-monitor.md)[PUT **Replace a monitor** /monitors/{monitorID}](https://upfour.io/docs/api/monitors/replace-a-monitor.md)[DEL **Delete a monitor** /monitors/{monitorID}](https://upfour.io/docs/api/monitors/delete-a-monitor.md) ## Control [POST **Pause a monitor** /monitors/{monitorID}/pause](https://upfour.io/docs/api/monitors/pause-a-monitor.md)[POST **Resume a monitor** /monitors/{monitorID}/resume](https://upfour.io/docs/api/monitors/resume-a-monitor.md)[POST **Run a check now** /monitors/{monitorID}/check](https://upfour.io/docs/api/monitors/run-a-check-now.md)[POST **Send a test alert** /monitors/{monitorID}/test-alert](https://upfour.io/docs/api/monitors/send-a-test-alert.md) ## Results and history [GET **List check results** /monitors/{monitorID}/results](https://upfour.io/docs/api/monitors/list-check-results.md)[GET **Get availability** /monitors/{monitorID}/availability](https://upfour.io/docs/api/monitors/get-availability.md)[GET **Get response times** /monitors/{monitorID}/response_times](https://upfour.io/docs/api/monitors/get-response-times.md)[GET **List incidents** /monitors/{monitorID}/incidents](https://upfour.io/docs/api/monitors/list-incidents.md)[GET **List certificates** /monitors/{monitorID}/certificates](https://upfour.io/docs/api/monitors/list-certificates.md)[GET **Get domain expiry** /monitors/{monitorID}/domain-expiry](https://upfour.io/docs/api/monitors/get-domain-expiry.md)[GET **List regions** /monitors/{monitorID}/regions](https://upfour.io/docs/api/monitors/list-regions.md)[GET **Get the summary** /monitors/summary](https://upfour.io/docs/api/monitors/get-the-summary.md) ## Alerting [GET **List alert channels** /monitors/{monitorID}/notification_channels](https://upfour.io/docs/api/monitors/list-alert-channels.md)[POST **Attach an alert channel** /monitors/{monitorID}/notification_channels/{channelID}](https://upfour.io/docs/api/monitors/attach-an-alert-channel.md)[DEL **Detach an alert channel** /monitors/{monitorID}/notification_channels/{channelID}](https://upfour.io/docs/api/monitors/detach-an-alert-channel.md)[PUT **Set the alert plan** /monitors/{monitorID}/escalation-policy](https://upfour.io/docs/api/monitors/set-the-alert-plan.md)[DEL **Remove the alert plan** /monitors/{monitorID}/escalation-policy](https://upfour.io/docs/api/monitors/remove-the-alert-plan.md) ## Many at once [POST **Import monitors** /monitors/batch](https://upfour.io/docs/api/monitors/import-monitors.md)[POST **Preview an import** /monitors/batch/dry-run](https://upfour.io/docs/api/monitors/preview-an-import.md)[GET **List imports** /monitors/batch/history](https://upfour.io/docs/api/monitors/list-imports.md)[POST **Change several monitors** /monitors/bulk](https://upfour.io/docs/api/monitors/change-several-monitors.md)[POST **Export monitors** /monitors/export-details](https://upfour.io/docs/api/monitors/export-monitors.md) --- # List monitors — API reference > Lists monitors in the current scope. Source: https://upfour.io/docs/api/monitors/list-monitors [Developers](https://upfour.io/docs/api.md)/[Monitors](https://upfour.io/docs/api/monitors.md) Lists monitors in the current scope. GET https://app.upfour.io/v1/monitors scope monitor.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `limit` optional | integer 1–250 default `50` | query parameter | | `cursor` optional | string | query parameter | | `client_id` optional | uuid | query parameter | | `all_clients` optional | boolean | query parameter | | `q` optional | string | query parameter | | `sort` optional | string | query parameter | | `status` optional | string one of`UP` `DOWN` `PAUSED` | query parameter | | `monitor_type` optional | string one of`http` `ping` `tcp` `dns` | query parameter | | `region` optional | string | query parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | Monitor[] · nullable | | | Each item · 70 fields FieldTypeDescription `alert_type` string one of`status` `keyword` `keyword_absence` `expected_status_code` `auth_username` string `capture_on_incident_open` boolean `confirmation_period` integer `created_at` string (date-time) `degraded_alert_enabled` boolean `degraded_threshold_ms` integer · nullable `domain_expiry_alert_days` integer · nullable `escalation_policy_id` uuid · nullable `expected_status_code` integer · nullable `feature_pause_reasons` string[] · nullable `follow_redirects` boolean `has_auth_password` boolean `has_request_headers` boolean `http_method` string `id` uuid `interval_seconds` integer `ip_version` string one of`""` `4` `6` `is_degraded` boolean `is_pending` boolean `keep_cookies` boolean `keyword` string `keyword_case_insensitive` boolean `maintenance_from` string · nullable `maintenance_timezone` string `maintenance_to` string · nullable `maintenance_weekdays` string[] · nullable one of`mon` `tue` `wed` `thu` `fri` `sat` `sun` `monitor_type` string one of`http` `tcp` `ping` `dns` `name` string `next_check_at` string (date-time) · nullable `notification_mode` string one of`quiet` `immediate` `notify_recovery` boolean `pronounceable_name` string `proxy_host` string `proxy_port` integer · nullable `public_name` string `recovery_period` integer `regions` string[] · nullable one of`eu` `us` `asia` `au` `default` `request_body` string `request_headers` any[] · nullable Always empty: header values are never returned. has_request_headers says whether the monitor sends any. `request_timeout_s` integer `ssl_expiry_alert_days` integer · nullable `ssl_verification` boolean `state` string one of`UP` `DOWN` `PAUSED` `url` string `user_id` uuid `badge_token` string `client_id` uuid `dns_expected` string `dns_query` string `dns_server` string `dns_type` string one of`A` `AAAA` `MX` `CNAME` `TXT` `NS` `freshness_timeout_seconds` integer `last_checked_at` string (date-time) `last_result_at` string (date-time) `last_state_change_at` string (date-time) `latency_p50_ms` integer `latency_p95_ms` integer `latency_samples_24h` integer `latest_result_successful` boolean `monitor_group_id` uuid `paused_until` string (date-time) `recent_incidents` MonitorRecentIncident[] Each item · 5 fields FieldTypeDescription `id` uuid `resolved_at` string (date-time) · nullable `started_at` string (date-time) `status` string `resolved_by_type` string `tcp_host` string `tcp_port` integer | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 400 | [cursor_expired](https://upfour.io/docs/api/errors#code-cursor_expired) | The list cursor has expired. **When:** Paging a monitor list with a cursor that is too old. **Fix:** Start again without a cursor. | | 400 | [cursor_invalid](https://upfour.io/docs/api/errors#code-cursor_invalid) | The list cursor is not valid. **When:** Paging a monitor list with a cursor that was not issued by the API or was altered. **Fix:** Use the cursor from the previous response unchanged, or start without one. | | 400 | [cursor_parameters_changed](https://upfour.io/docs/api/errors#code-cursor_parameters_changed) | The filters changed since the cursor was issued. **When:** Paging a monitor list with a cursor while sort or filter parameters differ from the first request. **Fix:** Keep the parameters the same for all pages, or start again without a cursor. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [NextCreate a monitor](https://upfour.io/docs/api/monitors/create-a-monitor.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/monitors" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitors", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/monitors", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/monitors", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "alert_type": "status", "auth_username": "", "capture_on_incident_open": false, "confirmation_period": 0, "created_at": "2026-10-27T09:00:00Z", "degraded_alert_enabled": false, "degraded_threshold_ms": null, "dns_type": "A", "domain_expiry_alert_days": null, "escalation_policy_id": null, "expected_status_code": null, "feature_pause_reasons": [], "follow_redirects": true, "freshness_timeout_seconds": 360, "has_auth_password": false, "has_request_headers": false, "http_method": "HEAD", "id": "00000000-0000-4000-8000-000000000001", "interval_seconds": 300, "ip_version": "", "is_degraded": false, "is_pending": true, "keep_cookies": false, "keyword": "", "keyword_case_insensitive": true, "maintenance_from": null, "maintenance_timezone": "UTC", "maintenance_to": null, "maintenance_weekdays": [ "mon", "tue", "wed", "thu", "fri", "sat", "sun" ], "monitor_type": "http", "name": "Staging API", "next_check_at": "2026-10-27T09:00:00Z", "notification_mode": "immediate", "notify_recovery": true, "pronounceable_name": "", "proxy_host": "", "proxy_port": null, "public_name": "", "recovery_period": 0, "regions": [ "eu" ], "request_body": "", "request_headers": [], "request_timeout_s": 30, "ssl_expiry_alert_days": null, "ssl_verification": true, "state": "UP", "url": "https://example.com", "user_id": "00000000-0000-4000-8000-000000000002" }, { "alert_type": "status", "auth_username": "", "capture_on_incident_open": false, "confirmation_period": 0, "created_at": "2026-10-27T09:00:00Z", "degraded_alert_enabled": false, "degraded_threshold_ms": null, "dns_type": "A", "domain_expiry_alert_days": null, "escalation_policy_id": null, "expected_status_code": null, "feature_pause_reasons": [], "follow_redirects": true, "freshness_timeout_seconds": 120, "has_auth_password": false, "has_request_headers": false, "http_method": "HEAD", "id": "00000000-0000-4000-8000-000000000003", "interval_seconds": 60, "ip_version": "", "is_degraded": false, "is_pending": false, "keep_cookies": false, "keyword": "", "keyword_case_insensitive": true, "last_result_at": "2026-10-27T08:50:00Z", "latest_result_successful": true, "maintenance_from": null, "maintenance_timezone": "UTC", "maintenance_to": null, "maintenance_weekdays": [ "mon", "tue", "wed", "thu", "fri", "sat", "sun" ], "monitor_type": "http", "name": "Marketing website", "next_check_at": "2026-10-27T09:00:00Z", "notification_mode": "immediate", "notify_recovery": true, "pronounceable_name": "", "proxy_host": "", "proxy_port": null, "public_name": "", "recovery_period": 0, "regions": [ "eu" ], "request_body": "", "request_headers": [], "request_timeout_s": 30, "ssl_expiry_alert_days": null, "ssl_verification": true, "state": "UP", "url": "https://example.org", "user_id": "00000000-0000-4000-8000-000000000002" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` --- # Create a monitor — API reference > Starts checking a website, port, DNS record or host. Source: https://upfour.io/docs/api/monitors/create-a-monitor [Developers](https://upfour.io/docs/api.md)/[Monitors](https://upfour.io/docs/api/monitors.md) Starts checking a website, port, DNS record or host. POST https://app.upfour.io/v1/monitors scope monitor.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Body The 6 fields most calls use come first; the rest are grouped below. `application/json` required 56 fields urlstring Target for http and ping monitors (ping takes a host) In the app: **URL to watch** monitor_typestring Default http httptcppingdns In the app: **Monitor type** namestring Display name In the app: **Name** interval_secondsinteger Down to 10, default 60; POST/PUT/PATCH/DELETE monitors need 60 or more In the app: **Check every** regionsstring[] eu (Germany) · us · asia (Singapore) · au (Australia), default eu; each region runs and counts its own checks euusasiaaudefault In the app: **Regions** notification_channel_idsuuid[] Alert channels to attach What counts as up5 fields alert_typestring status (default: the final response must be 2xx) · keyword (down when the text is missing) · keyword_absence (down when the text is found) · expected_status_code ""statuskeywordkeyword_absenceexpected_status_code In the app: **Counts as down when** keywordstring The text to look for; required for keyword and keyword_absence, which read the body with GET In the app: **Text to look for** keyword_case_insensitiveboolean · nullable Ignore letter case (default true) In the app: **Ignore case** expected_status_codeinteger · nullable 100–599; only used with alert_type expected_status_code, on its own it does nothing In the app: **Expected status code** ssl_verificationboolean · nullable Fail on broken certificate chains (default true) In the app: **Verify the TLS certificate** Request15 fields http_methodstring GET · HEAD · POST · PUT · PATCH · DELETE (default HEAD) In the app: **HTTP method** request_headersRequestHeader[] Custom request headers, at most 20 and 8 KB in total. On update they are kept unless request_headers_action is replace or clear. In the app: **Headers** Nested fields 2 fields | Field | Type | Description | | --- | --- | --- | | `name` optional | string | | | `value` optional | string | | request_headers_actionstring On update: keep · replace · clear keepreplaceclear request_bodystring Sent with methods that carry one; up to 8 KB In the app: **Request body** request_timeout_sinteger Per-check budget, default 30, at most 60; a timeout is a failed check In the app: **Request timeout** follow_redirectsboolean · nullable Follow 3xx (default true); with redirects off a 3xx is down unless alert_type is expected_status_code In the app: **Follow redirects** keep_cookiesboolean Persist cookies across a redirect chain In the app: **Keep cookies when redirecting** auth_usernamestring HTTP basic auth; password stored encrypted, never returned In the app: **Username** auth_passwordstring Basic-auth password. Write-only: responses carry has_auth_password instead. In the app: **Password** auth_password_actionstring On update: keep (default) · replace · clear ""keepreplaceclear proxy_hoststring Check through your proxy; the host is a plain host name or IP address In the app: **Proxy** proxy_portinteger · nullable Check through your proxy; the host is a plain host name or IP address ip_versionstring "4" or "6" to force IPv4 or IPv6, empty for either ""46 In the app: **IP version** Alerting11 fields notification_modestring immediate (default): alert once three checks confirm the outage · quiet: alert only after 3 minutes of downtime in total quietimmediate In the app: **When should we alert you?** alert_channel_namesstring[] Batch import only (POST /monitors/batch): names resolved to ids escalation_policy_iduuid · nullable Attach an alert plan In the app: **Alert plan** alert_plan_namestring Batch import only (POST /monitors/batch): names resolved to ids default_alert_planboolean Without escalation_policy_id, attach the default plan when one of the channels is your own email confirmation_periodinteger Extra seconds a failure has to last, on top of the three confirming checks, before the monitor goes down; 0 adds none. Only the way down is delayed In the app: **Extra confirmation time** recovery_periodinteger Seconds the monitor has to stay up after it recovers before its incident closes and the all-clear goes out; 0 closes it with the next good check In the app: **Recovery period** notify_recoveryboolean · nullable Default true; the all-clear for an outage is sent in both notification modes degraded_alert_enabledboolean Slowdown warnings for this monitor; off by default degraded_threshold_msinteger · nullable Slow from this response time; omit to let up4 learn one from the monitor’s own history capture_on_incident_openboolean · nullable Take an incident screenshot when an incident opens; omit to use the company default In the app: **Incident screenshots** Port and DNS checks6 fields tcp_hoststring Target for tcp monitors; port 1–65535 In the app: **Host** tcp_portinteger · nullable Target for tcp monitors; port 1–65535 In the app: **Port** dns_querystring Name to look up (dns monitors) In the app: **Domain to look up** dns_typestring Default A ""AAAAAMXCNAMETXTNS In the app: **Record type** dns_serverstring Resolver to ask, a plain host name or IP address In the app: **DNS server** dns_expectedstring The answer the record must contain In the app: **Expected answer** Expiry warnings2 fields ssl_expiry_alert_daysinteger · nullable Warn this many days before certificate / domain expiry In the app: **Certificate expiry warning** domain_expiry_alert_daysinteger · nullable Warn this many days before certificate / domain expiry In the app: **Domain expiry warning** Maintenance window4 fields maintenance_fromstring · nullable Recurring maintenance window, HH:MM In the app: **From** maintenance_tostring · nullable Recurring maintenance window, HH:MM In the app: **Until** maintenance_weekdaysstring[] Default every day montuewedthufrisatsun In the app: **On these days** maintenance_timezonestring IANA zone for the window (default UTC) In the app: **Timezone** Organizing9 fields client_iduuid · nullable Create inside a client (agency mode) client_namestring Batch import only (POST /monitors/batch): names resolved to ids monitor_group_iduuid · nullable Put the monitor into a group monitor_group_namestring Batch import only (POST /monitors/batch): names resolved to ids public_namestring · nullable Shown on status pages and client share links instead of name; up to 120 characters pronounceable_namestring Spoken name metadatamap of stringmax 100 keyseach value max length 4096 On update, omitted or null leaves the stored metadata unchanged, {} removes all keys and any other object replaces them. statestring UP, DOWN or PAUSED (any case); empty creates the monitor running. ""UPDOWNPAUSED creation_keyuuid · nullable Import key: a repeat create with the same key returns the existing monitor; a key that belongs to another owner or client is refused ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | Monitor | | | Monitor · 70 fields FieldTypeDescription `alert_type` string one of`status` `keyword` `keyword_absence` `expected_status_code` `auth_username` string `capture_on_incident_open` boolean `confirmation_period` integer `created_at` string (date-time) `degraded_alert_enabled` boolean `degraded_threshold_ms` integer · nullable `domain_expiry_alert_days` integer · nullable `escalation_policy_id` uuid · nullable `expected_status_code` integer · nullable `feature_pause_reasons` string[] · nullable `follow_redirects` boolean `has_auth_password` boolean `has_request_headers` boolean `http_method` string `id` uuid `interval_seconds` integer `ip_version` string one of`""` `4` `6` `is_degraded` boolean `is_pending` boolean `keep_cookies` boolean `keyword` string `keyword_case_insensitive` boolean `maintenance_from` string · nullable `maintenance_timezone` string `maintenance_to` string · nullable `maintenance_weekdays` string[] · nullable one of`mon` `tue` `wed` `thu` `fri` `sat` `sun` `monitor_type` string one of`http` `tcp` `ping` `dns` `name` string `next_check_at` string (date-time) · nullable `notification_mode` string one of`quiet` `immediate` `notify_recovery` boolean `pronounceable_name` string `proxy_host` string `proxy_port` integer · nullable `public_name` string `recovery_period` integer `regions` string[] · nullable one of`eu` `us` `asia` `au` `default` `request_body` string `request_headers` any[] · nullable Always empty: header values are never returned. has_request_headers says whether the monitor sends any. `request_timeout_s` integer `ssl_expiry_alert_days` integer · nullable `ssl_verification` boolean `state` string one of`UP` `DOWN` `PAUSED` `url` string `user_id` uuid `badge_token` string `client_id` uuid `dns_expected` string `dns_query` string `dns_server` string `dns_type` string one of`A` `AAAA` `MX` `CNAME` `TXT` `NS` `freshness_timeout_seconds` integer `last_checked_at` string (date-time) `last_result_at` string (date-time) `last_state_change_at` string (date-time) `latency_p50_ms` integer `latency_p95_ms` integer `latency_samples_24h` integer `latest_result_successful` boolean `monitor_group_id` uuid `paused_until` string (date-time) `recent_incidents` MonitorRecentIncident[] Each item · 5 fields FieldTypeDescription `id` uuid `resolved_at` string (date-time) · nullable `started_at` string (date-time) `status` string `resolved_by_type` string `tcp_host` string `tcp_port` integer | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [check_capacity_exceeded](https://upfour.io/docs/api/errors#code-check_capacity_exceeded) | The change would push the subscription's projected monthly check load over its included budget. **When:** Creating a monitor, directly or through a client template, or changing one, when the projected cost including the change exceeds the budget; running monitors are not affected, and details carries projected_cents, included_cents and change_cents. **Fix:** Use a longer interval, fewer regions or fewer monitors, or raise the included budget. `check_capacity_exceeded` (subscriptions only): the change would raise the projected 30-day list usage of all active monitors above the included check budget. `error.details` carries `projected_cents`, `included_cents` and `change_cents`; a longer interval, fewer regions or fewer monitors fit. Running monitors and changes that lower the load are never refused. | | 403 | [feature_disabled](https://upfour.io/docs/api/errors#code-feature_disabled) | The monitor uses a feature that is currently switched off. **When:** Saving a monitor whose configuration needs a feature the platform has disabled; the saved configuration stays as it was. **Fix:** Remove that feature from the monitor, or try again once the feature is back. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 402 | [payment_required](https://upfour.io/docs/api/errors#code-payment_required) | The account does not have the credit or plan required for this action. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). ## Related [guideWatch a website](https://upfour.io/docs/watch-a-website.md)[guideChecks and regions](https://upfour.io/docs/how/checks-and-regions.md)[guideCheck page content](https://upfour.io/docs/check-page-content.md)[guideWhen you get alerted](https://upfour.io/docs/how/when-you-get-alerted.md) [PreviousList monitors](https://upfour.io/docs/api/monitors/list-monitors.md) [NextGet a monitor](https://upfour.io/docs/api/monitors/get-a-monitor.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/monitors" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "interval_seconds": 60, "monitor_type": "http", "name": "Marketing website", "url": "https://example.com" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitors", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ interval_seconds: 60, monitor_type: "http", name: "Marketing website", url: "https://example.com", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/monitors", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "interval_seconds": 60, "monitor_type": "http", "name": "Marketing website", "url": "https://example.com", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "interval_seconds": 60, "monitor_type": "http", "name": "Marketing website", "url": "https://example.com" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/monitors", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "alert_type": "status", "auth_username": "", "capture_on_incident_open": false, "confirmation_period": 0, "created_at": "2026-10-27T09:00:00Z", "degraded_alert_enabled": false, "degraded_threshold_ms": null, "dns_type": "A", "domain_expiry_alert_days": null, "escalation_policy_id": null, "expected_status_code": null, "feature_pause_reasons": null, "follow_redirects": true, "freshness_timeout_seconds": 120, "has_auth_password": false, "has_request_headers": false, "http_method": "HEAD", "id": "00000000-0000-4000-8000-000000000001", "interval_seconds": 60, "ip_version": "", "is_degraded": false, "is_pending": true, "keep_cookies": false, "keyword": "", "keyword_case_insensitive": true, "maintenance_from": null, "maintenance_timezone": "UTC", "maintenance_to": null, "maintenance_weekdays": [ "mon", "tue", "wed", "thu", "fri", "sat", "sun" ], "monitor_type": "http", "name": "Marketing website", "next_check_at": "2026-10-27T09:00:00Z", "notification_mode": "immediate", "notify_recovery": true, "pronounceable_name": "", "proxy_host": "", "proxy_port": null, "public_name": "", "recovery_period": 0, "regions": [ "eu" ], "request_body": "", "request_headers": [], "request_timeout_s": 30, "ssl_expiry_alert_days": null, "ssl_verification": true, "state": "UP", "url": "https://example.com", "user_id": "00000000-0000-4000-8000-000000000002" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "payment_required", "message": "The account does not have the credit or plan required for this action.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "check_capacity_exceeded", "message": "The change would push the subscription's projected monthly check load over its included budget.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Get a monitor — API reference > Returns one monitor. Source: https://upfour.io/docs/api/monitors/get-a-monitor [Developers](https://upfour.io/docs/api.md)/[Monitors](https://upfour.io/docs/api/monitors.md) Returns one monitor. GET https://app.upfour.io/v1/monitors/{monitorID} scope monitor.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `monitorID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | Monitor | | | Monitor · 70 fields FieldTypeDescription `alert_type` string one of`status` `keyword` `keyword_absence` `expected_status_code` `auth_username` string `capture_on_incident_open` boolean `confirmation_period` integer `created_at` string (date-time) `degraded_alert_enabled` boolean `degraded_threshold_ms` integer · nullable `domain_expiry_alert_days` integer · nullable `escalation_policy_id` uuid · nullable `expected_status_code` integer · nullable `feature_pause_reasons` string[] · nullable `follow_redirects` boolean `has_auth_password` boolean `has_request_headers` boolean `http_method` string `id` uuid `interval_seconds` integer `ip_version` string one of`""` `4` `6` `is_degraded` boolean `is_pending` boolean `keep_cookies` boolean `keyword` string `keyword_case_insensitive` boolean `maintenance_from` string · nullable `maintenance_timezone` string `maintenance_to` string · nullable `maintenance_weekdays` string[] · nullable one of`mon` `tue` `wed` `thu` `fri` `sat` `sun` `monitor_type` string one of`http` `tcp` `ping` `dns` `name` string `next_check_at` string (date-time) · nullable `notification_mode` string one of`quiet` `immediate` `notify_recovery` boolean `pronounceable_name` string `proxy_host` string `proxy_port` integer · nullable `public_name` string `recovery_period` integer `regions` string[] · nullable one of`eu` `us` `asia` `au` `default` `request_body` string `request_headers` any[] · nullable Always empty: header values are never returned. has_request_headers says whether the monitor sends any. `request_timeout_s` integer `ssl_expiry_alert_days` integer · nullable `ssl_verification` boolean `state` string one of`UP` `DOWN` `PAUSED` `url` string `user_id` uuid `badge_token` string `client_id` uuid `dns_expected` string `dns_query` string `dns_server` string `dns_type` string one of`A` `AAAA` `MX` `CNAME` `TXT` `NS` `freshness_timeout_seconds` integer `last_checked_at` string (date-time) `last_result_at` string (date-time) `last_state_change_at` string (date-time) `latency_p50_ms` integer `latency_p95_ms` integer `latency_samples_24h` integer `latest_result_successful` boolean `monitor_group_id` uuid `paused_until` string (date-time) `recent_incidents` MonitorRecentIncident[] Each item · 5 fields FieldTypeDescription `id` uuid `resolved_at` string (date-time) · nullable `started_at` string (date-time) `status` string `resolved_by_type` string `tcp_host` string `tcp_port` integer | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousCreate a monitor](https://upfour.io/docs/api/monitors/create-a-monitor.md) [NextUpdate a monitor](https://upfour.io/docs/api/monitors/update-a-monitor.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "alert_type": "status", "auth_username": "", "capture_on_incident_open": false, "confirmation_period": 0, "created_at": "2026-10-27T09:00:00Z", "degraded_alert_enabled": false, "degraded_threshold_ms": null, "dns_type": "A", "domain_expiry_alert_days": null, "escalation_policy_id": null, "expected_status_code": null, "feature_pause_reasons": [], "follow_redirects": true, "freshness_timeout_seconds": 120, "has_auth_password": false, "has_request_headers": false, "http_method": "HEAD", "id": "00000000-0000-4000-8000-000000000001", "interval_seconds": 60, "ip_version": "", "is_degraded": false, "is_pending": false, "keep_cookies": false, "keyword": "", "keyword_case_insensitive": true, "last_result_at": "2026-10-27T08:50:00Z", "latest_result_successful": true, "maintenance_from": null, "maintenance_timezone": "UTC", "maintenance_to": null, "maintenance_weekdays": [ "mon", "tue", "wed", "thu", "fri", "sat", "sun" ], "monitor_type": "http", "name": "Marketing website", "next_check_at": "2026-10-27T09:00:00Z", "notification_mode": "immediate", "notify_recovery": true, "pronounceable_name": "", "proxy_host": "", "proxy_port": null, "public_name": "", "recovery_period": 0, "regions": [ "eu" ], "request_body": "", "request_headers": [], "request_timeout_s": 30, "ssl_expiry_alert_days": null, "ssl_verification": true, "state": "UP", "url": "https://example.com", "user_id": "00000000-0000-4000-8000-000000000002" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Update a monitor — API reference > Updates selected monitor fields. Source: https://upfour.io/docs/api/monitors/update-a-monitor [Developers](https://upfour.io/docs/api.md)/[Monitors](https://upfour.io/docs/api/monitors.md) Updates selected monitor fields. PATCH https://app.upfour.io/v1/monitors/{monitorID} scope monitor.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `monitorID` required | uuid | path parameter | ## Body The 6 fields most calls use come first; the rest are grouped below. `application/json` required 56 fields urlstring In the app: **URL to watch** monitor_typestring httptcppingdns namestring In the app: **Name** interval_secondsinteger In the app: **Check every** regionsstring[] euusasiaaudefault In the app: **Regions** notification_channel_idsuuid[] What counts as up5 fields alert_typestring ""statuskeywordkeyword_absenceexpected_status_code In the app: **Counts as down when** keywordstring In the app: **Text to look for** keyword_case_insensitiveboolean · nullable In the app: **Ignore case** expected_status_codeinteger · nullable In the app: **Expected status code** ssl_verificationboolean · nullable In the app: **Verify the TLS certificate** Request15 fields http_methodstring In the app: **HTTP method** request_headersRequestHeader[] Custom request headers, at most 20 and 8 KB in total. On update they are kept unless request_headers_action is replace or clear. In the app: **Headers** Nested fields 2 fields | Field | Type | Description | | --- | --- | --- | | `name` optional | string | | | `value` optional | string | | request_headers_actionstring keepreplaceclear request_bodystring In the app: **Request body** request_timeout_sinteger In the app: **Request timeout** follow_redirectsboolean · nullable In the app: **Follow redirects** keep_cookiesboolean In the app: **Keep cookies when redirecting** auth_usernamestring In the app: **Username** auth_passwordstring Basic-auth password. Write-only: responses carry has_auth_password instead. In the app: **Password** auth_password_actionstring ""keepreplaceclear proxy_hoststring In the app: **Proxy** proxy_portinteger · nullable ip_versionstring ""46 In the app: **IP version** Alerting11 fields notification_modestring quietimmediate In the app: **When should we alert you?** alert_channel_namesstring[] escalation_policy_iduuid · nullable In the app: **Alert plan** alert_plan_namestring default_alert_planboolean confirmation_periodinteger In the app: **Extra confirmation time** recovery_periodinteger In the app: **Recovery period** notify_recoveryboolean · nullable degraded_alert_enabledboolean degraded_threshold_msinteger · nullable capture_on_incident_openboolean · nullable In the app: **Incident screenshots** Port and DNS checks6 fields tcp_hoststring In the app: **Host** tcp_portinteger · nullable In the app: **Port** dns_querystring In the app: **Domain to look up** dns_typestring ""AAAAAMXCNAMETXTNS In the app: **Record type** dns_serverstring In the app: **DNS server** dns_expectedstring In the app: **Expected answer** Expiry warnings2 fields ssl_expiry_alert_daysinteger · nullable In the app: **Certificate expiry warning** domain_expiry_alert_daysinteger · nullable In the app: **Domain expiry warning** Maintenance window4 fields maintenance_fromstring · nullable In the app: **From** maintenance_tostring · nullable In the app: **Until** maintenance_weekdaysstring[] montuewedthufrisatsun In the app: **On these days** maintenance_timezonestring In the app: **Timezone** Organizing9 fields client_iduuid · nullable client_namestring monitor_group_iduuid · nullable monitor_group_namestring public_namestring · nullable pronounceable_namestring metadatamap of stringmax 100 keyseach value max length 4096 On update, omitted or null leaves the stored metadata unchanged, {} removes all keys and any other object replaces them. statestring UP, DOWN or PAUSED. Required on PUT; PATCH keeps the current state when omitted. UPDOWNPAUSED creation_keyuuid · nullable ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | Monitor | | | Monitor · 70 fields FieldTypeDescription `alert_type` string one of`status` `keyword` `keyword_absence` `expected_status_code` `auth_username` string `capture_on_incident_open` boolean `confirmation_period` integer `created_at` string (date-time) `degraded_alert_enabled` boolean `degraded_threshold_ms` integer · nullable `domain_expiry_alert_days` integer · nullable `escalation_policy_id` uuid · nullable `expected_status_code` integer · nullable `feature_pause_reasons` string[] · nullable `follow_redirects` boolean `has_auth_password` boolean `has_request_headers` boolean `http_method` string `id` uuid `interval_seconds` integer `ip_version` string one of`""` `4` `6` `is_degraded` boolean `is_pending` boolean `keep_cookies` boolean `keyword` string `keyword_case_insensitive` boolean `maintenance_from` string · nullable `maintenance_timezone` string `maintenance_to` string · nullable `maintenance_weekdays` string[] · nullable one of`mon` `tue` `wed` `thu` `fri` `sat` `sun` `monitor_type` string one of`http` `tcp` `ping` `dns` `name` string `next_check_at` string (date-time) · nullable `notification_mode` string one of`quiet` `immediate` `notify_recovery` boolean `pronounceable_name` string `proxy_host` string `proxy_port` integer · nullable `public_name` string `recovery_period` integer `regions` string[] · nullable one of`eu` `us` `asia` `au` `default` `request_body` string `request_headers` any[] · nullable Always empty: header values are never returned. has_request_headers says whether the monitor sends any. `request_timeout_s` integer `ssl_expiry_alert_days` integer · nullable `ssl_verification` boolean `state` string one of`UP` `DOWN` `PAUSED` `url` string `user_id` uuid `badge_token` string `client_id` uuid `dns_expected` string `dns_query` string `dns_server` string `dns_type` string one of`A` `AAAA` `MX` `CNAME` `TXT` `NS` `freshness_timeout_seconds` integer `last_checked_at` string (date-time) `last_result_at` string (date-time) `last_state_change_at` string (date-time) `latency_p50_ms` integer `latency_p95_ms` integer `latency_samples_24h` integer `latest_result_successful` boolean `monitor_group_id` uuid `paused_until` string (date-time) `recent_incidents` MonitorRecentIncident[] Each item · 5 fields FieldTypeDescription `id` uuid `resolved_at` string (date-time) · nullable `started_at` string (date-time) `status` string `resolved_by_type` string `tcp_host` string `tcp_port` integer | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [check_capacity_exceeded](https://upfour.io/docs/api/errors#code-check_capacity_exceeded) | The change would push the subscription's projected monthly check load over its included budget. **When:** Creating a monitor, directly or through a client template, or changing one, when the projected cost including the change exceeds the budget; running monitors are not affected, and details carries projected_cents, included_cents and change_cents. **Fix:** Use a longer interval, fewer regions or fewer monitors, or raise the included budget. `check_capacity_exceeded` (subscriptions only): the change would raise the projected 30-day list usage of all active monitors above the included check budget. `error.details` carries `projected_cents`, `included_cents` and `change_cents`; a longer interval, fewer regions or fewer monitors fit. Running monitors and changes that lower the load are never refused. | | 403 | [feature_disabled](https://upfour.io/docs/api/errors#code-feature_disabled) | The monitor uses a feature that is currently switched off. **When:** Saving a monitor whose configuration needs a feature the platform has disabled; the saved configuration stays as it was. **Fix:** Remove that feature from the monitor, or try again once the feature is back. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 402 | [payment_required](https://upfour.io/docs/api/errors#code-payment_required) | The account does not have the credit or plan required for this action. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). ## Related [guideWatch a website](https://upfour.io/docs/watch-a-website.md)[guideChecks and regions](https://upfour.io/docs/how/checks-and-regions.md)[guideCheck page content](https://upfour.io/docs/check-page-content.md)[guideWhen you get alerted](https://upfour.io/docs/how/when-you-get-alerted.md) [PreviousGet a monitor](https://upfour.io/docs/api/monitors/get-a-monitor.md) [NextReplace a monitor](https://upfour.io/docs/api/monitors/replace-a-monitor.md) ## Request ### cURL ```bash curl -s -X PATCH "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "interval_seconds": 120 }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001", { method: "PATCH", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ interval_seconds: 120, }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.patch( "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "interval_seconds": 120, }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "interval_seconds": 120 }`) req, _ := http.NewRequest("PATCH", "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PATCH", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "alert_type": "status", "auth_username": "", "capture_on_incident_open": false, "confirmation_period": 0, "created_at": "2026-10-27T09:00:00Z", "degraded_alert_enabled": false, "degraded_threshold_ms": null, "dns_type": "A", "domain_expiry_alert_days": null, "escalation_policy_id": null, "expected_status_code": null, "feature_pause_reasons": [], "follow_redirects": true, "freshness_timeout_seconds": 180, "has_auth_password": false, "has_request_headers": false, "http_method": "HEAD", "id": "00000000-0000-4000-8000-000000000001", "interval_seconds": 120, "ip_version": "", "is_degraded": false, "is_pending": false, "keep_cookies": false, "keyword": "", "keyword_case_insensitive": true, "last_result_at": "2026-10-27T08:50:00Z", "latest_result_successful": true, "maintenance_from": null, "maintenance_timezone": "UTC", "maintenance_to": null, "maintenance_weekdays": [ "mon", "tue", "wed", "thu", "fri", "sat", "sun" ], "monitor_type": "http", "name": "Marketing website", "next_check_at": "2026-10-27T09:00:00Z", "notification_mode": "immediate", "notify_recovery": true, "pronounceable_name": "", "proxy_host": "", "proxy_port": null, "public_name": "", "recovery_period": 0, "regions": [ "eu" ], "request_body": "", "request_headers": [], "request_timeout_s": 30, "ssl_expiry_alert_days": null, "ssl_verification": true, "state": "UP", "url": "https://example.com", "user_id": "00000000-0000-4000-8000-000000000002" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "payment_required", "message": "The account does not have the credit or plan required for this action.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "check_capacity_exceeded", "message": "The change would push the subscription's projected monthly check load over its included budget.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Replace a monitor — API reference > Replaces a monitor configuration. Source: https://upfour.io/docs/api/monitors/replace-a-monitor [Developers](https://upfour.io/docs/api.md)/[Monitors](https://upfour.io/docs/api/monitors.md) Replaces a monitor configuration. PUT https://app.upfour.io/v1/monitors/{monitorID} scope monitor.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `monitorID` required | uuid | path parameter | ## Body The 6 fields most calls use come first; the rest are grouped below. `application/json` required 56 fields urlstring monitor_typestring httptcppingdns namestring interval_secondsinteger regionsstring[] euusasiaaudefault notification_channel_idsuuid[] What counts as up5 fields alert_typestring ""statuskeywordkeyword_absenceexpected_status_code keywordstring keyword_case_insensitiveboolean · nullable expected_status_codeinteger · nullable ssl_verificationboolean · nullable Request15 fields http_methodstring request_headersRequestHeader[] Custom request headers, at most 20 and 8 KB in total. On update they are kept unless request_headers_action is replace or clear. Nested fields 2 fields | Field | Type | Description | | --- | --- | --- | | `name` optional | string | | | `value` optional | string | | request_headers_actionstring keepreplaceclear request_bodystring request_timeout_sinteger follow_redirectsboolean · nullable keep_cookiesboolean auth_usernamestring auth_passwordstring Basic-auth password. Write-only: responses carry has_auth_password instead. auth_password_actionstring ""keepreplaceclear proxy_hoststring proxy_portinteger · nullable ip_versionstring ""46 Alerting11 fields notification_modestring quietimmediate alert_channel_namesstring[] escalation_policy_iduuid · nullable alert_plan_namestring default_alert_planboolean confirmation_periodinteger recovery_periodinteger notify_recoveryboolean · nullable degraded_alert_enabledboolean degraded_threshold_msinteger · nullable capture_on_incident_openboolean · nullable Port and DNS checks6 fields tcp_hoststring tcp_portinteger · nullable dns_querystring dns_typestring ""AAAAAMXCNAMETXTNS dns_serverstring dns_expectedstring Expiry warnings2 fields ssl_expiry_alert_daysinteger · nullable domain_expiry_alert_daysinteger · nullable Maintenance window4 fields maintenance_fromstring · nullable maintenance_tostring · nullable maintenance_weekdaysstring[] montuewedthufrisatsun maintenance_timezonestring Organizing9 fields client_iduuid · nullable client_namestring monitor_group_iduuid · nullable monitor_group_namestring public_namestring · nullable pronounceable_namestring metadatamap of stringmax 100 keyseach value max length 4096 On update, omitted or null leaves the stored metadata unchanged, {} removes all keys and any other object replaces them. statestring (required) UP, DOWN or PAUSED. Required on PUT; PATCH keeps the current state when omitted. UPDOWNPAUSED creation_keyuuid · nullable ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | Monitor | | | Monitor · 70 fields FieldTypeDescription `alert_type` string one of`status` `keyword` `keyword_absence` `expected_status_code` `auth_username` string `capture_on_incident_open` boolean `confirmation_period` integer `created_at` string (date-time) `degraded_alert_enabled` boolean `degraded_threshold_ms` integer · nullable `domain_expiry_alert_days` integer · nullable `escalation_policy_id` uuid · nullable `expected_status_code` integer · nullable `feature_pause_reasons` string[] · nullable `follow_redirects` boolean `has_auth_password` boolean `has_request_headers` boolean `http_method` string `id` uuid `interval_seconds` integer `ip_version` string one of`""` `4` `6` `is_degraded` boolean `is_pending` boolean `keep_cookies` boolean `keyword` string `keyword_case_insensitive` boolean `maintenance_from` string · nullable `maintenance_timezone` string `maintenance_to` string · nullable `maintenance_weekdays` string[] · nullable one of`mon` `tue` `wed` `thu` `fri` `sat` `sun` `monitor_type` string one of`http` `tcp` `ping` `dns` `name` string `next_check_at` string (date-time) · nullable `notification_mode` string one of`quiet` `immediate` `notify_recovery` boolean `pronounceable_name` string `proxy_host` string `proxy_port` integer · nullable `public_name` string `recovery_period` integer `regions` string[] · nullable one of`eu` `us` `asia` `au` `default` `request_body` string `request_headers` any[] · nullable Always empty: header values are never returned. has_request_headers says whether the monitor sends any. `request_timeout_s` integer `ssl_expiry_alert_days` integer · nullable `ssl_verification` boolean `state` string one of`UP` `DOWN` `PAUSED` `url` string `user_id` uuid `badge_token` string `client_id` uuid `dns_expected` string `dns_query` string `dns_server` string `dns_type` string one of`A` `AAAA` `MX` `CNAME` `TXT` `NS` `freshness_timeout_seconds` integer `last_checked_at` string (date-time) `last_result_at` string (date-time) `last_state_change_at` string (date-time) `latency_p50_ms` integer `latency_p95_ms` integer `latency_samples_24h` integer `latest_result_successful` boolean `monitor_group_id` uuid `paused_until` string (date-time) `recent_incidents` MonitorRecentIncident[] Each item · 5 fields FieldTypeDescription `id` uuid `resolved_at` string (date-time) · nullable `started_at` string (date-time) `status` string `resolved_by_type` string `tcp_host` string `tcp_port` integer | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [check_capacity_exceeded](https://upfour.io/docs/api/errors#code-check_capacity_exceeded) | The change would push the subscription's projected monthly check load over its included budget. **When:** Creating a monitor, directly or through a client template, or changing one, when the projected cost including the change exceeds the budget; running monitors are not affected, and details carries projected_cents, included_cents and change_cents. **Fix:** Use a longer interval, fewer regions or fewer monitors, or raise the included budget. `check_capacity_exceeded` (subscriptions only): the change would raise the projected 30-day list usage of all active monitors above the included check budget. `error.details` carries `projected_cents`, `included_cents` and `change_cents`; a longer interval, fewer regions or fewer monitors fit. Running monitors and changes that lower the load are never refused. | | 403 | [feature_disabled](https://upfour.io/docs/api/errors#code-feature_disabled) | The monitor uses a feature that is currently switched off. **When:** Saving a monitor whose configuration needs a feature the platform has disabled; the saved configuration stays as it was. **Fix:** Remove that feature from the monitor, or try again once the feature is back. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 402 | [payment_required](https://upfour.io/docs/api/errors#code-payment_required) | The account does not have the credit or plan required for this action. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpdate a monitor](https://upfour.io/docs/api/monitors/update-a-monitor.md) [NextDelete a monitor](https://upfour.io/docs/api/monitors/delete-a-monitor.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "interval_seconds": 60, "monitor_type": "http", "name": "Marketing website", "state": "UP", "url": "https://example.com" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ interval_seconds: 60, monitor_type: "http", name: "Marketing website", state: "UP", url: "https://example.com", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "interval_seconds": 60, "monitor_type": "http", "name": "Marketing website", "state": "UP", "url": "https://example.com", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "interval_seconds": 60, "monitor_type": "http", "name": "Marketing website", "state": "UP", "url": "https://example.com" }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "alert_type": "status", "auth_username": "", "capture_on_incident_open": false, "confirmation_period": 0, "created_at": "2026-10-27T09:00:00Z", "degraded_alert_enabled": false, "degraded_threshold_ms": null, "dns_type": "A", "domain_expiry_alert_days": null, "escalation_policy_id": null, "expected_status_code": null, "feature_pause_reasons": [], "follow_redirects": true, "freshness_timeout_seconds": 120, "has_auth_password": false, "has_request_headers": false, "http_method": "HEAD", "id": "00000000-0000-4000-8000-000000000001", "interval_seconds": 60, "ip_version": "", "is_degraded": false, "is_pending": false, "keep_cookies": false, "keyword": "", "keyword_case_insensitive": true, "last_result_at": "2026-10-27T08:50:00Z", "latest_result_successful": true, "maintenance_from": null, "maintenance_timezone": "UTC", "maintenance_to": null, "maintenance_weekdays": [ "mon", "tue", "wed", "thu", "fri", "sat", "sun" ], "monitor_type": "http", "name": "Marketing website", "next_check_at": "2026-10-27T09:00:00Z", "notification_mode": "immediate", "notify_recovery": true, "pronounceable_name": "", "proxy_host": "", "proxy_port": null, "public_name": "", "recovery_period": 0, "regions": [ "eu" ], "request_body": "", "request_headers": [], "request_timeout_s": 30, "ssl_expiry_alert_days": null, "ssl_verification": true, "state": "UP", "url": "https://example.com", "user_id": "00000000-0000-4000-8000-000000000002" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "payment_required", "message": "The account does not have the credit or plan required for this action.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "check_capacity_exceeded", "message": "The change would push the subscription's projected monthly check load over its included budget.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Delete a monitor — API reference > Deletes a monitor. Source: https://upfour.io/docs/api/monitors/delete-a-monitor [Developers](https://upfour.io/docs/api.md)/[Monitors](https://upfour.io/docs/api/monitors.md) Deletes a monitor. DELETE https://app.upfour.io/v1/monitors/{monitorID} scope monitor.delete stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `monitorID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousReplace a monitor](https://upfour.io/docs/api/monitors/replace-a-monitor.md) [NextPause a monitor](https://upfour.io/docs/api/monitors/pause-a-monitor.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Pause a monitor — API reference > Pauses a monitor. Source: https://upfour.io/docs/api/monitors/pause-a-monitor [Developers](https://upfour.io/docs/api.md)/[Monitors](https://upfour.io/docs/api/monitors.md) Pauses a monitor. POST https://app.upfour.io/v1/monitors/{monitorID}/pause scope monitor.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `monitorID` required | uuid | path parameter | ## Body `application/json` optional 1 field All other fields1 fields untilstring (date-time) · nullable Optional end of a timed pause; the scheduler resumes the monitor then. Without it the monitor stays paused until resumed. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | Monitor | | | Monitor · 70 fields FieldTypeDescription `alert_type` string one of`status` `keyword` `keyword_absence` `expected_status_code` `auth_username` string `capture_on_incident_open` boolean `confirmation_period` integer `created_at` string (date-time) `degraded_alert_enabled` boolean `degraded_threshold_ms` integer · nullable `domain_expiry_alert_days` integer · nullable `escalation_policy_id` uuid · nullable `expected_status_code` integer · nullable `feature_pause_reasons` string[] · nullable `follow_redirects` boolean `has_auth_password` boolean `has_request_headers` boolean `http_method` string `id` uuid `interval_seconds` integer `ip_version` string one of`""` `4` `6` `is_degraded` boolean `is_pending` boolean `keep_cookies` boolean `keyword` string `keyword_case_insensitive` boolean `maintenance_from` string · nullable `maintenance_timezone` string `maintenance_to` string · nullable `maintenance_weekdays` string[] · nullable one of`mon` `tue` `wed` `thu` `fri` `sat` `sun` `monitor_type` string one of`http` `tcp` `ping` `dns` `name` string `next_check_at` string (date-time) · nullable `notification_mode` string one of`quiet` `immediate` `notify_recovery` boolean `pronounceable_name` string `proxy_host` string `proxy_port` integer · nullable `public_name` string `recovery_period` integer `regions` string[] · nullable one of`eu` `us` `asia` `au` `default` `request_body` string `request_headers` any[] · nullable Always empty: header values are never returned. has_request_headers says whether the monitor sends any. `request_timeout_s` integer `ssl_expiry_alert_days` integer · nullable `ssl_verification` boolean `state` string one of`UP` `DOWN` `PAUSED` `url` string `user_id` uuid `badge_token` string `client_id` uuid `dns_expected` string `dns_query` string `dns_server` string `dns_type` string one of`A` `AAAA` `MX` `CNAME` `TXT` `NS` `freshness_timeout_seconds` integer `last_checked_at` string (date-time) `last_result_at` string (date-time) `last_state_change_at` string (date-time) `latency_p50_ms` integer `latency_p95_ms` integer `latency_samples_24h` integer `latest_result_successful` boolean `monitor_group_id` uuid `paused_until` string (date-time) `recent_incidents` MonitorRecentIncident[] Each item · 5 fields FieldTypeDescription `id` uuid `resolved_at` string (date-time) · nullable `started_at` string (date-time) `status` string `resolved_by_type` string `tcp_host` string `tcp_port` integer | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [check_capacity_exceeded](https://upfour.io/docs/api/errors#code-check_capacity_exceeded) | The change would push the subscription's projected monthly check load over its included budget. **When:** Creating a monitor, directly or through a client template, or changing one, when the projected cost including the change exceeds the budget; running monitors are not affected, and details carries projected_cents, included_cents and change_cents. **Fix:** Use a longer interval, fewer regions or fewer monitors, or raise the included budget. | | 403 | [feature_disabled](https://upfour.io/docs/api/errors#code-feature_disabled) | The monitor uses a feature that is currently switched off. **When:** Saving a monitor whose configuration needs a feature the platform has disabled; the saved configuration stays as it was. **Fix:** Remove that feature from the monitor, or try again once the feature is back. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousDelete a monitor](https://upfour.io/docs/api/monitors/delete-a-monitor.md) [NextResume a monitor](https://upfour.io/docs/api/monitors/resume-a-monitor.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/pause" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/pause", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({}), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/pause", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{}`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/pause", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "alert_type": "status", "auth_username": "", "capture_on_incident_open": false, "confirmation_period": 0, "created_at": "2026-10-27T09:00:00Z", "degraded_alert_enabled": false, "degraded_threshold_ms": null, "dns_type": "A", "domain_expiry_alert_days": null, "escalation_policy_id": null, "expected_status_code": null, "feature_pause_reasons": [], "follow_redirects": true, "freshness_timeout_seconds": 120, "has_auth_password": false, "has_request_headers": false, "http_method": "HEAD", "id": "00000000-0000-4000-8000-000000000001", "interval_seconds": 60, "ip_version": "", "is_degraded": false, "is_pending": false, "keep_cookies": false, "keyword": "", "keyword_case_insensitive": true, "last_result_at": "2026-10-27T08:50:00Z", "last_state_change_at": "2026-10-27T09:00:00Z", "maintenance_from": null, "maintenance_timezone": "UTC", "maintenance_to": null, "maintenance_weekdays": [ "mon", "tue", "wed", "thu", "fri", "sat", "sun" ], "monitor_type": "http", "name": "Marketing website", "next_check_at": null, "notification_mode": "immediate", "notify_recovery": true, "pronounceable_name": "", "proxy_host": "", "proxy_port": null, "public_name": "", "recovery_period": 0, "regions": [ "eu" ], "request_body": "", "request_headers": [], "request_timeout_s": 30, "ssl_expiry_alert_days": null, "ssl_verification": true, "state": "PAUSED", "url": "https://example.com", "user_id": "00000000-0000-4000-8000-000000000002" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "check_capacity_exceeded", "message": "The change would push the subscription's projected monthly check load over its included budget.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Resume a monitor — API reference > Resumes a monitor. Source: https://upfour.io/docs/api/monitors/resume-a-monitor [Developers](https://upfour.io/docs/api.md)/[Monitors](https://upfour.io/docs/api/monitors.md) Resumes a monitor. POST https://app.upfour.io/v1/monitors/{monitorID}/resume scope monitor.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `monitorID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | Monitor | | | Monitor · 70 fields FieldTypeDescription `alert_type` string one of`status` `keyword` `keyword_absence` `expected_status_code` `auth_username` string `capture_on_incident_open` boolean `confirmation_period` integer `created_at` string (date-time) `degraded_alert_enabled` boolean `degraded_threshold_ms` integer · nullable `domain_expiry_alert_days` integer · nullable `escalation_policy_id` uuid · nullable `expected_status_code` integer · nullable `feature_pause_reasons` string[] · nullable `follow_redirects` boolean `has_auth_password` boolean `has_request_headers` boolean `http_method` string `id` uuid `interval_seconds` integer `ip_version` string one of`""` `4` `6` `is_degraded` boolean `is_pending` boolean `keep_cookies` boolean `keyword` string `keyword_case_insensitive` boolean `maintenance_from` string · nullable `maintenance_timezone` string `maintenance_to` string · nullable `maintenance_weekdays` string[] · nullable one of`mon` `tue` `wed` `thu` `fri` `sat` `sun` `monitor_type` string one of`http` `tcp` `ping` `dns` `name` string `next_check_at` string (date-time) · nullable `notification_mode` string one of`quiet` `immediate` `notify_recovery` boolean `pronounceable_name` string `proxy_host` string `proxy_port` integer · nullable `public_name` string `recovery_period` integer `regions` string[] · nullable one of`eu` `us` `asia` `au` `default` `request_body` string `request_headers` any[] · nullable Always empty: header values are never returned. has_request_headers says whether the monitor sends any. `request_timeout_s` integer `ssl_expiry_alert_days` integer · nullable `ssl_verification` boolean `state` string one of`UP` `DOWN` `PAUSED` `url` string `user_id` uuid `badge_token` string `client_id` uuid `dns_expected` string `dns_query` string `dns_server` string `dns_type` string one of`A` `AAAA` `MX` `CNAME` `TXT` `NS` `freshness_timeout_seconds` integer `last_checked_at` string (date-time) `last_result_at` string (date-time) `last_state_change_at` string (date-time) `latency_p50_ms` integer `latency_p95_ms` integer `latency_samples_24h` integer `latest_result_successful` boolean `monitor_group_id` uuid `paused_until` string (date-time) `recent_incidents` MonitorRecentIncident[] Each item · 5 fields FieldTypeDescription `id` uuid `resolved_at` string (date-time) · nullable `started_at` string (date-time) `status` string `resolved_by_type` string `tcp_host` string `tcp_port` integer | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [check_capacity_exceeded](https://upfour.io/docs/api/errors#code-check_capacity_exceeded) | The change would push the subscription's projected monthly check load over its included budget. **When:** Creating a monitor, directly or through a client template, or changing one, when the projected cost including the change exceeds the budget; running monitors are not affected, and details carries projected_cents, included_cents and change_cents. **Fix:** Use a longer interval, fewer regions or fewer monitors, or raise the included budget. `check_capacity_exceeded` (subscriptions only): the change would raise the projected 30-day list usage of all active monitors above the included check budget. `error.details` carries `projected_cents`, `included_cents` and `change_cents`; a longer interval, fewer regions or fewer monitors fit. Running monitors and changes that lower the load are never refused. | | 403 | [feature_disabled](https://upfour.io/docs/api/errors#code-feature_disabled) | The monitor uses a feature that is currently switched off. **When:** Saving a monitor whose configuration needs a feature the platform has disabled; the saved configuration stays as it was. **Fix:** Remove that feature from the monitor, or try again once the feature is back. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 402 | [payment_required](https://upfour.io/docs/api/errors#code-payment_required) | The account does not have the credit or plan required for this action. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousPause a monitor](https://upfour.io/docs/api/monitors/pause-a-monitor.md) [NextRun a check now](https://upfour.io/docs/api/monitors/run-a-check-now.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/resume" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/resume", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/resume", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/resume", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "alert_type": "status", "auth_username": "", "capture_on_incident_open": false, "confirmation_period": 0, "created_at": "2026-10-27T09:00:00Z", "degraded_alert_enabled": false, "degraded_threshold_ms": null, "dns_type": "A", "domain_expiry_alert_days": null, "escalation_policy_id": null, "expected_status_code": null, "feature_pause_reasons": [], "follow_redirects": true, "freshness_timeout_seconds": 120, "has_auth_password": false, "has_request_headers": false, "http_method": "HEAD", "id": "00000000-0000-4000-8000-000000000001", "interval_seconds": 60, "ip_version": "", "is_degraded": false, "is_pending": false, "keep_cookies": false, "keyword": "", "keyword_case_insensitive": true, "last_result_at": "2026-10-27T08:50:00Z", "last_state_change_at": "2026-10-27T09:00:00Z", "maintenance_from": null, "maintenance_timezone": "UTC", "maintenance_to": null, "maintenance_weekdays": [ "mon", "tue", "wed", "thu", "fri", "sat", "sun" ], "monitor_type": "http", "name": "Marketing website", "next_check_at": "2026-10-27T09:00:00Z", "notification_mode": "immediate", "notify_recovery": true, "pronounceable_name": "", "proxy_host": "", "proxy_port": null, "public_name": "", "recovery_period": 0, "regions": [ "eu" ], "request_body": "", "request_headers": [], "request_timeout_s": 30, "ssl_expiry_alert_days": null, "ssl_verification": true, "state": "UP", "url": "https://example.com", "user_id": "00000000-0000-4000-8000-000000000002" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "payment_required", "message": "The account does not have the credit or plan required for this action.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "check_capacity_exceeded", "message": "The change would push the subscription's projected monthly check load over its included budget.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Run a check now — API reference > Starts an immediate check for a monitor. Source: https://upfour.io/docs/api/monitors/run-a-check-now [Developers](https://upfour.io/docs/api.md)/[Monitors](https://upfour.io/docs/api/monitors.md) Starts an immediate check for a monitor. POST https://app.upfour.io/v1/monitors/{monitorID}/check scope monitor.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 202 ## Parameters | Field | Type | Description | | --- | --- | --- | | `monitorID` required | uuid | path parameter | ## Returns A successful request returns `202` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | MonitorCheckQueuedResponse | | | MonitorCheckQueuedResponse · 72 fields FieldTypeDescription `monitor` Monitor · nullable Monitor · 70 fields FieldTypeDescription `alert_type` string one of`status` `keyword` `keyword_absence` `expected_status_code` `auth_username` string `capture_on_incident_open` boolean `confirmation_period` integer `created_at` string (date-time) `degraded_alert_enabled` boolean `degraded_threshold_ms` integer · nullable `domain_expiry_alert_days` integer · nullable `escalation_policy_id` uuid · nullable `expected_status_code` integer · nullable `feature_pause_reasons` string[] · nullable `follow_redirects` boolean `has_auth_password` boolean `has_request_headers` boolean `http_method` string `id` uuid `interval_seconds` integer `ip_version` string one of`""` `4` `6` `is_degraded` boolean `is_pending` boolean `keep_cookies` boolean `keyword` string `keyword_case_insensitive` boolean `maintenance_from` string · nullable `maintenance_timezone` string `maintenance_to` string · nullable `maintenance_weekdays` string[] · nullable one of`mon` `tue` `wed` `thu` `fri` `sat` `sun` `monitor_type` string one of`http` `tcp` `ping` `dns` `name` string `next_check_at` string (date-time) · nullable `notification_mode` string one of`quiet` `immediate` `notify_recovery` boolean `pronounceable_name` string `proxy_host` string `proxy_port` integer · nullable `public_name` string `recovery_period` integer `regions` string[] · nullable one of`eu` `us` `asia` `au` `default` `request_body` string `request_headers` any[] · nullable Always empty: header values are never returned. has_request_headers says whether the monitor sends any. `request_timeout_s` integer `ssl_expiry_alert_days` integer · nullable `ssl_verification` boolean `state` string one of`UP` `DOWN` `PAUSED` `url` string `user_id` uuid `badge_token` string `client_id` uuid `dns_expected` string `dns_query` string `dns_server` string `dns_type` string one of`A` `AAAA` `MX` `CNAME` `TXT` `NS` `freshness_timeout_seconds` integer `last_checked_at` string (date-time) `last_result_at` string (date-time) `last_state_change_at` string (date-time) `latency_p50_ms` integer `latency_p95_ms` integer `latency_samples_24h` integer `latest_result_successful` boolean `monitor_group_id` uuid `paused_until` string (date-time) `recent_incidents` MonitorRecentIncident[] Each item · 5 fields FieldTypeDescription `id` uuid `resolved_at` string (date-time) · nullable `started_at` string (date-time) `status` string `resolved_by_type` string `tcp_host` string `tcp_port` integer `queued` boolean | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [check_capacity_exceeded](https://upfour.io/docs/api/errors#code-check_capacity_exceeded) | The change would push the subscription's projected monthly check load over its included budget. **When:** Creating a monitor, directly or through a client template, or changing one, when the projected cost including the change exceeds the budget; running monitors are not affected, and details carries projected_cents, included_cents and change_cents. **Fix:** Use a longer interval, fewer regions or fewer monitors, or raise the included budget. | | 403 | [feature_disabled](https://upfour.io/docs/api/errors#code-feature_disabled) | The monitor uses a feature that is currently switched off. **When:** Saving a monitor whose configuration needs a feature the platform has disabled; the saved configuration stays as it was. **Fix:** Remove that feature from the monitor, or try again once the feature is back. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousResume a monitor](https://upfour.io/docs/api/monitors/resume-a-monitor.md) [NextSend a test alert](https://upfour.io/docs/api/monitors/send-a-test-alert.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/check" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/check", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/check", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/check", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 202 ```json { "data": { "monitor": { "alert_type": "status", "auth_username": "", "capture_on_incident_open": false, "confirmation_period": 0, "created_at": "2026-10-27T09:00:00Z", "degraded_alert_enabled": false, "degraded_threshold_ms": null, "dns_type": "A", "domain_expiry_alert_days": null, "escalation_policy_id": null, "expected_status_code": null, "feature_pause_reasons": [], "follow_redirects": true, "freshness_timeout_seconds": 120, "has_auth_password": false, "has_request_headers": false, "http_method": "HEAD", "id": "00000000-0000-4000-8000-000000000001", "interval_seconds": 60, "ip_version": "", "is_degraded": false, "is_pending": false, "keep_cookies": false, "keyword": "", "keyword_case_insensitive": true, "last_result_at": "2026-10-27T08:50:00Z", "latest_result_successful": true, "maintenance_from": null, "maintenance_timezone": "UTC", "maintenance_to": null, "maintenance_weekdays": [ "mon", "tue", "wed", "thu", "fri", "sat", "sun" ], "monitor_type": "http", "name": "Marketing website", "next_check_at": "2026-10-27T09:00:00Z", "notification_mode": "immediate", "notify_recovery": true, "pronounceable_name": "", "proxy_host": "", "proxy_port": null, "public_name": "", "recovery_period": 0, "regions": [ "eu" ], "request_body": "", "request_headers": [], "request_timeout_s": 30, "ssl_expiry_alert_days": null, "ssl_verification": true, "state": "UP", "url": "https://example.com", "user_id": "00000000-0000-4000-8000-000000000002" }, "queued": true } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "check_capacity_exceeded", "message": "The change would push the subscription's projected monthly check load over its included budget.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Send a test alert — API reference > Sends a test alert for a monitor. Source: https://upfour.io/docs/api/monitors/send-a-test-alert [Developers](https://upfour.io/docs/api.md)/[Monitors](https://upfour.io/docs/api/monitors.md) Sends a test alert for a monitor. POST https://app.upfour.io/v1/monitors/{monitorID}/test-alert scope monitor.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `monitorID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | MonitorTestAlertResponse | | | MonitorTestAlertResponse · 8 fields FieldTypeDescription `channels_not_tested` integer `channels_sent` integer `message` string `not_tested_channels` AlertPlanChannelTest[] · nullable Each item · 3 fields FieldTypeDescription `channel_id` uuid `channel_name` string `provider` string `warning` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 402 | [billing_action_unavailable](https://upfour.io/docs/api/errors#code-billing_action_unavailable) | The Company's billing state does not allow this action. **When:** A metered or limited action while the billing state, such as paused or without credit, blocks it; details holds the action and billing_state. **Fix:** Top up the credit or change the plan, then retry. | | 502 | [channel_test_failed](https://upfour.io/docs/api/errors#code-channel_test_failed) | The alert channel's provider could not deliver the test. **When:** Sending a test alert to a channel when the provider fails in a way that fits none of the other provider codes. **Fix:** Check the channel's address and settings, then test again. | | 400 | [no_channels_linked](https://upfour.io/docs/api/errors#code-no_channels_linked) | The monitor has no alert channel to send a test to. **When:** A test alert for a monitor that has no alert channel linked. **Fix:** Link an alert channel to the monitor and test again. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 422 | [provider_auth_rejected](https://upfour.io/docs/api/errors#code-provider_auth_rejected) | The alert channel's provider rejected the credentials. **When:** A test alert whose provider answered 401 or 403. **Fix:** Check the token, secret or account details of the channel. | | 422 | [provider_rejected](https://upfour.io/docs/api/errors#code-provider_rejected) | The alert channel's provider rejected the test. **When:** A test alert whose provider answered 400, 404, 405, 410 or 422. **Fix:** Check the channel's address and settings. | | 504 | [provider_timeout](https://upfour.io/docs/api/errors#code-provider_timeout) | The alert channel's provider did not answer in time. **When:** A test alert whose provider took too long to respond. **Fix:** Check the address and try again later. | | 422 | [provider_unreachable](https://upfour.io/docs/api/errors#code-provider_unreachable) | The alert channel's address cannot be reached. **When:** A test alert to a host that does not resolve, refuses the connection, has an invalid URL or an invalid HTTPS certificate. **Fix:** Check the URL and its HTTPS certificate. | | 503 | [quota_gate_unavailable](https://upfour.io/docs/api/errors#code-quota_gate_unavailable) | The limit check itself failed, so the action was not carried out. **When:** The quota service could not decide while the request ran. **Fix:** Retry in a moment; nothing was changed. | | 403 / 429 | [quota_limit_reached](https://upfour.io/docs/api/errors#code-quota_limit_reached) | A usage or inventory limit of the Company was reached. **When:** Counters and concurrency limits answer 429 with Retry-After, inventory and capacity limits answer 403; details holds purpose, limit and window. **Fix:** On 429 wait Retry-After seconds; on 403 free something up, request a higher limit where details.raise_request is true, or buy an extension. | | 503 | [test_unavailable](https://upfour.io/docs/api/errors#code-test_unavailable) | Sending a test is not possible right now. **When:** A test alert request while the test sender cannot take it. **Fix:** Try again in a moment. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousRun a check now](https://upfour.io/docs/api/monitors/run-a-check-now.md) [NextList check results](https://upfour.io/docs/api/monitors/list-check-results.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/test-alert" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/test-alert", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/test-alert", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/test-alert", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "channels_not_tested": 0, "channels_sent": 1, "message": "Test alert sent to 1 channel(s)", "not_tested_channels": [] } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "billing_action_unavailable", "message": "The Company's billing state does not allow this action.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` ### 422 ```json { "error": { "code": "provider_auth_rejected", "message": "The alert channel's provider rejected the credentials.", "request_id": "req_…" } } ``` ### 429 ```json { "error": { "code": "quota_limit_reached", "message": "A usage or inventory limit of the Company was reached.", "request_id": "req_…" } } ``` ### 502 ```json { "error": { "code": "channel_test_failed", "message": "The alert channel's provider could not deliver the test.", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "quota_gate_unavailable", "message": "The limit check itself failed, so the action was not carried out.", "request_id": "req_…" } } ``` ### 504 ```json { "error": { "code": "provider_timeout", "message": "The alert channel's provider did not answer in time.", "request_id": "req_…" } } ``` --- # List check results — API reference > Lists check results for a monitor. Source: https://upfour.io/docs/api/monitors/list-check-results [Developers](https://upfour.io/docs/api.md)/[Monitors](https://upfour.io/docs/api/monitors.md) Lists check results for a monitor. GET https://app.upfour.io/v1/monitors/{monitorID}/results scope monitor.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `monitorID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | CheckResultPoint[] · nullable | | | Each item · 11 fields FieldTypeDescription `connect_ms` integer `dns_ms` integer `is_checker_failure` boolean `is_degraded` boolean `latency_ms` integer `status_code` integer `timestamp` string (date-time) `tls_ms` integer `ttfb_ms` integer `was_successful` boolean `region` string | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousSend a test alert](https://upfour.io/docs/api/monitors/send-a-test-alert.md) [NextGet availability](https://upfour.io/docs/api/monitors/get-availability.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/results" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/results", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/results", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/results", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "connect_ms": 0, "dns_ms": 0, "is_checker_failure": false, "is_degraded": false, "latency_ms": 115, "status_code": 200, "timestamp": "2026-10-27T08:00:00Z", "tls_ms": 0, "ttfb_ms": 0, "was_successful": true }, { "connect_ms": 0, "dns_ms": 0, "is_checker_failure": false, "is_degraded": false, "latency_ms": 108, "status_code": 200, "timestamp": "2026-10-27T08:10:00Z", "tls_ms": 0, "ttfb_ms": 0, "was_successful": true }, { "connect_ms": 0, "dns_ms": 0, "is_checker_failure": false, "is_degraded": false, "latency_ms": 101, "status_code": 200, "timestamp": "2026-10-27T08:20:00Z", "tls_ms": 0, "ttfb_ms": 0, "was_successful": true }, { "connect_ms": 0, "dns_ms": 0, "is_checker_failure": false, "is_degraded": false, "latency_ms": 94, "status_code": 200, "timestamp": "2026-10-27T08:30:00Z", "tls_ms": 0, "ttfb_ms": 0, "was_successful": true }, { "connect_ms": 0, "dns_ms": 0, "is_checker_failure": false, "is_degraded": false, "latency_ms": 87, "status_code": 200, "timestamp": "2026-10-27T08:40:00Z", "tls_ms": 0, "ttfb_ms": 0, "was_successful": true }, { "connect_ms": 0, "dns_ms": 0, "is_checker_failure": false, "is_degraded": false, "latency_ms": 80, "status_code": 200, "timestamp": "2026-10-27T08:50:00Z", "tls_ms": 0, "ttfb_ms": 0, "was_successful": true } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Get availability — API reference > Returns availability data for a monitor. Source: https://upfour.io/docs/api/monitors/get-availability [Developers](https://upfour.io/docs/api.md)/[Monitors](https://upfour.io/docs/api/monitors.md) Returns availability data for a monitor. GET https://app.upfour.io/v1/monitors/{monitorID}/availability scope monitor.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `monitorID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | AvailabilityPeriod[] · nullable | | | Each item · 14 fields FieldTypeDescription `covered` boolean `downtime_seconds` integer `elapsed_seconds` integer `ends_at` string (date-time) `failed_checks` integer `longest_outage_seconds` integer `outages` integer `period` string `starts_at` string (date-time) · nullable `total_checks` integer `uptime_percentage` number `watching_since` string (date-time) `consistency_error` string `paused_seconds` integer | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList check results](https://upfour.io/docs/api/monitors/list-check-results.md) [NextGet response times](https://upfour.io/docs/api/monitors/get-response-times.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/availability" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/availability", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/availability", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/availability", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "covered": false, "downtime_seconds": 0, "elapsed_seconds": 61200, "ends_at": "2026-10-27T09:00:00Z", "failed_checks": 0, "longest_outage_seconds": 0, "outages": 0, "period": "today", "starts_at": "2026-10-26T15:55:00Z", "total_checks": 0, "uptime_percentage": 0, "watching_since": "2026-10-27T09:00:00Z" }, { "covered": false, "downtime_seconds": 0, "elapsed_seconds": 608400, "ends_at": "2026-10-27T09:00:00Z", "failed_checks": 0, "longest_outage_seconds": 0, "outages": 0, "period": "7d", "starts_at": "2026-10-20T08:00:00Z", "total_checks": 0, "uptime_percentage": 0, "watching_since": "2026-10-27T09:00:00Z" }, { "covered": false, "downtime_seconds": 0, "elapsed_seconds": 2595600, "ends_at": "2026-10-27T09:00:00Z", "failed_checks": 0, "longest_outage_seconds": 0, "outages": 0, "period": "30d", "starts_at": "2026-09-27T08:00:00Z", "total_checks": 0, "uptime_percentage": 0, "watching_since": "2026-10-27T09:00:00Z" }, { "covered": false, "downtime_seconds": 0, "elapsed_seconds": 7779600, "ends_at": "2026-10-27T09:00:00Z", "failed_checks": 0, "longest_outage_seconds": 0, "outages": 0, "period": "90d", "starts_at": "2026-07-29T08:00:00Z", "total_checks": 0, "uptime_percentage": 0, "watching_since": "2026-10-27T09:00:00Z" }, { "covered": false, "downtime_seconds": 0, "elapsed_seconds": 31536000, "ends_at": "2026-10-27T09:00:00Z", "failed_checks": 0, "longest_outage_seconds": 0, "outages": 0, "period": "365d", "starts_at": "2025-10-27T09:00:00Z", "total_checks": 0, "uptime_percentage": 0, "watching_since": "2026-10-27T09:00:00Z" }, { "covered": false, "downtime_seconds": 0, "elapsed_seconds": 0, "ends_at": "2026-10-27T09:00:00Z", "failed_checks": 0, "longest_outage_seconds": 0, "outages": 0, "period": "all", "starts_at": null, "total_checks": 0, "uptime_percentage": 0, "watching_since": "2026-10-27T09:00:00Z" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Get response times — API reference > Returns response times for a monitor. Source: https://upfour.io/docs/api/monitors/get-response-times [Developers](https://upfour.io/docs/api.md)/[Monitors](https://upfour.io/docs/api/monitors.md) Returns response times for a monitor. GET https://app.upfour.io/v1/monitors/{monitorID}/response_times scope monitor.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `monitorID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | CheckResultPoint[] · nullable | | | Each item · 11 fields FieldTypeDescription `connect_ms` integer `dns_ms` integer `is_checker_failure` boolean `is_degraded` boolean `latency_ms` integer `status_code` integer `timestamp` string (date-time) `tls_ms` integer `ttfb_ms` integer `was_successful` boolean `region` string | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet availability](https://upfour.io/docs/api/monitors/get-availability.md) [NextList incidents](https://upfour.io/docs/api/monitors/list-incidents.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/response_times" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/response_times", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/response_times", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/response_times", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "connect_ms": 0, "dns_ms": 0, "is_checker_failure": false, "is_degraded": false, "latency_ms": 115, "status_code": 200, "timestamp": "2026-10-27T08:00:00Z", "tls_ms": 0, "ttfb_ms": 0, "was_successful": true }, { "connect_ms": 0, "dns_ms": 0, "is_checker_failure": false, "is_degraded": false, "latency_ms": 108, "status_code": 200, "timestamp": "2026-10-27T08:10:00Z", "tls_ms": 0, "ttfb_ms": 0, "was_successful": true }, { "connect_ms": 0, "dns_ms": 0, "is_checker_failure": false, "is_degraded": false, "latency_ms": 101, "status_code": 200, "timestamp": "2026-10-27T08:20:00Z", "tls_ms": 0, "ttfb_ms": 0, "was_successful": true }, { "connect_ms": 0, "dns_ms": 0, "is_checker_failure": false, "is_degraded": false, "latency_ms": 94, "status_code": 200, "timestamp": "2026-10-27T08:30:00Z", "tls_ms": 0, "ttfb_ms": 0, "was_successful": true }, { "connect_ms": 0, "dns_ms": 0, "is_checker_failure": false, "is_degraded": false, "latency_ms": 87, "status_code": 200, "timestamp": "2026-10-27T08:40:00Z", "tls_ms": 0, "ttfb_ms": 0, "was_successful": true }, { "connect_ms": 0, "dns_ms": 0, "is_checker_failure": false, "is_degraded": false, "latency_ms": 80, "status_code": 200, "timestamp": "2026-10-27T08:50:00Z", "tls_ms": 0, "ttfb_ms": 0, "was_successful": true } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # List incidents — API reference > Lists incidents for a monitor. Source: https://upfour.io/docs/api/monitors/list-incidents [Developers](https://upfour.io/docs/api.md)/[Monitors](https://upfour.io/docs/api/monitors.md) Lists incidents for a monitor. GET https://app.upfour.io/v1/monitors/{monitorID}/incidents scope monitor.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `monitorID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | IncidentListResult | | | IncidentListResult · 49 fields FieldTypeDescription `counts` IncidentCounts IncidentCounts · 7 fields FieldTypeDescription `acknowledged` integer `open` integer `resolved_in_window` integer `started_in_window` integer `unresolved` integer `window_from` string (date-time) `window_to` string (date-time) `items` Incident[] · nullable Each item · 37 fields FieldTypeDescription `acknowledged_at` string (date-time) · nullable `cause` string `check_method` string `check_url` string `client_id` uuid · nullable `correlated_count` integer `created_at` string (date-time) `created_by` string `description` string `duration_seconds` integer · nullable `escalation_target_label` string `heartbeat_id` uuid · nullable `heartbeat_name` string `id` uuid `metadata` map of string · nullable max 100 keys each value max length 4096 Custom key/value pairs (at most 100 keys; keys 1-128 characters, values up to 4096). Sent to machine-readable alert channels and API webhook subscriptions, and copied onto the incidents the resource opens. `monitor_id` uuid · nullable `monitor_name` string `notified_label` string `override_channel_ids` uuid[] · nullable `resolved_at` string (date-time) · nullable `resolved_by_type` string · nullable `resolved_by_user_id` uuid · nullable `routing_target_id` uuid · nullable `routing_type` string one of`everyone` `escalation_policy` `team` `severity` string one of`critical` `warning` `info` `started_at` string (date-time) `started_by_type` string `started_by_user_id` uuid · nullable `status` string one of`open` `acknowledged` `resolved` `title` string `updated_at` string (date-time) `user_id` uuid `escalation_target_channel_count` integer `escalation_target_kind` string one of`nobody` `channel_count` `channel_count_with_policy` `name` `escalation_target_name` string `notified_channel_count` integer `notified_kind` string one of`nobody` `delivery_failed` `channel_count` `channel` `page` integer `per_page` integer `total` integer | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet response times](https://upfour.io/docs/api/monitors/get-response-times.md) [NextList certificates](https://upfour.io/docs/api/monitors/list-certificates.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/incidents" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/incidents", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/incidents", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/incidents", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "counts": { "acknowledged": 0, "open": 0, "resolved_in_window": 0, "started_in_window": 0, "unresolved": 0 }, "items": [], "page": 1, "per_page": 25, "total": 0 } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # List certificates — API reference > Lists certificates associated with a monitor. Source: https://upfour.io/docs/api/monitors/list-certificates [Developers](https://upfour.io/docs/api.md)/[Monitors](https://upfour.io/docs/api/monitors.md) Lists certificates associated with a monitor. GET https://app.upfour.io/v1/monitors/{monitorID}/certificates scope monitor.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `monitorID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | MonitorCertificate[] · nullable | | | Each item · 13 fields FieldTypeDescription `discovered_at` string (date-time) `dns_names` string[] · nullable `fingerprint_sha256` string `id` uuid `is_active` boolean `issuer` string `last_seen_at` string (date-time) `monitor_id` uuid `not_after` string (date-time) `not_before` string (date-time) `serial_number` string `subject` string `retired_at` string (date-time) | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList incidents](https://upfour.io/docs/api/monitors/list-incidents.md) [NextGet domain expiry](https://upfour.io/docs/api/monitors/get-domain-expiry.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/certificates" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/certificates", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/certificates", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/certificates", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Get domain expiry — API reference > Returns when the domain of a monitor expires, or why its registry cannot be checked. Source: https://upfour.io/docs/api/monitors/get-domain-expiry [Developers](https://upfour.io/docs/api.md)/[Monitors](https://upfour.io/docs/api/monitors.md) Returns when the domain of a monitor expires, or why its registry cannot be checked. GET https://app.upfour.io/v1/monitors/{monitorID}/domain-expiry scope monitor.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `monitorID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | MonitorDomainExpiryResponse | | | MonitorDomainExpiryResponse · 4 fields FieldTypeDescription `domain` string `expires_at` string (date-time) · nullable `status` string `tld` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList certificates](https://upfour.io/docs/api/monitors/list-certificates.md) [NextList regions](https://upfour.io/docs/api/monitors/list-regions.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/monitors/3f6c1a52-8b1e-4f0d-9a43-2c5d7e8f1a90/domain-expiry" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitors/3f6c1a52-8b1e-4f0d-9a43-2c5d7e8f1a90/domain-expiry", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/monitors/3f6c1a52-8b1e-4f0d-9a43-2c5d7e8f1a90/domain-expiry", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/monitors/3f6c1a52-8b1e-4f0d-9a43-2c5d7e8f1a90/domain-expiry", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "domain": "example.com", "expires_at": "2026-01-15T09:30:00Z", "status": "example", "tld": "example" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # List regions — API reference > Lists the regions for a monitor. Source: https://upfour.io/docs/api/monitors/list-regions [Developers](https://upfour.io/docs/api.md)/[Monitors](https://upfour.io/docs/api/monitors.md) Lists the regions for a monitor. GET https://app.upfour.io/v1/monitors/{monitorID}/regions scope monitor.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `monitorID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | MonitorRegionStatus[] · nullable | | | Each item · 7 fields FieldTypeDescription `region` string `state` string `freshness_timeout_seconds` integer `last_checked_at` string (date-time) `latency_ms` integer `status_code` integer `was_successful` boolean | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet domain expiry](https://upfour.io/docs/api/monitors/get-domain-expiry.md) [NextGet the summary](https://upfour.io/docs/api/monitors/get-the-summary.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/regions" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/regions", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/regions", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/regions", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "freshness_timeout_seconds": 120, "last_checked_at": "2026-10-27T08:50:00Z", "latency_ms": 80, "region": "eu", "state": "UP", "status_code": 200, "was_successful": true } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Get the summary — API reference > Returns a summary of the monitors in scope. Source: https://upfour.io/docs/api/monitors/get-the-summary [Developers](https://upfour.io/docs/api.md)/[Monitors](https://upfour.io/docs/api/monitors.md) Returns a summary of the monitors in scope. GET https://app.upfour.io/v1/monitors/summary scope monitor.read stable returns 200 ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | map of integer · nullable | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList regions](https://upfour.io/docs/api/monitors/list-regions.md) [NextList alert channels](https://upfour.io/docs/api/monitors/list-alert-channels.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/monitors/summary" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitors/summary", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/monitors/summary", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/monitors/summary", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "all": 2, "degraded": 0, "down": 0, "paused": 0, "pending": 1, "up": 1 } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # List alert channels — API reference > Lists alert channels attached to a monitor. Source: https://upfour.io/docs/api/monitors/list-alert-channels [Developers](https://upfour.io/docs/api.md)/[Monitors](https://upfour.io/docs/api/monitors.md) Lists alert channels attached to a monitor. GET https://app.upfour.io/v1/monitors/{monitorID}/notification_channels scope monitor.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `monitorID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | NotificationChannelResponse[] · nullable | | | Each item · 17 fields FieldTypeDescription `config` object free-form keys The keys of the channel's type as sent on create. Secret values (auth_token, secret, webhook_url, bot_token, routing_key, api_key, license_key, api_token, app_token, user_key, access_token) are masked: \*\*\*\*\*\* followed by their last four characters when longer than eight characters, otherwise only \*\*\*\*\*\*. `created_at` string (date-time) `id` uuid `monitor_count` integer Monitors that route to this channel; 0 means nothing uses it. Only filled in the list. `name` string `scope` string one of`organization` `client` `team` `type` string one of`email` `sms` `voice` `webhook` `slack` `telegram` `msteams` `discord` `pagerduty` `zapier` `datadog` `newrelic` `jira` `grafana` `ntfy` `pushover` `updated_at` string (date-time) `user_id` uuid `client_id` uuid `last_delivery_at` string (date-time) Last successful delivery. Filled in the list. `last_failure_at` string (date-time) Last failed delivery attempt. Filled in the list. `last_failure_reason` string `last_voice_at` string (date-time) `last_voice_status` string Status of the last phone call, voice channels only. Filled in the list. `organization_id` uuid Company the channel belongs to. `team_id` uuid | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet the summary](https://upfour.io/docs/api/monitors/get-the-summary.md) [NextAttach an alert channel](https://upfour.io/docs/api/monitors/attach-an-alert-channel.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/notification_channels" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/notification_channels", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/notification_channels", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/notification_channels", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "config": { "email": "oncall@example.com" }, "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000002", "monitor_count": 0, "name": "On-call email", "organization_id": "00000000-0000-4000-8000-000000000003", "scope": "organization", "team_id": "00000000-0000-4000-8000-000000000004", "type": "email", "updated_at": "2026-10-27T09:00:00Z", "user_id": "00000000-0000-4000-8000-000000000005" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Attach an alert channel — API reference > Attaches an alert channel to a monitor. Source: https://upfour.io/docs/api/monitors/attach-an-alert-channel [Developers](https://upfour.io/docs/api.md)/[Monitors](https://upfour.io/docs/api/monitors.md) Attaches an alert channel to a monitor. POST https://app.upfour.io/v1/monitors/{monitorID}/notification_channels/{channelID} scope monitor.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Parameters | Field | Type | Description | | --- | --- | --- | | `monitorID` required | uuid | path parameter | | `channelID` required | uuid | path parameter | ## Body `application/json` optional 1 field All other fields1 fields default_alert_planboolean ## Returns A successful request returns `201` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList alert channels](https://upfour.io/docs/api/monitors/list-alert-channels.md) [NextDetach an alert channel](https://upfour.io/docs/api/monitors/detach-an-alert-channel.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/notification_channels/00000000-0000-4000-8000-000000000002" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "default_alert_plan": false }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/notification_channels/00000000-0000-4000-8000-000000000002", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ default_alert_plan: false, }), }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/notification_channels/00000000-0000-4000-8000-000000000002", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "default_alert_plan": False, }, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "default_alert_plan": false }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/notification_channels/00000000-0000-4000-8000-000000000002", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 201 ``` Done. The response has no body. ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Detach an alert channel — API reference > Detaches an alert channel from a monitor. Source: https://upfour.io/docs/api/monitors/detach-an-alert-channel [Developers](https://upfour.io/docs/api.md)/[Monitors](https://upfour.io/docs/api/monitors.md) Detaches an alert channel from a monitor. DELETE https://app.upfour.io/v1/monitors/{monitorID}/notification_channels/{channelID} scope monitor.delete stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `monitorID` required | uuid | path parameter | | `channelID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousAttach an alert channel](https://upfour.io/docs/api/monitors/attach-an-alert-channel.md) [NextSet the alert plan](https://upfour.io/docs/api/monitors/set-the-alert-plan.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/notification_channels/00000000-0000-4000-8000-000000000002" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/notification_channels/00000000-0000-4000-8000-000000000002", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/notification_channels/00000000-0000-4000-8000-000000000002", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/notification_channels/00000000-0000-4000-8000-000000000002", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Set the alert plan — API reference > Sets the alert plan for a monitor. Source: https://upfour.io/docs/api/monitors/set-the-alert-plan [Developers](https://upfour.io/docs/api.md)/[Monitors](https://upfour.io/docs/api/monitors.md) Sets the alert plan for a monitor. PUT https://app.upfour.io/v1/monitors/{monitorID}/escalation-policy scope monitor.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `monitorID` required | uuid | path parameter | ## Body `application/json` required 1 field policy_iduuid (required) ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | MonitorEscalationPolicyAssigned | | | MonitorEscalationPolicyAssigned · 1 fields FieldTypeDescription `status` string one of`assigned` | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousDetach an alert channel](https://upfour.io/docs/api/monitors/detach-an-alert-channel.md) [NextRemove the alert plan](https://upfour.io/docs/api/monitors/remove-the-alert-plan.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/escalation-policy" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "policy_id": "00000000-0000-4000-8000-000000000002" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/escalation-policy", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ policy_id: "00000000-0000-4000-8000-000000000002", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/escalation-policy", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "policy_id": "00000000-0000-4000-8000-000000000002", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "policy_id": "00000000-0000-4000-8000-000000000002" }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/escalation-policy", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "status": "assigned" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Remove the alert plan — API reference > Removes the alert plan from a monitor. Source: https://upfour.io/docs/api/monitors/remove-the-alert-plan [Developers](https://upfour.io/docs/api.md)/[Monitors](https://upfour.io/docs/api/monitors.md) Removes the alert plan from a monitor. DELETE https://app.upfour.io/v1/monitors/{monitorID}/escalation-policy scope monitor.delete stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `monitorID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousSet the alert plan](https://upfour.io/docs/api/monitors/set-the-alert-plan.md) [NextImport monitors](https://upfour.io/docs/api/monitors/import-monitors.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/escalation-policy" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/escalation-policy", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/escalation-policy", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/monitors/00000000-0000-4000-8000-000000000001/escalation-policy", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Import monitors — API reference > Imports multiple monitors in one request. Source: https://upfour.io/docs/api/monitors/import-monitors [Developers](https://upfour.io/docs/api.md)/[Monitors](https://upfour.io/docs/api/monitors.md) Imports multiple monitors in one request. POST https://app.upfour.io/v1/monitors/batch scope monitor.write stable [Idempotency-Key required](https://upfour.io/docs/api/idempotency.md) returns 201 ## Body `application/json` required 8 fields rowsBatchMonitorRowInput[] (required) Nested fields 19 fields | Field | Type | Description | | --- | --- | --- | | `allow_duplicate` optional | boolean | | | `client_id` optional | uuid · nullable | | | `confirmation_period` optional | integer | | | `dns_expected` optional | string | | | `dns_query` optional | string | | | `dns_type` optional | string one of`A` `AAAA` `MX` `CNAME` `TXT` `NS` | | | `expected_status_code` optional | integer · nullable | | | `fields` optional | map of any | Per-row overrides of monitor fields by name. Any other key of the row object is read as an entry of fields, too. | | `interval_seconds` optional | integer | | | `keyword` optional | string | | | `line` optional | integer | | | `monitor_type` optional | string one of`http` `tcp` `ping` `dns` | | | `name` optional | string | | | `raw` optional | string | | | `recovery_period` optional | integer | | | `regions` optional | string[] one of`eu` `us` `asia` `au` `default` | | | `tcp_host` optional | string | | | `tcp_port` optional | integer · nullable | | | `url` optional | string | | All other fields65 fields create_pausedboolean default_alert_planboolean · nullable defaultsCreateMonitorInput Nested fields 58 fields | Field | Type | Description | | --- | --- | --- | | `alert_channel_names` optional | string[] | | | `alert_plan_name` optional | string | | | `alert_type` optional | string one of`""` `status` `keyword` `keyword_absence` `expected_status_code` | | | `auth_password` optional | string | Basic-auth password. Write-only: responses carry has_auth_password instead. | | `auth_password_action` optional | string one of`""` `keep` `replace` `clear` | | | `auth_username` optional | string | | | `capture_on_incident_open` optional | boolean · nullable | | | `client_id` optional | uuid · nullable | | | `client_name` optional | string | | | `confirmation_period` optional | integer | | | `creation_key` optional | uuid · nullable | | | `default_alert_plan` optional | boolean | | | `degraded_alert_enabled` optional | boolean | | | `degraded_threshold_ms` optional | integer · nullable | | | `dns_expected` optional | string | | | `dns_query` optional | string | | | `dns_server` optional | string | | | `dns_type` optional | string one of`""` `A` `AAAA` `MX` `CNAME` `TXT` `NS` | | | `domain_expiry_alert_days` optional | integer · nullable | | | `escalation_policy_id` optional | uuid · nullable | | | `expected_status_code` optional | integer · nullable | | | `follow_redirects` optional | boolean · nullable | | | `http_method` optional | string | | | `interval_seconds` optional | integer | | | `ip_version` optional | string one of`""` `4` `6` | | | `keep_cookies` optional | boolean | | | `keyword` optional | string | | | `keyword_case_insensitive` optional | boolean · nullable | | | `maintenance_from` optional | string · nullable | | | `maintenance_timezone` optional | string | | | `maintenance_to` optional | string · nullable | | | `maintenance_weekdays` optional | string[] one of`mon` `tue` `wed` `thu` `fri` `sat` `sun` | | | `metadata` optional | map of string max 100 keys each value max length 4096 | On update, omitted or null leaves the stored metadata unchanged, {} removes all keys and any other object replaces them. | | `monitor_group_id` optional | uuid · nullable | | | `monitor_group_name` optional | string | | | `monitor_type` optional | string one of`http` `tcp` `ping` `dns` | | | `name` optional | string | | | `notification_channel_ids` optional | uuid[] | | | `notification_mode` optional | string one of`quiet` `immediate` | | | `notify_recovery` optional | boolean · nullable | | | `pronounceable_name` optional | string | | | `proxy_host` optional | string | | | `proxy_port` optional | integer · nullable | | | `public_name` optional | string · nullable | | | `recovery_period` optional | integer | | | `regions` optional | string[] one of`eu` `us` `asia` `au` `default` | | | `request_body` optional | string | | | `request_headers` optional | RequestHeader[] | Custom request headers, at most 20 and 8 KB in total. On update they are kept unless request_headers_action is replace or clear. | | Each item · 2 fields FieldTypeDescription `name` optional string `value` optional string | | | | `request_headers_action` optional | string one of`keep` `replace` `clear` | | | `request_timeout_s` optional | integer | | | `ssl_expiry_alert_days` optional | integer · nullable | | | `ssl_verification` optional | boolean · nullable | | | `state` optional | string one of`""` `UP` `DOWN` `PAUSED` | UP, DOWN or PAUSED (any case); empty creates the monitor running. | | `tcp_host` optional | string | | | `tcp_port` optional | integer · nullable | | | `url` optional | string | | dry_run_idstring escalation_policy_iduuid · nullable notification_channel_idsuuid[] sourcestring ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | BatchMonitorResponse | | | BatchMonitorResponse · 160 fields FieldTypeDescription `dry_run_id` string `rows` BatchMonitorRowResult[] · nullable Each item · 148 fields FieldTypeDescription `errors` BatchMonitorRowIssue[] · nullable Each item · 3 fields FieldTypeDescription `code` string `message` string `details` object free-form keys `index` integer `line` integer `normalized_input` ResolvedMonitorInput ResolvedMonitorInput · 58 fields FieldTypeDescription `alert_type` string one of`""` `status` `keyword` `keyword_absence` `expected_status_code` `auth_password` string Basic-auth password. Write-only: responses carry has_auth_password instead. `auth_password_action` string one of`""` `keep` `replace` `clear` `auth_username` string `confirmation_period` integer `degraded_alert_enabled` boolean `degraded_threshold_ms` integer · nullable `dns_expected` string `dns_query` string `dns_server` string `dns_type` string one of`""` `A` `AAAA` `MX` `CNAME` `TXT` `NS` `domain_expiry_alert_days` integer · nullable `expected_status_code` integer · nullable `follow_redirects` boolean · nullable `http_method` string `interval_seconds` integer `ip_version` string one of`""` `4` `6` `keep_cookies` boolean `keyword` string `keyword_case_insensitive` boolean · nullable `maintenance_from` string · nullable `maintenance_timezone` string `maintenance_to` string · nullable `maintenance_weekdays` string[] · nullable one of`mon` `tue` `wed` `thu` `fri` `sat` `sun` `monitor_type` string one of`http` `tcp` `ping` `dns` `name` string `pronounceable_name` string `proxy_host` string `proxy_port` integer · nullable `recovery_period` integer `regions` string[] · nullable one of`eu` `us` `asia` `au` `default` `request_body` string `request_headers` ResolvedRequestHeader[] · nullable Custom request headers, at most 20 and 8 KB in total. On update they are kept unless request_headers_action is replace or clear. Each item · 2 fields FieldTypeDescription `name` string `value` string `request_timeout_s` integer `ssl_expiry_alert_days` integer · nullable `ssl_verification` boolean · nullable `state` string one of`""` `UP` `DOWN` `PAUSED` UP, DOWN or PAUSED (any case); empty creates the monitor running. `tcp_host` string `tcp_port` integer · nullable `url` string `alert_channel_names` string[] `alert_plan_name` string `capture_on_incident_open` boolean `client_id` uuid `client_name` string `creation_key` uuid `default_alert_plan` boolean `escalation_policy_id` uuid `metadata` map of string max 100 keys each value max length 4096 On update, omitted or null leaves the stored metadata unchanged, {} removes all keys and any other object replaces them. `monitor_group_id` uuid `monitor_group_name` string `notification_channel_ids` uuid[] `notification_mode` string one of`quiet` `immediate` `notify_recovery` boolean `public_name` string `request_headers_action` string one of`keep` `replace` `clear` `raw` string `status` string one of`ready` `duplicate` `error` `created` `warnings` BatchMonitorRowIssue[] · nullable Each item · 3 fields FieldTypeDescription `code` string `message` string `details` object free-form keys `created_monitor` Monitor Monitor · 70 fields FieldTypeDescription `alert_type` string one of`status` `keyword` `keyword_absence` `expected_status_code` `auth_username` string `capture_on_incident_open` boolean `confirmation_period` integer `created_at` string (date-time) `degraded_alert_enabled` boolean `degraded_threshold_ms` integer · nullable `domain_expiry_alert_days` integer · nullable `escalation_policy_id` uuid · nullable `expected_status_code` integer · nullable `feature_pause_reasons` string[] · nullable `follow_redirects` boolean `has_auth_password` boolean `has_request_headers` boolean `http_method` string `id` uuid `interval_seconds` integer `ip_version` string one of`""` `4` `6` `is_degraded` boolean `is_pending` boolean `keep_cookies` boolean `keyword` string `keyword_case_insensitive` boolean `maintenance_from` string · nullable `maintenance_timezone` string `maintenance_to` string · nullable `maintenance_weekdays` string[] · nullable one of`mon` `tue` `wed` `thu` `fri` `sat` `sun` `monitor_type` string one of`http` `tcp` `ping` `dns` `name` string `next_check_at` string (date-time) · nullable `notification_mode` string one of`quiet` `immediate` `notify_recovery` boolean `pronounceable_name` string `proxy_host` string `proxy_port` integer · nullable `public_name` string `recovery_period` integer `regions` string[] · nullable one of`eu` `us` `asia` `au` `default` `request_body` string `request_headers` any[] · nullable Always empty: header values are never returned. has_request_headers says whether the monitor sends any. `request_timeout_s` integer `ssl_expiry_alert_days` integer · nullable `ssl_verification` boolean `state` string one of`UP` `DOWN` `PAUSED` `url` string `user_id` uuid `badge_token` string `client_id` uuid `dns_expected` string `dns_query` string `dns_server` string `dns_type` string one of`A` `AAAA` `MX` `CNAME` `TXT` `NS` `freshness_timeout_seconds` integer `last_checked_at` string (date-time) `last_result_at` string (date-time) `last_state_change_at` string (date-time) `latency_p50_ms` integer `latency_p95_ms` integer `latency_samples_24h` integer `latest_result_successful` boolean `monitor_group_id` uuid `paused_until` string (date-time) `recent_incidents` MonitorRecentIncident[] Each item · 5 fields FieldTypeDescription `id` uuid `resolved_at` string (date-time) · nullable `started_at` string (date-time) `status` string `resolved_by_type` string `tcp_host` string `tcp_port` integer `duplicate_match` MonitorBatchMatch MonitorBatchMatch · 5 fields FieldTypeDescription `id` uuid `monitor_type` string `name` string `url` string `client_id` uuid `summary` BatchMonitorSummary BatchMonitorSummary · 5 fields FieldTypeDescription `duplicates` integer `errors` integer `ready` integer `total` integer `warnings` integer `capacity` MonitorCapacity MonitorCapacity · 2 fields FieldTypeDescription `current` integer `limit` integer `import_id` uuid | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 409 | [conflict](https://upfour.io/docs/api/errors#code-conflict) | The request conflicts with the current state. | | 400 | [import_invalid](https://upfour.io/docs/api/errors#code-import_invalid) | The import body is not valid. **When:** A batch monitor import whose body cannot be parsed; the message names the problem. **Fix:** Correct the body and send it again. Stable codes next to the English message: `import_invalid` (the body is not a valid import), `import_rows_out_of_range` (`error.details.min_rows` and `max_rows`: 1 to 500 rows per import). Notes on single rows carry a `code` and, where the sentence names something, `details` (`field`, `option` or `line`). | | 400 | [import_rows_out_of_range](https://upfour.io/docs/api/errors#code-import_rows_out_of_range) | The import has too few or too many rows. **When:** A batch monitor import with fewer than min_rows or more than max_rows rows, both in details. **Fix:** Send between min_rows and max_rows rows per request. | | 413 | [import_too_large](https://upfour.io/docs/api/errors#code-import_too_large) | The import body is over the size limit. **When:** A batch monitor import larger than details.max_bytes. **Fix:** Split the import into several requests. `import_too_large`: the import body is larger than 2 MiB (`error.details.max_bytes`). Nothing is imported. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 503 | [service_unavailable](https://upfour.io/docs/api/errors#code-service_unavailable) | The service or a required dependency is temporarily unavailable. | | 403 | documented response | `check_capacity_exceeded` (subscriptions only): the change would raise the projected 30-day list usage of all active monitors above the included check budget. `error.details` carries `projected_cents`, `included_cents` and `change_cents`; a longer interval, fewer regions or fewer monitors fit. Running monitors and changes that lower the load are never refused. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousRemove the alert plan](https://upfour.io/docs/api/monitors/remove-the-alert-plan.md) [NextPreview an import](https://upfour.io/docs/api/monitors/preview-an-import.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/monitors/batch" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "dry_run_id": "721378bc96ef2ddece4a66b447ba60d6a1266860d9cb23f4e0e6c152f30279cb", "rows": [ { "name": "Docs site", "url": "https://example.com" }, { "name": "Status page", "url": "https://example.org" } ], "source": "csv" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitors/batch", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ dry_run_id: "721378bc96ef2ddece4a66b447ba60d6a1266860d9cb23f4e0e6c152f30279cb", rows: [ { name: "Docs site", url: "https://example.com", }, { name: "Status page", url: "https://example.org", }, ], source: "csv", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/monitors/batch", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "dry_run_id": "721378bc96ef2ddece4a66b447ba60d6a1266860d9cb23f4e0e6c152f30279cb", "rows": [ { "name": "Docs site", "url": "https://example.com", }, { "name": "Status page", "url": "https://example.org", }, ], "source": "csv", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "dry_run_id": "721378bc96ef2ddece4a66b447ba60d6a1266860d9cb23f4e0e6c152f30279cb", "rows": [ { "name": "Docs site", "url": "https://example.com" }, { "name": "Status page", "url": "https://example.org" } ], "source": "csv" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/monitors/batch", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "capacity": { "current": 2, "limit": 500 }, "dry_run_id": "721378bc96ef2ddece4a66b447ba60d6a1266860d9cb23f4e0e6c152f30279cb", "import_id": "00000000-0000-4000-8000-000000000001", "rows": [ { "created_monitor": { "alert_type": "status", "auth_username": "", "capture_on_incident_open": false, "confirmation_period": 0, "created_at": "2026-10-27T09:00:00Z", "degraded_alert_enabled": false, "degraded_threshold_ms": null, "dns_type": "A", "domain_expiry_alert_days": null, "escalation_policy_id": null, "expected_status_code": null, "feature_pause_reasons": null, "follow_redirects": true, "freshness_timeout_seconds": 120, "has_auth_password": false, "has_request_headers": false, "http_method": "HEAD", "id": "00000000-0000-4000-8000-000000000002", "interval_seconds": 60, "ip_version": "", "is_degraded": false, "is_pending": true, "keep_cookies": false, "keyword": "", "keyword_case_insensitive": true, "maintenance_from": null, "maintenance_timezone": "UTC", "maintenance_to": null, "maintenance_weekdays": [ "mon", "tue", "wed", "thu", "fri", "sat", "sun" ], "monitor_type": "http", "name": "Docs site", "next_check_at": "2026-10-27T09:00:00Z", "notification_mode": "immediate", "notify_recovery": true, "pronounceable_name": "", "proxy_host": "", "proxy_port": null, "public_name": "", "recovery_period": 0, "regions": [ "eu" ], "request_body": "", "request_headers": [], "request_timeout_s": 30, "ssl_expiry_alert_days": null, "ssl_verification": true, "state": "UP", "url": "https://example.com", "user_id": "00000000-0000-4000-8000-000000000003" }, "errors": null, "index": 0, "line": 1, "normalized_input": { "alert_type": "status", "auth_password": "", "auth_password_action": "", "auth_username": "", "capture_on_incident_open": false, "confirmation_period": 0, "degraded_alert_enabled": false, "degraded_threshold_ms": null, "dns_expected": "", "dns_query": "", "dns_server": "", "dns_type": "A", "domain_expiry_alert_days": null, "expected_status_code": null, "follow_redirects": true, "http_method": "HEAD", "interval_seconds": 60, "ip_version": "", "keep_cookies": false, "keyword": "", "keyword_case_insensitive": true, "maintenance_from": null, "maintenance_timezone": "UTC", "maintenance_to": null, "maintenance_weekdays": [ "mon", "tue", "wed", "thu", "fri", "sat", "sun" ], "monitor_type": "http", "name": "Docs site", "notification_mode": "immediate", "notify_recovery": true, "pronounceable_name": "", "proxy_host": "", "proxy_port": null, "recovery_period": 0, "regions": [ "eu" ], "request_body": "", "request_headers": [], "request_timeout_s": 30, "ssl_expiry_alert_days": null, "ssl_verification": true, "state": "UP", "tcp_host": "", "tcp_port": null, "url": "https://example.com" }, "raw": "", "status": "created", "warnings": null }, { "created_monitor": { "alert_type": "status", "auth_username": "", "capture_on_incident_open": false, "confirmation_period": 0, "created_at": "2026-10-27T09:00:00Z", "degraded_alert_enabled": false, "degraded_threshold_ms": null, "dns_type": "A", "domain_expiry_alert_days": null, "escalation_policy_id": null, "expected_status_code": null, "feature_pause_reasons": null, "follow_redirects": true, "freshness_timeout_seconds": 120, "has_auth_password": false, "has_request_headers": false, "http_method": "HEAD", "id": "00000000-0000-4000-8000-000000000004", "interval_seconds": 60, "ip_version": "", "is_degraded": false, "is_pending": true, "keep_cookies": false, "keyword": "", "keyword_case_insensitive": true, "maintenance_from": null, "maintenance_timezone": "UTC", "maintenance_to": null, "maintenance_weekdays": [ "mon", "tue", "wed", "thu", "fri", "sat", "sun" ], "monitor_type": "http", "name": "Status page", "next_check_at": "2026-10-27T09:00:00Z", "notification_mode": "immediate", "notify_recovery": true, "pronounceable_name": "", "proxy_host": "", "proxy_port": null, "public_name": "", "recovery_period": 0, "regions": [ "eu" ], "request_body": "", "request_headers": [], "request_timeout_s": 30, "ssl_expiry_alert_days": null, "ssl_verification": true, "state": "UP", "url": "https://example.org", "user_id": "00000000-0000-4000-8000-000000000003" }, "errors": null, "index": 1, "line": 2, "normalized_input": { "alert_type": "status", "auth_password": "", "auth_password_action": "", "auth_username": "", "capture_on_incident_open": false, "confirmation_period": 0, "degraded_alert_enabled": false, "degraded_threshold_ms": null, "dns_expected": "", "dns_query": "", "dns_server": "", "dns_type": "A", "domain_expiry_alert_days": null, "expected_status_code": null, "follow_redirects": true, "http_method": "HEAD", "interval_seconds": 60, "ip_version": "", "keep_cookies": false, "keyword": "", "keyword_case_insensitive": true, "maintenance_from": null, "maintenance_timezone": "UTC", "maintenance_to": null, "maintenance_weekdays": [ "mon", "tue", "wed", "thu", "fri", "sat", "sun" ], "monitor_type": "http", "name": "Status page", "notification_mode": "immediate", "notify_recovery": true, "pronounceable_name": "", "proxy_host": "", "proxy_port": null, "recovery_period": 0, "regions": [ "eu" ], "request_body": "", "request_headers": [], "request_timeout_s": 30, "ssl_expiry_alert_days": null, "ssl_verification": true, "state": "UP", "tcp_host": "", "tcp_port": null, "url": "https://example.org" }, "raw": "", "status": "created", "warnings": null } ], "summary": { "duplicates": 0, "errors": 0, "ready": 2, "total": 2, "warnings": 0 } } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 409 ```json { "error": { "code": "conflict", "message": "The request conflicts with the current state.", "request_id": "req_…" } } ``` ### 413 ```json { "error": { "code": "import_too_large", "message": "The import body is over the size limit.", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "service_unavailable", "message": "The service or a required dependency is temporarily unavailable.", "request_id": "req_…" } } ``` --- # Preview an import — API reference > Previews a monitor import without applying it. Source: https://upfour.io/docs/api/monitors/preview-an-import [Developers](https://upfour.io/docs/api.md)/[Monitors](https://upfour.io/docs/api/monitors.md) Previews a monitor import without applying it. POST https://app.upfour.io/v1/monitors/batch/dry-run scope monitor.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Body `application/json` required 8 fields rowsBatchMonitorRowInput[] (required) Nested fields 19 fields | Field | Type | Description | | --- | --- | --- | | `allow_duplicate` optional | boolean | | | `client_id` optional | uuid · nullable | | | `confirmation_period` optional | integer | | | `dns_expected` optional | string | | | `dns_query` optional | string | | | `dns_type` optional | string one of`A` `AAAA` `MX` `CNAME` `TXT` `NS` | | | `expected_status_code` optional | integer · nullable | | | `fields` optional | map of any | Per-row overrides of monitor fields by name. Any other key of the row object is read as an entry of fields, too. | | `interval_seconds` optional | integer | | | `keyword` optional | string | | | `line` optional | integer | | | `monitor_type` optional | string one of`http` `tcp` `ping` `dns` | | | `name` optional | string | | | `raw` optional | string | | | `recovery_period` optional | integer | | | `regions` optional | string[] one of`eu` `us` `asia` `au` `default` | | | `tcp_host` optional | string | | | `tcp_port` optional | integer · nullable | | | `url` optional | string | | All other fields65 fields create_pausedboolean default_alert_planboolean · nullable defaultsCreateMonitorInput Nested fields 58 fields | Field | Type | Description | | --- | --- | --- | | `alert_channel_names` optional | string[] | | | `alert_plan_name` optional | string | | | `alert_type` optional | string one of`""` `status` `keyword` `keyword_absence` `expected_status_code` | | | `auth_password` optional | string | Basic-auth password. Write-only: responses carry has_auth_password instead. | | `auth_password_action` optional | string one of`""` `keep` `replace` `clear` | | | `auth_username` optional | string | | | `capture_on_incident_open` optional | boolean · nullable | | | `client_id` optional | uuid · nullable | | | `client_name` optional | string | | | `confirmation_period` optional | integer | | | `creation_key` optional | uuid · nullable | | | `default_alert_plan` optional | boolean | | | `degraded_alert_enabled` optional | boolean | | | `degraded_threshold_ms` optional | integer · nullable | | | `dns_expected` optional | string | | | `dns_query` optional | string | | | `dns_server` optional | string | | | `dns_type` optional | string one of`""` `A` `AAAA` `MX` `CNAME` `TXT` `NS` | | | `domain_expiry_alert_days` optional | integer · nullable | | | `escalation_policy_id` optional | uuid · nullable | | | `expected_status_code` optional | integer · nullable | | | `follow_redirects` optional | boolean · nullable | | | `http_method` optional | string | | | `interval_seconds` optional | integer | | | `ip_version` optional | string one of`""` `4` `6` | | | `keep_cookies` optional | boolean | | | `keyword` optional | string | | | `keyword_case_insensitive` optional | boolean · nullable | | | `maintenance_from` optional | string · nullable | | | `maintenance_timezone` optional | string | | | `maintenance_to` optional | string · nullable | | | `maintenance_weekdays` optional | string[] one of`mon` `tue` `wed` `thu` `fri` `sat` `sun` | | | `metadata` optional | map of string max 100 keys each value max length 4096 | On update, omitted or null leaves the stored metadata unchanged, {} removes all keys and any other object replaces them. | | `monitor_group_id` optional | uuid · nullable | | | `monitor_group_name` optional | string | | | `monitor_type` optional | string one of`http` `tcp` `ping` `dns` | | | `name` optional | string | | | `notification_channel_ids` optional | uuid[] | | | `notification_mode` optional | string one of`quiet` `immediate` | | | `notify_recovery` optional | boolean · nullable | | | `pronounceable_name` optional | string | | | `proxy_host` optional | string | | | `proxy_port` optional | integer · nullable | | | `public_name` optional | string · nullable | | | `recovery_period` optional | integer | | | `regions` optional | string[] one of`eu` `us` `asia` `au` `default` | | | `request_body` optional | string | | | `request_headers` optional | RequestHeader[] | Custom request headers, at most 20 and 8 KB in total. On update they are kept unless request_headers_action is replace or clear. | | Each item · 2 fields FieldTypeDescription `name` optional string `value` optional string | | | | `request_headers_action` optional | string one of`keep` `replace` `clear` | | | `request_timeout_s` optional | integer | | | `ssl_expiry_alert_days` optional | integer · nullable | | | `ssl_verification` optional | boolean · nullable | | | `state` optional | string one of`""` `UP` `DOWN` `PAUSED` | UP, DOWN or PAUSED (any case); empty creates the monitor running. | | `tcp_host` optional | string | | | `tcp_port` optional | integer · nullable | | | `url` optional | string | | dry_run_idstring escalation_policy_iduuid · nullable notification_channel_idsuuid[] sourcestring ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | BatchMonitorResponse | | | BatchMonitorResponse · 160 fields FieldTypeDescription `dry_run_id` string `rows` BatchMonitorRowResult[] · nullable Each item · 148 fields FieldTypeDescription `errors` BatchMonitorRowIssue[] · nullable Each item · 3 fields FieldTypeDescription `code` string `message` string `details` object free-form keys `index` integer `line` integer `normalized_input` ResolvedMonitorInput ResolvedMonitorInput · 58 fields FieldTypeDescription `alert_type` string one of`""` `status` `keyword` `keyword_absence` `expected_status_code` `auth_password` string Basic-auth password. Write-only: responses carry has_auth_password instead. `auth_password_action` string one of`""` `keep` `replace` `clear` `auth_username` string `confirmation_period` integer `degraded_alert_enabled` boolean `degraded_threshold_ms` integer · nullable `dns_expected` string `dns_query` string `dns_server` string `dns_type` string one of`""` `A` `AAAA` `MX` `CNAME` `TXT` `NS` `domain_expiry_alert_days` integer · nullable `expected_status_code` integer · nullable `follow_redirects` boolean · nullable `http_method` string `interval_seconds` integer `ip_version` string one of`""` `4` `6` `keep_cookies` boolean `keyword` string `keyword_case_insensitive` boolean · nullable `maintenance_from` string · nullable `maintenance_timezone` string `maintenance_to` string · nullable `maintenance_weekdays` string[] · nullable one of`mon` `tue` `wed` `thu` `fri` `sat` `sun` `monitor_type` string one of`http` `tcp` `ping` `dns` `name` string `pronounceable_name` string `proxy_host` string `proxy_port` integer · nullable `recovery_period` integer `regions` string[] · nullable one of`eu` `us` `asia` `au` `default` `request_body` string `request_headers` ResolvedRequestHeader[] · nullable Custom request headers, at most 20 and 8 KB in total. On update they are kept unless request_headers_action is replace or clear. Each item · 2 fields FieldTypeDescription `name` string `value` string `request_timeout_s` integer `ssl_expiry_alert_days` integer · nullable `ssl_verification` boolean · nullable `state` string one of`""` `UP` `DOWN` `PAUSED` UP, DOWN or PAUSED (any case); empty creates the monitor running. `tcp_host` string `tcp_port` integer · nullable `url` string `alert_channel_names` string[] `alert_plan_name` string `capture_on_incident_open` boolean `client_id` uuid `client_name` string `creation_key` uuid `default_alert_plan` boolean `escalation_policy_id` uuid `metadata` map of string max 100 keys each value max length 4096 On update, omitted or null leaves the stored metadata unchanged, {} removes all keys and any other object replaces them. `monitor_group_id` uuid `monitor_group_name` string `notification_channel_ids` uuid[] `notification_mode` string one of`quiet` `immediate` `notify_recovery` boolean `public_name` string `request_headers_action` string one of`keep` `replace` `clear` `raw` string `status` string one of`ready` `duplicate` `error` `created` `warnings` BatchMonitorRowIssue[] · nullable Each item · 3 fields FieldTypeDescription `code` string `message` string `details` object free-form keys `created_monitor` Monitor Monitor · 70 fields FieldTypeDescription `alert_type` string one of`status` `keyword` `keyword_absence` `expected_status_code` `auth_username` string `capture_on_incident_open` boolean `confirmation_period` integer `created_at` string (date-time) `degraded_alert_enabled` boolean `degraded_threshold_ms` integer · nullable `domain_expiry_alert_days` integer · nullable `escalation_policy_id` uuid · nullable `expected_status_code` integer · nullable `feature_pause_reasons` string[] · nullable `follow_redirects` boolean `has_auth_password` boolean `has_request_headers` boolean `http_method` string `id` uuid `interval_seconds` integer `ip_version` string one of`""` `4` `6` `is_degraded` boolean `is_pending` boolean `keep_cookies` boolean `keyword` string `keyword_case_insensitive` boolean `maintenance_from` string · nullable `maintenance_timezone` string `maintenance_to` string · nullable `maintenance_weekdays` string[] · nullable one of`mon` `tue` `wed` `thu` `fri` `sat` `sun` `monitor_type` string one of`http` `tcp` `ping` `dns` `name` string `next_check_at` string (date-time) · nullable `notification_mode` string one of`quiet` `immediate` `notify_recovery` boolean `pronounceable_name` string `proxy_host` string `proxy_port` integer · nullable `public_name` string `recovery_period` integer `regions` string[] · nullable one of`eu` `us` `asia` `au` `default` `request_body` string `request_headers` any[] · nullable Always empty: header values are never returned. has_request_headers says whether the monitor sends any. `request_timeout_s` integer `ssl_expiry_alert_days` integer · nullable `ssl_verification` boolean `state` string one of`UP` `DOWN` `PAUSED` `url` string `user_id` uuid `badge_token` string `client_id` uuid `dns_expected` string `dns_query` string `dns_server` string `dns_type` string one of`A` `AAAA` `MX` `CNAME` `TXT` `NS` `freshness_timeout_seconds` integer `last_checked_at` string (date-time) `last_result_at` string (date-time) `last_state_change_at` string (date-time) `latency_p50_ms` integer `latency_p95_ms` integer `latency_samples_24h` integer `latest_result_successful` boolean `monitor_group_id` uuid `paused_until` string (date-time) `recent_incidents` MonitorRecentIncident[] Each item · 5 fields FieldTypeDescription `id` uuid `resolved_at` string (date-time) · nullable `started_at` string (date-time) `status` string `resolved_by_type` string `tcp_host` string `tcp_port` integer `duplicate_match` MonitorBatchMatch MonitorBatchMatch · 5 fields FieldTypeDescription `id` uuid `monitor_type` string `name` string `url` string `client_id` uuid `summary` BatchMonitorSummary BatchMonitorSummary · 5 fields FieldTypeDescription `duplicates` integer `errors` integer `ready` integer `total` integer `warnings` integer `capacity` MonitorCapacity MonitorCapacity · 2 fields FieldTypeDescription `current` integer `limit` integer `import_id` uuid | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 400 | [import_invalid](https://upfour.io/docs/api/errors#code-import_invalid) | The import body is not valid. **When:** A batch monitor import whose body cannot be parsed; the message names the problem. **Fix:** Correct the body and send it again. Stable codes next to the English message: `import_invalid` (the body is not a valid import), `import_rows_out_of_range` (`error.details.min_rows` and `max_rows`: 1 to 500 rows per import). Notes on single rows carry a `code` and, where the sentence names something, `details` (`field`, `option` or `line`). | | 400 | [import_rows_out_of_range](https://upfour.io/docs/api/errors#code-import_rows_out_of_range) | The import has too few or too many rows. **When:** A batch monitor import with fewer than min_rows or more than max_rows rows, both in details. **Fix:** Send between min_rows and max_rows rows per request. | | 413 | [import_too_large](https://upfour.io/docs/api/errors#code-import_too_large) | The import body is over the size limit. **When:** A batch monitor import larger than details.max_bytes. **Fix:** Split the import into several requests. `import_too_large`: the import body is larger than 2 MiB (`error.details.max_bytes`). Nothing is imported. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousImport monitors](https://upfour.io/docs/api/monitors/import-monitors.md) [NextList imports](https://upfour.io/docs/api/monitors/list-imports.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/monitors/batch/dry-run" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "rows": [ { "name": "Docs site", "url": "https://example.com" }, { "name": "Status page", "url": "https://example.org" } ], "source": "csv" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitors/batch/dry-run", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ rows: [ { name: "Docs site", url: "https://example.com", }, { name: "Status page", url: "https://example.org", }, ], source: "csv", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/monitors/batch/dry-run", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "rows": [ { "name": "Docs site", "url": "https://example.com", }, { "name": "Status page", "url": "https://example.org", }, ], "source": "csv", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "rows": [ { "name": "Docs site", "url": "https://example.com" }, { "name": "Status page", "url": "https://example.org" } ], "source": "csv" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/monitors/batch/dry-run", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "capacity": { "current": 2, "limit": 500 }, "dry_run_id": "721378bc96ef2ddece4a66b447ba60d6a1266860d9cb23f4e0e6c152f30279cb", "rows": [ { "errors": null, "index": 0, "line": 1, "normalized_input": { "alert_type": "status", "auth_password": "", "auth_password_action": "", "auth_username": "", "capture_on_incident_open": false, "confirmation_period": 0, "degraded_alert_enabled": false, "degraded_threshold_ms": null, "dns_expected": "", "dns_query": "", "dns_server": "", "dns_type": "A", "domain_expiry_alert_days": null, "expected_status_code": null, "follow_redirects": true, "http_method": "HEAD", "interval_seconds": 60, "ip_version": "", "keep_cookies": false, "keyword": "", "keyword_case_insensitive": true, "maintenance_from": null, "maintenance_timezone": "UTC", "maintenance_to": null, "maintenance_weekdays": [ "mon", "tue", "wed", "thu", "fri", "sat", "sun" ], "monitor_type": "http", "name": "Docs site", "notification_mode": "immediate", "notify_recovery": true, "pronounceable_name": "", "proxy_host": "", "proxy_port": null, "recovery_period": 0, "regions": [ "eu" ], "request_body": "", "request_headers": [], "request_timeout_s": 30, "ssl_expiry_alert_days": null, "ssl_verification": true, "state": "", "tcp_host": "", "tcp_port": null, "url": "https://example.com" }, "raw": "", "status": "ready", "warnings": null }, { "errors": null, "index": 1, "line": 2, "normalized_input": { "alert_type": "status", "auth_password": "", "auth_password_action": "", "auth_username": "", "capture_on_incident_open": false, "confirmation_period": 0, "degraded_alert_enabled": false, "degraded_threshold_ms": null, "dns_expected": "", "dns_query": "", "dns_server": "", "dns_type": "A", "domain_expiry_alert_days": null, "expected_status_code": null, "follow_redirects": true, "http_method": "HEAD", "interval_seconds": 60, "ip_version": "", "keep_cookies": false, "keyword": "", "keyword_case_insensitive": true, "maintenance_from": null, "maintenance_timezone": "UTC", "maintenance_to": null, "maintenance_weekdays": [ "mon", "tue", "wed", "thu", "fri", "sat", "sun" ], "monitor_type": "http", "name": "Status page", "notification_mode": "immediate", "notify_recovery": true, "pronounceable_name": "", "proxy_host": "", "proxy_port": null, "recovery_period": 0, "regions": [ "eu" ], "request_body": "", "request_headers": [], "request_timeout_s": 30, "ssl_expiry_alert_days": null, "ssl_verification": true, "state": "", "tcp_host": "", "tcp_port": null, "url": "https://example.org" }, "raw": "", "status": "ready", "warnings": null } ], "summary": { "duplicates": 0, "errors": 0, "ready": 2, "total": 2, "warnings": 0 } } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 413 ```json { "error": { "code": "import_too_large", "message": "The import body is over the size limit.", "request_id": "req_…" } } ``` --- # List imports — API reference > Lists monitor import history. Source: https://upfour.io/docs/api/monitors/list-imports [Developers](https://upfour.io/docs/api.md)/[Monitors](https://upfour.io/docs/api/monitors.md) Lists monitor import history. GET https://app.upfour.io/v1/monitors/batch/history scope monitor.write stable returns 200 ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | MonitorBatchHistoryEntry[] · nullable | | | Each item · 156 fields FieldTypeDescription `created_at` string (date-time) `id` string `receipt` BatchMonitorResponse BatchMonitorResponse · 153 fields FieldTypeDescription `dry_run_id` string `rows` BatchMonitorRowResult[] · nullable Each item · 141 fields FieldTypeDescription `errors` BatchMonitorRowIssue[] · nullable Each item · 3 fields FieldTypeDescription `code` string `message` string `details` object free-form keys `index` integer `line` integer `normalized_input` ResolvedMonitorInput ResolvedMonitorInput · 56 fields FieldTypeDescription `alert_type` string one of`""` `status` `keyword` `keyword_absence` `expected_status_code` `auth_password` string Basic-auth password. Write-only: responses carry has_auth_password instead. `auth_password_action` string one of`""` `keep` `replace` `clear` `auth_username` string `confirmation_period` integer `degraded_alert_enabled` boolean `degraded_threshold_ms` integer · nullable `dns_expected` string `dns_query` string `dns_server` string `dns_type` string one of`""` `A` `AAAA` `MX` `CNAME` `TXT` `NS` `domain_expiry_alert_days` integer · nullable `expected_status_code` integer · nullable `follow_redirects` boolean · nullable `http_method` string `interval_seconds` integer `ip_version` string one of`""` `4` `6` `keep_cookies` boolean `keyword` string `keyword_case_insensitive` boolean · nullable `maintenance_from` string · nullable `maintenance_timezone` string `maintenance_to` string · nullable `maintenance_weekdays` string[] · nullable one of`mon` `tue` `wed` `thu` `fri` `sat` `sun` `monitor_type` string one of`http` `tcp` `ping` `dns` `name` string `pronounceable_name` string `proxy_host` string `proxy_port` integer · nullable `recovery_period` integer `regions` string[] · nullable one of`eu` `us` `asia` `au` `default` `request_body` string `request_headers` ResolvedRequestHeader[] · nullable Custom request headers, at most 20 and 8 KB in total. On update they are kept unless request_headers_action is replace or clear. `request_timeout_s` integer `ssl_expiry_alert_days` integer · nullable `ssl_verification` boolean · nullable `state` string one of`""` `UP` `DOWN` `PAUSED` UP, DOWN or PAUSED (any case); empty creates the monitor running. `tcp_host` string `tcp_port` integer · nullable `url` string `alert_channel_names` string[] `alert_plan_name` string `capture_on_incident_open` boolean `client_id` uuid `client_name` string `creation_key` uuid `default_alert_plan` boolean `escalation_policy_id` uuid `metadata` map of string max 100 keys each value max length 4096 On update, omitted or null leaves the stored metadata unchanged, {} removes all keys and any other object replaces them. `monitor_group_id` uuid `monitor_group_name` string `notification_channel_ids` uuid[] `notification_mode` string one of`quiet` `immediate` `notify_recovery` boolean `public_name` string `request_headers_action` string one of`keep` `replace` `clear` `raw` string `status` string one of`ready` `duplicate` `error` `created` `warnings` BatchMonitorRowIssue[] · nullable Each item · 3 fields FieldTypeDescription `code` string `message` string `details` object free-form keys `created_monitor` Monitor Monitor · 65 fields FieldTypeDescription `alert_type` string one of`status` `keyword` `keyword_absence` `expected_status_code` `auth_username` string `capture_on_incident_open` boolean `confirmation_period` integer `created_at` string (date-time) `degraded_alert_enabled` boolean `degraded_threshold_ms` integer · nullable `domain_expiry_alert_days` integer · nullable `escalation_policy_id` uuid · nullable `expected_status_code` integer · nullable `feature_pause_reasons` string[] · nullable `follow_redirects` boolean `has_auth_password` boolean `has_request_headers` boolean `http_method` string `id` uuid `interval_seconds` integer `ip_version` string one of`""` `4` `6` `is_degraded` boolean `is_pending` boolean `keep_cookies` boolean `keyword` string `keyword_case_insensitive` boolean `maintenance_from` string · nullable `maintenance_timezone` string `maintenance_to` string · nullable `maintenance_weekdays` string[] · nullable one of`mon` `tue` `wed` `thu` `fri` `sat` `sun` `monitor_type` string one of`http` `tcp` `ping` `dns` `name` string `next_check_at` string (date-time) · nullable `notification_mode` string one of`quiet` `immediate` `notify_recovery` boolean `pronounceable_name` string `proxy_host` string `proxy_port` integer · nullable `public_name` string `recovery_period` integer `regions` string[] · nullable one of`eu` `us` `asia` `au` `default` `request_body` string `request_headers` any[] · nullable Always empty: header values are never returned. has_request_headers says whether the monitor sends any. `request_timeout_s` integer `ssl_expiry_alert_days` integer · nullable `ssl_verification` boolean `state` string one of`UP` `DOWN` `PAUSED` `url` string `user_id` uuid `badge_token` string `client_id` uuid `dns_expected` string `dns_query` string `dns_server` string `dns_type` string one of`A` `AAAA` `MX` `CNAME` `TXT` `NS` `freshness_timeout_seconds` integer `last_checked_at` string (date-time) `last_result_at` string (date-time) `last_state_change_at` string (date-time) `latency_p50_ms` integer `latency_p95_ms` integer `latency_samples_24h` integer `latest_result_successful` boolean `monitor_group_id` uuid `paused_until` string (date-time) `recent_incidents` MonitorRecentIncident[] `tcp_host` string `tcp_port` integer `duplicate_match` MonitorBatchMatch MonitorBatchMatch · 5 fields FieldTypeDescription `id` uuid `monitor_type` string `name` string `url` string `client_id` uuid `summary` BatchMonitorSummary BatchMonitorSummary · 5 fields FieldTypeDescription `duplicates` integer `errors` integer `ready` integer `total` integer `warnings` integer `capacity` MonitorCapacity MonitorCapacity · 2 fields FieldTypeDescription `current` integer `limit` integer `import_id` uuid | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousPreview an import](https://upfour.io/docs/api/monitors/preview-an-import.md) [NextChange several monitors](https://upfour.io/docs/api/monitors/change-several-monitors.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/monitors/batch/history" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitors/batch/history", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/monitors/batch/history", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/monitors/batch/history", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000001", "receipt": { "capacity": { "current": 2, "limit": 500 }, "dry_run_id": "721378bc96ef2ddece4a66b447ba60d6a1266860d9cb23f4e0e6c152f30279cb", "import_id": "00000000-0000-4000-8000-000000000001", "rows": [ { "created_monitor": { "alert_type": "status", "auth_username": "", "capture_on_incident_open": false, "confirmation_period": 0, "created_at": "2026-10-27T09:00:00Z", "degraded_alert_enabled": false, "degraded_threshold_ms": null, "dns_type": "A", "domain_expiry_alert_days": null, "escalation_policy_id": null, "expected_status_code": null, "feature_pause_reasons": null, "follow_redirects": true, "freshness_timeout_seconds": 120, "has_auth_password": false, "has_request_headers": false, "http_method": "HEAD", "id": "00000000-0000-4000-8000-000000000002", "interval_seconds": 60, "ip_version": "", "is_degraded": false, "is_pending": true, "keep_cookies": false, "keyword": "", "keyword_case_insensitive": true, "maintenance_from": null, "maintenance_timezone": "UTC", "maintenance_to": null, "maintenance_weekdays": [ "mon", "tue", "wed", "thu", "fri", "sat", "sun" ], "monitor_type": "http", "name": "Docs site", "next_check_at": "2026-10-27T09:00:00Z", "notification_mode": "immediate", "notify_recovery": true, "pronounceable_name": "", "proxy_host": "", "proxy_port": null, "public_name": "", "recovery_period": 0, "regions": [ "eu" ], "request_body": "", "request_headers": [], "request_timeout_s": 30, "ssl_expiry_alert_days": null, "ssl_verification": true, "state": "UP", "url": "https://example.com", "user_id": "00000000-0000-4000-8000-000000000003" }, "errors": null, "index": 0, "line": 1, "normalized_input": { "alert_type": "status", "auth_password": "", "auth_password_action": "", "auth_username": "", "capture_on_incident_open": false, "confirmation_period": 0, "degraded_alert_enabled": false, "degraded_threshold_ms": null, "dns_expected": "", "dns_query": "", "dns_server": "", "dns_type": "A", "domain_expiry_alert_days": null, "expected_status_code": null, "follow_redirects": true, "http_method": "HEAD", "interval_seconds": 60, "ip_version": "", "keep_cookies": false, "keyword": "", "keyword_case_insensitive": true, "maintenance_from": null, "maintenance_timezone": "UTC", "maintenance_to": null, "maintenance_weekdays": [ "mon", "tue", "wed", "thu", "fri", "sat", "sun" ], "monitor_type": "http", "name": "Docs site", "notification_mode": "immediate", "notify_recovery": true, "pronounceable_name": "", "proxy_host": "", "proxy_port": null, "recovery_period": 0, "regions": [ "eu" ], "request_body": "", "request_headers": [], "request_timeout_s": 30, "ssl_expiry_alert_days": null, "ssl_verification": true, "state": "UP", "tcp_host": "", "tcp_port": null, "url": "https://example.com" }, "raw": "", "status": "created", "warnings": null }, { "created_monitor": { "alert_type": "status", "auth_username": "", "capture_on_incident_open": false, "confirmation_period": 0, "created_at": "2026-10-27T09:00:00Z", "degraded_alert_enabled": false, "degraded_threshold_ms": null, "dns_type": "A", "domain_expiry_alert_days": null, "escalation_policy_id": null, "expected_status_code": null, "feature_pause_reasons": null, "follow_redirects": true, "freshness_timeout_seconds": 120, "has_auth_password": false, "has_request_headers": false, "http_method": "HEAD", "id": "00000000-0000-4000-8000-000000000004", "interval_seconds": 60, "ip_version": "", "is_degraded": false, "is_pending": true, "keep_cookies": false, "keyword": "", "keyword_case_insensitive": true, "maintenance_from": null, "maintenance_timezone": "UTC", "maintenance_to": null, "maintenance_weekdays": [ "mon", "tue", "wed", "thu", "fri", "sat", "sun" ], "monitor_type": "http", "name": "Status page", "next_check_at": "2026-10-27T09:00:00Z", "notification_mode": "immediate", "notify_recovery": true, "pronounceable_name": "", "proxy_host": "", "proxy_port": null, "public_name": "", "recovery_period": 0, "regions": [ "eu" ], "request_body": "", "request_headers": [], "request_timeout_s": 30, "ssl_expiry_alert_days": null, "ssl_verification": true, "state": "UP", "url": "https://example.org", "user_id": "00000000-0000-4000-8000-000000000003" }, "errors": null, "index": 1, "line": 2, "normalized_input": { "alert_type": "status", "auth_password": "", "auth_password_action": "", "auth_username": "", "capture_on_incident_open": false, "confirmation_period": 0, "degraded_alert_enabled": false, "degraded_threshold_ms": null, "dns_expected": "", "dns_query": "", "dns_server": "", "dns_type": "A", "domain_expiry_alert_days": null, "expected_status_code": null, "follow_redirects": true, "http_method": "HEAD", "interval_seconds": 60, "ip_version": "", "keep_cookies": false, "keyword": "", "keyword_case_insensitive": true, "maintenance_from": null, "maintenance_timezone": "UTC", "maintenance_to": null, "maintenance_weekdays": [ "mon", "tue", "wed", "thu", "fri", "sat", "sun" ], "monitor_type": "http", "name": "Status page", "notification_mode": "immediate", "notify_recovery": true, "pronounceable_name": "", "proxy_host": "", "proxy_port": null, "recovery_period": 0, "regions": [ "eu" ], "request_body": "", "request_headers": [], "request_timeout_s": 30, "ssl_expiry_alert_days": null, "ssl_verification": true, "state": "UP", "tcp_host": "", "tcp_port": null, "url": "https://example.org" }, "raw": "", "status": "created", "warnings": null } ], "summary": { "duplicates": 0, "errors": 0, "ready": 2, "total": 2, "warnings": 0 } } } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Change several monitors — API reference > Changes several monitors in one request. Source: https://upfour.io/docs/api/monitors/change-several-monitors [Developers](https://upfour.io/docs/api.md)/[Monitors](https://upfour.io/docs/api/monitors.md) Changes several monitors in one request. POST https://app.upfour.io/v1/monitors/bulk scope monitor.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Body `application/json` required 10 fields actionstring (required) pauseresumeintervalregionsclientgroupplanchannels idsuuid[] (required) All other fields8 fields channel_idsuuid[] channel_modestring addreplaceremove clear_client_settingsboolean client_iduuid · nullable group_iduuid · nullable interval_secondsinteger plan_iduuid · nullable regionsstring[] euusasiaaudefault ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | MonitorBulkResponse | | | MonitorBulkResponse · 8 fields FieldTypeDescription `failed` integer `results` MonitorBulkResult[] · nullable Each item · 5 fields FieldTypeDescription `id` uuid `success` boolean `code` string `error` string `name` string `updated` integer | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList imports](https://upfour.io/docs/api/monitors/list-imports.md) [NextExport monitors](https://upfour.io/docs/api/monitors/export-monitors.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/monitors/bulk" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "action": "interval", "ids": [ "00000000-0000-4000-8000-000000000001" ], "interval_seconds": 600 }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitors/bulk", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ action: "interval", ids: ["00000000-0000-4000-8000-000000000001"], interval_seconds: 600, }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/monitors/bulk", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "action": "interval", "ids": ["00000000-0000-4000-8000-000000000001"], "interval_seconds": 600, }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "action": "interval", "ids": [ "00000000-0000-4000-8000-000000000001" ], "interval_seconds": 600 }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/monitors/bulk", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "failed": 0, "results": [ { "id": "00000000-0000-4000-8000-000000000001", "name": "Staging API", "success": true } ], "updated": 1 } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Export monitors — API reference > Exports details for selected monitors. Source: https://upfour.io/docs/api/monitors/export-monitors [Developers](https://upfour.io/docs/api.md)/[Monitors](https://upfour.io/docs/api/monitors.md) Exports details for selected monitors. POST https://app.upfour.io/v1/monitors/export-details scope monitor.read stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Body `application/json` required 1 field All other fields1 fields monitor_idsuuid[] ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | MonitorExportDetails[] · nullable | | | Each item · 6 fields FieldTypeDescription `channels` MonitorExportChannel[] · nullable Each item · 3 fields FieldTypeDescription `id` uuid `name` string `type` string `metadata` map of string · nullable max 100 keys each value max length 4096 Custom key/value pairs (at most 100 keys; keys 1-128 characters, values up to 4096). Sent to machine-readable alert channels and API webhook subscriptions, and copied onto the incidents the resource opens. `monitor_id` uuid | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousChange several monitors](https://upfour.io/docs/api/monitors/change-several-monitors.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/monitors/export-details" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "monitor_ids": [ "00000000-0000-4000-8000-000000000001" ] }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitors/export-details", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ monitor_ids: ["00000000-0000-4000-8000-000000000001"], }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/monitors/export-details", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "monitor_ids": ["00000000-0000-4000-8000-000000000001"], }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "monitor_ids": [ "00000000-0000-4000-8000-000000000001" ] }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/monitors/export-details", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "channels": [], "metadata": {}, "monitor_id": "00000000-0000-4000-8000-000000000001" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` --- # Monitor groups — API reference > Folders for monitors — group fleets per project or per customer. REST endpoints with request and response fields, examples in curl, JavaScript, Python, Go and PHP, and an in-browser Try it console. Source: https://upfour.io/docs/api/monitor-groups [Developers](https://upfour.io/docs/api.md)/Monitoring Folders for monitors — group fleets per project or per customer. Base URL `https://app.upfour.io/v1`. Send your key as `Authorization: Bearer `; every key acts on your real Company, so [test without paging anyone](https://upfour.io/docs/api/testing.md). New here? Start with the [quickstart](https://upfour.io/docs/api/quickstart.md). The examples read the key from `UP4_API_KEY`. ## Everyday [GET **List monitor groups** /monitor-groups](https://upfour.io/docs/api/monitor-groups/list-monitor-groups.md)[POST **Create a monitor group** /monitor-groups](https://upfour.io/docs/api/monitor-groups/create-a-monitor-group.md)[GET **Get a monitor group** /monitor-groups/{groupID}](https://upfour.io/docs/api/monitor-groups/get-a-monitor-group.md)[PUT **Replace a monitor group** /monitor-groups/{groupID}](https://upfour.io/docs/api/monitor-groups/replace-a-monitor-group.md) ## Control [POST **Assign resources to a monitor group** /monitor-groups/assign](https://upfour.io/docs/api/monitor-groups/assign-resources-to-a-monitor-group.md)[DEL **Delete a monitor group** /monitor-groups/{groupID}](https://upfour.io/docs/api/monitor-groups/delete-a-monitor-group.md) --- # List monitor groups — API reference > Returns group names, positions and resource counts. Source: https://upfour.io/docs/api/monitor-groups/list-monitor-groups [Developers](https://upfour.io/docs/api.md)/[Monitor groups](https://upfour.io/docs/api/monitor-groups.md) Returns group names, positions and resource counts. GET https://app.upfour.io/v1/monitor-groups scope monitor.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `limit` optional | integer 1–250 default `50` | query parameter | | `cursor` optional | string | query parameter | | `client_id` optional | uuid | query parameter | | `all_clients` optional | boolean | query parameter | | `q` optional | string | query parameter | | `sort` optional | string | query parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ResourceGroup[] · nullable | | | Each item · 9 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `kind` string one of`monitor` `heartbeat` `name` string `organization_id` uuid `position` integer `resource_count` integer `updated_at` string (date-time) `client_id` uuid | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [NextCreate a monitor group](https://upfour.io/docs/api/monitor-groups/create-a-monitor-group.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/monitor-groups" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitor-groups", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/monitor-groups", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/monitor-groups", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000001", "kind": "monitor", "name": "Production", "organization_id": "00000000-0000-4000-8000-000000000002", "position": 0, "resource_count": 0, "updated_at": "2026-10-27T09:00:00Z" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` --- # Create a monitor group — API reference > Stores a group name, position and optional client association. Source: https://upfour.io/docs/api/monitor-groups/create-a-monitor-group [Developers](https://upfour.io/docs/api.md)/[Monitor groups](https://upfour.io/docs/api/monitor-groups.md) Stores a group name, position and optional client association. POST https://app.upfour.io/v1/monitor-groups scope monitor.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Body `application/json` required 3 fields namestring (required) All other fields2 fields client_iduuid · nullable positioninteger ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ResourceGroup | | | ResourceGroup · 9 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `kind` string one of`monitor` `heartbeat` `name` string `organization_id` uuid `position` integer `resource_count` integer `updated_at` string (date-time) `client_id` uuid | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList monitor groups](https://upfour.io/docs/api/monitor-groups/list-monitor-groups.md) [NextGet a monitor group](https://upfour.io/docs/api/monitor-groups/get-a-monitor-group.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/monitor-groups" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Production" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitor-groups", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "Production", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/monitor-groups", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "name": "Production", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "name": "Production" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/monitor-groups", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000001", "kind": "monitor", "name": "Production", "organization_id": "00000000-0000-4000-8000-000000000002", "position": 0, "resource_count": 0, "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Get a monitor group — API reference > Returns a group's name, position, resource count and client association. Source: https://upfour.io/docs/api/monitor-groups/get-a-monitor-group [Developers](https://upfour.io/docs/api.md)/[Monitor groups](https://upfour.io/docs/api/monitor-groups.md) Returns a group's name, position, resource count and client association. GET https://app.upfour.io/v1/monitor-groups/{groupID} scope monitor.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `groupID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ResourceGroup | | | ResourceGroup · 9 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `kind` string one of`monitor` `heartbeat` `name` string `organization_id` uuid `position` integer `resource_count` integer `updated_at` string (date-time) `client_id` uuid | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousCreate a monitor group](https://upfour.io/docs/api/monitor-groups/create-a-monitor-group.md) [NextReplace a monitor group](https://upfour.io/docs/api/monitor-groups/replace-a-monitor-group.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/monitor-groups/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitor-groups/00000000-0000-4000-8000-000000000001", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/monitor-groups/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/monitor-groups/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000001", "kind": "monitor", "name": "Production", "organization_id": "00000000-0000-4000-8000-000000000002", "position": 0, "resource_count": 0, "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Replace a monitor group — API reference > Changes a group name, position or client association. Source: https://upfour.io/docs/api/monitor-groups/replace-a-monitor-group [Developers](https://upfour.io/docs/api.md)/[Monitor groups](https://upfour.io/docs/api/monitor-groups.md) Changes a group name, position or client association. PUT https://app.upfour.io/v1/monitor-groups/{groupID} scope monitor.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `groupID` required | uuid | path parameter | ## Body `application/json` required 3 fields namestring (required) All other fields2 fields client_iduuid · nullable positioninteger ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ResourceGroup | | | ResourceGroup · 9 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `kind` string one of`monitor` `heartbeat` `name` string `organization_id` uuid `position` integer `resource_count` integer `updated_at` string (date-time) `client_id` uuid | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet a monitor group](https://upfour.io/docs/api/monitor-groups/get-a-monitor-group.md) [NextAssign resources to a monitor group](https://upfour.io/docs/api/monitor-groups/assign-resources-to-a-monitor-group.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/monitor-groups/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Production sites" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitor-groups/00000000-0000-4000-8000-000000000001", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "Production sites", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/monitor-groups/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "name": "Production sites", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "name": "Production sites" }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/monitor-groups/00000000-0000-4000-8000-000000000001", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000001", "kind": "monitor", "name": "Production sites", "organization_id": "00000000-0000-4000-8000-000000000002", "position": 0, "resource_count": 0, "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Assign resources to a monitor group — API reference > Moves resource IDs to a target group or removes their group membership. Source: https://upfour.io/docs/api/monitor-groups/assign-resources-to-a-monitor-group [Developers](https://upfour.io/docs/api.md)/[Monitor groups](https://upfour.io/docs/api/monitor-groups.md) Moves resource IDs to a target group or removes their group membership. POST https://app.upfour.io/v1/monitor-groups/assign scope monitor.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Body `application/json` required 5 fields All other fields5 fields heartbeat_group_iduuid · nullable Same as monitor_group_id. heartbeat_idsuuid[] Same as resource_ids on /heartbeat-groups/assign. monitor_group_iduuid · nullable Target group; null or empty removes the resources from their group. heartbeat_group_id is accepted instead. monitor_idsuuid[] Same as resource_ids on /monitor-groups/assign. resource_idsuuid[] The monitors or scheduled jobs to move, at least one. monitor_ids and heartbeat_ids are accepted instead. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ResourceGroupAssignResponse | | | ResourceGroupAssignResponse · 1 fields FieldTypeDescription `updated` integer | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousReplace a monitor group](https://upfour.io/docs/api/monitor-groups/replace-a-monitor-group.md) [NextDelete a monitor group](https://upfour.io/docs/api/monitor-groups/delete-a-monitor-group.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/monitor-groups/assign" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "monitor_group_id": "00000000-0000-4000-8000-000000000001", "monitor_ids": [ "00000000-0000-4000-8000-000000000002" ] }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitor-groups/assign", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ monitor_group_id: "00000000-0000-4000-8000-000000000001", monitor_ids: ["00000000-0000-4000-8000-000000000002"], }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/monitor-groups/assign", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "monitor_group_id": "00000000-0000-4000-8000-000000000001", "monitor_ids": ["00000000-0000-4000-8000-000000000002"], }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "monitor_group_id": "00000000-0000-4000-8000-000000000001", "monitor_ids": [ "00000000-0000-4000-8000-000000000002" ] }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/monitor-groups/assign", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "updated": 1 } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Delete a monitor group — API reference > The endpoint returns status 204 with no response body. Source: https://upfour.io/docs/api/monitor-groups/delete-a-monitor-group [Developers](https://upfour.io/docs/api.md)/[Monitor groups](https://upfour.io/docs/api/monitor-groups.md) The endpoint returns status 204 with no response body. DELETE https://app.upfour.io/v1/monitor-groups/{groupID} scope monitor.delete stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `groupID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousAssign resources to a monitor group](https://upfour.io/docs/api/monitor-groups/assign-resources-to-a-monitor-group.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/monitor-groups/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/monitor-groups/00000000-0000-4000-8000-000000000001", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/monitor-groups/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/monitor-groups/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Scheduled jobs (heartbeats) — API reference > Cron jobs and backups that must call in before a deadline. Silence becomes an incident. REST endpoints with request and response fields, examples in curl, JavaScript, Python, Go and PHP, and an in-browser Try it console. Source: https://upfour.io/docs/api/heartbeats [Developers](https://upfour.io/docs/api.md)/Monitoring Cron jobs and backups that must call in before a deadline. Silence becomes an incident. Base URL `https://app.upfour.io/v1`. Send your key as `Authorization: Bearer `; every key acts on your real Company, so [test without paging anyone](https://upfour.io/docs/api/testing.md). New here? Start with the [quickstart](https://upfour.io/docs/api/quickstart.md). The examples read the key from `UP4_API_KEY`. ## Everyday [GET **List scheduled jobs** /heartbeats](https://upfour.io/docs/api/heartbeats/list-scheduled-jobs.md)[POST **Create a scheduled job** /heartbeats](https://upfour.io/docs/api/heartbeats/create-a-scheduled-job.md)[GET **Get a scheduled job** /heartbeats/{heartbeatID}](https://upfour.io/docs/api/heartbeats/get-a-scheduled-job.md)[PATCH **Update a scheduled job** /heartbeats/{heartbeatID}](https://upfour.io/docs/api/heartbeats/update-a-scheduled-job.md)[PUT **Replace a scheduled job** /heartbeats/{heartbeatID}](https://upfour.io/docs/api/heartbeats/replace-a-scheduled-job.md) ## Control [POST **Pause a scheduled job** /heartbeats/{heartbeatID}/pause](https://upfour.io/docs/api/heartbeats/pause-a-scheduled-job.md)[PUT **Pause a scheduled job using PUT** /heartbeats/{heartbeatID}/pause](https://upfour.io/docs/api/heartbeats/pause-a-scheduled-job-using-put.md)[POST **Resume a scheduled job** /heartbeats/{heartbeatID}/resume](https://upfour.io/docs/api/heartbeats/resume-a-scheduled-job.md)[PUT **Resume a scheduled job using PUT** /heartbeats/{heartbeatID}/resume](https://upfour.io/docs/api/heartbeats/resume-a-scheduled-job-using-put.md)[DEL **Delete a scheduled job** /heartbeats/{heartbeatID}](https://upfour.io/docs/api/heartbeats/delete-a-scheduled-job.md) ## Results and history [GET **Get scheduled job availability** /heartbeats/{heartbeatID}/availability](https://upfour.io/docs/api/heartbeats/get-scheduled-job-availability.md)[GET **List scheduled job pings** /heartbeats/{heartbeatID}/pings](https://upfour.io/docs/api/heartbeats/list-scheduled-job-pings.md) --- # List scheduled jobs — API reference > Returns job names, statuses, timing and billing-pause details when present, with pagination. Source: https://upfour.io/docs/api/heartbeats/list-scheduled-jobs [Developers](https://upfour.io/docs/api.md)/[Scheduled jobs (heartbeats)](https://upfour.io/docs/api/heartbeats.md) Returns job names, statuses, timing and billing-pause details when present, with pagination. GET https://app.upfour.io/v1/heartbeats scope heartbeat.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `limit` optional | integer 1–250 default `50` | query parameter | | `cursor` optional | string | query parameter | | `client_id` optional | uuid | query parameter | | `all_clients` optional | boolean | query parameter | | `q` optional | string | query parameter | | `sort` optional | string | query parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | Heartbeat[] · nullable | | | Each item · 26 fields FieldTypeDescription `created_at` string (date-time) `expected_interval_seconds` integer `first_ping_at` string (date-time) · nullable `grace_period_seconds` integer `has_signing_secret` boolean `id` uuid `last_outcome_cause` string `last_ping_at` string (date-time) · nullable `name` string `organization_id` uuid `slug` string `status` string one of`up` `down` `paused` `pending` `updated_at` string (date-time) `billing_paused_at` string (date-time) `billing_pauses` BillingPause[] Each item · 2 fields FieldTypeDescription `paused_at` string (date-time) · nullable `resumed_at` string (date-time) · nullable `billing_resumed_at` string (date-time) `client_id` uuid `flapping` HeartbeatFlap HeartbeatFlap · 3 fields FieldTypeDescription `changes_last_hour` integer `next_notice_at` string (date-time) `since` string (date-time) `heartbeat_group_id` uuid `ping_url` string `signing_secret` string Only in the answer to the create or update that set it; has_signing_secret says whether one is set. | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [NextCreate a scheduled job](https://upfour.io/docs/api/heartbeats/create-a-scheduled-job.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/heartbeats" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/heartbeats", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/heartbeats", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/heartbeats", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "created_at": "2026-10-27T09:00:00Z", "expected_interval_seconds": 86400, "first_ping_at": "2026-10-27T09:00:00Z", "grace_period_seconds": 900, "has_signing_secret": false, "id": "00000000-0000-4000-8000-000000000001", "last_outcome_cause": "reported_success", "last_ping_at": "2026-10-27T09:00:00Z", "name": "Nightly backup", "organization_id": "00000000-0000-4000-8000-000000000002", "slug": "721378bc96ef2ddece4a66b447ba60d6", "status": "up", "updated_at": "2026-10-27T09:00:00Z" }, { "created_at": "2026-10-27T09:00:00Z", "expected_interval_seconds": 604800, "first_ping_at": null, "grace_period_seconds": 3600, "has_signing_secret": false, "id": "00000000-0000-4000-8000-000000000003", "last_outcome_cause": "unknown", "last_ping_at": null, "name": "Weekly export", "organization_id": "00000000-0000-4000-8000-000000000002", "slug": "3c8dc35de8b20338c83d488ff7e387f6", "status": "pending", "updated_at": "2026-10-27T09:00:00Z" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` --- # Create a scheduled job — API reference > Stores name, expected interval, grace period, signing, group, client and metadata settings. Source: https://upfour.io/docs/api/heartbeats/create-a-scheduled-job [Developers](https://upfour.io/docs/api.md)/[Scheduled jobs (heartbeats)](https://upfour.io/docs/api/heartbeats.md) Stores name, expected interval, grace period, signing, group, client and metadata settings. POST https://app.upfour.io/v1/heartbeats scope heartbeat.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Body `application/json` required 8 fields namestring (required) Display name In the app: **Name** All other fields7 fields client_iduuid · nullable Create inside a client (agency mode) In the app: **Client** enable_signingboolean expected_interval_secondsinteger How often the job should check in In the app: **Expect a check-in every** grace_period_secondsinteger Slack on top before the silence alerts In the app: **with a grace period of** heartbeat_group_iduuid · nullable Optional folder In the app: **Group** metadatamap of stringmax 100 keyseach value max length 4096 On update, omitted or null leaves the stored metadata unchanged, {} removes all keys and any other object replaces them. In the app: **Metadata** signing_secretstring · nullable With enable_signing and no secret, up4 generates one. ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | Heartbeat | | | Heartbeat · 26 fields FieldTypeDescription `created_at` string (date-time) `expected_interval_seconds` integer `first_ping_at` string (date-time) · nullable `grace_period_seconds` integer `has_signing_secret` boolean `id` uuid `last_outcome_cause` string `last_ping_at` string (date-time) · nullable `name` string `organization_id` uuid `slug` string `status` string one of`up` `down` `paused` `pending` `updated_at` string (date-time) `billing_paused_at` string (date-time) `billing_pauses` BillingPause[] Each item · 2 fields FieldTypeDescription `paused_at` string (date-time) · nullable `resumed_at` string (date-time) · nullable `billing_resumed_at` string (date-time) `client_id` uuid `flapping` HeartbeatFlap HeartbeatFlap · 3 fields FieldTypeDescription `changes_last_hour` integer `next_notice_at` string (date-time) `since` string (date-time) `heartbeat_group_id` uuid `ping_url` string `signing_secret` string Only in the answer to the create or update that set it; has_signing_secret says whether one is set. | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 402 | [payment_required](https://upfour.io/docs/api/errors#code-payment_required) | The account does not have the credit or plan required for this action. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList scheduled jobs](https://upfour.io/docs/api/heartbeats/list-scheduled-jobs.md) [NextGet a scheduled job](https://upfour.io/docs/api/heartbeats/get-a-scheduled-job.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/heartbeats" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "expected_interval_seconds": 86400, "grace_period_seconds": 900, "name": "Nightly backup" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/heartbeats", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ expected_interval_seconds: 86400, grace_period_seconds: 900, name: "Nightly backup", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/heartbeats", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "expected_interval_seconds": 86400, "grace_period_seconds": 900, "name": "Nightly backup", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "expected_interval_seconds": 86400, "grace_period_seconds": 900, "name": "Nightly backup" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/heartbeats", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "expected_interval_seconds": 86400, "first_ping_at": null, "grace_period_seconds": 900, "has_signing_secret": false, "id": "00000000-0000-4000-8000-000000000001", "last_outcome_cause": "unknown", "last_ping_at": null, "name": "Nightly backup", "organization_id": "00000000-0000-4000-8000-000000000002", "slug": "721378bc96ef2ddece4a66b447ba60d6", "status": "pending", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "payment_required", "message": "The account does not have the credit or plan required for this action.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Get a scheduled job — API reference > Returns status, timing, signing and billing-pause details when present. Source: https://upfour.io/docs/api/heartbeats/get-a-scheduled-job [Developers](https://upfour.io/docs/api.md)/[Scheduled jobs (heartbeats)](https://upfour.io/docs/api/heartbeats.md) Returns status, timing, signing and billing-pause details when present. GET https://app.upfour.io/v1/heartbeats/{heartbeatID} scope heartbeat.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `heartbeatID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | Heartbeat | | | Heartbeat · 26 fields FieldTypeDescription `created_at` string (date-time) `expected_interval_seconds` integer `first_ping_at` string (date-time) · nullable `grace_period_seconds` integer `has_signing_secret` boolean `id` uuid `last_outcome_cause` string `last_ping_at` string (date-time) · nullable `name` string `organization_id` uuid `slug` string `status` string one of`up` `down` `paused` `pending` `updated_at` string (date-time) `billing_paused_at` string (date-time) `billing_pauses` BillingPause[] Each item · 2 fields FieldTypeDescription `paused_at` string (date-time) · nullable `resumed_at` string (date-time) · nullable `billing_resumed_at` string (date-time) `client_id` uuid `flapping` HeartbeatFlap HeartbeatFlap · 3 fields FieldTypeDescription `changes_last_hour` integer `next_notice_at` string (date-time) `since` string (date-time) `heartbeat_group_id` uuid `ping_url` string `signing_secret` string Only in the answer to the create or update that set it; has_signing_secret says whether one is set. | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousCreate a scheduled job](https://upfour.io/docs/api/heartbeats/create-a-scheduled-job.md) [NextUpdate a scheduled job](https://upfour.io/docs/api/heartbeats/update-a-scheduled-job.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "expected_interval_seconds": 86400, "first_ping_at": "2026-10-27T09:00:00Z", "grace_period_seconds": 900, "has_signing_secret": false, "id": "00000000-0000-4000-8000-000000000001", "last_outcome_cause": "reported_success", "last_ping_at": "2026-10-27T09:00:00Z", "name": "Nightly backup", "organization_id": "00000000-0000-4000-8000-000000000002", "slug": "721378bc96ef2ddece4a66b447ba60d6", "status": "up", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Update a scheduled job — API reference > Changes name, timing, signing, group, client or metadata settings. Source: https://upfour.io/docs/api/heartbeats/update-a-scheduled-job [Developers](https://upfour.io/docs/api.md)/[Scheduled jobs (heartbeats)](https://upfour.io/docs/api/heartbeats.md) Changes name, timing, signing, group, client or metadata settings. PATCH https://app.upfour.io/v1/heartbeats/{heartbeatID} scope heartbeat.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `heartbeatID` required | uuid | path parameter | ## Body `application/json` required 8 fields namestring (required) In the app: **Name** All other fields7 fields client_iduuid · nullable In the app: **Client** enable_signingboolean expected_interval_secondsinteger In the app: **Expect a check-in every** grace_period_secondsinteger In the app: **with a grace period of** heartbeat_group_iduuid · nullable In the app: **Group** metadatamap of stringmax 100 keyseach value max length 4096 On update, omitted or null leaves the stored metadata unchanged, {} removes all keys and any other object replaces them. In the app: **Metadata** signing_secretstring · nullable With enable_signing and no secret, up4 generates one. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | Heartbeat | | | Heartbeat · 26 fields FieldTypeDescription `created_at` string (date-time) `expected_interval_seconds` integer `first_ping_at` string (date-time) · nullable `grace_period_seconds` integer `has_signing_secret` boolean `id` uuid `last_outcome_cause` string `last_ping_at` string (date-time) · nullable `name` string `organization_id` uuid `slug` string `status` string one of`up` `down` `paused` `pending` `updated_at` string (date-time) `billing_paused_at` string (date-time) `billing_pauses` BillingPause[] Each item · 2 fields FieldTypeDescription `paused_at` string (date-time) · nullable `resumed_at` string (date-time) · nullable `billing_resumed_at` string (date-time) `client_id` uuid `flapping` HeartbeatFlap HeartbeatFlap · 3 fields FieldTypeDescription `changes_last_hour` integer `next_notice_at` string (date-time) `since` string (date-time) `heartbeat_group_id` uuid `ping_url` string `signing_secret` string Only in the answer to the create or update that set it; has_signing_secret says whether one is set. | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet a scheduled job](https://upfour.io/docs/api/heartbeats/get-a-scheduled-job.md) [NextReplace a scheduled job](https://upfour.io/docs/api/heartbeats/replace-a-scheduled-job.md) ## Request ### cURL ```bash curl -s -X PATCH "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Nightly database backup" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001", { method: "PATCH", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "Nightly database backup", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.patch( "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "name": "Nightly database backup", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "name": "Nightly database backup" }`) req, _ := http.NewRequest("PATCH", "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PATCH", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "expected_interval_seconds": 300, "first_ping_at": "2026-10-27T09:00:00Z", "grace_period_seconds": 60, "has_signing_secret": false, "id": "00000000-0000-4000-8000-000000000001", "last_outcome_cause": "reported_success", "last_ping_at": "2026-10-27T09:00:00Z", "name": "Nightly database backup", "organization_id": "00000000-0000-4000-8000-000000000002", "slug": "721378bc96ef2ddece4a66b447ba60d6", "status": "up", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Replace a scheduled job — API reference > Changes name, timing, signing, group, client or metadata settings. Source: https://upfour.io/docs/api/heartbeats/replace-a-scheduled-job [Developers](https://upfour.io/docs/api.md)/[Scheduled jobs (heartbeats)](https://upfour.io/docs/api/heartbeats.md) Changes name, timing, signing, group, client or metadata settings. PUT https://app.upfour.io/v1/heartbeats/{heartbeatID} scope heartbeat.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `heartbeatID` required | uuid | path parameter | ## Body `application/json` required 8 fields namestring (required) All other fields7 fields client_iduuid · nullable enable_signingboolean expected_interval_secondsinteger grace_period_secondsinteger heartbeat_group_iduuid · nullable metadatamap of stringmax 100 keyseach value max length 4096 On update, omitted or null leaves the stored metadata unchanged, {} removes all keys and any other object replaces them. signing_secretstring · nullable With enable_signing and no secret, up4 generates one. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | Heartbeat | | | Heartbeat · 26 fields FieldTypeDescription `created_at` string (date-time) `expected_interval_seconds` integer `first_ping_at` string (date-time) · nullable `grace_period_seconds` integer `has_signing_secret` boolean `id` uuid `last_outcome_cause` string `last_ping_at` string (date-time) · nullable `name` string `organization_id` uuid `slug` string `status` string one of`up` `down` `paused` `pending` `updated_at` string (date-time) `billing_paused_at` string (date-time) `billing_pauses` BillingPause[] Each item · 2 fields FieldTypeDescription `paused_at` string (date-time) · nullable `resumed_at` string (date-time) · nullable `billing_resumed_at` string (date-time) `client_id` uuid `flapping` HeartbeatFlap HeartbeatFlap · 3 fields FieldTypeDescription `changes_last_hour` integer `next_notice_at` string (date-time) `since` string (date-time) `heartbeat_group_id` uuid `ping_url` string `signing_secret` string Only in the answer to the create or update that set it; has_signing_secret says whether one is set. | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpdate a scheduled job](https://upfour.io/docs/api/heartbeats/update-a-scheduled-job.md) [NextPause a scheduled job](https://upfour.io/docs/api/heartbeats/pause-a-scheduled-job.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "expected_interval_seconds": 86400, "grace_period_seconds": 1800, "name": "Nightly backup" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ expected_interval_seconds: 86400, grace_period_seconds: 1800, name: "Nightly backup", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "expected_interval_seconds": 86400, "grace_period_seconds": 1800, "name": "Nightly backup", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "expected_interval_seconds": 86400, "grace_period_seconds": 1800, "name": "Nightly backup" }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "expected_interval_seconds": 86400, "first_ping_at": "2026-10-27T09:00:00Z", "grace_period_seconds": 1800, "has_signing_secret": false, "id": "00000000-0000-4000-8000-000000000001", "last_outcome_cause": "reported_success", "last_ping_at": "2026-10-27T09:00:00Z", "name": "Nightly backup", "organization_id": "00000000-0000-4000-8000-000000000002", "slug": "721378bc96ef2ddece4a66b447ba60d6", "status": "up", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Pause a scheduled job — API reference > Returns the resulting status. Source: https://upfour.io/docs/api/heartbeats/pause-a-scheduled-job [Developers](https://upfour.io/docs/api.md)/[Scheduled jobs (heartbeats)](https://upfour.io/docs/api/heartbeats.md) Returns the resulting status. POST https://app.upfour.io/v1/heartbeats/{heartbeatID}/pause scope heartbeat.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `heartbeatID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | HeartbeatStatusResponse | | | HeartbeatStatusResponse · 1 fields FieldTypeDescription `status` string one of`up` `down` `paused` `pending` | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousReplace a scheduled job](https://upfour.io/docs/api/heartbeats/replace-a-scheduled-job.md) [NextPause a scheduled job using PUT](https://upfour.io/docs/api/heartbeats/pause-a-scheduled-job-using-put.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001/pause" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001/pause", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001/pause", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001/pause", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "status": "paused" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Pause a scheduled job using PUT — API reference > Returns the resulting status. Source: https://upfour.io/docs/api/heartbeats/pause-a-scheduled-job-using-put [Developers](https://upfour.io/docs/api.md)/[Scheduled jobs (heartbeats)](https://upfour.io/docs/api/heartbeats.md) Returns the resulting status. PUT https://app.upfour.io/v1/heartbeats/{heartbeatID}/pause scope heartbeat.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `heartbeatID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | HeartbeatStatusResponse | | | HeartbeatStatusResponse · 1 fields FieldTypeDescription `status` string one of`up` `down` `paused` `pending` | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousPause a scheduled job](https://upfour.io/docs/api/heartbeats/pause-a-scheduled-job.md) [NextResume a scheduled job](https://upfour.io/docs/api/heartbeats/resume-a-scheduled-job.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001/pause" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001/pause", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001/pause", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001/pause", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "status": "paused" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Resume a scheduled job — API reference > Returns the resulting status. Source: https://upfour.io/docs/api/heartbeats/resume-a-scheduled-job [Developers](https://upfour.io/docs/api.md)/[Scheduled jobs (heartbeats)](https://upfour.io/docs/api/heartbeats.md) Returns the resulting status. POST https://app.upfour.io/v1/heartbeats/{heartbeatID}/resume scope heartbeat.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `heartbeatID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | HeartbeatStatusResponse | | | HeartbeatStatusResponse · 1 fields FieldTypeDescription `status` string one of`up` `down` `paused` `pending` | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousPause a scheduled job using PUT](https://upfour.io/docs/api/heartbeats/pause-a-scheduled-job-using-put.md) [NextResume a scheduled job using PUT](https://upfour.io/docs/api/heartbeats/resume-a-scheduled-job-using-put.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001/resume" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001/resume", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001/resume", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001/resume", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "status": "up" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Resume a scheduled job using PUT — API reference > Returns the resulting status. Source: https://upfour.io/docs/api/heartbeats/resume-a-scheduled-job-using-put [Developers](https://upfour.io/docs/api.md)/[Scheduled jobs (heartbeats)](https://upfour.io/docs/api/heartbeats.md) Returns the resulting status. PUT https://app.upfour.io/v1/heartbeats/{heartbeatID}/resume scope heartbeat.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `heartbeatID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | HeartbeatStatusResponse | | | HeartbeatStatusResponse · 1 fields FieldTypeDescription `status` string one of`up` `down` `paused` `pending` | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousResume a scheduled job](https://upfour.io/docs/api/heartbeats/resume-a-scheduled-job.md) [NextDelete a scheduled job](https://upfour.io/docs/api/heartbeats/delete-a-scheduled-job.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001/resume" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001/resume", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001/resume", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001/resume", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "status": "up" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Delete a scheduled job — API reference > The endpoint returns status 204 with no response body. Source: https://upfour.io/docs/api/heartbeats/delete-a-scheduled-job [Developers](https://upfour.io/docs/api.md)/[Scheduled jobs (heartbeats)](https://upfour.io/docs/api/heartbeats.md) The endpoint returns status 204 with no response body. DELETE https://app.upfour.io/v1/heartbeats/{heartbeatID} scope heartbeat.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `heartbeatID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousResume a scheduled job using PUT](https://upfour.io/docs/api/heartbeats/resume-a-scheduled-job-using-put.md) [NextGet scheduled job availability](https://upfour.io/docs/api/heartbeats/get-scheduled-job-availability.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Get scheduled job availability — API reference > Returns uptime, downtime and outage statistics for each reported period. Source: https://upfour.io/docs/api/heartbeats/get-scheduled-job-availability [Developers](https://upfour.io/docs/api.md)/[Scheduled jobs (heartbeats)](https://upfour.io/docs/api/heartbeats.md) Returns uptime, downtime and outage statistics for each reported period. GET https://app.upfour.io/v1/heartbeats/{heartbeatID}/availability scope heartbeat.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `heartbeatID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | HeartbeatAvailabilityPeriod[] · nullable | | | Each item · 12 fields FieldTypeDescription `covered` boolean `downtime_seconds` integer `elapsed_seconds` integer `ends_at` string (date-time) `longest_outage_seconds` integer `outages` integer `period` string `starts_at` string (date-time) · nullable `uptime_percentage` number `watching_since` string (date-time) · nullable `consistency_error` string `paused_seconds` integer | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousDelete a scheduled job](https://upfour.io/docs/api/heartbeats/delete-a-scheduled-job.md) [NextList scheduled job pings](https://upfour.io/docs/api/heartbeats/list-scheduled-job-pings.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001/availability" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001/availability", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001/availability", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001/availability", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "covered": false, "downtime_seconds": 0, "elapsed_seconds": 0, "ends_at": "2026-10-27T09:00:00Z", "longest_outage_seconds": 0, "outages": 0, "period": "today", "starts_at": "2026-10-26T15:55:00Z", "uptime_percentage": 0, "watching_since": "2026-10-27T09:00:00Z" }, { "covered": false, "downtime_seconds": 0, "elapsed_seconds": 0, "ends_at": "2026-10-27T09:00:00Z", "longest_outage_seconds": 0, "outages": 0, "period": "7d", "starts_at": "2026-10-20T08:00:00Z", "uptime_percentage": 0, "watching_since": "2026-10-27T09:00:00Z" }, { "covered": false, "downtime_seconds": 0, "elapsed_seconds": 0, "ends_at": "2026-10-27T09:00:00Z", "longest_outage_seconds": 0, "outages": 0, "period": "30d", "starts_at": "2026-09-27T08:00:00Z", "uptime_percentage": 0, "watching_since": "2026-10-27T09:00:00Z" }, { "covered": false, "downtime_seconds": 0, "elapsed_seconds": 0, "ends_at": "2026-10-27T09:00:00Z", "longest_outage_seconds": 0, "outages": 0, "period": "90d", "starts_at": "2026-07-29T08:00:00Z", "uptime_percentage": 0, "watching_since": "2026-10-27T09:00:00Z" }, { "covered": false, "downtime_seconds": 0, "elapsed_seconds": 0, "ends_at": "2026-10-27T09:00:00Z", "longest_outage_seconds": 0, "outages": 0, "period": "365d", "starts_at": "2025-10-27T09:00:00Z", "uptime_percentage": 0, "watching_since": "2026-10-27T09:00:00Z" }, { "covered": false, "downtime_seconds": 0, "elapsed_seconds": 0, "ends_at": "2026-10-27T09:00:00Z", "longest_outage_seconds": 0, "outages": 0, "period": "all", "starts_at": null, "uptime_percentage": 0, "watching_since": "2026-10-27T09:00:00Z" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # List scheduled job pings — API reference > Returns recorded check-in times and source IP addresses, with pagination. Source: https://upfour.io/docs/api/heartbeats/list-scheduled-job-pings [Developers](https://upfour.io/docs/api.md)/[Scheduled jobs (heartbeats)](https://upfour.io/docs/api/heartbeats.md) Returns recorded check-in times and source IP addresses, with pagination. GET https://app.upfour.io/v1/heartbeats/{heartbeatID}/pings scope heartbeat.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `heartbeatID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | HeartbeatPing[] · nullable | | | Each item · 4 fields FieldTypeDescription `heartbeat_id` uuid `id` uuid `received_at` string (date-time) `source_ip` string | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet scheduled job availability](https://upfour.io/docs/api/heartbeats/get-scheduled-job-availability.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001/pings" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001/pings", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001/pings", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/heartbeats/00000000-0000-4000-8000-000000000001/pings", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "heartbeat_id": "00000000-0000-4000-8000-000000000001", "id": "00000000-0000-4000-8000-000000000002", "received_at": "2026-10-27T09:00:00Z", "source_ip": "198.51.100.250:40000" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Scheduled job groups — API reference > Folders for scheduled jobs. REST endpoints with request and response fields, examples in curl, JavaScript, Python, Go and PHP, and an in-browser Try it console. Source: https://upfour.io/docs/api/heartbeat-groups [Developers](https://upfour.io/docs/api.md)/Monitoring Folders for scheduled jobs. Base URL `https://app.upfour.io/v1`. Send your key as `Authorization: Bearer `; every key acts on your real Company, so [test without paging anyone](https://upfour.io/docs/api/testing.md). New here? Start with the [quickstart](https://upfour.io/docs/api/quickstart.md). The examples read the key from `UP4_API_KEY`. ## Everyday [GET **List scheduled job groups** /heartbeat-groups](https://upfour.io/docs/api/heartbeat-groups/list-scheduled-job-groups.md)[POST **Create a scheduled job group** /heartbeat-groups](https://upfour.io/docs/api/heartbeat-groups/create-a-scheduled-job-group.md)[GET **Get a scheduled job group** /heartbeat-groups/{groupID}](https://upfour.io/docs/api/heartbeat-groups/get-a-scheduled-job-group.md)[PUT **Replace a scheduled job group** /heartbeat-groups/{groupID}](https://upfour.io/docs/api/heartbeat-groups/replace-a-scheduled-job-group.md) ## Control [POST **Assign scheduled jobs to a group** /heartbeat-groups/assign](https://upfour.io/docs/api/heartbeat-groups/assign-scheduled-jobs-to-a-group.md)[DEL **Delete a scheduled job group** /heartbeat-groups/{groupID}](https://upfour.io/docs/api/heartbeat-groups/delete-a-scheduled-job-group.md) --- # List scheduled job groups — API reference > Returns group names, positions and resource counts. Source: https://upfour.io/docs/api/heartbeat-groups/list-scheduled-job-groups [Developers](https://upfour.io/docs/api.md)/[Scheduled job groups](https://upfour.io/docs/api/heartbeat-groups.md) Returns group names, positions and resource counts. GET https://app.upfour.io/v1/heartbeat-groups scope heartbeat.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `limit` optional | integer 1–250 default `50` | query parameter | | `cursor` optional | string | query parameter | | `client_id` optional | uuid | query parameter | | `all_clients` optional | boolean | query parameter | | `q` optional | string | query parameter | | `sort` optional | string | query parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ResourceGroup[] · nullable | | | Each item · 9 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `kind` string one of`monitor` `heartbeat` `name` string `organization_id` uuid `position` integer `resource_count` integer `updated_at` string (date-time) `client_id` uuid | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [NextCreate a scheduled job group](https://upfour.io/docs/api/heartbeat-groups/create-a-scheduled-job-group.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/heartbeat-groups" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/heartbeat-groups", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/heartbeat-groups", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/heartbeat-groups", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000001", "kind": "heartbeat", "name": "Nightly jobs", "organization_id": "00000000-0000-4000-8000-000000000002", "position": 0, "resource_count": 0, "updated_at": "2026-10-27T09:00:00Z" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` --- # Create a scheduled job group — API reference > Stores a group name, position and optional client association. Source: https://upfour.io/docs/api/heartbeat-groups/create-a-scheduled-job-group [Developers](https://upfour.io/docs/api.md)/[Scheduled job groups](https://upfour.io/docs/api/heartbeat-groups.md) Stores a group name, position and optional client association. POST https://app.upfour.io/v1/heartbeat-groups scope heartbeat.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Body `application/json` required 3 fields namestring (required) All other fields2 fields client_iduuid · nullable positioninteger ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ResourceGroup | | | ResourceGroup · 9 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `kind` string one of`monitor` `heartbeat` `name` string `organization_id` uuid `position` integer `resource_count` integer `updated_at` string (date-time) `client_id` uuid | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList scheduled job groups](https://upfour.io/docs/api/heartbeat-groups/list-scheduled-job-groups.md) [NextGet a scheduled job group](https://upfour.io/docs/api/heartbeat-groups/get-a-scheduled-job-group.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/heartbeat-groups" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Nightly jobs" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/heartbeat-groups", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "Nightly jobs", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/heartbeat-groups", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "name": "Nightly jobs", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "name": "Nightly jobs" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/heartbeat-groups", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000001", "kind": "heartbeat", "name": "Nightly jobs", "organization_id": "00000000-0000-4000-8000-000000000002", "position": 0, "resource_count": 0, "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Get a scheduled job group — API reference > Returns a group's name, position, resource count and client association. Source: https://upfour.io/docs/api/heartbeat-groups/get-a-scheduled-job-group [Developers](https://upfour.io/docs/api.md)/[Scheduled job groups](https://upfour.io/docs/api/heartbeat-groups.md) Returns a group's name, position, resource count and client association. GET https://app.upfour.io/v1/heartbeat-groups/{groupID} scope heartbeat.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `groupID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ResourceGroup | | | ResourceGroup · 9 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `kind` string one of`monitor` `heartbeat` `name` string `organization_id` uuid `position` integer `resource_count` integer `updated_at` string (date-time) `client_id` uuid | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousCreate a scheduled job group](https://upfour.io/docs/api/heartbeat-groups/create-a-scheduled-job-group.md) [NextReplace a scheduled job group](https://upfour.io/docs/api/heartbeat-groups/replace-a-scheduled-job-group.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/heartbeat-groups/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/heartbeat-groups/00000000-0000-4000-8000-000000000001", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/heartbeat-groups/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/heartbeat-groups/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000001", "kind": "heartbeat", "name": "Nightly jobs", "organization_id": "00000000-0000-4000-8000-000000000002", "position": 0, "resource_count": 0, "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Replace a scheduled job group — API reference > Changes a group name, position or client association. Source: https://upfour.io/docs/api/heartbeat-groups/replace-a-scheduled-job-group [Developers](https://upfour.io/docs/api.md)/[Scheduled job groups](https://upfour.io/docs/api/heartbeat-groups.md) Changes a group name, position or client association. PUT https://app.upfour.io/v1/heartbeat-groups/{groupID} scope heartbeat.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `groupID` required | uuid | path parameter | ## Body `application/json` required 3 fields namestring (required) All other fields2 fields client_iduuid · nullable positioninteger ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ResourceGroup | | | ResourceGroup · 9 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `kind` string one of`monitor` `heartbeat` `name` string `organization_id` uuid `position` integer `resource_count` integer `updated_at` string (date-time) `client_id` uuid | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet a scheduled job group](https://upfour.io/docs/api/heartbeat-groups/get-a-scheduled-job-group.md) [NextAssign scheduled jobs to a group](https://upfour.io/docs/api/heartbeat-groups/assign-scheduled-jobs-to-a-group.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/heartbeat-groups/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Nightly and weekly jobs" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/heartbeat-groups/00000000-0000-4000-8000-000000000001", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "Nightly and weekly jobs", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/heartbeat-groups/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "name": "Nightly and weekly jobs", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "name": "Nightly and weekly jobs" }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/heartbeat-groups/00000000-0000-4000-8000-000000000001", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000001", "kind": "heartbeat", "name": "Nightly and weekly jobs", "organization_id": "00000000-0000-4000-8000-000000000002", "position": 0, "resource_count": 0, "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Assign scheduled jobs to a group — API reference > Moves resource IDs to a target group or removes their group membership. Source: https://upfour.io/docs/api/heartbeat-groups/assign-scheduled-jobs-to-a-group [Developers](https://upfour.io/docs/api.md)/[Scheduled job groups](https://upfour.io/docs/api/heartbeat-groups.md) Moves resource IDs to a target group or removes their group membership. POST https://app.upfour.io/v1/heartbeat-groups/assign scope heartbeat.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Body `application/json` required 5 fields All other fields5 fields heartbeat_group_iduuid · nullable Same as monitor_group_id. heartbeat_idsuuid[] Same as resource_ids on /heartbeat-groups/assign. monitor_group_iduuid · nullable Target group; null or empty removes the resources from their group. heartbeat_group_id is accepted instead. monitor_idsuuid[] Same as resource_ids on /monitor-groups/assign. resource_idsuuid[] The monitors or scheduled jobs to move, at least one. monitor_ids and heartbeat_ids are accepted instead. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ResourceGroupAssignResponse | | | ResourceGroupAssignResponse · 1 fields FieldTypeDescription `updated` integer | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousReplace a scheduled job group](https://upfour.io/docs/api/heartbeat-groups/replace-a-scheduled-job-group.md) [NextDelete a scheduled job group](https://upfour.io/docs/api/heartbeat-groups/delete-a-scheduled-job-group.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/heartbeat-groups/assign" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "heartbeat_group_id": "00000000-0000-4000-8000-000000000001", "heartbeat_ids": [ "00000000-0000-4000-8000-000000000002" ] }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/heartbeat-groups/assign", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ heartbeat_group_id: "00000000-0000-4000-8000-000000000001", heartbeat_ids: ["00000000-0000-4000-8000-000000000002"], }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/heartbeat-groups/assign", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "heartbeat_group_id": "00000000-0000-4000-8000-000000000001", "heartbeat_ids": ["00000000-0000-4000-8000-000000000002"], }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "heartbeat_group_id": "00000000-0000-4000-8000-000000000001", "heartbeat_ids": [ "00000000-0000-4000-8000-000000000002" ] }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/heartbeat-groups/assign", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "updated": 1 } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Delete a scheduled job group — API reference > The endpoint returns status 204 with no response body. Source: https://upfour.io/docs/api/heartbeat-groups/delete-a-scheduled-job-group [Developers](https://upfour.io/docs/api.md)/[Scheduled job groups](https://upfour.io/docs/api/heartbeat-groups.md) The endpoint returns status 204 with no response body. DELETE https://app.upfour.io/v1/heartbeat-groups/{groupID} scope heartbeat.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `groupID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousAssign scheduled jobs to a group](https://upfour.io/docs/api/heartbeat-groups/assign-scheduled-jobs-to-a-group.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/heartbeat-groups/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/heartbeat-groups/00000000-0000-4000-8000-000000000001", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/heartbeat-groups/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/heartbeat-groups/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Resource metadata — API reference > Free-form key/value metadata on monitors and scheduled jobs — owner, playbook link, cost center. REST endpoints with request and response fields, examples in curl, JavaScript, Python, Go and PHP, and an in-browser Try it console. Source: https://upfour.io/docs/api/metadata [Developers](https://upfour.io/docs/api.md)/Monitoring Free-form key/value metadata on monitors and scheduled jobs — owner, playbook link, cost center. Base URL `https://app.upfour.io/v1`. Send your key as `Authorization: Bearer `; every key acts on your real Company, so [test without paging anyone](https://upfour.io/docs/api/testing.md). New here? Start with the [quickstart](https://upfour.io/docs/api/quickstart.md). The examples read the key from `UP4_API_KEY`. ## Everyday [GET **List resource metadata** /metadata](https://upfour.io/docs/api/metadata/list-resource-metadata.md) ## Control [PUT **Replace resource metadata** /metadata](https://upfour.io/docs/api/metadata/replace-resource-metadata.md) --- # List resource metadata — API reference > Returns keys and values for the requested resource, with pagination. Source: https://upfour.io/docs/api/metadata/list-resource-metadata [Developers](https://upfour.io/docs/api.md)/[Resource metadata](https://upfour.io/docs/api/metadata.md) Returns keys and values for the requested resource, with pagination. GET https://app.upfour.io/v1/metadata scope metadata.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `resource_type` required | string one of`monitor` `heartbeat` `incident` `client` | query parameter | | `resource_id` required | uuid | query parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ResourceMetadata[] · nullable | | | Each item · 8 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `key` string `organization_id` uuid `resource_id` uuid `resource_type` string one of`monitor` `heartbeat` `incident` `client` `updated_at` string (date-time) `value` string | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [NextReplace resource metadata](https://upfour.io/docs/api/metadata/replace-resource-metadata.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/metadata?resource_type=monitor&resource_id=00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/metadata?resource_type=monitor&resource_id=00000000-0000-4000-8000-000000000001", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/metadata?resource_type=monitor&resource_id=00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/metadata?resource_type=monitor&resource_id=00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000002", "key": "team", "organization_id": "00000000-0000-4000-8000-000000000003", "resource_id": "00000000-0000-4000-8000-000000000001", "resource_type": "monitor", "updated_at": "2026-10-27T09:00:00Z", "value": "platform" }, { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000004", "key": "tier", "organization_id": "00000000-0000-4000-8000-000000000003", "resource_id": "00000000-0000-4000-8000-000000000001", "resource_type": "monitor", "updated_at": "2026-10-27T09:00:00Z", "value": "1" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Replace resource metadata — API reference > Accepts a resource type, resource ID and key/value pairs. Source: https://upfour.io/docs/api/metadata/replace-resource-metadata [Developers](https://upfour.io/docs/api.md)/[Resource metadata](https://upfour.io/docs/api/metadata.md) Accepts a resource type, resource ID and key/value pairs. PUT https://app.upfour.io/v1/metadata scope metadata.write stable returns 200 ## Body `application/json` required 3 fields metadatamap of string (required)max 100 keyseach value max length 4096 Custom key/value pairs (at most 100 keys; keys 1-128 characters, values up to 4096). Sent to machine-readable alert channels and API webhook subscriptions, and copied onto the incidents the resource opens. resource_iduuid (required) resource_typestring (required) monitorheartbeatincidentclient ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ResourceMetadata[] · nullable | | | Each item · 8 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `key` string `organization_id` uuid `resource_id` uuid `resource_type` string one of`monitor` `heartbeat` `incident` `client` `updated_at` string (date-time) `value` string | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList resource metadata](https://upfour.io/docs/api/metadata/list-resource-metadata.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/metadata" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "metadata": { "team": "platform", "tier": "1" }, "resource_id": "00000000-0000-4000-8000-000000000001", "resource_type": "monitor" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/metadata", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ metadata: { team: "platform", tier: "1", }, resource_id: "00000000-0000-4000-8000-000000000001", resource_type: "monitor", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/metadata", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "metadata": { "team": "platform", "tier": "1", }, "resource_id": "00000000-0000-4000-8000-000000000001", "resource_type": "monitor", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "metadata": { "team": "platform", "tier": "1" }, "resource_id": "00000000-0000-4000-8000-000000000001", "resource_type": "monitor" }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/metadata", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000002", "key": "team", "organization_id": "00000000-0000-4000-8000-000000000003", "resource_id": "00000000-0000-4000-8000-000000000001", "resource_type": "monitor", "updated_at": "2026-10-27T09:00:00Z", "value": "platform" }, { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000004", "key": "tier", "organization_id": "00000000-0000-4000-8000-000000000003", "resource_id": "00000000-0000-4000-8000-000000000001", "resource_type": "monitor", "updated_at": "2026-10-27T09:00:00Z", "value": "1" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Incidents — API reference > Open, acknowledge, comment, escalate, resolve. REST endpoints with request and response fields, examples in curl, JavaScript, Python, Go and PHP, and an in-browser Try it console. Source: https://upfour.io/docs/api/incidents [Developers](https://upfour.io/docs/api.md)/Alerting & response Open, acknowledge, comment, escalate, resolve. Base URL `https://app.upfour.io/v1`. Send your key as `Authorization: Bearer `; every key acts on your real Company, so [test without paging anyone](https://upfour.io/docs/api/testing.md). New here? Start with the [quickstart](https://upfour.io/docs/api/quickstart.md). The examples read the key from `UP4_API_KEY`. ## Everyday [GET **List incidents** /incidents](https://upfour.io/docs/api/incidents/list-incidents.md)[POST **Create an incident** /incidents](https://upfour.io/docs/api/incidents/create-an-incident.md)[GET **Get an incident** /incidents/{incidentID}](https://upfour.io/docs/api/incidents/get-an-incident.md) ## Control [POST **Acknowledge an incident** /incidents/{incidentID}/acknowledge](https://upfour.io/docs/api/incidents/acknowledge-an-incident.md)[PUT **Acknowledge an incident using PUT** /incidents/{incidentID}/acknowledge](https://upfour.io/docs/api/incidents/acknowledge-an-incident-using-put.md)[POST **Escalate an incident** /incidents/{incidentID}/escalate](https://upfour.io/docs/api/incidents/escalate-an-incident.md)[POST **Reopen an incident** /incidents/{incidentID}/reopen](https://upfour.io/docs/api/incidents/reopen-an-incident.md)[PUT **Reopen an incident using PUT** /incidents/{incidentID}/reopen](https://upfour.io/docs/api/incidents/reopen-an-incident-using-put.md)[POST **Resolve an incident** /incidents/{incidentID}/resolve](https://upfour.io/docs/api/incidents/resolve-an-incident.md)[PUT **Resolve an incident using PUT** /incidents/{incidentID}/resolve](https://upfour.io/docs/api/incidents/resolve-an-incident-using-put.md) ## Communication [GET **List incident comments** /incidents/{incidentID}/comments](https://upfour.io/docs/api/incidents/list-incident-comments.md)[POST **Add an incident comment** /incidents/{incidentID}/comments](https://upfour.io/docs/api/incidents/add-an-incident-comment.md)[DEL **Delete an incident comment** /incidents/{incidentID}/comments/{commentID}](https://upfour.io/docs/api/incidents/delete-an-incident-comment.md)[GET **Get an incident timeline** /incidents/{incidentID}/timeline](https://upfour.io/docs/api/incidents/get-an-incident-timeline.md)[POST **Add an incident update** /incidents/{incidentID}/updates](https://upfour.io/docs/api/incidents/add-an-incident-update.md) ## Context [GET **List incident attachments** /incidents/{incidentID}/attachments](https://upfour.io/docs/api/incidents/list-incident-attachments.md)[POST **Upload an incident attachment** /incidents/{incidentID}/attachments](https://upfour.io/docs/api/incidents/upload-an-incident-attachment.md)[DEL **Delete an incident attachment** /incidents/{incidentID}/attachments/{attachmentID}](https://upfour.io/docs/api/incidents/delete-an-incident-attachment.md)[GET **Get an incident attachment** /incidents/{incidentID}/attachments/{attachmentID}](https://upfour.io/docs/api/incidents/get-an-incident-attachment.md)[GET **Get an incident domain case** /incidents/{incidentID}/domain-case](https://upfour.io/docs/api/incidents/get-an-incident-domain-case.md)[GET **Get an incident group** /incidents/{incidentID}/group](https://upfour.io/docs/api/incidents/get-an-incident-group.md) ## Analysis and playbooks [GET **Get incident AI details** /incidents/{incidentID}/ai](https://upfour.io/docs/api/incidents/get-incident-ai-details.md)[POST **Generate an incident postmortem** /incidents/{incidentID}/ai/postmortem](https://upfour.io/docs/api/incidents/generate-an-incident-postmortem.md)[POST **Generate an incident summary** /incidents/{incidentID}/ai/summary](https://upfour.io/docs/api/incidents/generate-an-incident-summary.md)[PUT **Rate an incident AI result** /incidents/{incidentID}/ai/{kind}/rating](https://upfour.io/docs/api/incidents/rate-an-incident-ai-result.md)[GET **Get incident playbook progress** /incidents/{incidentID}/runbook-progress](https://upfour.io/docs/api/incidents/get-incident-playbook-progress.md)[PUT **Update incident playbook step completion** /incidents/{incidentID}/runbook-steps/{stepID}/completion](https://upfour.io/docs/api/incidents/update-incident-playbook-step-completion.md) --- # List incidents — API reference > Returns incident counts and items for the selected client scope, with optional `q`, `sort` and cursor parameters. Source: https://upfour.io/docs/api/incidents/list-incidents [Developers](https://upfour.io/docs/api.md)/[Incidents](https://upfour.io/docs/api/incidents.md) Returns incident counts and items for the selected client scope, with optional `q`, `sort` and cursor parameters. GET https://app.upfour.io/v1/incidents scope incident.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `limit` optional | integer 1–250 default `50` | query parameter | | `cursor` optional | string | query parameter | | `client_id` optional | uuid | query parameter | | `all_clients` optional | boolean | query parameter | | `q` optional | string | query parameter | | `sort` optional | string | query parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | IncidentListResult | | | IncidentListResult · 49 fields FieldTypeDescription `counts` IncidentCounts IncidentCounts · 7 fields FieldTypeDescription `acknowledged` integer `open` integer `resolved_in_window` integer `started_in_window` integer `unresolved` integer `window_from` string (date-time) `window_to` string (date-time) `items` Incident[] · nullable Each item · 37 fields FieldTypeDescription `acknowledged_at` string (date-time) · nullable `cause` string `check_method` string `check_url` string `client_id` uuid · nullable `correlated_count` integer `created_at` string (date-time) `created_by` string `description` string `duration_seconds` integer · nullable `escalation_target_label` string `heartbeat_id` uuid · nullable `heartbeat_name` string `id` uuid `metadata` map of string · nullable max 100 keys each value max length 4096 Custom key/value pairs (at most 100 keys; keys 1-128 characters, values up to 4096). Sent to machine-readable alert channels and API webhook subscriptions, and copied onto the incidents the resource opens. `monitor_id` uuid · nullable `monitor_name` string `notified_label` string `override_channel_ids` uuid[] · nullable `resolved_at` string (date-time) · nullable `resolved_by_type` string · nullable `resolved_by_user_id` uuid · nullable `routing_target_id` uuid · nullable `routing_type` string one of`everyone` `escalation_policy` `team` `severity` string one of`critical` `warning` `info` `started_at` string (date-time) `started_by_type` string `started_by_user_id` uuid · nullable `status` string one of`open` `acknowledged` `resolved` `title` string `updated_at` string (date-time) `user_id` uuid `escalation_target_channel_count` integer `escalation_target_kind` string one of`nobody` `channel_count` `channel_count_with_policy` `name` `escalation_target_name` string `notified_channel_count` integer `notified_kind` string one of`nobody` `delivery_failed` `channel_count` `channel` `page` integer `per_page` integer `total` integer | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [NextCreate an incident](https://upfour.io/docs/api/incidents/create-an-incident.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/incidents" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/incidents", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/incidents", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/incidents", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "counts": { "acknowledged": 0, "open": 1, "resolved_in_window": 0, "started_in_window": 0, "unresolved": 1 }, "items": [ { "acknowledged_at": null, "cause": "Reported manually", "check_method": "GET", "check_url": "https://example.com", "client_id": null, "correlated_count": 0, "created_at": "2026-10-27T09:00:00Z", "created_by": "anna@example.com", "description": "Response times above 2 seconds since 08:40.", "duration_seconds": 0, "escalation_target_kind": "name", "escalation_target_label": "Default escalation", "escalation_target_name": "Default escalation", "heartbeat_id": null, "heartbeat_name": "", "id": "00000000-0000-4000-8000-000000000001", "metadata": {}, "monitor_id": "00000000-0000-4000-8000-000000000002", "monitor_name": "Marketing website", "notified_kind": "nobody", "notified_label": "Nobody", "override_channel_ids": [], "resolved_at": null, "resolved_by_type": null, "resolved_by_user_id": null, "routing_target_id": "00000000-0000-4000-8000-000000000003", "routing_type": "escalation_policy", "severity": "warning", "started_at": "2026-10-27T09:00:00Z", "started_by_type": "user", "started_by_user_id": "00000000-0000-4000-8000-000000000004", "status": "open", "title": "Checkout is slow", "updated_at": "2026-10-27T09:00:00Z", "user_id": "00000000-0000-4000-8000-000000000004" } ], "page": 1, "per_page": 25, "total": 1 } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` --- # Create an incident — API reference > Creates an incident with severity, routing, an optional monitor, alert channels and reporter context. Source: https://upfour.io/docs/api/incidents/create-an-incident [Developers](https://upfour.io/docs/api.md)/[Incidents](https://upfour.io/docs/api/incidents.md) Creates an incident with severity, routing, an optional monitor, alert channels and reporter context. POST https://app.upfour.io/v1/incidents scope incident.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Body The 4 fields most calls use come first; the rest are grouped below. `application/json` required 12 fields titlestring (required) In the app: **Brief description** severitystring Defaults to critical. criticalwarninginfo In the app: **Severity** descriptionstring In the app: **In-depth description** monitor_iduuid · nullable The monitor the incident is about. Without one the incident is company-level, unless the escalation policy belongs to a client. In the app: **Affected monitor (optional)** Routing3 fields routing_typestring everyone (default) or escalation_policy, which needs routing_target_id. Any other value, team included, is read as everyone. everyoneescalation_policy routing_target_iduuid · nullable The escalation policy for routing_type escalation_policy; must equal escalation_policy_id when both are given. escalation_policy_iduuid · nullable Shortcut for routing_type escalation_policy with this policy as the target. Alert channels3 fields notifyboolean · nullable Defaults to true. Set false to record the incident without alerting anyone: nothing goes out through alert channels or alert plans (email, SMS, voice, chat). The live view in the app and the outbound `incident.opened` webhook event still fire. With `notify: false`, `escalation_policy_id`, `routing_type: escalation_policy`, `notification_channel_ids` and `override_channel_ids` are rejected with 400, because each of them pages people. notification_channel_idsuuid[] Alert channels to notify instead of the default routing; each must be visible to the caller and, for a client channel, belong to the incident's client. override_channel_idsuuid[] Same as notification_channel_ids; only read when that list is empty. Incident context2 fields causestring Defaults to "Reported manually". reporter_emailstring Kept in the payload of the opening timeline entry. ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | IncidentDetail | | | IncidentDetail · 60 fields FieldTypeDescription `acknowledged_at` string (date-time) · nullable `cause` string `check_method` string `check_url` string `client_id` uuid · nullable `correlated_count` integer `created_at` string (date-time) `created_by` string `description` string `duration_seconds` integer · nullable `escalation_target_label` string `heartbeat_id` uuid · nullable `heartbeat_name` string `id` uuid `metadata` map of string · nullable max 100 keys each value max length 4096 Custom key/value pairs (at most 100 keys; keys 1-128 characters, values up to 4096). Sent to machine-readable alert channels and API webhook subscriptions, and copied onto the incidents the resource opens. `monitor_id` uuid · nullable `monitor_name` string `notification_deliveries` IncidentNotificationDelivery[] · nullable Each item · 10 fields FieldTypeDescription `attempt_count` integer · nullable `channel_name` string `created_at` string (date-time) `event_type` string `provider` string `status` string one of`delivered` `in_progress` `cancelled` `not_sent` `failed` `unknown` `channel_id` uuid `notice_kind` string one of`initial` `reminder` `escalation` `reason_code` string one of`notification_policy` `do_not_disturb` `initial_not_accepted` `not_sent` `reminder_number` integer `notified_label` string `override_channel_ids` uuid[] · nullable `resolved_at` string (date-time) · nullable `resolved_by_type` string · nullable `resolved_by_user_id` uuid · nullable `routing_target_id` uuid · nullable `routing_type` string one of`everyone` `escalation_policy` `team` `severity` string one of`critical` `warning` `info` `started_at` string (date-time) `started_by_type` string `started_by_user_id` uuid · nullable `status` string one of`open` `acknowledged` `resolved` `title` string `updated_at` string (date-time) `updates` IncidentUpdate[] · nullable Each item · 11 fields FieldTypeDescription `actor_label` string `actor_type` string `actor_user_id` uuid · nullable `created_at` string (date-time) `event_type` string `id` uuid `incident_id` uuid `message` string `payload` object · nullable free-form keys `message_code` string Names a fixed system text of message so a client can translate it; empty for free text. `message_params` map of string Values for the message_code text. `user_id` uuid `escalation_target_channel_count` integer `escalation_target_kind` string one of`nobody` `channel_count` `channel_count_with_policy` `name` `escalation_target_name` string `notified_channel_count` integer `notified_kind` string one of`nobody` `delivery_failed` `channel_count` `channel` | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 400 | documented response | The body is invalid, or `notify: false` is combined with an escalation policy or with notification channels (`notify cannot be false when an escalation policy is set: an alert plan pages people`, `notify cannot be false when notification channels are selected`). | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList incidents](https://upfour.io/docs/api/incidents/list-incidents.md) [NextGet an incident](https://upfour.io/docs/api/incidents/get-an-incident.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/incidents" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "description": "Response times above 2 seconds since 08:40.", "escalation_policy_id": "00000000-0000-4000-8000-000000000002", "monitor_id": "00000000-0000-4000-8000-000000000001", "severity": "warning", "title": "Checkout is slow" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/incidents", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ description: "Response times above 2 seconds since 08:40.", escalation_policy_id: "00000000-0000-4000-8000-000000000002", monitor_id: "00000000-0000-4000-8000-000000000001", severity: "warning", title: "Checkout is slow", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/incidents", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "description": "Response times above 2 seconds since 08:40.", "escalation_policy_id": "00000000-0000-4000-8000-000000000002", "monitor_id": "00000000-0000-4000-8000-000000000001", "severity": "warning", "title": "Checkout is slow", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "description": "Response times above 2 seconds since 08:40.", "escalation_policy_id": "00000000-0000-4000-8000-000000000002", "monitor_id": "00000000-0000-4000-8000-000000000001", "severity": "warning", "title": "Checkout is slow" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/incidents", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "acknowledged_at": null, "cause": "Reported manually", "check_method": "GET", "check_url": "https://example.com", "client_id": null, "correlated_count": 0, "created_at": "2026-10-27T09:00:00Z", "created_by": "anna@example.com", "description": "Response times above 2 seconds since 08:40.", "duration_seconds": 0, "escalation_target_kind": "name", "escalation_target_label": "Default escalation", "escalation_target_name": "Default escalation", "heartbeat_id": null, "heartbeat_name": "", "id": "00000000-0000-4000-8000-000000000003", "metadata": {}, "monitor_id": "00000000-0000-4000-8000-000000000001", "monitor_name": "Marketing website", "notification_deliveries": [], "notified_kind": "nobody", "notified_label": "Nobody", "override_channel_ids": [], "resolved_at": null, "resolved_by_type": null, "resolved_by_user_id": null, "routing_target_id": "00000000-0000-4000-8000-000000000002", "routing_type": "escalation_policy", "severity": "warning", "started_at": "2026-10-27T09:00:00Z", "started_by_type": "user", "started_by_user_id": "00000000-0000-4000-8000-000000000004", "status": "open", "title": "Checkout is slow", "updated_at": "2026-10-27T09:00:00Z", "updates": [ { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "created", "id": "00000000-0000-4000-8000-000000000005", "incident_id": "00000000-0000-4000-8000-000000000003", "message": "Incident reported manually.", "message_code": "incident_created_manual", "payload": {} } ], "user_id": "00000000-0000-4000-8000-000000000004" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Get an incident — API reference > Returns the incident record with its current status, timing, routing details and update history. Source: https://upfour.io/docs/api/incidents/get-an-incident [Developers](https://upfour.io/docs/api.md)/[Incidents](https://upfour.io/docs/api/incidents.md) Returns the incident record with its current status, timing, routing details and update history. GET https://app.upfour.io/v1/incidents/{incidentID} scope incident.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `incidentID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | IncidentDetail | | | IncidentDetail · 60 fields FieldTypeDescription `acknowledged_at` string (date-time) · nullable `cause` string `check_method` string `check_url` string `client_id` uuid · nullable `correlated_count` integer `created_at` string (date-time) `created_by` string `description` string `duration_seconds` integer · nullable `escalation_target_label` string `heartbeat_id` uuid · nullable `heartbeat_name` string `id` uuid `metadata` map of string · nullable max 100 keys each value max length 4096 Custom key/value pairs (at most 100 keys; keys 1-128 characters, values up to 4096). Sent to machine-readable alert channels and API webhook subscriptions, and copied onto the incidents the resource opens. `monitor_id` uuid · nullable `monitor_name` string `notification_deliveries` IncidentNotificationDelivery[] · nullable Each item · 10 fields FieldTypeDescription `attempt_count` integer · nullable `channel_name` string `created_at` string (date-time) `event_type` string `provider` string `status` string one of`delivered` `in_progress` `cancelled` `not_sent` `failed` `unknown` `channel_id` uuid `notice_kind` string one of`initial` `reminder` `escalation` `reason_code` string one of`notification_policy` `do_not_disturb` `initial_not_accepted` `not_sent` `reminder_number` integer `notified_label` string `override_channel_ids` uuid[] · nullable `resolved_at` string (date-time) · nullable `resolved_by_type` string · nullable `resolved_by_user_id` uuid · nullable `routing_target_id` uuid · nullable `routing_type` string one of`everyone` `escalation_policy` `team` `severity` string one of`critical` `warning` `info` `started_at` string (date-time) `started_by_type` string `started_by_user_id` uuid · nullable `status` string one of`open` `acknowledged` `resolved` `title` string `updated_at` string (date-time) `updates` IncidentUpdate[] · nullable Each item · 11 fields FieldTypeDescription `actor_label` string `actor_type` string `actor_user_id` uuid · nullable `created_at` string (date-time) `event_type` string `id` uuid `incident_id` uuid `message` string `payload` object · nullable free-form keys `message_code` string Names a fixed system text of message so a client can translate it; empty for free text. `message_params` map of string Values for the message_code text. `user_id` uuid `escalation_target_channel_count` integer `escalation_target_kind` string one of`nobody` `channel_count` `channel_count_with_policy` `name` `escalation_target_name` string `notified_channel_count` integer `notified_kind` string one of`nobody` `delivery_failed` `channel_count` `channel` | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousCreate an incident](https://upfour.io/docs/api/incidents/create-an-incident.md) [NextAcknowledge an incident](https://upfour.io/docs/api/incidents/acknowledge-an-incident.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "acknowledged_at": null, "cause": "Reported manually", "check_method": "GET", "check_url": "https://example.com", "client_id": null, "correlated_count": 0, "created_at": "2026-10-27T09:00:00Z", "created_by": "anna@example.com", "description": "Response times above 2 seconds since 08:40.", "duration_seconds": 0, "escalation_target_kind": "name", "escalation_target_label": "Default escalation", "escalation_target_name": "Default escalation", "heartbeat_id": null, "heartbeat_name": "", "id": "00000000-0000-4000-8000-000000000001", "metadata": {}, "monitor_id": "00000000-0000-4000-8000-000000000002", "monitor_name": "Marketing website", "notification_deliveries": [], "notified_kind": "nobody", "notified_label": "Nobody", "override_channel_ids": [], "resolved_at": null, "resolved_by_type": null, "resolved_by_user_id": null, "routing_target_id": "00000000-0000-4000-8000-000000000003", "routing_type": "escalation_policy", "severity": "warning", "started_at": "2026-10-27T09:00:00Z", "started_by_type": "user", "started_by_user_id": "00000000-0000-4000-8000-000000000004", "status": "open", "title": "Checkout is slow", "updated_at": "2026-10-27T09:00:00Z", "updates": [ { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "created", "id": "00000000-0000-4000-8000-000000000005", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Incident reported manually.", "message_code": "incident_created_manual", "payload": {} } ], "user_id": "00000000-0000-4000-8000-000000000004" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Acknowledge an incident — API reference > Acknowledges the incident and returns its updated status and update history. Source: https://upfour.io/docs/api/incidents/acknowledge-an-incident [Developers](https://upfour.io/docs/api.md)/[Incidents](https://upfour.io/docs/api/incidents.md) Acknowledges the incident and returns its updated status and update history. POST https://app.upfour.io/v1/incidents/{incidentID}/acknowledge scope incident.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `incidentID` required | uuid | path parameter | ## Body `application/json` optional 1 field All other fields1 fields notifyboolean · nullable Defaults to true. Set false to acknowledge without sending the `incident_acknowledged` notification to alert channels. The live view in the app and the outbound `incident.acknowledged` webhook event still fire, and a running alert plan is stopped exactly as before. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | IncidentDetail | | | IncidentDetail · 60 fields FieldTypeDescription `acknowledged_at` string (date-time) · nullable `cause` string `check_method` string `check_url` string `client_id` uuid · nullable `correlated_count` integer `created_at` string (date-time) `created_by` string `description` string `duration_seconds` integer · nullable `escalation_target_label` string `heartbeat_id` uuid · nullable `heartbeat_name` string `id` uuid `metadata` map of string · nullable max 100 keys each value max length 4096 Custom key/value pairs (at most 100 keys; keys 1-128 characters, values up to 4096). Sent to machine-readable alert channels and API webhook subscriptions, and copied onto the incidents the resource opens. `monitor_id` uuid · nullable `monitor_name` string `notification_deliveries` IncidentNotificationDelivery[] · nullable Each item · 10 fields FieldTypeDescription `attempt_count` integer · nullable `channel_name` string `created_at` string (date-time) `event_type` string `provider` string `status` string one of`delivered` `in_progress` `cancelled` `not_sent` `failed` `unknown` `channel_id` uuid `notice_kind` string one of`initial` `reminder` `escalation` `reason_code` string one of`notification_policy` `do_not_disturb` `initial_not_accepted` `not_sent` `reminder_number` integer `notified_label` string `override_channel_ids` uuid[] · nullable `resolved_at` string (date-time) · nullable `resolved_by_type` string · nullable `resolved_by_user_id` uuid · nullable `routing_target_id` uuid · nullable `routing_type` string one of`everyone` `escalation_policy` `team` `severity` string one of`critical` `warning` `info` `started_at` string (date-time) `started_by_type` string `started_by_user_id` uuid · nullable `status` string one of`open` `acknowledged` `resolved` `title` string `updated_at` string (date-time) `updates` IncidentUpdate[] · nullable Each item · 11 fields FieldTypeDescription `actor_label` string `actor_type` string `actor_user_id` uuid · nullable `created_at` string (date-time) `event_type` string `id` uuid `incident_id` uuid `message` string `payload` object · nullable free-form keys `message_code` string Names a fixed system text of message so a client can translate it; empty for free text. `message_params` map of string Values for the message_code text. `user_id` uuid `escalation_target_channel_count` integer `escalation_target_kind` string one of`nobody` `channel_count` `channel_count_with_policy` `name` `escalation_target_name` string `notified_channel_count` integer `notified_kind` string one of`nobody` `delivery_failed` `channel_count` `channel` | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 400 | documented response | The body is not valid JSON, or `notify` is not a boolean. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet an incident](https://upfour.io/docs/api/incidents/get-an-incident.md) [NextAcknowledge an incident using PUT](https://upfour.io/docs/api/incidents/acknowledge-an-incident-using-put.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/acknowledge" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/acknowledge", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({}), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/acknowledge", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{}`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/acknowledge", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "acknowledged_at": "2026-10-27T09:00:00Z", "cause": "Reported manually", "check_method": "GET", "check_url": "https://example.com", "client_id": null, "correlated_count": 0, "created_at": "2026-10-27T09:00:00Z", "created_by": "anna@example.com", "description": "Response times above 2 seconds since 08:40.", "duration_seconds": 0, "escalation_target_kind": "name", "escalation_target_label": "Default escalation", "escalation_target_name": "Default escalation", "heartbeat_id": null, "heartbeat_name": "", "id": "00000000-0000-4000-8000-000000000001", "metadata": {}, "monitor_id": "00000000-0000-4000-8000-000000000002", "monitor_name": "Marketing website", "notification_deliveries": [], "notified_kind": "nobody", "notified_label": "Nobody", "override_channel_ids": [], "resolved_at": null, "resolved_by_type": null, "resolved_by_user_id": null, "routing_target_id": "00000000-0000-4000-8000-000000000003", "routing_type": "escalation_policy", "severity": "warning", "started_at": "2026-10-27T09:00:00Z", "started_by_type": "user", "started_by_user_id": "00000000-0000-4000-8000-000000000004", "status": "acknowledged", "title": "Checkout is slow", "updated_at": "2026-10-27T09:00:00Z", "updates": [ { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "escalation_target_acknowledged", "id": "00000000-0000-4000-8000-000000000005", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "anna@example.com confirmed the manual escalation; the alert plan stopped", "payload": { "manual_escalation_id": "00000000-0000-4000-8000-000000000006", "source": "authenticated_user", "target_user_id": "00000000-0000-4000-8000-000000000004" } }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "escalated", "id": "00000000-0000-4000-8000-000000000006", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Escalated to anna@example.com; the alert plan continues until they confirm", "payload": { "alert_plan_continues": true, "channel_ids": [ "00000000-0000-4000-8000-000000000007" ], "manual_escalation_id": "00000000-0000-4000-8000-000000000006", "manual_escalation_step": 1000001, "request_key": "walk-60", "status": "waiting_for_target_confirmation", "target_label": "anna@example.com", "target_user_id": "00000000-0000-4000-8000-000000000004" } }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "created", "id": "00000000-0000-4000-8000-000000000008", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Incident reported manually.", "message_code": "incident_created_manual", "payload": {} } ], "user_id": "00000000-0000-4000-8000-000000000004" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Acknowledge an incident using PUT — API reference > Acknowledges the incident using the PUT action and returns its updated status and update history. Source: https://upfour.io/docs/api/incidents/acknowledge-an-incident-using-put [Developers](https://upfour.io/docs/api.md)/[Incidents](https://upfour.io/docs/api/incidents.md) Acknowledges the incident using the PUT action and returns its updated status and update history. PUT https://app.upfour.io/v1/incidents/{incidentID}/acknowledge scope incident.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `incidentID` required | uuid | path parameter | ## Body `application/json` optional 1 field All other fields1 fields notifyboolean · nullable Defaults to true. Set false to acknowledge without sending the `incident_acknowledged` notification to alert channels. The live view in the app and the outbound `incident.acknowledged` webhook event still fire, and a running alert plan is stopped exactly as before. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | IncidentDetail | | | IncidentDetail · 60 fields FieldTypeDescription `acknowledged_at` string (date-time) · nullable `cause` string `check_method` string `check_url` string `client_id` uuid · nullable `correlated_count` integer `created_at` string (date-time) `created_by` string `description` string `duration_seconds` integer · nullable `escalation_target_label` string `heartbeat_id` uuid · nullable `heartbeat_name` string `id` uuid `metadata` map of string · nullable max 100 keys each value max length 4096 Custom key/value pairs (at most 100 keys; keys 1-128 characters, values up to 4096). Sent to machine-readable alert channels and API webhook subscriptions, and copied onto the incidents the resource opens. `monitor_id` uuid · nullable `monitor_name` string `notification_deliveries` IncidentNotificationDelivery[] · nullable Each item · 10 fields FieldTypeDescription `attempt_count` integer · nullable `channel_name` string `created_at` string (date-time) `event_type` string `provider` string `status` string one of`delivered` `in_progress` `cancelled` `not_sent` `failed` `unknown` `channel_id` uuid `notice_kind` string one of`initial` `reminder` `escalation` `reason_code` string one of`notification_policy` `do_not_disturb` `initial_not_accepted` `not_sent` `reminder_number` integer `notified_label` string `override_channel_ids` uuid[] · nullable `resolved_at` string (date-time) · nullable `resolved_by_type` string · nullable `resolved_by_user_id` uuid · nullable `routing_target_id` uuid · nullable `routing_type` string one of`everyone` `escalation_policy` `team` `severity` string one of`critical` `warning` `info` `started_at` string (date-time) `started_by_type` string `started_by_user_id` uuid · nullable `status` string one of`open` `acknowledged` `resolved` `title` string `updated_at` string (date-time) `updates` IncidentUpdate[] · nullable Each item · 11 fields FieldTypeDescription `actor_label` string `actor_type` string `actor_user_id` uuid · nullable `created_at` string (date-time) `event_type` string `id` uuid `incident_id` uuid `message` string `payload` object · nullable free-form keys `message_code` string Names a fixed system text of message so a client can translate it; empty for free text. `message_params` map of string Values for the message_code text. `user_id` uuid `escalation_target_channel_count` integer `escalation_target_kind` string one of`nobody` `channel_count` `channel_count_with_policy` `name` `escalation_target_name` string `notified_channel_count` integer `notified_kind` string one of`nobody` `delivery_failed` `channel_count` `channel` | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 400 | documented response | The body is not valid JSON, or `notify` is not a boolean. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousAcknowledge an incident](https://upfour.io/docs/api/incidents/acknowledge-an-incident.md) [NextEscalate an incident](https://upfour.io/docs/api/incidents/escalate-an-incident.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/acknowledge" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/acknowledge", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({}), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/acknowledge", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{}`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/acknowledge", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "acknowledged_at": "2026-10-27T09:00:00Z", "cause": "Reported manually", "check_method": "GET", "check_url": "https://example.com", "client_id": null, "correlated_count": 0, "created_at": "2026-10-27T09:00:00Z", "created_by": "anna@example.com", "description": "Response times above 2 seconds since 08:40.", "duration_seconds": 0, "escalation_target_kind": "name", "escalation_target_label": "Default escalation", "escalation_target_name": "Default escalation", "heartbeat_id": null, "heartbeat_name": "", "id": "00000000-0000-4000-8000-000000000001", "metadata": {}, "monitor_id": "00000000-0000-4000-8000-000000000002", "monitor_name": "Marketing website", "notification_deliveries": [], "notified_kind": "nobody", "notified_label": "Nobody", "override_channel_ids": [], "resolved_at": null, "resolved_by_type": null, "resolved_by_user_id": null, "routing_target_id": "00000000-0000-4000-8000-000000000003", "routing_type": "escalation_policy", "severity": "warning", "started_at": "2026-10-27T09:00:00Z", "started_by_type": "user", "started_by_user_id": "00000000-0000-4000-8000-000000000004", "status": "acknowledged", "title": "Checkout is slow", "updated_at": "2026-10-27T09:00:00Z", "updates": [ { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "acknowledged", "id": "00000000-0000-4000-8000-000000000005", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Acknowledged by anna@example.com after 0s", "message_code": "acknowledged_by", "message_params": { "duration": "0s", "name": "anna@example.com" }, "payload": {} }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "reopened", "id": "00000000-0000-4000-8000-000000000006", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Reopened by anna@example.com", "message_code": "reopened_by", "message_params": { "name": "anna@example.com" }, "payload": {} }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "resolved", "id": "00000000-0000-4000-8000-000000000007", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Resolved by anna@example.com · open for 0s", "message_code": "resolved_by", "message_params": { "duration": "0s", "name": "anna@example.com" }, "payload": {} }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "attachment_added", "id": "00000000-0000-4000-8000-000000000008", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "checkout-error.txt", "payload": { "attachment_id": "00000000-0000-4000-8000-000000000009", "file_size": 42, "filename": "checkout-error.txt", "mime_type": "text/plain" } }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "comment", "id": "00000000-0000-4000-8000-000000000010", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Looking into the payment provider.", "payload": {} }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "escalation_target_acknowledged", "id": "00000000-0000-4000-8000-000000000011", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "anna@example.com confirmed the manual escalation; the alert plan stopped", "payload": { "manual_escalation_id": "00000000-0000-4000-8000-000000000012", "source": "authenticated_user", "target_user_id": "00000000-0000-4000-8000-000000000004" } }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "escalated", "id": "00000000-0000-4000-8000-000000000012", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Escalated to anna@example.com; the alert plan continues until they confirm", "payload": { "alert_plan_continues": true, "channel_ids": [ "00000000-0000-4000-8000-000000000013" ], "manual_escalation_id": "00000000-0000-4000-8000-000000000012", "manual_escalation_step": 1000001, "request_key": "walk-60", "status": "waiting_for_target_confirmation", "target_label": "anna@example.com", "target_user_id": "00000000-0000-4000-8000-000000000004" } }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "created", "id": "00000000-0000-4000-8000-000000000014", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Incident reported manually.", "message_code": "incident_created_manual", "payload": {} } ], "user_id": "00000000-0000-4000-8000-000000000004" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Escalate an incident — API reference > Escalates the incident to the specified user and returns the updated incident detail. Source: https://upfour.io/docs/api/incidents/escalate-an-incident [Developers](https://upfour.io/docs/api.md)/[Incidents](https://upfour.io/docs/api/incidents.md) Escalates the incident to the specified user and returns the updated incident detail. POST https://app.upfour.io/v1/incidents/{incidentID}/escalate scope incident.write stable [Idempotency-Key required](https://upfour.io/docs/api/idempotency.md) returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `incidentID` required | uuid | path parameter | ## Body `application/json` required 1 field target_user_iduuid (required) In the app: **Escalate to a specific person** ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | IncidentDetail | | | IncidentDetail · 60 fields FieldTypeDescription `acknowledged_at` string (date-time) · nullable `cause` string `check_method` string `check_url` string `client_id` uuid · nullable `correlated_count` integer `created_at` string (date-time) `created_by` string `description` string `duration_seconds` integer · nullable `escalation_target_label` string `heartbeat_id` uuid · nullable `heartbeat_name` string `id` uuid `metadata` map of string · nullable max 100 keys each value max length 4096 Custom key/value pairs (at most 100 keys; keys 1-128 characters, values up to 4096). Sent to machine-readable alert channels and API webhook subscriptions, and copied onto the incidents the resource opens. `monitor_id` uuid · nullable `monitor_name` string `notification_deliveries` IncidentNotificationDelivery[] · nullable Each item · 10 fields FieldTypeDescription `attempt_count` integer · nullable `channel_name` string `created_at` string (date-time) `event_type` string `provider` string `status` string one of`delivered` `in_progress` `cancelled` `not_sent` `failed` `unknown` `channel_id` uuid `notice_kind` string one of`initial` `reminder` `escalation` `reason_code` string one of`notification_policy` `do_not_disturb` `initial_not_accepted` `not_sent` `reminder_number` integer `notified_label` string `override_channel_ids` uuid[] · nullable `resolved_at` string (date-time) · nullable `resolved_by_type` string · nullable `resolved_by_user_id` uuid · nullable `routing_target_id` uuid · nullable `routing_type` string one of`everyone` `escalation_policy` `team` `severity` string one of`critical` `warning` `info` `started_at` string (date-time) `started_by_type` string `started_by_user_id` uuid · nullable `status` string one of`open` `acknowledged` `resolved` `title` string `updated_at` string (date-time) `updates` IncidentUpdate[] · nullable Each item · 11 fields FieldTypeDescription `actor_label` string `actor_type` string `actor_user_id` uuid · nullable `created_at` string (date-time) `event_type` string `id` uuid `incident_id` uuid `message` string `payload` object · nullable free-form keys `message_code` string Names a fixed system text of message so a client can translate it; empty for free text. `message_params` map of string Values for the message_code text. `user_id` uuid `escalation_target_channel_count` integer `escalation_target_kind` string one of`nobody` `channel_count` `channel_count_with_policy` `name` `escalation_target_name` string `notified_channel_count` integer `notified_kind` string one of`nobody` `delivery_failed` `channel_count` `channel` | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousAcknowledge an incident using PUT](https://upfour.io/docs/api/incidents/acknowledge-an-incident-using-put.md) [NextReopen an incident](https://upfour.io/docs/api/incidents/reopen-an-incident.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/escalate" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "target_user_id": "00000000-0000-4000-8000-000000000002" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/escalate", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ target_user_id: "00000000-0000-4000-8000-000000000002", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/escalate", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "target_user_id": "00000000-0000-4000-8000-000000000002", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "target_user_id": "00000000-0000-4000-8000-000000000002" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/escalate", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "acknowledged_at": null, "cause": "Reported manually", "check_method": "GET", "check_url": "https://example.com", "client_id": null, "correlated_count": 0, "created_at": "2026-10-27T09:00:00Z", "created_by": "anna@example.com", "description": "Response times above 2 seconds since 08:40.", "duration_seconds": 0, "escalation_target_kind": "name", "escalation_target_label": "Default escalation", "escalation_target_name": "Default escalation", "heartbeat_id": null, "heartbeat_name": "", "id": "00000000-0000-4000-8000-000000000001", "metadata": {}, "monitor_id": "00000000-0000-4000-8000-000000000003", "monitor_name": "Marketing website", "notification_deliveries": [], "notified_kind": "nobody", "notified_label": "Nobody", "override_channel_ids": [], "resolved_at": null, "resolved_by_type": null, "resolved_by_user_id": null, "routing_target_id": "00000000-0000-4000-8000-000000000004", "routing_type": "escalation_policy", "severity": "warning", "started_at": "2026-10-27T09:00:00Z", "started_by_type": "user", "started_by_user_id": "00000000-0000-4000-8000-000000000002", "status": "open", "title": "Checkout is slow", "updated_at": "2026-10-27T09:00:00Z", "updates": [ { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000002", "created_at": "2026-10-27T09:00:00Z", "event_type": "escalated", "id": "00000000-0000-4000-8000-000000000005", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Escalated to anna@example.com; the alert plan continues until they confirm", "payload": { "alert_plan_continues": true, "channel_ids": [ "00000000-0000-4000-8000-000000000006" ], "manual_escalation_id": "00000000-0000-4000-8000-000000000005", "manual_escalation_step": 1000001, "request_key": "walk-60", "status": "waiting_for_target_confirmation", "target_label": "anna@example.com", "target_user_id": "00000000-0000-4000-8000-000000000002" } }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000002", "created_at": "2026-10-27T09:00:00Z", "event_type": "created", "id": "00000000-0000-4000-8000-000000000007", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Incident reported manually.", "message_code": "incident_created_manual", "payload": {} } ], "user_id": "00000000-0000-4000-8000-000000000002" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Reopen an incident — API reference > Reopens the incident and returns its updated status and update history. Source: https://upfour.io/docs/api/incidents/reopen-an-incident [Developers](https://upfour.io/docs/api.md)/[Incidents](https://upfour.io/docs/api/incidents.md) Reopens the incident and returns its updated status and update history. POST https://app.upfour.io/v1/incidents/{incidentID}/reopen scope incident.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `incidentID` required | uuid | path parameter | ## Body `application/json` optional 1 field All other fields1 fields notifyboolean · nullable Defaults to true. Set false to reopen without sending the `incident_reopened` notification to alert channels. The live view in the app and the outbound `incident.reopened` webhook event still fire. Reopening never starts an alert plan. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | IncidentDetail | | | IncidentDetail · 60 fields FieldTypeDescription `acknowledged_at` string (date-time) · nullable `cause` string `check_method` string `check_url` string `client_id` uuid · nullable `correlated_count` integer `created_at` string (date-time) `created_by` string `description` string `duration_seconds` integer · nullable `escalation_target_label` string `heartbeat_id` uuid · nullable `heartbeat_name` string `id` uuid `metadata` map of string · nullable max 100 keys each value max length 4096 Custom key/value pairs (at most 100 keys; keys 1-128 characters, values up to 4096). Sent to machine-readable alert channels and API webhook subscriptions, and copied onto the incidents the resource opens. `monitor_id` uuid · nullable `monitor_name` string `notification_deliveries` IncidentNotificationDelivery[] · nullable Each item · 10 fields FieldTypeDescription `attempt_count` integer · nullable `channel_name` string `created_at` string (date-time) `event_type` string `provider` string `status` string one of`delivered` `in_progress` `cancelled` `not_sent` `failed` `unknown` `channel_id` uuid `notice_kind` string one of`initial` `reminder` `escalation` `reason_code` string one of`notification_policy` `do_not_disturb` `initial_not_accepted` `not_sent` `reminder_number` integer `notified_label` string `override_channel_ids` uuid[] · nullable `resolved_at` string (date-time) · nullable `resolved_by_type` string · nullable `resolved_by_user_id` uuid · nullable `routing_target_id` uuid · nullable `routing_type` string one of`everyone` `escalation_policy` `team` `severity` string one of`critical` `warning` `info` `started_at` string (date-time) `started_by_type` string `started_by_user_id` uuid · nullable `status` string one of`open` `acknowledged` `resolved` `title` string `updated_at` string (date-time) `updates` IncidentUpdate[] · nullable Each item · 11 fields FieldTypeDescription `actor_label` string `actor_type` string `actor_user_id` uuid · nullable `created_at` string (date-time) `event_type` string `id` uuid `incident_id` uuid `message` string `payload` object · nullable free-form keys `message_code` string Names a fixed system text of message so a client can translate it; empty for free text. `message_params` map of string Values for the message_code text. `user_id` uuid `escalation_target_channel_count` integer `escalation_target_kind` string one of`nobody` `channel_count` `channel_count_with_policy` `name` `escalation_target_name` string `notified_channel_count` integer `notified_kind` string one of`nobody` `delivery_failed` `channel_count` `channel` | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 400 | documented response | The body is not valid JSON, or `notify` is not a boolean. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousEscalate an incident](https://upfour.io/docs/api/incidents/escalate-an-incident.md) [NextReopen an incident using PUT](https://upfour.io/docs/api/incidents/reopen-an-incident-using-put.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/reopen" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/reopen", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({}), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/reopen", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{}`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/reopen", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "acknowledged_at": "2026-10-27T09:00:00Z", "cause": "Reported manually", "check_method": "GET", "check_url": "https://example.com", "client_id": null, "correlated_count": 0, "created_at": "2026-10-27T09:00:00Z", "created_by": "anna@example.com", "description": "Response times above 2 seconds since 08:40.", "duration_seconds": 0, "escalation_target_kind": "name", "escalation_target_label": "Default escalation", "escalation_target_name": "Default escalation", "heartbeat_id": null, "heartbeat_name": "", "id": "00000000-0000-4000-8000-000000000001", "metadata": {}, "monitor_id": "00000000-0000-4000-8000-000000000002", "monitor_name": "Marketing website", "notification_deliveries": [], "notified_kind": "nobody", "notified_label": "Nobody", "override_channel_ids": [], "resolved_at": null, "resolved_by_type": null, "resolved_by_user_id": null, "routing_target_id": "00000000-0000-4000-8000-000000000003", "routing_type": "escalation_policy", "severity": "warning", "started_at": "2026-10-27T09:00:00Z", "started_by_type": "user", "started_by_user_id": "00000000-0000-4000-8000-000000000004", "status": "open", "title": "Checkout is slow", "updated_at": "2026-10-27T09:00:00Z", "updates": [ { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "reopened", "id": "00000000-0000-4000-8000-000000000005", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Reopened by anna@example.com", "message_code": "reopened_by", "message_params": { "name": "anna@example.com" }, "payload": {} }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "resolved", "id": "00000000-0000-4000-8000-000000000006", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Resolved by anna@example.com · open for 0s", "message_code": "resolved_by", "message_params": { "duration": "0s", "name": "anna@example.com" }, "payload": {} }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "attachment_added", "id": "00000000-0000-4000-8000-000000000007", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "checkout-error.txt", "payload": { "attachment_id": "00000000-0000-4000-8000-000000000008", "file_size": 42, "filename": "checkout-error.txt", "mime_type": "text/plain" } }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "comment", "id": "00000000-0000-4000-8000-000000000009", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Looking into the payment provider.", "payload": {} }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "escalation_target_acknowledged", "id": "00000000-0000-4000-8000-000000000010", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "anna@example.com confirmed the manual escalation; the alert plan stopped", "payload": { "manual_escalation_id": "00000000-0000-4000-8000-000000000011", "source": "authenticated_user", "target_user_id": "00000000-0000-4000-8000-000000000004" } }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "escalated", "id": "00000000-0000-4000-8000-000000000011", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Escalated to anna@example.com; the alert plan continues until they confirm", "payload": { "alert_plan_continues": true, "channel_ids": [ "00000000-0000-4000-8000-000000000012" ], "manual_escalation_id": "00000000-0000-4000-8000-000000000011", "manual_escalation_step": 1000001, "request_key": "walk-60", "status": "waiting_for_target_confirmation", "target_label": "anna@example.com", "target_user_id": "00000000-0000-4000-8000-000000000004" } }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "created", "id": "00000000-0000-4000-8000-000000000013", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Incident reported manually.", "message_code": "incident_created_manual", "payload": {} } ], "user_id": "00000000-0000-4000-8000-000000000004" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Reopen an incident using PUT — API reference > Reopens the incident using the PUT action and returns its updated status and update history. Source: https://upfour.io/docs/api/incidents/reopen-an-incident-using-put [Developers](https://upfour.io/docs/api.md)/[Incidents](https://upfour.io/docs/api/incidents.md) Reopens the incident using the PUT action and returns its updated status and update history. PUT https://app.upfour.io/v1/incidents/{incidentID}/reopen scope incident.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `incidentID` required | uuid | path parameter | ## Body `application/json` optional 1 field All other fields1 fields notifyboolean · nullable Defaults to true. Set false to reopen without sending the `incident_reopened` notification to alert channels. The live view in the app and the outbound `incident.reopened` webhook event still fire. Reopening never starts an alert plan. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | IncidentDetail | | | IncidentDetail · 60 fields FieldTypeDescription `acknowledged_at` string (date-time) · nullable `cause` string `check_method` string `check_url` string `client_id` uuid · nullable `correlated_count` integer `created_at` string (date-time) `created_by` string `description` string `duration_seconds` integer · nullable `escalation_target_label` string `heartbeat_id` uuid · nullable `heartbeat_name` string `id` uuid `metadata` map of string · nullable max 100 keys each value max length 4096 Custom key/value pairs (at most 100 keys; keys 1-128 characters, values up to 4096). Sent to machine-readable alert channels and API webhook subscriptions, and copied onto the incidents the resource opens. `monitor_id` uuid · nullable `monitor_name` string `notification_deliveries` IncidentNotificationDelivery[] · nullable Each item · 10 fields FieldTypeDescription `attempt_count` integer · nullable `channel_name` string `created_at` string (date-time) `event_type` string `provider` string `status` string one of`delivered` `in_progress` `cancelled` `not_sent` `failed` `unknown` `channel_id` uuid `notice_kind` string one of`initial` `reminder` `escalation` `reason_code` string one of`notification_policy` `do_not_disturb` `initial_not_accepted` `not_sent` `reminder_number` integer `notified_label` string `override_channel_ids` uuid[] · nullable `resolved_at` string (date-time) · nullable `resolved_by_type` string · nullable `resolved_by_user_id` uuid · nullable `routing_target_id` uuid · nullable `routing_type` string one of`everyone` `escalation_policy` `team` `severity` string one of`critical` `warning` `info` `started_at` string (date-time) `started_by_type` string `started_by_user_id` uuid · nullable `status` string one of`open` `acknowledged` `resolved` `title` string `updated_at` string (date-time) `updates` IncidentUpdate[] · nullable Each item · 11 fields FieldTypeDescription `actor_label` string `actor_type` string `actor_user_id` uuid · nullable `created_at` string (date-time) `event_type` string `id` uuid `incident_id` uuid `message` string `payload` object · nullable free-form keys `message_code` string Names a fixed system text of message so a client can translate it; empty for free text. `message_params` map of string Values for the message_code text. `user_id` uuid `escalation_target_channel_count` integer `escalation_target_kind` string one of`nobody` `channel_count` `channel_count_with_policy` `name` `escalation_target_name` string `notified_channel_count` integer `notified_kind` string one of`nobody` `delivery_failed` `channel_count` `channel` | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 400 | documented response | The body is not valid JSON, or `notify` is not a boolean. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousReopen an incident](https://upfour.io/docs/api/incidents/reopen-an-incident.md) [NextResolve an incident](https://upfour.io/docs/api/incidents/resolve-an-incident.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/reopen" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/reopen", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({}), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/reopen", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{}`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/reopen", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "acknowledged_at": "2026-10-27T09:00:00Z", "cause": "Reported manually", "check_method": "GET", "check_url": "https://example.com", "client_id": null, "correlated_count": 0, "created_at": "2026-10-27T09:00:00Z", "created_by": "anna@example.com", "description": "Response times above 2 seconds since 08:40.", "duration_seconds": 0, "escalation_target_kind": "name", "escalation_target_label": "Default escalation", "escalation_target_name": "Default escalation", "heartbeat_id": null, "heartbeat_name": "", "id": "00000000-0000-4000-8000-000000000001", "metadata": {}, "monitor_id": "00000000-0000-4000-8000-000000000002", "monitor_name": "Marketing website", "notification_deliveries": [], "notified_kind": "nobody", "notified_label": "Nobody", "override_channel_ids": [], "resolved_at": null, "resolved_by_type": null, "resolved_by_user_id": null, "routing_target_id": "00000000-0000-4000-8000-000000000003", "routing_type": "escalation_policy", "severity": "warning", "started_at": "2026-10-27T09:00:00Z", "started_by_type": "user", "started_by_user_id": "00000000-0000-4000-8000-000000000004", "status": "open", "title": "Checkout is slow", "updated_at": "2026-10-27T09:00:00Z", "updates": [ { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "reopened", "id": "00000000-0000-4000-8000-000000000005", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Reopened by anna@example.com", "message_code": "reopened_by", "message_params": { "name": "anna@example.com" }, "payload": {} }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "resolved", "id": "00000000-0000-4000-8000-000000000006", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Resolved by anna@example.com · open for 0s", "message_code": "resolved_by", "message_params": { "duration": "0s", "name": "anna@example.com" }, "payload": {} }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "acknowledged", "id": "00000000-0000-4000-8000-000000000007", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Acknowledged by anna@example.com after 0s", "message_code": "acknowledged_by", "message_params": { "duration": "0s", "name": "anna@example.com" }, "payload": {} }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "reopened", "id": "00000000-0000-4000-8000-000000000008", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Reopened by anna@example.com", "message_code": "reopened_by", "message_params": { "name": "anna@example.com" }, "payload": {} }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "resolved", "id": "00000000-0000-4000-8000-000000000009", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Resolved by anna@example.com · open for 0s", "message_code": "resolved_by", "message_params": { "duration": "0s", "name": "anna@example.com" }, "payload": {} }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "attachment_added", "id": "00000000-0000-4000-8000-000000000010", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "checkout-error.txt", "payload": { "attachment_id": "00000000-0000-4000-8000-000000000011", "file_size": 42, "filename": "checkout-error.txt", "mime_type": "text/plain" } }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "comment", "id": "00000000-0000-4000-8000-000000000012", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Looking into the payment provider.", "payload": {} }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "escalation_target_acknowledged", "id": "00000000-0000-4000-8000-000000000013", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "anna@example.com confirmed the manual escalation; the alert plan stopped", "payload": { "manual_escalation_id": "00000000-0000-4000-8000-000000000014", "source": "authenticated_user", "target_user_id": "00000000-0000-4000-8000-000000000004" } }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "escalated", "id": "00000000-0000-4000-8000-000000000014", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Escalated to anna@example.com; the alert plan continues until they confirm", "payload": { "alert_plan_continues": true, "channel_ids": [ "00000000-0000-4000-8000-000000000015" ], "manual_escalation_id": "00000000-0000-4000-8000-000000000014", "manual_escalation_step": 1000001, "request_key": "walk-60", "status": "waiting_for_target_confirmation", "target_label": "anna@example.com", "target_user_id": "00000000-0000-4000-8000-000000000004" } }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "created", "id": "00000000-0000-4000-8000-000000000016", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Incident reported manually.", "message_code": "incident_created_manual", "payload": {} } ], "user_id": "00000000-0000-4000-8000-000000000004" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Resolve an incident — API reference > Resolves the incident and returns its updated status and update history. Source: https://upfour.io/docs/api/incidents/resolve-an-incident [Developers](https://upfour.io/docs/api.md)/[Incidents](https://upfour.io/docs/api/incidents.md) Resolves the incident and returns its updated status and update history. POST https://app.upfour.io/v1/incidents/{incidentID}/resolve scope incident.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `incidentID` required | uuid | path parameter | ## Body `application/json` optional 1 field All other fields1 fields notifyboolean · nullable Defaults to true. Set false to resolve without sending the `incident_resolved` notification to alert channels. The live view in the app and the outbound `incident.resolved` webhook event still fire, and a running alert plan is stopped exactly as before. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | IncidentDetail | | | IncidentDetail · 60 fields FieldTypeDescription `acknowledged_at` string (date-time) · nullable `cause` string `check_method` string `check_url` string `client_id` uuid · nullable `correlated_count` integer `created_at` string (date-time) `created_by` string `description` string `duration_seconds` integer · nullable `escalation_target_label` string `heartbeat_id` uuid · nullable `heartbeat_name` string `id` uuid `metadata` map of string · nullable max 100 keys each value max length 4096 Custom key/value pairs (at most 100 keys; keys 1-128 characters, values up to 4096). Sent to machine-readable alert channels and API webhook subscriptions, and copied onto the incidents the resource opens. `monitor_id` uuid · nullable `monitor_name` string `notification_deliveries` IncidentNotificationDelivery[] · nullable Each item · 10 fields FieldTypeDescription `attempt_count` integer · nullable `channel_name` string `created_at` string (date-time) `event_type` string `provider` string `status` string one of`delivered` `in_progress` `cancelled` `not_sent` `failed` `unknown` `channel_id` uuid `notice_kind` string one of`initial` `reminder` `escalation` `reason_code` string one of`notification_policy` `do_not_disturb` `initial_not_accepted` `not_sent` `reminder_number` integer `notified_label` string `override_channel_ids` uuid[] · nullable `resolved_at` string (date-time) · nullable `resolved_by_type` string · nullable `resolved_by_user_id` uuid · nullable `routing_target_id` uuid · nullable `routing_type` string one of`everyone` `escalation_policy` `team` `severity` string one of`critical` `warning` `info` `started_at` string (date-time) `started_by_type` string `started_by_user_id` uuid · nullable `status` string one of`open` `acknowledged` `resolved` `title` string `updated_at` string (date-time) `updates` IncidentUpdate[] · nullable Each item · 11 fields FieldTypeDescription `actor_label` string `actor_type` string `actor_user_id` uuid · nullable `created_at` string (date-time) `event_type` string `id` uuid `incident_id` uuid `message` string `payload` object · nullable free-form keys `message_code` string Names a fixed system text of message so a client can translate it; empty for free text. `message_params` map of string Values for the message_code text. `user_id` uuid `escalation_target_channel_count` integer `escalation_target_kind` string one of`nobody` `channel_count` `channel_count_with_policy` `name` `escalation_target_name` string `notified_channel_count` integer `notified_kind` string one of`nobody` `delivery_failed` `channel_count` `channel` | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 400 | documented response | The body is not valid JSON, or `notify` is not a boolean. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousReopen an incident using PUT](https://upfour.io/docs/api/incidents/reopen-an-incident-using-put.md) [NextResolve an incident using PUT](https://upfour.io/docs/api/incidents/resolve-an-incident-using-put.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/resolve" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/resolve", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({}), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/resolve", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{}`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/resolve", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "acknowledged_at": "2026-10-27T09:00:00Z", "cause": "Reported manually", "check_method": "GET", "check_url": "https://example.com", "client_id": null, "correlated_count": 0, "created_at": "2026-10-27T09:00:00Z", "created_by": "anna@example.com", "description": "Response times above 2 seconds since 08:40.", "duration_seconds": 0, "escalation_target_kind": "name", "escalation_target_label": "Default escalation", "escalation_target_name": "Default escalation", "heartbeat_id": null, "heartbeat_name": "", "id": "00000000-0000-4000-8000-000000000001", "metadata": {}, "monitor_id": "00000000-0000-4000-8000-000000000002", "monitor_name": "Marketing website", "notification_deliveries": [], "notified_kind": "nobody", "notified_label": "Nobody", "override_channel_ids": [], "resolved_at": "2026-10-27T09:00:00Z", "resolved_by_type": "user", "resolved_by_user_id": "00000000-0000-4000-8000-000000000003", "routing_target_id": "00000000-0000-4000-8000-000000000004", "routing_type": "escalation_policy", "severity": "warning", "started_at": "2026-10-27T09:00:00Z", "started_by_type": "user", "started_by_user_id": "00000000-0000-4000-8000-000000000003", "status": "resolved", "title": "Checkout is slow", "updated_at": "2026-10-27T09:00:00Z", "updates": [ { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000003", "created_at": "2026-10-27T09:00:00Z", "event_type": "resolved", "id": "00000000-0000-4000-8000-000000000005", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Resolved by anna@example.com · open for 0s", "message_code": "resolved_by", "message_params": { "duration": "0s", "name": "anna@example.com" }, "payload": {} }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000003", "created_at": "2026-10-27T09:00:00Z", "event_type": "attachment_added", "id": "00000000-0000-4000-8000-000000000006", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "checkout-error.txt", "payload": { "attachment_id": "00000000-0000-4000-8000-000000000007", "file_size": 42, "filename": "checkout-error.txt", "mime_type": "text/plain" } }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000003", "created_at": "2026-10-27T09:00:00Z", "event_type": "comment", "id": "00000000-0000-4000-8000-000000000008", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Looking into the payment provider.", "payload": {} }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000003", "created_at": "2026-10-27T09:00:00Z", "event_type": "escalation_target_acknowledged", "id": "00000000-0000-4000-8000-000000000009", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "anna@example.com confirmed the manual escalation; the alert plan stopped", "payload": { "manual_escalation_id": "00000000-0000-4000-8000-000000000010", "source": "authenticated_user", "target_user_id": "00000000-0000-4000-8000-000000000003" } }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000003", "created_at": "2026-10-27T09:00:00Z", "event_type": "escalated", "id": "00000000-0000-4000-8000-000000000010", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Escalated to anna@example.com; the alert plan continues until they confirm", "payload": { "alert_plan_continues": true, "channel_ids": [ "00000000-0000-4000-8000-000000000011" ], "manual_escalation_id": "00000000-0000-4000-8000-000000000010", "manual_escalation_step": 1000001, "request_key": "walk-60", "status": "waiting_for_target_confirmation", "target_label": "anna@example.com", "target_user_id": "00000000-0000-4000-8000-000000000003" } }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000003", "created_at": "2026-10-27T09:00:00Z", "event_type": "created", "id": "00000000-0000-4000-8000-000000000012", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Incident reported manually.", "message_code": "incident_created_manual", "payload": {} } ], "user_id": "00000000-0000-4000-8000-000000000003" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Resolve an incident using PUT — API reference > Resolves the incident using the PUT action and returns its updated status and update history. Source: https://upfour.io/docs/api/incidents/resolve-an-incident-using-put [Developers](https://upfour.io/docs/api.md)/[Incidents](https://upfour.io/docs/api/incidents.md) Resolves the incident using the PUT action and returns its updated status and update history. PUT https://app.upfour.io/v1/incidents/{incidentID}/resolve scope incident.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `incidentID` required | uuid | path parameter | ## Body `application/json` optional 1 field All other fields1 fields notifyboolean · nullable Defaults to true. Set false to resolve without sending the `incident_resolved` notification to alert channels. The live view in the app and the outbound `incident.resolved` webhook event still fire, and a running alert plan is stopped exactly as before. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | IncidentDetail | | | IncidentDetail · 60 fields FieldTypeDescription `acknowledged_at` string (date-time) · nullable `cause` string `check_method` string `check_url` string `client_id` uuid · nullable `correlated_count` integer `created_at` string (date-time) `created_by` string `description` string `duration_seconds` integer · nullable `escalation_target_label` string `heartbeat_id` uuid · nullable `heartbeat_name` string `id` uuid `metadata` map of string · nullable max 100 keys each value max length 4096 Custom key/value pairs (at most 100 keys; keys 1-128 characters, values up to 4096). Sent to machine-readable alert channels and API webhook subscriptions, and copied onto the incidents the resource opens. `monitor_id` uuid · nullable `monitor_name` string `notification_deliveries` IncidentNotificationDelivery[] · nullable Each item · 10 fields FieldTypeDescription `attempt_count` integer · nullable `channel_name` string `created_at` string (date-time) `event_type` string `provider` string `status` string one of`delivered` `in_progress` `cancelled` `not_sent` `failed` `unknown` `channel_id` uuid `notice_kind` string one of`initial` `reminder` `escalation` `reason_code` string one of`notification_policy` `do_not_disturb` `initial_not_accepted` `not_sent` `reminder_number` integer `notified_label` string `override_channel_ids` uuid[] · nullable `resolved_at` string (date-time) · nullable `resolved_by_type` string · nullable `resolved_by_user_id` uuid · nullable `routing_target_id` uuid · nullable `routing_type` string one of`everyone` `escalation_policy` `team` `severity` string one of`critical` `warning` `info` `started_at` string (date-time) `started_by_type` string `started_by_user_id` uuid · nullable `status` string one of`open` `acknowledged` `resolved` `title` string `updated_at` string (date-time) `updates` IncidentUpdate[] · nullable Each item · 11 fields FieldTypeDescription `actor_label` string `actor_type` string `actor_user_id` uuid · nullable `created_at` string (date-time) `event_type` string `id` uuid `incident_id` uuid `message` string `payload` object · nullable free-form keys `message_code` string Names a fixed system text of message so a client can translate it; empty for free text. `message_params` map of string Values for the message_code text. `user_id` uuid `escalation_target_channel_count` integer `escalation_target_kind` string one of`nobody` `channel_count` `channel_count_with_policy` `name` `escalation_target_name` string `notified_channel_count` integer `notified_kind` string one of`nobody` `delivery_failed` `channel_count` `channel` | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 400 | documented response | The body is not valid JSON, or `notify` is not a boolean. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousResolve an incident](https://upfour.io/docs/api/incidents/resolve-an-incident.md) [NextList incident comments](https://upfour.io/docs/api/incidents/list-incident-comments.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/resolve" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/resolve", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({}), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/resolve", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{}`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/resolve", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "acknowledged_at": "2026-10-27T09:00:00Z", "cause": "Reported manually", "check_method": "GET", "check_url": "https://example.com", "client_id": null, "correlated_count": 0, "created_at": "2026-10-27T09:00:00Z", "created_by": "anna@example.com", "description": "Response times above 2 seconds since 08:40.", "duration_seconds": 0, "escalation_target_kind": "name", "escalation_target_label": "Default escalation", "escalation_target_name": "Default escalation", "heartbeat_id": null, "heartbeat_name": "", "id": "00000000-0000-4000-8000-000000000001", "metadata": {}, "monitor_id": "00000000-0000-4000-8000-000000000002", "monitor_name": "Marketing website", "notification_deliveries": [], "notified_kind": "nobody", "notified_label": "Nobody", "override_channel_ids": [], "resolved_at": "2026-10-27T09:00:00Z", "resolved_by_type": "user", "resolved_by_user_id": "00000000-0000-4000-8000-000000000003", "routing_target_id": "00000000-0000-4000-8000-000000000004", "routing_type": "escalation_policy", "severity": "warning", "started_at": "2026-10-27T09:00:00Z", "started_by_type": "user", "started_by_user_id": "00000000-0000-4000-8000-000000000003", "status": "resolved", "title": "Checkout is slow", "updated_at": "2026-10-27T09:00:00Z", "updates": [ { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000003", "created_at": "2026-10-27T09:00:00Z", "event_type": "resolved", "id": "00000000-0000-4000-8000-000000000005", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Resolved by anna@example.com · open for 0s", "message_code": "resolved_by", "message_params": { "duration": "0s", "name": "anna@example.com" }, "payload": {} }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000003", "created_at": "2026-10-27T09:00:00Z", "event_type": "acknowledged", "id": "00000000-0000-4000-8000-000000000006", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Acknowledged by anna@example.com after 0s", "message_code": "acknowledged_by", "message_params": { "duration": "0s", "name": "anna@example.com" }, "payload": {} }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000003", "created_at": "2026-10-27T09:00:00Z", "event_type": "reopened", "id": "00000000-0000-4000-8000-000000000007", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Reopened by anna@example.com", "message_code": "reopened_by", "message_params": { "name": "anna@example.com" }, "payload": {} }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000003", "created_at": "2026-10-27T09:00:00Z", "event_type": "resolved", "id": "00000000-0000-4000-8000-000000000008", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Resolved by anna@example.com · open for 0s", "message_code": "resolved_by", "message_params": { "duration": "0s", "name": "anna@example.com" }, "payload": {} }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000003", "created_at": "2026-10-27T09:00:00Z", "event_type": "attachment_added", "id": "00000000-0000-4000-8000-000000000009", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "checkout-error.txt", "payload": { "attachment_id": "00000000-0000-4000-8000-000000000010", "file_size": 42, "filename": "checkout-error.txt", "mime_type": "text/plain" } }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000003", "created_at": "2026-10-27T09:00:00Z", "event_type": "comment", "id": "00000000-0000-4000-8000-000000000011", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Looking into the payment provider.", "payload": {} }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000003", "created_at": "2026-10-27T09:00:00Z", "event_type": "escalation_target_acknowledged", "id": "00000000-0000-4000-8000-000000000012", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "anna@example.com confirmed the manual escalation; the alert plan stopped", "payload": { "manual_escalation_id": "00000000-0000-4000-8000-000000000013", "source": "authenticated_user", "target_user_id": "00000000-0000-4000-8000-000000000003" } }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000003", "created_at": "2026-10-27T09:00:00Z", "event_type": "escalated", "id": "00000000-0000-4000-8000-000000000013", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Escalated to anna@example.com; the alert plan continues until they confirm", "payload": { "alert_plan_continues": true, "channel_ids": [ "00000000-0000-4000-8000-000000000014" ], "manual_escalation_id": "00000000-0000-4000-8000-000000000013", "manual_escalation_step": 1000001, "request_key": "walk-60", "status": "waiting_for_target_confirmation", "target_label": "anna@example.com", "target_user_id": "00000000-0000-4000-8000-000000000003" } }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000003", "created_at": "2026-10-27T09:00:00Z", "event_type": "created", "id": "00000000-0000-4000-8000-000000000015", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Incident reported manually.", "message_code": "incident_created_manual", "payload": {} } ], "user_id": "00000000-0000-4000-8000-000000000003" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # List incident comments — API reference > Returns the incident's comment entries with their actors, messages and timestamps. Source: https://upfour.io/docs/api/incidents/list-incident-comments [Developers](https://upfour.io/docs/api.md)/[Incidents](https://upfour.io/docs/api/incidents.md) Returns the incident's comment entries with their actors, messages and timestamps. GET https://app.upfour.io/v1/incidents/{incidentID}/comments scope incident.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `incidentID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | IncidentUpdate[] · nullable | | | Each item · 11 fields FieldTypeDescription `actor_label` string `actor_type` string `actor_user_id` uuid · nullable `created_at` string (date-time) `event_type` string `id` uuid `incident_id` uuid `message` string `payload` object · nullable free-form keys `message_code` string Names a fixed system text of message so a client can translate it; empty for free text. `message_params` map of string Values for the message_code text. | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousResolve an incident using PUT](https://upfour.io/docs/api/incidents/resolve-an-incident-using-put.md) [NextAdd an incident comment](https://upfour.io/docs/api/incidents/add-an-incident-comment.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/comments" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/comments", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/comments", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/comments", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000002", "created_at": "2026-10-27T09:00:00Z", "event_type": "comment", "id": "00000000-0000-4000-8000-000000000003", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "The provider confirms a partial outage.", "payload": {} }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000002", "created_at": "2026-10-27T09:00:00Z", "event_type": "comment", "id": "00000000-0000-4000-8000-000000000004", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Looking into the payment provider.", "payload": {} } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Add an incident comment — API reference > Adds a message to the incident and returns the updated incident detail. Source: https://upfour.io/docs/api/incidents/add-an-incident-comment [Developers](https://upfour.io/docs/api.md)/[Incidents](https://upfour.io/docs/api/incidents.md) Adds a message to the incident and returns the updated incident detail. POST https://app.upfour.io/v1/incidents/{incidentID}/comments scope incident.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `incidentID` required | uuid | path parameter | ## Body `application/json` required 2 fields messagestring (required) In the app: **Post an update** All other fields1 fields notifyboolean · nullable Defaults to true. Set false to add the comment without sending the `incident_comment` notification to alert channels. The comment is saved and the live view in the app updates either way. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | IncidentDetail | | | IncidentDetail · 60 fields FieldTypeDescription `acknowledged_at` string (date-time) · nullable `cause` string `check_method` string `check_url` string `client_id` uuid · nullable `correlated_count` integer `created_at` string (date-time) `created_by` string `description` string `duration_seconds` integer · nullable `escalation_target_label` string `heartbeat_id` uuid · nullable `heartbeat_name` string `id` uuid `metadata` map of string · nullable max 100 keys each value max length 4096 Custom key/value pairs (at most 100 keys; keys 1-128 characters, values up to 4096). Sent to machine-readable alert channels and API webhook subscriptions, and copied onto the incidents the resource opens. `monitor_id` uuid · nullable `monitor_name` string `notification_deliveries` IncidentNotificationDelivery[] · nullable Each item · 10 fields FieldTypeDescription `attempt_count` integer · nullable `channel_name` string `created_at` string (date-time) `event_type` string `provider` string `status` string one of`delivered` `in_progress` `cancelled` `not_sent` `failed` `unknown` `channel_id` uuid `notice_kind` string one of`initial` `reminder` `escalation` `reason_code` string one of`notification_policy` `do_not_disturb` `initial_not_accepted` `not_sent` `reminder_number` integer `notified_label` string `override_channel_ids` uuid[] · nullable `resolved_at` string (date-time) · nullable `resolved_by_type` string · nullable `resolved_by_user_id` uuid · nullable `routing_target_id` uuid · nullable `routing_type` string one of`everyone` `escalation_policy` `team` `severity` string one of`critical` `warning` `info` `started_at` string (date-time) `started_by_type` string `started_by_user_id` uuid · nullable `status` string one of`open` `acknowledged` `resolved` `title` string `updated_at` string (date-time) `updates` IncidentUpdate[] · nullable Each item · 11 fields FieldTypeDescription `actor_label` string `actor_type` string `actor_user_id` uuid · nullable `created_at` string (date-time) `event_type` string `id` uuid `incident_id` uuid `message` string `payload` object · nullable free-form keys `message_code` string Names a fixed system text of message so a client can translate it; empty for free text. `message_params` map of string Values for the message_code text. `user_id` uuid `escalation_target_channel_count` integer `escalation_target_kind` string one of`nobody` `channel_count` `channel_count_with_policy` `name` `escalation_target_name` string `notified_channel_count` integer `notified_kind` string one of`nobody` `delivery_failed` `channel_count` `channel` | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList incident comments](https://upfour.io/docs/api/incidents/list-incident-comments.md) [NextDelete an incident comment](https://upfour.io/docs/api/incidents/delete-an-incident-comment.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/comments" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "message": "Looking into the payment provider." }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/comments", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ message: "Looking into the payment provider.", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/comments", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "message": "Looking into the payment provider.", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "message": "Looking into the payment provider." }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/comments", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "acknowledged_at": "2026-10-27T09:00:00Z", "cause": "Reported manually", "check_method": "GET", "check_url": "https://example.com", "client_id": null, "correlated_count": 0, "created_at": "2026-10-27T09:00:00Z", "created_by": "anna@example.com", "description": "Response times above 2 seconds since 08:40.", "duration_seconds": 0, "escalation_target_kind": "name", "escalation_target_label": "Default escalation", "escalation_target_name": "Default escalation", "heartbeat_id": null, "heartbeat_name": "", "id": "00000000-0000-4000-8000-000000000001", "metadata": {}, "monitor_id": "00000000-0000-4000-8000-000000000002", "monitor_name": "Marketing website", "notification_deliveries": [], "notified_kind": "nobody", "notified_label": "Nobody", "override_channel_ids": [], "resolved_at": null, "resolved_by_type": null, "resolved_by_user_id": null, "routing_target_id": "00000000-0000-4000-8000-000000000003", "routing_type": "escalation_policy", "severity": "warning", "started_at": "2026-10-27T09:00:00Z", "started_by_type": "user", "started_by_user_id": "00000000-0000-4000-8000-000000000004", "status": "acknowledged", "title": "Checkout is slow", "updated_at": "2026-10-27T09:00:00Z", "updates": [ { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "comment", "id": "00000000-0000-4000-8000-000000000005", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Looking into the payment provider.", "payload": {} }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "escalation_target_acknowledged", "id": "00000000-0000-4000-8000-000000000006", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "anna@example.com confirmed the manual escalation; the alert plan stopped", "payload": { "manual_escalation_id": "00000000-0000-4000-8000-000000000007", "source": "authenticated_user", "target_user_id": "00000000-0000-4000-8000-000000000004" } }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "escalated", "id": "00000000-0000-4000-8000-000000000007", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Escalated to anna@example.com; the alert plan continues until they confirm", "payload": { "alert_plan_continues": true, "channel_ids": [ "00000000-0000-4000-8000-000000000008" ], "manual_escalation_id": "00000000-0000-4000-8000-000000000007", "manual_escalation_step": 1000001, "request_key": "walk-60", "status": "waiting_for_target_confirmation", "target_label": "anna@example.com", "target_user_id": "00000000-0000-4000-8000-000000000004" } }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "created", "id": "00000000-0000-4000-8000-000000000009", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Incident reported manually.", "message_code": "incident_created_manual", "payload": {} } ], "user_id": "00000000-0000-4000-8000-000000000004" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Delete an incident comment — API reference > Deletes one incident comment and returns status 204 with no response body. Source: https://upfour.io/docs/api/incidents/delete-an-incident-comment [Developers](https://upfour.io/docs/api.md)/[Incidents](https://upfour.io/docs/api/incidents.md) Deletes one incident comment and returns status 204 with no response body. DELETE https://app.upfour.io/v1/incidents/{incidentID}/comments/{commentID} scope incident.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `incidentID` required | uuid | path parameter | | `commentID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousAdd an incident comment](https://upfour.io/docs/api/incidents/add-an-incident-comment.md) [NextGet an incident timeline](https://upfour.io/docs/api/incidents/get-an-incident-timeline.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/comments/00000000-0000-4000-8000-000000000002" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/comments/00000000-0000-4000-8000-000000000002", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/comments/00000000-0000-4000-8000-000000000002", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/comments/00000000-0000-4000-8000-000000000002", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Get an incident timeline — API reference > Returns incident timeline entries with actors, event types, messages and timestamps. Source: https://upfour.io/docs/api/incidents/get-an-incident-timeline [Developers](https://upfour.io/docs/api.md)/[Incidents](https://upfour.io/docs/api/incidents.md) Returns incident timeline entries with actors, event types, messages and timestamps. GET https://app.upfour.io/v1/incidents/{incidentID}/timeline scope incident.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `incidentID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | IncidentUpdate[] · nullable | | | Each item · 11 fields FieldTypeDescription `actor_label` string `actor_type` string `actor_user_id` uuid · nullable `created_at` string (date-time) `event_type` string `id` uuid `incident_id` uuid `message` string `payload` object · nullable free-form keys `message_code` string Names a fixed system text of message so a client can translate it; empty for free text. `message_params` map of string Values for the message_code text. | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousDelete an incident comment](https://upfour.io/docs/api/incidents/delete-an-incident-comment.md) [NextAdd an incident update](https://upfour.io/docs/api/incidents/add-an-incident-update.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/timeline" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/timeline", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/timeline", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/timeline", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000002", "created_at": "2026-10-27T09:00:00Z", "event_type": "created", "id": "00000000-0000-4000-8000-000000000003", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Incident reported manually.", "message_code": "incident_created_manual", "payload": {} }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000002", "created_at": "2026-10-27T09:00:00Z", "event_type": "escalated", "id": "00000000-0000-4000-8000-000000000004", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Escalated to anna@example.com; the alert plan continues until they confirm", "payload": { "alert_plan_continues": true, "channel_ids": [ "00000000-0000-4000-8000-000000000005" ], "manual_escalation_id": "00000000-0000-4000-8000-000000000004", "manual_escalation_step": 1000001, "request_key": "walk-60", "status": "waiting_for_target_confirmation", "target_label": "anna@example.com", "target_user_id": "00000000-0000-4000-8000-000000000002" } }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000002", "created_at": "2026-10-27T09:00:00Z", "event_type": "escalation_target_acknowledged", "id": "00000000-0000-4000-8000-000000000006", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "anna@example.com confirmed the manual escalation; the alert plan stopped", "payload": { "manual_escalation_id": "00000000-0000-4000-8000-000000000004", "source": "authenticated_user", "target_user_id": "00000000-0000-4000-8000-000000000002" } }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000002", "created_at": "2026-10-27T09:00:00Z", "event_type": "comment", "id": "00000000-0000-4000-8000-000000000007", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Looking into the payment provider.", "payload": {} }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000002", "created_at": "2026-10-27T09:00:00Z", "event_type": "comment", "id": "00000000-0000-4000-8000-000000000008", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "The provider confirms a partial outage.", "payload": {} } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Add an incident update — API reference > Adds a message to the incident update stream and returns the updated incident detail. Source: https://upfour.io/docs/api/incidents/add-an-incident-update [Developers](https://upfour.io/docs/api.md)/[Incidents](https://upfour.io/docs/api/incidents.md) Adds a message to the incident update stream and returns the updated incident detail. POST https://app.upfour.io/v1/incidents/{incidentID}/updates scope incident.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `incidentID` required | uuid | path parameter | ## Body `application/json` required 2 fields messagestring (required) All other fields1 fields notifyboolean · nullable Defaults to true. Set false to add the comment without sending the `incident_comment` notification to alert channels. The comment is saved and the live view in the app updates either way. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | IncidentDetail | | | IncidentDetail · 60 fields FieldTypeDescription `acknowledged_at` string (date-time) · nullable `cause` string `check_method` string `check_url` string `client_id` uuid · nullable `correlated_count` integer `created_at` string (date-time) `created_by` string `description` string `duration_seconds` integer · nullable `escalation_target_label` string `heartbeat_id` uuid · nullable `heartbeat_name` string `id` uuid `metadata` map of string · nullable max 100 keys each value max length 4096 Custom key/value pairs (at most 100 keys; keys 1-128 characters, values up to 4096). Sent to machine-readable alert channels and API webhook subscriptions, and copied onto the incidents the resource opens. `monitor_id` uuid · nullable `monitor_name` string `notification_deliveries` IncidentNotificationDelivery[] · nullable Each item · 10 fields FieldTypeDescription `attempt_count` integer · nullable `channel_name` string `created_at` string (date-time) `event_type` string `provider` string `status` string one of`delivered` `in_progress` `cancelled` `not_sent` `failed` `unknown` `channel_id` uuid `notice_kind` string one of`initial` `reminder` `escalation` `reason_code` string one of`notification_policy` `do_not_disturb` `initial_not_accepted` `not_sent` `reminder_number` integer `notified_label` string `override_channel_ids` uuid[] · nullable `resolved_at` string (date-time) · nullable `resolved_by_type` string · nullable `resolved_by_user_id` uuid · nullable `routing_target_id` uuid · nullable `routing_type` string one of`everyone` `escalation_policy` `team` `severity` string one of`critical` `warning` `info` `started_at` string (date-time) `started_by_type` string `started_by_user_id` uuid · nullable `status` string one of`open` `acknowledged` `resolved` `title` string `updated_at` string (date-time) `updates` IncidentUpdate[] · nullable Each item · 11 fields FieldTypeDescription `actor_label` string `actor_type` string `actor_user_id` uuid · nullable `created_at` string (date-time) `event_type` string `id` uuid `incident_id` uuid `message` string `payload` object · nullable free-form keys `message_code` string Names a fixed system text of message so a client can translate it; empty for free text. `message_params` map of string Values for the message_code text. `user_id` uuid `escalation_target_channel_count` integer `escalation_target_kind` string one of`nobody` `channel_count` `channel_count_with_policy` `name` `escalation_target_name` string `notified_channel_count` integer `notified_kind` string one of`nobody` `delivery_failed` `channel_count` `channel` | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet an incident timeline](https://upfour.io/docs/api/incidents/get-an-incident-timeline.md) [NextList incident attachments](https://upfour.io/docs/api/incidents/list-incident-attachments.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/updates" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "message": "The provider confirms a partial outage." }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/updates", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ message: "The provider confirms a partial outage.", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/updates", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "message": "The provider confirms a partial outage.", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "message": "The provider confirms a partial outage." }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/updates", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "acknowledged_at": "2026-10-27T09:00:00Z", "cause": "Reported manually", "check_method": "GET", "check_url": "https://example.com", "client_id": null, "correlated_count": 0, "created_at": "2026-10-27T09:00:00Z", "created_by": "anna@example.com", "description": "Response times above 2 seconds since 08:40.", "duration_seconds": 0, "escalation_target_kind": "name", "escalation_target_label": "Default escalation", "escalation_target_name": "Default escalation", "heartbeat_id": null, "heartbeat_name": "", "id": "00000000-0000-4000-8000-000000000001", "metadata": {}, "monitor_id": "00000000-0000-4000-8000-000000000002", "monitor_name": "Marketing website", "notification_deliveries": [], "notified_kind": "nobody", "notified_label": "Nobody", "override_channel_ids": [], "resolved_at": null, "resolved_by_type": null, "resolved_by_user_id": null, "routing_target_id": "00000000-0000-4000-8000-000000000003", "routing_type": "escalation_policy", "severity": "warning", "started_at": "2026-10-27T09:00:00Z", "started_by_type": "user", "started_by_user_id": "00000000-0000-4000-8000-000000000004", "status": "acknowledged", "title": "Checkout is slow", "updated_at": "2026-10-27T09:00:00Z", "updates": [ { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "comment", "id": "00000000-0000-4000-8000-000000000005", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "The provider confirms a partial outage.", "payload": {} }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "comment", "id": "00000000-0000-4000-8000-000000000006", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Looking into the payment provider.", "payload": {} }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "escalation_target_acknowledged", "id": "00000000-0000-4000-8000-000000000007", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "anna@example.com confirmed the manual escalation; the alert plan stopped", "payload": { "manual_escalation_id": "00000000-0000-4000-8000-000000000008", "source": "authenticated_user", "target_user_id": "00000000-0000-4000-8000-000000000004" } }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "escalated", "id": "00000000-0000-4000-8000-000000000008", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Escalated to anna@example.com; the alert plan continues until they confirm", "payload": { "alert_plan_continues": true, "channel_ids": [ "00000000-0000-4000-8000-000000000009" ], "manual_escalation_id": "00000000-0000-4000-8000-000000000008", "manual_escalation_step": 1000001, "request_key": "walk-60", "status": "waiting_for_target_confirmation", "target_label": "anna@example.com", "target_user_id": "00000000-0000-4000-8000-000000000004" } }, { "actor_label": "anna@example.com", "actor_type": "user", "actor_user_id": "00000000-0000-4000-8000-000000000004", "created_at": "2026-10-27T09:00:00Z", "event_type": "created", "id": "00000000-0000-4000-8000-000000000010", "incident_id": "00000000-0000-4000-8000-000000000001", "message": "Incident reported manually.", "message_code": "incident_created_manual", "payload": {} } ], "user_id": "00000000-0000-4000-8000-000000000004" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # List incident attachments — API reference > Returns uploaded files for the incident with filenames, media types, sizes, uploader IDs and pagination. Source: https://upfour.io/docs/api/incidents/list-incident-attachments [Developers](https://upfour.io/docs/api.md)/[Incidents](https://upfour.io/docs/api/incidents.md) Returns uploaded files for the incident with filenames, media types, sizes, uploader IDs and pagination. GET https://app.upfour.io/v1/incidents/{incidentID}/attachments scope incident.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `incidentID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | IncidentAttachment[] · nullable | | | Each item · 8 fields FieldTypeDescription `created_at` string (date-time) `file_size` integer `filename` string `id` uuid `incident_id` uuid `mime_type` string `storage_key` string The server-side storage key of the file. Download it from the attachment endpoint instead. `uploaded_by` uuid · nullable | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousAdd an incident update](https://upfour.io/docs/api/incidents/add-an-incident-update.md) [NextUpload an incident attachment](https://upfour.io/docs/api/incidents/upload-an-incident-attachment.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/attachments" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/attachments", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/attachments", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/attachments", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "created_at": "2026-10-27T09:00:00Z", "file_size": 42, "filename": "checkout-error.txt", "id": "00000000-0000-4000-8000-000000000002", "incident_id": "00000000-0000-4000-8000-000000000001", "mime_type": "text/plain", "storage_key": "00000000-0000-4000-8000-000000000001/00000000-0000-4000-8000-000000000003_checkout-error.txt", "uploaded_by": "00000000-0000-4000-8000-000000000004" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Upload an incident attachment — API reference > Uploads one incident attachment and returns its stored filename, media type, size and identifier. Source: https://upfour.io/docs/api/incidents/upload-an-incident-attachment [Developers](https://upfour.io/docs/api.md)/[Incidents](https://upfour.io/docs/api/incidents.md) Uploads one incident attachment and returns its stored filename, media type, size and identifier. POST https://app.upfour.io/v1/incidents/{incidentID}/attachments scope incident.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Parameters | Field | Type | Description | | --- | --- | --- | | `incidentID` required | uuid | path parameter | ## Body `multipart/form-data` required filefile (required) ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | IncidentAttachment | | | IncidentAttachment · 8 fields FieldTypeDescription `created_at` string (date-time) `file_size` integer `filename` string `id` uuid `incident_id` uuid `mime_type` string `storage_key` string The server-side storage key of the file. Download it from the attachment endpoint instead. `uploaded_by` uuid · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [attachment_empty](https://upfour.io/docs/api/errors#code-attachment_empty) | The attachment file is empty. **When:** Uploading an incident attachment of zero bytes. **Fix:** Upload a file that has content. Stable codes next to the English message: `attachment_empty` (the file has no content), `attachment_type_unsupported` (only images, PDF, text, CSV and JSON can be attached), `upload_no_file` (the form has no `file` part), `upload_invalid` (the multipart form could not be read). | | 413 | [attachment_too_large](https://upfour.io/docs/api/errors#code-attachment_too_large) | The attachment is over the size limit. **When:** Uploading an incident attachment larger than the limit; details.max_bytes gives it. **Fix:** Compress or split the file below details.max_bytes. `attachment_too_large`: the file is larger than 10 MB (`error.details.max_bytes`). A proxy in front of the API may answer 413 with a plain HTML body and no code. | | 400 | [attachment_type_unsupported](https://upfour.io/docs/api/errors#code-attachment_type_unsupported) | The attachment's file type is not accepted. **When:** Uploading an incident attachment whose MIME type is not on the allowed list. **Fix:** Upload a file of an allowed type. | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 402 | [billing_action_unavailable](https://upfour.io/docs/api/errors#code-billing_action_unavailable) | The Company's billing state does not allow this action. **When:** A metered or limited action while the billing state, such as paused or without credit, blocks it; details holds the action and billing_state. **Fix:** Top up the credit or change the plan, then retry. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 503 | [quota_gate_unavailable](https://upfour.io/docs/api/errors#code-quota_gate_unavailable) | The limit check itself failed, so the action was not carried out. **When:** The quota service could not decide while the request ran. **Fix:** Retry in a moment; nothing was changed. | | 403 / 429 | [quota_limit_reached](https://upfour.io/docs/api/errors#code-quota_limit_reached) | A usage or inventory limit of the Company was reached. **When:** Counters and concurrency limits answer 429 with Retry-After, inventory and capacity limits answer 403; details holds purpose, limit and window. **Fix:** On 429 wait Retry-After seconds; on 403 free something up, request a higher limit where details.raise_request is true, or buy an extension. | | 200 / 410 / 413 / 422 | [request_failed](https://upfour.io/docs/api/errors#code-request_failed) | The request could not be completed. | | 503 | [service_unavailable](https://upfour.io/docs/api/errors#code-service_unavailable) | The service or a required dependency is temporarily unavailable. | | 400 | [upload_invalid](https://upfour.io/docs/api/errors#code-upload_invalid) | The upload could not be read as a multipart form. **When:** A body that is not a valid multipart form, or whose file cannot be parsed. **Fix:** Send a well-formed multipart/form-data request. | | 400 | [upload_no_file](https://upfour.io/docs/api/errors#code-upload_no_file) | The multipart request has no file part. **When:** An upload request without a file field. **Fix:** Send the file as multipart/form-data in the field the operation names. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList incident attachments](https://upfour.io/docs/api/incidents/list-incident-attachments.md) [NextDelete an incident attachment](https://upfour.io/docs/api/incidents/delete-an-incident-attachment.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/attachments" \ -H "Authorization: Bearer $UP4_API_KEY" \ -F "file=@./file.png" ``` ### JavaScript ```javascript import { readFile } from "node:fs/promises"; const fileBytes = await readFile("./file.png"); const form = new FormData(); form.append("file", new Blob([fileBytes]), "file.png"); const res = await fetch("https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/attachments", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, body: form, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/attachments", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, files={"file": open("./file.png", "rb")}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "bytes" "fmt" "io" "mime/multipart" "net/http" "os" ) func main() { var buf bytes.Buffer w := multipart.NewWriter(&buf) part, _ := w.CreateFormFile("file", "file.png") file, _ := os.Open("./file.png") io.Copy(part, file) w.Close() req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/attachments", &buf) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", w.FormDataContentType()) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], CURLOPT_POSTFIELDS => ["file" => new CURLFile("./file.png")], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "file_size": 42, "filename": "checkout-error.txt", "id": "00000000-0000-4000-8000-000000000002", "incident_id": "00000000-0000-4000-8000-000000000001", "mime_type": "text/plain", "storage_key": "00000000-0000-4000-8000-000000000001/00000000-0000-4000-8000-000000000003_checkout-error.txt", "uploaded_by": "00000000-0000-4000-8000-000000000004" } } ``` ### 200 ```json { "error": { "code": "request_failed", "message": "The request could not be completed.", "request_id": "req_…" } } ``` ### 400 ```json { "error": { "code": "attachment_empty", "message": "The attachment file is empty.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "billing_action_unavailable", "message": "The Company's billing state does not allow this action.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` ### 410 ```json { "error": { "code": "request_failed", "message": "The request could not be completed.", "request_id": "req_…" } } ``` ### 413 ```json { "error": { "code": "attachment_too_large", "message": "The attachment is over the size limit.", "request_id": "req_…" } } ``` ### 422 ```json { "error": { "code": "request_failed", "message": "The request could not be completed.", "request_id": "req_…" } } ``` ### 429 ```json { "error": { "code": "quota_limit_reached", "message": "A usage or inventory limit of the Company was reached.", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "quota_gate_unavailable", "message": "The limit check itself failed, so the action was not carried out.", "request_id": "req_…" } } ``` --- # Delete an incident attachment — API reference > Deletes one incident attachment and returns status 204 with no response body. Source: https://upfour.io/docs/api/incidents/delete-an-incident-attachment [Developers](https://upfour.io/docs/api.md)/[Incidents](https://upfour.io/docs/api/incidents.md) Deletes one incident attachment and returns status 204 with no response body. DELETE https://app.upfour.io/v1/incidents/{incidentID}/attachments/{attachmentID} scope incident.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `incidentID` required | uuid | path parameter | | `attachmentID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpload an incident attachment](https://upfour.io/docs/api/incidents/upload-an-incident-attachment.md) [NextGet an incident attachment](https://upfour.io/docs/api/incidents/get-an-incident-attachment.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/attachments/00000000-0000-4000-8000-000000000002" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/attachments/00000000-0000-4000-8000-000000000002", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/attachments/00000000-0000-4000-8000-000000000002", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/attachments/00000000-0000-4000-8000-000000000002", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Get an incident attachment — API reference > Returns the stored file with the content type recorded at upload. Source: https://upfour.io/docs/api/incidents/get-an-incident-attachment [Developers](https://upfour.io/docs/api.md)/[Incidents](https://upfour.io/docs/api/incidents.md) Returns the stored file with the content type recorded at upload. GET https://app.upfour.io/v1/incidents/{incidentID}/attachments/{attachmentID} scope incident.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `incidentID` required | uuid | path parameter | | `attachmentID` required | uuid | path parameter | ## Returns A successful request returns `200` with \*/\*. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousDelete an incident attachment](https://upfour.io/docs/api/incidents/delete-an-incident-attachment.md) [NextGet an incident domain case](https://upfour.io/docs/api/incidents/get-an-incident-domain-case.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/incidents/3f6c1a52-8b1e-4f0d-9a43-2c5d7e8f1a90/attachments/3f6c1a52-8b1e-4f0d-9a43-2c5d7e8f1a90" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/incidents/3f6c1a52-8b1e-4f0d-9a43-2c5d7e8f1a90/attachments/3f6c1a52-8b1e-4f0d-9a43-2c5d7e8f1a90", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.text()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/incidents/3f6c1a52-8b1e-4f0d-9a43-2c5d7e8f1a90/attachments/3f6c1a52-8b1e-4f0d-9a43-2c5d7e8f1a90", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.text) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/incidents/3f6c1a52-8b1e-4f0d-9a43-2c5d7e8f1a90/attachments/3f6c1a52-8b1e-4f0d-9a43-2c5d7e8f1a90", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ``` Successful response. The stored file with the content type recorded at upload. ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Get an incident domain case — API reference > Returns the incident's domain case, or null when no domain case exists. Source: https://upfour.io/docs/api/incidents/get-an-incident-domain-case [Developers](https://upfour.io/docs/api.md)/[Incidents](https://upfour.io/docs/api/incidents.md) Returns the incident's domain case, or null when no domain case exists. GET https://app.upfour.io/v1/incidents/{incidentID}/domain-case scope incident.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `incidentID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | IncidentDomainCase · nullable | | | IncidentDomainCase · 21 fields FieldTypeDescription `end_reason` string · nullable one of`recovered` `partially_recovered` `members_left` `ended_at` string (date-time) · nullable `host_key` string `id` uuid `level` integer `max_level` integer `members` IncidentDomainCaseMember[] · nullable Each item · 12 fields FieldTypeDescription `cause_class` string one of`dns` `tls` `timeout` `network` `http_5xx` `cause_text` string `detected_at` string (date-time) `incident_id` uuid · nullable `is_current` boolean `is_start_page` boolean `monitor_id` uuid `monitor_type` string `name` string `recovered_at` string (date-time) · nullable `state` string one of`down` `recovered` `left` `target` string `monitored_pages` integer `opened_at` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet an incident attachment](https://upfour.io/docs/api/incidents/get-an-incident-attachment.md) [NextGet an incident group](https://upfour.io/docs/api/incidents/get-an-incident-group.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/domain-case" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/domain-case", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/domain-case", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/domain-case", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": null } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Get an incident group — API reference > Returns the incident group, or null when the incident belongs to no group. Source: https://upfour.io/docs/api/incidents/get-an-incident-group [Developers](https://upfour.io/docs/api.md)/[Incidents](https://upfour.io/docs/api/incidents.md) Returns the incident group, or null when the incident belongs to no group. GET https://app.upfour.io/v1/incidents/{incidentID}/group scope incident.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `incidentID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | IncidentGroup · nullable | | | IncidentGroup · 20 fields FieldTypeDescription `client_id` uuid · nullable `correlation_key` string one of`dns` `tls` `timeout` `network` `content` `http_5xx` `http_4xx` `availability` `id` uuid `last_incident_at` string (date-time) `members` IncidentGroupMember[] · nullable Each item · 10 fields FieldTypeDescription `incident_id` uuid `joined_at` string (date-time) `monitor_id` uuid · nullable `monitor_name` string `recovered_at` string (date-time) · nullable `resolved_at` string (date-time) · nullable `severity` string one of`critical` `warning` `info` `started_at` string (date-time) `status` string one of`open` `acknowledged` `resolved` `title` string `organization_id` uuid `resolved_at` string (date-time) · nullable `started_at` string (date-time) `status` string one of`open` `resolved` `title` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet an incident domain case](https://upfour.io/docs/api/incidents/get-an-incident-domain-case.md) [NextGet incident AI details](https://upfour.io/docs/api/incidents/get-incident-ai-details.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/group" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/group", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/group", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/group", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": null } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Get incident AI details — API reference > Returns AI artifacts and current monthly call and budget usage. Source: https://upfour.io/docs/api/incidents/get-incident-ai-details [Developers](https://upfour.io/docs/api.md)/[Incidents](https://upfour.io/docs/api/incidents.md) Returns AI artifacts and current monthly call and budget usage. GET https://app.upfour.io/v1/incidents/{incidentID}/ai scope incident.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `incidentID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | IncidentAIResponse | | | IncidentAIResponse · 26 fields FieldTypeDescription `artifacts` IncidentAIArtifact[] · nullable Each item · 16 fields FieldTypeDescription `automatic` boolean `billed_cost_cents` integer `content_markdown` string `created_at` string (date-time) `generated_by` uuid · nullable `id` uuid `incident_id` uuid `input_tokens` integer `kind` string one of`summary` `postmortem` `model` string `output_tokens` integer `provider_cost_cents` number `rated_at` string (date-time) · nullable `rated_by` uuid · nullable `rating` integer · nullable 1 (helpful), -1 (not helpful) or null until rated. `updated_at` string (date-time) `usage` AIUsageSummary AIUsageSummary · 8 fields FieldTypeDescription `call_cap` integer The monthly call limit in force: the Company's own value, never above platform_call_cap. `calls` integer `cap_cents` integer The monthly budget in force: the Company's own value, never above platform_cap_cents. `period_resets_at` string (date-time) `period_started_at` string (date-time) `platform_call_cap` integer `platform_cap_cents` integer `used_cents` integer This month's AI usage in cents, at the price the Company pays. | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 402 | [ai_billing_blocked](https://upfour.io/docs/api/errors#code-ai_billing_blocked) | AI features are switched off because the Company has no active credit. **When:** An AI summary or postmortem request while the Company has no active credit. **Fix:** Top up the credit, then retry. | | 422 | [ai_budget_above_platform_limit](https://upfour.io/docs/api/errors#code-ai_budget_above_platform_limit) | The monthly AI budget is above the platform limit. **When:** Saving an AI budget larger than the platform allows; details.limit gives the maximum. **Fix:** Set a budget at or below details.limit. | | 422 | [ai_call_cap_above_platform_limit](https://upfour.io/docs/api/errors#code-ai_call_cap_above_platform_limit) | The monthly AI call cap is above the platform limit. **When:** Saving an AI call cap larger than the platform allows; details.limit gives the maximum. **Fix:** Set a cap at or below details.limit. | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 409 | [conflict](https://upfour.io/docs/api/errors#code-conflict) | The request conflicts with the current state. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 503 | [service_unavailable](https://upfour.io/docs/api/errors#code-service_unavailable) | The service or a required dependency is temporarily unavailable. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet an incident group](https://upfour.io/docs/api/incidents/get-an-incident-group.md) [NextGenerate an incident postmortem](https://upfour.io/docs/api/incidents/generate-an-incident-postmortem.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/ai" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/ai", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/ai", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/ai", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "artifacts": [], "usage": { "call_cap": 100, "calls": 0, "cap_cents": 500, "period_resets_at": "2026-10-31T16:55:00Z", "period_started_at": "2026-09-30T16:55:00Z", "platform_call_cap": 1000, "platform_cap_cents": 4000, "used_cents": 0 } } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "ai_billing_blocked", "message": "AI features are switched off because the Company has no active credit.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` ### 409 ```json { "error": { "code": "conflict", "message": "The request conflicts with the current state.", "request_id": "req_…" } } ``` ### 422 ```json { "error": { "code": "ai_budget_above_platform_limit", "message": "The monthly AI budget is above the platform limit.", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "service_unavailable", "message": "The service or a required dependency is temporarily unavailable.", "request_id": "req_…" } } ``` --- # Generate an incident postmortem — API reference > Generates a postmortem artifact for the incident and returns its content, model usage and rating state. Source: https://upfour.io/docs/api/incidents/generate-an-incident-postmortem [Developers](https://upfour.io/docs/api.md)/[Incidents](https://upfour.io/docs/api/incidents.md) Generates a postmortem artifact for the incident and returns its content, model usage and rating state. POST https://app.upfour.io/v1/incidents/{incidentID}/ai/postmortem scope incident.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Parameters | Field | Type | Description | | --- | --- | --- | | `incidentID` required | uuid | path parameter | ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | IncidentAIArtifact | | | IncidentAIArtifact · 16 fields FieldTypeDescription `automatic` boolean `billed_cost_cents` integer `content_markdown` string `created_at` string (date-time) `generated_by` uuid · nullable `id` uuid `incident_id` uuid `input_tokens` integer `kind` string one of`summary` `postmortem` `model` string `output_tokens` integer `provider_cost_cents` number `rated_at` string (date-time) · nullable `rated_by` uuid · nullable `rating` integer · nullable 1 (helpful), -1 (not helpful) or null until rated. `updated_at` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 402 | [ai_billing_blocked](https://upfour.io/docs/api/errors#code-ai_billing_blocked) | AI features are switched off because the Company has no active credit. **When:** An AI summary or postmortem request while the Company has no active credit. **Fix:** Top up the credit, then retry. `ai_billing_blocked`: AI features are switched off while the Company is paused or in its grace window. A used-up monthly AI budget also answers 402, with the body `{error, quota}`. | | 422 | [ai_budget_above_platform_limit](https://upfour.io/docs/api/errors#code-ai_budget_above_platform_limit) | The monthly AI budget is above the platform limit. **When:** Saving an AI budget larger than the platform allows; details.limit gives the maximum. **Fix:** Set a budget at or below details.limit. | | 422 | [ai_call_cap_above_platform_limit](https://upfour.io/docs/api/errors#code-ai_call_cap_above_platform_limit) | The monthly AI call cap is above the platform limit. **When:** Saving an AI call cap larger than the platform allows; details.limit gives the maximum. **Fix:** Set a cap at or below details.limit. | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 409 | [conflict](https://upfour.io/docs/api/errors#code-conflict) | The request conflicts with the current state. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 503 | [service_unavailable](https://upfour.io/docs/api/errors#code-service_unavailable) | The service or a required dependency is temporarily unavailable. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet incident AI details](https://upfour.io/docs/api/incidents/get-incident-ai-details.md) [NextGenerate an incident summary](https://upfour.io/docs/api/incidents/generate-an-incident-summary.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/ai/postmortem" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/ai/postmortem", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/ai/postmortem", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/ai/postmortem", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "automatic": false, "billed_cost_cents": 0, "content_markdown": "The checkout slowed down at 08:40 after the payment provider reported a partial outage. Response times recovered once the provider fixed it.", "created_at": "2026-10-27T09:00:00Z", "generated_by": "00000000-0000-4000-8000-000000000002", "id": "00000000-0000-4000-8000-000000000003", "incident_id": "00000000-0000-4000-8000-000000000001", "input_tokens": 420, "kind": "postmortem", "model": "example-model", "output_tokens": 60, "provider_cost_cents": 0.072, "rated_at": null, "rated_by": null, "rating": null, "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "ai_billing_blocked", "message": "AI features are switched off because the Company has no active credit.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` ### 409 ```json { "error": { "code": "conflict", "message": "The request conflicts with the current state.", "request_id": "req_…" } } ``` ### 422 ```json { "error": { "code": "ai_budget_above_platform_limit", "message": "The monthly AI budget is above the platform limit.", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "service_unavailable", "message": "The service or a required dependency is temporarily unavailable.", "request_id": "req_…" } } ``` --- # Generate an incident summary — API reference > Generates an AI summary artifact for the incident and returns its content, model usage and rating state. Source: https://upfour.io/docs/api/incidents/generate-an-incident-summary [Developers](https://upfour.io/docs/api.md)/[Incidents](https://upfour.io/docs/api/incidents.md) Generates an AI summary artifact for the incident and returns its content, model usage and rating state. POST https://app.upfour.io/v1/incidents/{incidentID}/ai/summary scope incident.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Parameters | Field | Type | Description | | --- | --- | --- | | `incidentID` required | uuid | path parameter | ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | IncidentAIArtifact | | | IncidentAIArtifact · 16 fields FieldTypeDescription `automatic` boolean `billed_cost_cents` integer `content_markdown` string `created_at` string (date-time) `generated_by` uuid · nullable `id` uuid `incident_id` uuid `input_tokens` integer `kind` string one of`summary` `postmortem` `model` string `output_tokens` integer `provider_cost_cents` number `rated_at` string (date-time) · nullable `rated_by` uuid · nullable `rating` integer · nullable 1 (helpful), -1 (not helpful) or null until rated. `updated_at` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 402 | [ai_billing_blocked](https://upfour.io/docs/api/errors#code-ai_billing_blocked) | AI features are switched off because the Company has no active credit. **When:** An AI summary or postmortem request while the Company has no active credit. **Fix:** Top up the credit, then retry. `ai_billing_blocked`: AI features are switched off while the Company is paused or in its grace window. A used-up monthly AI budget also answers 402, with the body `{error, quota}`. | | 422 | [ai_budget_above_platform_limit](https://upfour.io/docs/api/errors#code-ai_budget_above_platform_limit) | The monthly AI budget is above the platform limit. **When:** Saving an AI budget larger than the platform allows; details.limit gives the maximum. **Fix:** Set a budget at or below details.limit. | | 422 | [ai_call_cap_above_platform_limit](https://upfour.io/docs/api/errors#code-ai_call_cap_above_platform_limit) | The monthly AI call cap is above the platform limit. **When:** Saving an AI call cap larger than the platform allows; details.limit gives the maximum. **Fix:** Set a cap at or below details.limit. | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 409 | [conflict](https://upfour.io/docs/api/errors#code-conflict) | The request conflicts with the current state. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 503 | [service_unavailable](https://upfour.io/docs/api/errors#code-service_unavailable) | The service or a required dependency is temporarily unavailable. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGenerate an incident postmortem](https://upfour.io/docs/api/incidents/generate-an-incident-postmortem.md) [NextRate an incident AI result](https://upfour.io/docs/api/incidents/rate-an-incident-ai-result.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/ai/summary" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/ai/summary", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/ai/summary", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/ai/summary", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "automatic": false, "billed_cost_cents": 0, "content_markdown": "The checkout slowed down at 08:40 after the payment provider reported a partial outage. Response times recovered once the provider fixed it.", "created_at": "2026-10-27T09:00:00Z", "generated_by": "00000000-0000-4000-8000-000000000002", "id": "00000000-0000-4000-8000-000000000003", "incident_id": "00000000-0000-4000-8000-000000000001", "input_tokens": 420, "kind": "summary", "model": "example-model", "output_tokens": 60, "provider_cost_cents": 0.072, "rated_at": null, "rated_by": null, "rating": null, "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "ai_billing_blocked", "message": "AI features are switched off because the Company has no active credit.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` ### 409 ```json { "error": { "code": "conflict", "message": "The request conflicts with the current state.", "request_id": "req_…" } } ``` ### 422 ```json { "error": { "code": "ai_budget_above_platform_limit", "message": "The monthly AI budget is above the platform limit.", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "service_unavailable", "message": "The service or a required dependency is temporarily unavailable.", "request_id": "req_…" } } ``` --- # Rate an incident AI result — API reference > Records a helpful or not-helpful rating for a summary or postmortem and returns the rated artifact. Source: https://upfour.io/docs/api/incidents/rate-an-incident-ai-result [Developers](https://upfour.io/docs/api.md)/[Incidents](https://upfour.io/docs/api/incidents.md) Records a helpful or not-helpful rating for a summary or postmortem and returns the rated artifact. PUT https://app.upfour.io/v1/incidents/{incidentID}/ai/{kind}/rating scope incident.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `incidentID` required | uuid | path parameter | | `kind` required | string one of`summary` `postmortem` | path parameter · The AI text that is rated. | ## Body `application/json` required 1 field ratinginteger (required) 1 (helpful) or -1 (not helpful). ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | IncidentAIArtifact | | | IncidentAIArtifact · 16 fields FieldTypeDescription `automatic` boolean `billed_cost_cents` integer `content_markdown` string `created_at` string (date-time) `generated_by` uuid · nullable `id` uuid `incident_id` uuid `input_tokens` integer `kind` string one of`summary` `postmortem` `model` string `output_tokens` integer `provider_cost_cents` number `rated_at` string (date-time) · nullable `rated_by` uuid · nullable `rating` integer · nullable 1 (helpful), -1 (not helpful) or null until rated. `updated_at` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 402 | [ai_billing_blocked](https://upfour.io/docs/api/errors#code-ai_billing_blocked) | AI features are switched off because the Company has no active credit. **When:** An AI summary or postmortem request while the Company has no active credit. **Fix:** Top up the credit, then retry. | | 422 | [ai_budget_above_platform_limit](https://upfour.io/docs/api/errors#code-ai_budget_above_platform_limit) | The monthly AI budget is above the platform limit. **When:** Saving an AI budget larger than the platform allows; details.limit gives the maximum. **Fix:** Set a budget at or below details.limit. | | 422 | [ai_call_cap_above_platform_limit](https://upfour.io/docs/api/errors#code-ai_call_cap_above_platform_limit) | The monthly AI call cap is above the platform limit. **When:** Saving an AI call cap larger than the platform allows; details.limit gives the maximum. **Fix:** Set a cap at or below details.limit. | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 409 | [conflict](https://upfour.io/docs/api/errors#code-conflict) | The request conflicts with the current state. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 503 | [service_unavailable](https://upfour.io/docs/api/errors#code-service_unavailable) | The service or a required dependency is temporarily unavailable. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGenerate an incident summary](https://upfour.io/docs/api/incidents/generate-an-incident-summary.md) [NextGet incident playbook progress](https://upfour.io/docs/api/incidents/get-incident-playbook-progress.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/ai/summary/rating" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "rating": 1 }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/ai/summary/rating", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ rating: 1, }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/ai/summary/rating", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "rating": 1, }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "rating": 1 }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/ai/summary/rating", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "automatic": false, "billed_cost_cents": 0, "content_markdown": "The checkout slowed down at 08:40 after the payment provider reported a partial outage. Response times recovered once the provider fixed it.", "created_at": "2026-10-27T09:00:00Z", "generated_by": "00000000-0000-4000-8000-000000000002", "id": "00000000-0000-4000-8000-000000000003", "incident_id": "00000000-0000-4000-8000-000000000001", "input_tokens": 420, "kind": "summary", "model": "example-model", "output_tokens": 60, "provider_cost_cents": 0.072, "rated_at": "2026-10-27T09:00:00Z", "rated_by": "00000000-0000-4000-8000-000000000002", "rating": 1, "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "ai_billing_blocked", "message": "AI features are switched off because the Company has no active credit.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` ### 409 ```json { "error": { "code": "conflict", "message": "The request conflicts with the current state.", "request_id": "req_…" } } ``` ### 422 ```json { "error": { "code": "ai_budget_above_platform_limit", "message": "The monthly AI budget is above the platform limit.", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "service_unavailable", "message": "The service or a required dependency is temporarily unavailable.", "request_id": "req_…" } } ``` --- # Get incident playbook progress — API reference > Returns completion records for the incident's playbook steps. Source: https://upfour.io/docs/api/incidents/get-incident-playbook-progress [Developers](https://upfour.io/docs/api.md)/[Incidents](https://upfour.io/docs/api/incidents.md) Returns completion records for the incident's playbook steps. GET https://app.upfour.io/v1/incidents/{incidentID}/runbook-progress scope incident.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `incidentID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | IncidentRunbookProgressResponse | | | IncidentRunbookProgressResponse · 7 fields FieldTypeDescription `completions` IncidentRunbookStepCompletion[] · nullable Each item · 6 fields FieldTypeDescription `completed_at` string (date-time) `completed_by` uuid · nullable `completed_by_name` string The email address of the user who last changed the step; empty when unknown. `incident_id` uuid `runbook_id` uuid `step_id` uuid | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousRate an incident AI result](https://upfour.io/docs/api/incidents/rate-an-incident-ai-result.md) [NextUpdate incident playbook step completion](https://upfour.io/docs/api/incidents/update-incident-playbook-step-completion.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/runbook-progress" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/runbook-progress", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/runbook-progress", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/runbook-progress", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "completions": [] } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Update incident playbook step completion — API reference > Sets whether a playbook step is completed and returns the result. Source: https://upfour.io/docs/api/incidents/update-incident-playbook-step-completion [Developers](https://upfour.io/docs/api.md)/[Incidents](https://upfour.io/docs/api/incidents.md) Sets whether a playbook step is completed and returns the result. PUT https://app.upfour.io/v1/incidents/{incidentID}/runbook-steps/{stepID}/completion scope incident.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `incidentID` required | uuid | path parameter | | `stepID` required | uuid | path parameter | ## Body `application/json` required 1 field All other fields1 fields completedboolean ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | RunbookStepCompletionResponse | | | RunbookStepCompletionResponse · 1 fields FieldTypeDescription `success` boolean | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet incident playbook progress](https://upfour.io/docs/api/incidents/get-incident-playbook-progress.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/runbook-steps/00000000-0000-4000-8000-000000000002/completion" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "completed": true }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/runbook-steps/00000000-0000-4000-8000-000000000002/completion", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ completed: true, }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/runbook-steps/00000000-0000-4000-8000-000000000002/completion", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "completed": True, }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "completed": true }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/incidents/00000000-0000-4000-8000-000000000001/runbook-steps/00000000-0000-4000-8000-000000000002/completion", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "success": true } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Severities — API reference > Your incident severity ladder (SEV1…), fully customizable. REST endpoints with request and response fields, examples in curl, JavaScript, Python, Go and PHP, and an in-browser Try it console. Source: https://upfour.io/docs/api/severities [Developers](https://upfour.io/docs/api.md)/Alerting & response Your incident severity ladder (SEV1…), fully customizable. Base URL `https://app.upfour.io/v1`. Send your key as `Authorization: Bearer `; every key acts on your real Company, so [test without paging anyone](https://upfour.io/docs/api/testing.md). New here? Start with the [quickstart](https://upfour.io/docs/api/quickstart.md). The examples read the key from `UP4_API_KEY`. ## Everyday [GET **List severities** /severities](https://upfour.io/docs/api/severities/list-severities.md) --- # List severities — API reference > Returns the available severity options with their ranks. Source: https://upfour.io/docs/api/severities/list-severities [Developers](https://upfour.io/docs/api.md)/[Severities](https://upfour.io/docs/api/severities.md) Returns the available severity options with their ranks. GET https://app.upfour.io/v1/severities scope incident.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `limit` optional | integer 1–250 default `50` | query parameter | | `cursor` optional | string | query parameter | | `client_id` optional | uuid | query parameter | | `all_clients` optional | boolean | query parameter | | `q` optional | string | query parameter | | `sort` optional | string | query parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | SeverityOption[] · nullable | | | Each item · 3 fields FieldTypeDescription `id` string one of`critical` `warning` `info` `name` string `rank` integer | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). The specification does not list additional operation-specific errors. Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/severities" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/severities", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/severities", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/severities", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "id": "critical", "name": "Critical", "rank": 1 }, { "id": "warning", "name": "Warning", "rank": 2 }, { "id": "info", "name": "Info", "rank": 3 } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` --- # Severity groups — API reference > Group severities into reusable sets. REST endpoints with request and response fields, examples in curl, JavaScript, Python, Go and PHP, and an in-browser Try it console. Source: https://upfour.io/docs/api/severity-groups [Developers](https://upfour.io/docs/api.md)/Alerting & response Group severities into reusable sets. Base URL `https://app.upfour.io/v1`. Send your key as `Authorization: Bearer `; every key acts on your real Company, so [test without paging anyone](https://upfour.io/docs/api/testing.md). New here? Start with the [quickstart](https://upfour.io/docs/api/quickstart.md). The examples read the key from `UP4_API_KEY`. ## Everyday [GET **List severity groups** /severity-groups](https://upfour.io/docs/api/severity-groups/list-severity-groups.md) --- # List severity groups — API reference > Returns reusable severity groups with the severities each group selects. Source: https://upfour.io/docs/api/severity-groups/list-severity-groups [Developers](https://upfour.io/docs/api.md)/[Severity groups](https://upfour.io/docs/api/severity-groups.md) Returns reusable severity groups with the severities each group selects. GET https://app.upfour.io/v1/severity-groups scope incident.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `limit` optional | integer 1–250 default `50` | query parameter | | `cursor` optional | string | query parameter | | `client_id` optional | uuid | query parameter | | `all_clients` optional | boolean | query parameter | | `q` optional | string | query parameter | | `sort` optional | string | query parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | SeverityGroupOption[] · nullable | | | Each item · 3 fields FieldTypeDescription `id` string one of`critical` `non_critical` `all` `name` string `severities` string[] · nullable one of`critical` `warning` `info` The severities the group selects; its id is the value of the severity_group filter. | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). The specification does not list additional operation-specific errors. Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/severity-groups" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/severity-groups", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/severity-groups", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/severity-groups", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "id": "critical", "name": "Critical", "severities": [ "critical" ] }, { "id": "non_critical", "name": "Non-critical", "severities": [ "warning", "info" ] }, { "id": "all", "name": "All severities", "severities": [ "critical", "warning", "info" ] } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` --- # Alert channels — API reference > Where alerts go: email, Slack, Telegram, PagerDuty and friends. REST endpoints with request and response fields, examples in curl, JavaScript, Python, Go and PHP, and an in-browser Try it console. Source: https://upfour.io/docs/api/notification-channels [Developers](https://upfour.io/docs/api.md)/Alerting & response Where alerts go: email, Slack, Telegram, PagerDuty and friends. Base URL `https://app.upfour.io/v1`. Send your key as `Authorization: Bearer `; every key acts on your real Company, so [test without paging anyone](https://upfour.io/docs/api/testing.md). New here? Start with the [quickstart](https://upfour.io/docs/api/quickstart.md). The examples read the key from `UP4_API_KEY`. ## Everyday [GET **List alert channels** /notification_channels](https://upfour.io/docs/api/notification-channels/list-alert-channels.md)[POST **Create an alert channel** /notification_channels](https://upfour.io/docs/api/notification-channels/create-an-alert-channel.md)[GET **Get an alert channel** /notification_channels/{channelID}](https://upfour.io/docs/api/notification-channels/get-an-alert-channel.md)[PUT **Update an alert channel** /notification_channels/{channelID}](https://upfour.io/docs/api/notification-channels/update-an-alert-channel.md) ## Control [DEL **Delete an alert channel** /notification_channels/{channelID}](https://upfour.io/docs/api/notification-channels/delete-an-alert-channel.md)[POST **Test an alert channel** /notification_channels/{channelID}/test](https://upfour.io/docs/api/notification-channels/test-an-alert-channel.md) --- # List alert channels — API reference > Lists alert channels in scope with pagination. Source: https://upfour.io/docs/api/notification-channels/list-alert-channels [Developers](https://upfour.io/docs/api.md)/[Alert channels](https://upfour.io/docs/api/notification-channels.md) Lists alert channels in scope with pagination. GET https://app.upfour.io/v1/notification_channels scope notification_channel.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `limit` optional | integer 1–250 default `50` | query parameter | | `cursor` optional | string | query parameter | | `client_id` optional | uuid | query parameter | | `all_clients` optional | boolean | query parameter | | `q` optional | string | query parameter | | `sort` optional | string | query parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | NotificationChannelResponse[] · nullable | | | Each item · 17 fields FieldTypeDescription `config` object free-form keys The keys of the channel's type as sent on create. Secret values (auth_token, secret, webhook_url, bot_token, routing_key, api_key, license_key, api_token, app_token, user_key, access_token) are masked: \*\*\*\*\*\* followed by their last four characters when longer than eight characters, otherwise only \*\*\*\*\*\*. `created_at` string (date-time) `id` uuid `monitor_count` integer Monitors that route to this channel; 0 means nothing uses it. Only filled in the list. `name` string `scope` string one of`organization` `client` `team` `type` string one of`email` `sms` `voice` `webhook` `slack` `telegram` `msteams` `discord` `pagerduty` `zapier` `datadog` `newrelic` `jira` `grafana` `ntfy` `pushover` `updated_at` string (date-time) `user_id` uuid `client_id` uuid `last_delivery_at` string (date-time) Last successful delivery. Filled in the list. `last_failure_at` string (date-time) Last failed delivery attempt. Filled in the list. `last_failure_reason` string `last_voice_at` string (date-time) `last_voice_status` string Status of the last phone call, voice channels only. Filled in the list. `organization_id` uuid Company the channel belongs to. `team_id` uuid | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [NextCreate an alert channel](https://upfour.io/docs/api/notification-channels/create-an-alert-channel.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/notification_channels" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/notification_channels", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/notification_channels", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/notification_channels", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "config": { "email": "oncall@example.com" }, "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000001", "monitor_count": 0, "name": "On-call email", "organization_id": "00000000-0000-4000-8000-000000000002", "scope": "organization", "team_id": "00000000-0000-4000-8000-000000000003", "type": "email", "updated_at": "2026-10-27T09:00:00Z", "user_id": "00000000-0000-4000-8000-000000000004" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Create an alert channel — API reference > Creates an alert channel with type-specific configuration and an organization, client or team scope. Source: https://upfour.io/docs/api/notification-channels/create-an-alert-channel [Developers](https://upfour.io/docs/api.md)/[Alert channels](https://upfour.io/docs/api/notification-channels.md) Creates an alert channel with type-specific configuration and an organization, client or team scope. POST https://app.upfour.io/v1/notification_channels scope notification_channel.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Body `application/json` required 6 fields configobject (required) Depends on the channel type; the variants below list the keys per type. Keys marked secret are stored encrypted and returned masked. Possible shapes 16 `email` · object type email. | Field | Type | Description | | --- | --- | --- | | `email` required | string | Recipient email address. In the app: **Email address** | | `language` optional | string one of`en` `de` `""` | Language of the notifications; empty inherits the company language. | | `send_individually` optional | boolean | Skip the digest for this channel and send every notification on its own. In the app: **Send every notice on its own** | `sms` · object type sms (Twilio). | Field | Type | Description | | --- | --- | --- | | `account_sid` required | string | Twilio Account SID. | | `auth_token` required | string | Twilio Auth Token of that region and account. Secret. | | `from_number` required | string pattern `^\+[0-9]{7,15}$` | Sender number in E.164 format. | | `to_number` required | string pattern `^\+[0-9]{7,15}$` | Recipient number in E.164 format. | | `language` optional | string one of`en` `de` `""` | Language of the notifications; empty inherits the company language. | | `region` optional | string one of`us1` `ie1` `""` | Twilio region of the account; empty means us1\. ie1 does not take numbers starting with +1. | `voice` · object type voice (Twilio phone call). | Field | Type | Description | | --- | --- | --- | | `account_sid` required | string pattern `^AC[0-9a-fA-F]{32}$` | Twilio Account SID (AC followed by 32 hex digits). | | `auth_token` required | string min length 16 | Twilio Auth Token of that region and account, without surrounding spaces. Secret. | | `from_number` required | string pattern `^\+[0-9]{7,15}$` | Sender number in E.164 format. | | `to_number` required | string pattern `^\+[0-9]{7,15}$` | Recipient number in E.164 format. | | `language` optional | string one of`en` `de` `""` | Language of the notifications; empty inherits the company language. | | `region` optional | string one of`us1` `ie1` `au1` `""` | Twilio region of the account; empty means us1. | `webhook` · object type webhook. | Field | Type | Description | | --- | --- | --- | | `url` required | string (uri) pattern `^https://` | https URL that receives the alert. In the app: **Webhook URL** | | `secret` optional | string | Signs the request in X-Up4-Signature when set. Secret. In the app: **Signing secret** | `slack` · object type slack. | Field | Type | Description | | --- | --- | --- | | `webhook_url` required | string (uri) pattern `^https://` | Incoming-webhook URL; the host must end in slack.com. Secret. In the app: **Slack Webhook URL** | | `channel` optional | string | Channel to post to, if the webhook allows overriding it. | | `language` optional | string one of`en` `de` `""` | Language of the notifications; empty inherits the company language. | `telegram` · object type telegram. | Field | Type | Description | | --- | --- | --- | | `bot_token` required | string | Token of the Telegram bot. Secret. In the app: **Bot Token** | | `chat_id` required | string | Chat, group or channel ID the bot posts to. In the app: **Chat ID** | | `language` optional | string one of`en` `de` `""` | Language of the notifications; empty inherits the company language. | `msteams` · object type msteams. | Field | Type | Description | | --- | --- | --- | | `webhook_url` required | string (uri) pattern `^https://` | Incoming-webhook URL of the Teams channel. Secret. | | `language` optional | string one of`en` `de` `""` | Language of the notifications; empty inherits the company language. | `discord` · object type discord. | Field | Type | Description | | --- | --- | --- | | `webhook_url` required | string (uri) pattern `^https://` | Discord webhook URL. Secret. | | `language` optional | string one of`en` `de` `""` | Language of the notifications; empty inherits the company language. | `pagerduty` · object type pagerduty. | Field | Type | Description | | --- | --- | --- | | `routing_key` required | string | Integration (routing) key of the PagerDuty service. Secret. In the app: **Routing Key** | `zapier` · object type zapier. | Field | Type | Description | | --- | --- | --- | | `webhook_url` required | string (uri) pattern `^https://` | Zapier catch-hook URL. Secret. | `datadog` · object type datadog. | Field | Type | Description | | --- | --- | --- | | `api_key` required | string | Datadog API key. Secret. | | `region` optional | string one of`us1` `eu1` `us3` `us5` `ap1` `""` | Datadog site; empty means us1. | `newrelic` · object type newrelic. | Field | Type | Description | | --- | --- | --- | | `account_id` required | string | New Relic account ID. | | `license_key` required | string | New Relic license key. Secret. | | `region` optional | string one of`us` `eu` `""` | New Relic data centre; empty means us. | `jira` · object type jira. | Field | Type | Description | | --- | --- | --- | | `api_token` required | string | Jira API token. Secret. | | `base_url` required | string (uri) pattern `^https://` | https URL of the Jira site. | | `email` required | string | Account email of the API token. | | `issue_type` required | string | Issue type to create, e.g. Bug, Task or Story. | | `project_key` required | string pattern `^[A-Z][A-Z0-9_]{0,19}$` | Key of the project that receives the issues. | | `language` optional | string one of`en` `de` `""` | Language of the notifications; empty inherits the company language. | `grafana` · object type grafana. | Field | Type | Description | | --- | --- | --- | | `webhook_url` required | string (uri) pattern `^https?://` | Grafana webhook URL, http or https. Secret. | `ntfy` · object type ntfy. | Field | Type | Description | | --- | --- | --- | | `topic` required | string pattern `^[-_A-Za-z0-9]{1,64}$` | Topic to publish to. | | `access_token` optional | string | Access token of a protected topic. Secret. | | `language` optional | string one of`en` `de` `""` | Language of the notifications; empty inherits the company language. | | `priority` optional | string | min, low, default, high, max, urgent or 1 to 5. | | `server_url` optional | string (uri) pattern `^https://` | HTTPS origin of the ntfy server without credentials, query or fragment; empty means https://ntfy.sh. | `pushover` · object type pushover. | Field | Type | Description | | --- | --- | --- | | `app_token` required | string pattern `^[A-Za-z0-9]{30}$` | 30-character Pushover application token. Secret. | | `user_key` required | string pattern `^[A-Za-z0-9]{30}$` | 30-character Pushover user or group key. Secret. | | `device` optional | string | Device name to send to; empty reaches every device. | | `language` optional | string one of`en` `de` `""` | Language of the notifications; empty inherits the company language. | | `priority` optional | integer \-2–1 | Pushover priority from -2 to 1; emergency priority is not supported. | namestring (required) Display name of the channel. In the app: **Name** typestring (required) The channel type; it fixes which keys config takes and cannot be changed afterwards. emailsmsvoicewebhookslacktelegrammsteamsdiscordpagerdutyzapierdatadognewrelicjiragrafanantfypushover In the app: **Type** All other fields3 fields client_iduuid · nullable Required for scope client. scopestring Defaults to organization. client needs client_id, team needs team_id. Team API keys default to team and cannot create organization channels. organizationclientteam team_iduuid · nullable Required for scope team. ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | NotificationChannelResponse | | | NotificationChannelResponse · 17 fields FieldTypeDescription `config` object free-form keys The keys of the channel's type as sent on create. Secret values (auth_token, secret, webhook_url, bot_token, routing_key, api_key, license_key, api_token, app_token, user_key, access_token) are masked: \*\*\*\*\*\* followed by their last four characters when longer than eight characters, otherwise only \*\*\*\*\*\*. `created_at` string (date-time) `id` uuid `monitor_count` integer Monitors that route to this channel; 0 means nothing uses it. Only filled in the list. `name` string `scope` string one of`organization` `client` `team` `type` string one of`email` `sms` `voice` `webhook` `slack` `telegram` `msteams` `discord` `pagerduty` `zapier` `datadog` `newrelic` `jira` `grafana` `ntfy` `pushover` `updated_at` string (date-time) `user_id` uuid `client_id` uuid `last_delivery_at` string (date-time) Last successful delivery. Filled in the list. `last_failure_at` string (date-time) Last failed delivery attempt. Filled in the list. `last_failure_reason` string `last_voice_at` string (date-time) `last_voice_status` string Status of the last phone call, voice channels only. Filled in the list. `organization_id` uuid Company the channel belongs to. `team_id` uuid | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 503 | [notification_setup_incomplete](https://upfour.io/docs/api/errors#code-notification_setup_incomplete) | The Company's setup is incomplete, so the channel cannot be created. **When:** Creating an alert channel in a Company that has no default team. **Fix:** Contact support with the request_id so the setup can be completed. | | 402 | [payment_required](https://upfour.io/docs/api/errors#code-payment_required) | The account does not have the credit or plan required for this action. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList alert channels](https://upfour.io/docs/api/notification-channels/list-alert-channels.md) [NextGet an alert channel](https://upfour.io/docs/api/notification-channels/get-an-alert-channel.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/notification_channels" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "config": { "email": "oncall@example.com" }, "name": "On-call email", "type": "email" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/notification_channels", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ config: { email: "oncall@example.com", }, name: "On-call email", type: "email", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/notification_channels", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "config": { "email": "oncall@example.com", }, "name": "On-call email", "type": "email", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "config": { "email": "oncall@example.com" }, "name": "On-call email", "type": "email" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/notification_channels", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "config": { "email": "oncall@example.com" }, "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000001", "monitor_count": 0, "name": "On-call email", "organization_id": "00000000-0000-4000-8000-000000000002", "scope": "organization", "team_id": "00000000-0000-4000-8000-000000000003", "type": "email", "updated_at": "2026-10-27T09:00:00Z", "user_id": "00000000-0000-4000-8000-000000000004" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "payment_required", "message": "The account does not have the credit or plan required for this action.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "notification_setup_incomplete", "message": "The Company's setup is incomplete, so the channel cannot be created.", "request_id": "req_…" } } ``` --- # Get an alert channel — API reference > Returns one alert channel with its type, scope and configuration. Source: https://upfour.io/docs/api/notification-channels/get-an-alert-channel [Developers](https://upfour.io/docs/api.md)/[Alert channels](https://upfour.io/docs/api/notification-channels.md) Returns one alert channel with its type, scope and configuration. GET https://app.upfour.io/v1/notification_channels/{channelID} scope notification_channel.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `channelID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | NotificationChannelResponse | | | NotificationChannelResponse · 17 fields FieldTypeDescription `config` object free-form keys The keys of the channel's type as sent on create. Secret values (auth_token, secret, webhook_url, bot_token, routing_key, api_key, license_key, api_token, app_token, user_key, access_token) are masked: \*\*\*\*\*\* followed by their last four characters when longer than eight characters, otherwise only \*\*\*\*\*\*. `created_at` string (date-time) `id` uuid `monitor_count` integer Monitors that route to this channel; 0 means nothing uses it. Only filled in the list. `name` string `scope` string one of`organization` `client` `team` `type` string one of`email` `sms` `voice` `webhook` `slack` `telegram` `msteams` `discord` `pagerduty` `zapier` `datadog` `newrelic` `jira` `grafana` `ntfy` `pushover` `updated_at` string (date-time) `user_id` uuid `client_id` uuid `last_delivery_at` string (date-time) Last successful delivery. Filled in the list. `last_failure_at` string (date-time) Last failed delivery attempt. Filled in the list. `last_failure_reason` string `last_voice_at` string (date-time) `last_voice_status` string Status of the last phone call, voice channels only. Filled in the list. `organization_id` uuid Company the channel belongs to. `team_id` uuid | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousCreate an alert channel](https://upfour.io/docs/api/notification-channels/create-an-alert-channel.md) [NextUpdate an alert channel](https://upfour.io/docs/api/notification-channels/update-an-alert-channel.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/notification_channels/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/notification_channels/00000000-0000-4000-8000-000000000001", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/notification_channels/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/notification_channels/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "config": { "email": "oncall@example.com" }, "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000001", "monitor_count": 0, "name": "On-call email", "organization_id": "00000000-0000-4000-8000-000000000002", "scope": "organization", "team_id": "00000000-0000-4000-8000-000000000003", "type": "email", "updated_at": "2026-10-27T09:00:00Z", "user_id": "00000000-0000-4000-8000-000000000004" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Update an alert channel — API reference > Updates an alert channel's display name, scope or type-specific configuration. Source: https://upfour.io/docs/api/notification-channels/update-an-alert-channel [Developers](https://upfour.io/docs/api.md)/[Alert channels](https://upfour.io/docs/api/notification-channels.md) Updates an alert channel's display name, scope or type-specific configuration. PUT https://app.upfour.io/v1/notification_channels/{channelID} scope notification_channel.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `channelID` required | uuid | path parameter | ## Body `application/json` required 5 fields All other fields56 fields client_iduuid · nullable configobject Only the keys of the channel's type are read. Secret keys left empty or sent back as the masked value keep the stored secret; an empty object or null keeps the whole config. Possible shapes 16 `email` · object type email. | Field | Type | Description | | --- | --- | --- | | `email` required | string | Recipient email address. In the app: **Email address** | | `language` optional | string one of`en` `de` `""` | Language of the notifications; empty inherits the company language. | | `send_individually` optional | boolean | Skip the digest for this channel and send every notification on its own. In the app: **Send every notice on its own** | `sms` · object type sms (Twilio). | Field | Type | Description | | --- | --- | --- | | `account_sid` required | string | Twilio Account SID. | | `auth_token` required | string | Twilio Auth Token of that region and account. Secret. | | `from_number` required | string pattern `^\+[0-9]{7,15}$` | Sender number in E.164 format. | | `to_number` required | string pattern `^\+[0-9]{7,15}$` | Recipient number in E.164 format. | | `language` optional | string one of`en` `de` `""` | Language of the notifications; empty inherits the company language. | | `region` optional | string one of`us1` `ie1` `""` | Twilio region of the account; empty means us1\. ie1 does not take numbers starting with +1. | `voice` · object type voice (Twilio phone call). | Field | Type | Description | | --- | --- | --- | | `account_sid` required | string pattern `^AC[0-9a-fA-F]{32}$` | Twilio Account SID (AC followed by 32 hex digits). | | `auth_token` required | string min length 16 | Twilio Auth Token of that region and account, without surrounding spaces. Secret. | | `from_number` required | string pattern `^\+[0-9]{7,15}$` | Sender number in E.164 format. | | `to_number` required | string pattern `^\+[0-9]{7,15}$` | Recipient number in E.164 format. | | `language` optional | string one of`en` `de` `""` | Language of the notifications; empty inherits the company language. | | `region` optional | string one of`us1` `ie1` `au1` `""` | Twilio region of the account; empty means us1. | `webhook` · object type webhook. | Field | Type | Description | | --- | --- | --- | | `url` required | string (uri) pattern `^https://` | https URL that receives the alert. In the app: **Webhook URL** | | `secret` optional | string | Signs the request in X-Up4-Signature when set. Secret. In the app: **Signing secret** | `slack` · object type slack. | Field | Type | Description | | --- | --- | --- | | `webhook_url` required | string (uri) pattern `^https://` | Incoming-webhook URL; the host must end in slack.com. Secret. In the app: **Slack Webhook URL** | | `channel` optional | string | Channel to post to, if the webhook allows overriding it. | | `language` optional | string one of`en` `de` `""` | Language of the notifications; empty inherits the company language. | `telegram` · object type telegram. | Field | Type | Description | | --- | --- | --- | | `bot_token` required | string | Token of the Telegram bot. Secret. In the app: **Bot Token** | | `chat_id` required | string | Chat, group or channel ID the bot posts to. In the app: **Chat ID** | | `language` optional | string one of`en` `de` `""` | Language of the notifications; empty inherits the company language. | `msteams` · object type msteams. | Field | Type | Description | | --- | --- | --- | | `webhook_url` required | string (uri) pattern `^https://` | Incoming-webhook URL of the Teams channel. Secret. | | `language` optional | string one of`en` `de` `""` | Language of the notifications; empty inherits the company language. | `discord` · object type discord. | Field | Type | Description | | --- | --- | --- | | `webhook_url` required | string (uri) pattern `^https://` | Discord webhook URL. Secret. | | `language` optional | string one of`en` `de` `""` | Language of the notifications; empty inherits the company language. | `pagerduty` · object type pagerduty. | Field | Type | Description | | --- | --- | --- | | `routing_key` required | string | Integration (routing) key of the PagerDuty service. Secret. In the app: **Routing Key** | `zapier` · object type zapier. | Field | Type | Description | | --- | --- | --- | | `webhook_url` required | string (uri) pattern `^https://` | Zapier catch-hook URL. Secret. | `datadog` · object type datadog. | Field | Type | Description | | --- | --- | --- | | `api_key` required | string | Datadog API key. Secret. | | `region` optional | string one of`us1` `eu1` `us3` `us5` `ap1` `""` | Datadog site; empty means us1. | `newrelic` · object type newrelic. | Field | Type | Description | | --- | --- | --- | | `account_id` required | string | New Relic account ID. | | `license_key` required | string | New Relic license key. Secret. | | `region` optional | string one of`us` `eu` `""` | New Relic data centre; empty means us. | `jira` · object type jira. | Field | Type | Description | | --- | --- | --- | | `api_token` required | string | Jira API token. Secret. | | `base_url` required | string (uri) pattern `^https://` | https URL of the Jira site. | | `email` required | string | Account email of the API token. | | `issue_type` required | string | Issue type to create, e.g. Bug, Task or Story. | | `project_key` required | string pattern `^[A-Z][A-Z0-9_]{0,19}$` | Key of the project that receives the issues. | | `language` optional | string one of`en` `de` `""` | Language of the notifications; empty inherits the company language. | `grafana` · object type grafana. | Field | Type | Description | | --- | --- | --- | | `webhook_url` required | string (uri) pattern `^https?://` | Grafana webhook URL, http or https. Secret. | `ntfy` · object type ntfy. | Field | Type | Description | | --- | --- | --- | | `topic` required | string pattern `^[-_A-Za-z0-9]{1,64}$` | Topic to publish to. | | `access_token` optional | string | Access token of a protected topic. Secret. | | `language` optional | string one of`en` `de` `""` | Language of the notifications; empty inherits the company language. | | `priority` optional | string | min, low, default, high, max, urgent or 1 to 5. | | `server_url` optional | string (uri) pattern `^https://` | HTTPS origin of the ntfy server without credentials, query or fragment; empty means https://ntfy.sh. | `pushover` · object type pushover. | Field | Type | Description | | --- | --- | --- | | `app_token` required | string pattern `^[A-Za-z0-9]{30}$` | 30-character Pushover application token. Secret. | | `user_key` required | string pattern `^[A-Za-z0-9]{30}$` | 30-character Pushover user or group key. Secret. | | `device` optional | string | Device name to send to; empty reaches every device. | | `language` optional | string one of`en` `de` `""` | Language of the notifications; empty inherits the company language. | | `priority` optional | integer \-2–1 | Pushover priority from -2 to 1; emergency priority is not supported. | namestring A new display name; empty keeps the current one. In the app: **Name** scopestring · nullable Changes the scope together with client_id or team_id; leave all three out to keep it. organizationclientteam team_iduuid · nullable ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | NotificationChannelResponse | | | NotificationChannelResponse · 17 fields FieldTypeDescription `config` object free-form keys The keys of the channel's type as sent on create. Secret values (auth_token, secret, webhook_url, bot_token, routing_key, api_key, license_key, api_token, app_token, user_key, access_token) are masked: \*\*\*\*\*\* followed by their last four characters when longer than eight characters, otherwise only \*\*\*\*\*\*. `created_at` string (date-time) `id` uuid `monitor_count` integer Monitors that route to this channel; 0 means nothing uses it. Only filled in the list. `name` string `scope` string one of`organization` `client` `team` `type` string one of`email` `sms` `voice` `webhook` `slack` `telegram` `msteams` `discord` `pagerduty` `zapier` `datadog` `newrelic` `jira` `grafana` `ntfy` `pushover` `updated_at` string (date-time) `user_id` uuid `client_id` uuid `last_delivery_at` string (date-time) Last successful delivery. Filled in the list. `last_failure_at` string (date-time) Last failed delivery attempt. Filled in the list. `last_failure_reason` string `last_voice_at` string (date-time) `last_voice_status` string Status of the last phone call, voice channels only. Filled in the list. `organization_id` uuid Company the channel belongs to. `team_id` uuid | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet an alert channel](https://upfour.io/docs/api/notification-channels/get-an-alert-channel.md) [NextDelete an alert channel](https://upfour.io/docs/api/notification-channels/delete-an-alert-channel.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/notification_channels/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "config": { "email": "oncall@example.com" }, "name": "On-call email", "type": "email" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/notification_channels/00000000-0000-4000-8000-000000000001", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ config: { email: "oncall@example.com", }, name: "On-call email", type: "email", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/notification_channels/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "config": { "email": "oncall@example.com", }, "name": "On-call email", "type": "email", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "config": { "email": "oncall@example.com" }, "name": "On-call email", "type": "email" }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/notification_channels/00000000-0000-4000-8000-000000000001", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "config": { "email": "oncall@example.com" }, "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000001", "monitor_count": 0, "name": "On-call email", "organization_id": "00000000-0000-4000-8000-000000000002", "scope": "organization", "team_id": "00000000-0000-4000-8000-000000000003", "type": "email", "updated_at": "2026-10-27T09:00:00Z", "user_id": "00000000-0000-4000-8000-000000000004" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Delete an alert channel — API reference > Confirms deletion with an empty response body. Source: https://upfour.io/docs/api/notification-channels/delete-an-alert-channel [Developers](https://upfour.io/docs/api.md)/[Alert channels](https://upfour.io/docs/api/notification-channels.md) Confirms deletion with an empty response body. DELETE https://app.upfour.io/v1/notification_channels/{channelID} scope notification_channel.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `channelID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 409 | [channel_in_use](https://upfour.io/docs/api/errors#code-channel_in_use) | The alert channel is still used by monitors or alert plans, so it was not deleted. **When:** Deleting an alert channel that a monitor notifies or an alert plan step targets. `details` carries the counts `monitors` and `alert_plans`. **Fix:** Remove the channel from those monitors and alert plan steps first, then delete it. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpdate an alert channel](https://upfour.io/docs/api/notification-channels/update-an-alert-channel.md) [NextTest an alert channel](https://upfour.io/docs/api/notification-channels/test-an-alert-channel.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/notification_channels/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/notification_channels/00000000-0000-4000-8000-000000000001", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/notification_channels/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/notification_channels/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` ### 409 ```json { "error": { "code": "channel_in_use", "message": "The alert channel is still used by monitors or alert plans, so it was not deleted.", "request_id": "req_…" } } ``` --- # Test an alert channel — API reference > Sends a test notification through the selected alert channel. Source: https://upfour.io/docs/api/notification-channels/test-an-alert-channel [Developers](https://upfour.io/docs/api.md)/[Alert channels](https://upfour.io/docs/api/notification-channels.md) Sends a test notification through the selected alert channel. POST https://app.upfour.io/v1/notification_channels/{channelID}/test scope notification_channel.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `channelID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | NotificationChannelTestResponse | | | NotificationChannelTestResponse · 1 fields FieldTypeDescription `message` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 402 | [billing_action_unavailable](https://upfour.io/docs/api/errors#code-billing_action_unavailable) | The Company's billing state does not allow this action. **When:** A metered or limited action while the billing state, such as paused or without credit, blocks it; details holds the action and billing_state. **Fix:** Top up the credit or change the plan, then retry. | | 502 | [channel_test_failed](https://upfour.io/docs/api/errors#code-channel_test_failed) | The alert channel's provider could not deliver the test. **When:** Sending a test alert to a channel when the provider fails in a way that fits none of the other provider codes. **Fix:** Check the channel's address and settings, then test again. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 422 | [provider_auth_rejected](https://upfour.io/docs/api/errors#code-provider_auth_rejected) | The alert channel's provider rejected the credentials. **When:** A test alert whose provider answered 401 or 403. **Fix:** Check the token, secret or account details of the channel. | | 422 | [provider_rejected](https://upfour.io/docs/api/errors#code-provider_rejected) | The alert channel's provider rejected the test. **When:** A test alert whose provider answered 400, 404, 405, 410 or 422. **Fix:** Check the channel's address and settings. | | 504 | [provider_timeout](https://upfour.io/docs/api/errors#code-provider_timeout) | The alert channel's provider did not answer in time. **When:** A test alert whose provider took too long to respond. **Fix:** Check the address and try again later. | | 422 | [provider_unreachable](https://upfour.io/docs/api/errors#code-provider_unreachable) | The alert channel's address cannot be reached. **When:** A test alert to a host that does not resolve, refuses the connection, has an invalid URL or an invalid HTTPS certificate. **Fix:** Check the URL and its HTTPS certificate. | | 503 | [quota_gate_unavailable](https://upfour.io/docs/api/errors#code-quota_gate_unavailable) | The limit check itself failed, so the action was not carried out. **When:** The quota service could not decide while the request ran. **Fix:** Retry in a moment; nothing was changed. | | 403 / 429 | [quota_limit_reached](https://upfour.io/docs/api/errors#code-quota_limit_reached) | A usage or inventory limit of the Company was reached. **When:** Counters and concurrency limits answer 429 with Retry-After, inventory and capacity limits answer 403; details holds purpose, limit and window. **Fix:** On 429 wait Retry-After seconds; on 403 free something up, request a higher limit where details.raise_request is true, or buy an extension. | | 503 | [test_unavailable](https://upfour.io/docs/api/errors#code-test_unavailable) | Sending a test is not possible right now. **When:** A test alert request while the test sender cannot take it. **Fix:** Try again in a moment. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousDelete an alert channel](https://upfour.io/docs/api/notification-channels/delete-an-alert-channel.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/notification_channels/00000000-0000-4000-8000-000000000001/test" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/notification_channels/00000000-0000-4000-8000-000000000001/test", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/notification_channels/00000000-0000-4000-8000-000000000001/test", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/notification_channels/00000000-0000-4000-8000-000000000001/test", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "message": "Test notification sent successfully" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "billing_action_unavailable", "message": "The Company's billing state does not allow this action.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` ### 422 ```json { "error": { "code": "provider_auth_rejected", "message": "The alert channel's provider rejected the credentials.", "request_id": "req_…" } } ``` ### 429 ```json { "error": { "code": "quota_limit_reached", "message": "A usage or inventory limit of the Company was reached.", "request_id": "req_…" } } ``` ### 502 ```json { "error": { "code": "channel_test_failed", "message": "The alert channel's provider could not deliver the test.", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "quota_gate_unavailable", "message": "The limit check itself failed, so the action was not carried out.", "request_id": "req_…" } } ``` ### 504 ```json { "error": { "code": "provider_timeout", "message": "The alert channel's provider did not answer in time.", "request_id": "req_…" } } ``` --- # Alert plans (escalation policies) — API reference > Who is told, in what order, after how long. REST endpoints with request and response fields, examples in curl, JavaScript, Python, Go and PHP, and an in-browser Try it console. Source: https://upfour.io/docs/api/escalation-policies [Developers](https://upfour.io/docs/api.md)/Alerting & response Who is told, in what order, after how long. Base URL `https://app.upfour.io/v1`. Send your key as `Authorization: Bearer `; every key acts on your real Company, so [test without paging anyone](https://upfour.io/docs/api/testing.md). New here? Start with the [quickstart](https://upfour.io/docs/api/quickstart.md). The examples read the key from `UP4_API_KEY`. ## Everyday [GET **List alert plans** /escalation-policies](https://upfour.io/docs/api/escalation-policies/list-alert-plans.md)[POST **Create an alert plan** /escalation-policies](https://upfour.io/docs/api/escalation-policies/create-an-alert-plan.md)[GET **Get an alert plan** /escalation-policies/{policyID}](https://upfour.io/docs/api/escalation-policies/get-an-alert-plan.md)[PUT **Update an alert plan** /escalation-policies/{policyID}](https://upfour.io/docs/api/escalation-policies/update-an-alert-plan.md) ## Control [DEL **Delete an alert plan** /escalation-policies/{policyID}](https://upfour.io/docs/api/escalation-policies/delete-an-alert-plan.md)[POST **Test an alert plan** /escalation-policies/{policyID}/test](https://upfour.io/docs/api/escalation-policies/test-an-alert-plan.md) ## Results and history [GET **Get the latest alert plan test run** /escalation-policies/{policyID}/test-runs/latest](https://upfour.io/docs/api/escalation-policies/get-the-latest-alert-plan-test-run.md)[GET **Get an alert plan test run** /escalation-policies/{policyID}/test-runs/{runID}](https://upfour.io/docs/api/escalation-policies/get-an-alert-plan-test-run.md) --- # List alert plans — API reference > Returns alert plans in scope with ordered steps and pagination. Source: https://upfour.io/docs/api/escalation-policies/list-alert-plans [Developers](https://upfour.io/docs/api.md)/[Alert plans (escalation policies)](https://upfour.io/docs/api/escalation-policies.md) Returns alert plans in scope with ordered steps and pagination. GET https://app.upfour.io/v1/escalation-policies scope escalation_policy.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `limit` optional | integer 1–250 default `50` | query parameter | | `cursor` optional | string | query parameter | | `client_id` optional | uuid | query parameter | | `all_clients` optional | boolean | query parameter | | `q` optional | string | query parameter | | `sort` optional | string | query parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | EscalationPolicy[] · nullable | | | Each item · 18 fields FieldTypeDescription `created_at` string (date-time) `description` string `id` uuid `monitor_count` integer Monitors currently assigned to the plan. Only filled in the list. `name` string `organization_id` uuid `repeat_count` integer `repeat_interval_seconds` integer Pause between repeats of the plan; fixed at 15 minutes. `steps` EscalationStep[] · nullable Each item · 7 fields FieldTypeDescription `created_at` string (date-time) `escalation_policy_id` uuid `id` uuid `position` integer Zero-based position within the plan. `targets` string[] · nullable Alert channel IDs and on-call rotations written as oncall:. `urgency` string one of`high` `low` `sms` `voice` `wait_seconds` integer Seconds to wait after the previous step. `updated_at` string (date-time) `client_id` uuid | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [NextCreate an alert plan](https://upfour.io/docs/api/escalation-policies/create-an-alert-plan.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/escalation-policies" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/escalation-policies", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/escalation-policies", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/escalation-policies", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "created_at": "2026-10-27T09:00:00Z", "description": "Email the on-call mailbox, repeat once.", "id": "00000000-0000-4000-8000-000000000001", "monitor_count": 0, "name": "Default escalation", "organization_id": "00000000-0000-4000-8000-000000000002", "repeat_count": 1, "repeat_interval_seconds": 900, "steps": [ { "created_at": "2026-10-27T09:00:00Z", "escalation_policy_id": "00000000-0000-4000-8000-000000000001", "id": "00000000-0000-4000-8000-000000000003", "position": 0, "targets": [ "00000000-0000-4000-8000-000000000004" ], "urgency": "high", "wait_seconds": 0 } ], "updated_at": "2026-10-27T09:00:00Z" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` --- # Create an alert plan — API reference > Creates an alert plan with ordered notification steps and optional repeats. Source: https://upfour.io/docs/api/escalation-policies/create-an-alert-plan [Developers](https://upfour.io/docs/api.md)/[Alert plans (escalation policies)](https://upfour.io/docs/api/escalation-policies.md) Creates an alert plan with ordered notification steps and optional repeats. POST https://app.upfour.io/v1/escalation-policies scope escalation_policy.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Body `application/json` required 5 fields namestring (required) Unique within the company. In the app: **Alert plan name** stepsCreateEscalationStepInput[] (required) The steps in order, at least one. On update they replace the current steps. Nested fields 3 fields | Field | Type | Description | | --- | --- | --- | | `targets` optional | string[] | What the step notifies, at least one entry: alert channel IDs, or on-call rotations written as oncall:. A rotation is resolved when the step fires, to the person on call at that moment (an override wins) and their own alert channels. Channels and rotations must belong to the company; a client plan may also use that client's. In the app: **Who should we alert?** | | `urgency` optional | string one of`high` `low` `sms` `voice` | Which channel types the step uses: high every type, sms only SMS, voice only voice, low only email channels. Empty means high. In the app: **How urgent is this alert?** | | `wait_seconds` optional | integer | Seconds to wait after the previous step; from the second step on at least 60\. In the app: **Wait for** | All other fields3 fields client_iduuid · nullable descriptionstring repeat_countinteger How often the whole plan repeats after the last step, 0 to 8\. A value outside that range is stored as 0. In the app: **Reminders after the last step** ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | EscalationPolicy | | | EscalationPolicy · 18 fields FieldTypeDescription `created_at` string (date-time) `description` string `id` uuid `monitor_count` integer Monitors currently assigned to the plan. Only filled in the list. `name` string `organization_id` uuid `repeat_count` integer `repeat_interval_seconds` integer Pause between repeats of the plan; fixed at 15 minutes. `steps` EscalationStep[] · nullable Each item · 7 fields FieldTypeDescription `created_at` string (date-time) `escalation_policy_id` uuid `id` uuid `position` integer Zero-based position within the plan. `targets` string[] · nullable Alert channel IDs and on-call rotations written as oncall:. `urgency` string one of`high` `low` `sms` `voice` `wait_seconds` integer Seconds to wait after the previous step. `updated_at` string (date-time) `client_id` uuid | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 402 | [billing_action_unavailable](https://upfour.io/docs/api/errors#code-billing_action_unavailable) | The Company's billing state does not allow this action. **When:** A metered or limited action while the billing state, such as paused or without credit, blocks it; details holds the action and billing_state. **Fix:** Top up the credit or change the plan, then retry. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 503 | [quota_gate_unavailable](https://upfour.io/docs/api/errors#code-quota_gate_unavailable) | The limit check itself failed, so the action was not carried out. **When:** The quota service could not decide while the request ran. **Fix:** Retry in a moment; nothing was changed. | | 403 / 429 | [quota_limit_reached](https://upfour.io/docs/api/errors#code-quota_limit_reached) | A usage or inventory limit of the Company was reached. **When:** Counters and concurrency limits answer 429 with Retry-After, inventory and capacity limits answer 403; details holds purpose, limit and window. **Fix:** On 429 wait Retry-After seconds; on 403 free something up, request a higher limit where details.raise_request is true, or buy an extension. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList alert plans](https://upfour.io/docs/api/escalation-policies/list-alert-plans.md) [NextGet an alert plan](https://upfour.io/docs/api/escalation-policies/get-an-alert-plan.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/escalation-policies" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "description": "Email the on-call mailbox, repeat once.", "name": "Default escalation", "repeat_count": 1, "steps": [ { "targets": [ "00000000-0000-4000-8000-000000000001" ], "urgency": "high", "wait_seconds": 0 } ] }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/escalation-policies", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ description: "Email the on-call mailbox, repeat once.", name: "Default escalation", repeat_count: 1, steps: [ { targets: ["00000000-0000-4000-8000-000000000001"], urgency: "high", wait_seconds: 0, }, ], }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/escalation-policies", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "description": "Email the on-call mailbox, repeat once.", "name": "Default escalation", "repeat_count": 1, "steps": [ { "targets": ["00000000-0000-4000-8000-000000000001"], "urgency": "high", "wait_seconds": 0, }, ], }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "description": "Email the on-call mailbox, repeat once.", "name": "Default escalation", "repeat_count": 1, "steps": [ { "targets": [ "00000000-0000-4000-8000-000000000001" ], "urgency": "high", "wait_seconds": 0 } ] }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/escalation-policies", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "description": "Email the on-call mailbox, repeat once.", "id": "00000000-0000-4000-8000-000000000002", "monitor_count": 0, "name": "Default escalation", "organization_id": "00000000-0000-4000-8000-000000000003", "repeat_count": 1, "repeat_interval_seconds": 900, "steps": [ { "created_at": "2026-10-27T09:00:00Z", "escalation_policy_id": "00000000-0000-4000-8000-000000000002", "id": "00000000-0000-4000-8000-000000000004", "position": 0, "targets": [ "00000000-0000-4000-8000-000000000001" ], "urgency": "high", "wait_seconds": 0 } ], "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "billing_action_unavailable", "message": "The Company's billing state does not allow this action.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 429 ```json { "error": { "code": "quota_limit_reached", "message": "A usage or inventory limit of the Company was reached.", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "quota_gate_unavailable", "message": "The limit check itself failed, so the action was not carried out.", "request_id": "req_…" } } ``` --- # Get an alert plan — API reference > Returns one alert plan with its ordered steps and repeat settings. Source: https://upfour.io/docs/api/escalation-policies/get-an-alert-plan [Developers](https://upfour.io/docs/api.md)/[Alert plans (escalation policies)](https://upfour.io/docs/api/escalation-policies.md) Returns one alert plan with its ordered steps and repeat settings. GET https://app.upfour.io/v1/escalation-policies/{policyID} scope escalation_policy.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `policyID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | EscalationPolicy | | | EscalationPolicy · 18 fields FieldTypeDescription `created_at` string (date-time) `description` string `id` uuid `monitor_count` integer Monitors currently assigned to the plan. Only filled in the list. `name` string `organization_id` uuid `repeat_count` integer `repeat_interval_seconds` integer Pause between repeats of the plan; fixed at 15 minutes. `steps` EscalationStep[] · nullable Each item · 7 fields FieldTypeDescription `created_at` string (date-time) `escalation_policy_id` uuid `id` uuid `position` integer Zero-based position within the plan. `targets` string[] · nullable Alert channel IDs and on-call rotations written as oncall:. `urgency` string one of`high` `low` `sms` `voice` `wait_seconds` integer Seconds to wait after the previous step. `updated_at` string (date-time) `client_id` uuid | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousCreate an alert plan](https://upfour.io/docs/api/escalation-policies/create-an-alert-plan.md) [NextUpdate an alert plan](https://upfour.io/docs/api/escalation-policies/update-an-alert-plan.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/escalation-policies/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/escalation-policies/00000000-0000-4000-8000-000000000001", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/escalation-policies/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/escalation-policies/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "description": "Email the on-call mailbox, repeat once.", "id": "00000000-0000-4000-8000-000000000001", "monitor_count": 0, "name": "Default escalation", "organization_id": "00000000-0000-4000-8000-000000000002", "repeat_count": 1, "repeat_interval_seconds": 900, "steps": [ { "created_at": "2026-10-27T09:00:00Z", "escalation_policy_id": "00000000-0000-4000-8000-000000000001", "id": "00000000-0000-4000-8000-000000000003", "position": 0, "targets": [ "00000000-0000-4000-8000-000000000004" ], "urgency": "high", "wait_seconds": 0 } ], "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Update an alert plan — API reference > Replaces an alert plan's name, description, repeat count and steps. Source: https://upfour.io/docs/api/escalation-policies/update-an-alert-plan [Developers](https://upfour.io/docs/api.md)/[Alert plans (escalation policies)](https://upfour.io/docs/api/escalation-policies.md) Replaces an alert plan's name, description, repeat count and steps. PUT https://app.upfour.io/v1/escalation-policies/{policyID} scope escalation_policy.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `policyID` required | uuid | path parameter | ## Body `application/json` required 5 fields namestring (required) Unique within the company. In the app: **Alert plan name** stepsCreateEscalationStepInput[] (required) The steps in order, at least one. On update they replace the current steps. Nested fields 3 fields | Field | Type | Description | | --- | --- | --- | | `targets` optional | string[] | What the step notifies, at least one entry: alert channel IDs, or on-call rotations written as oncall:. A rotation is resolved when the step fires, to the person on call at that moment (an override wins) and their own alert channels. Channels and rotations must belong to the company; a client plan may also use that client's. In the app: **Who should we alert?** | | `urgency` optional | string one of`high` `low` `sms` `voice` | Which channel types the step uses: high every type, sms only SMS, voice only voice, low only email channels. Empty means high. In the app: **How urgent is this alert?** | | `wait_seconds` optional | integer | Seconds to wait after the previous step; from the second step on at least 60\. In the app: **Wait for** | All other fields3 fields client_iduuid · nullable descriptionstring repeat_countinteger How often the whole plan repeats after the last step, 0 to 8\. A value outside that range is stored as 0. In the app: **Reminders after the last step** ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | EscalationPolicy | | | EscalationPolicy · 18 fields FieldTypeDescription `created_at` string (date-time) `description` string `id` uuid `monitor_count` integer Monitors currently assigned to the plan. Only filled in the list. `name` string `organization_id` uuid `repeat_count` integer `repeat_interval_seconds` integer Pause between repeats of the plan; fixed at 15 minutes. `steps` EscalationStep[] · nullable Each item · 7 fields FieldTypeDescription `created_at` string (date-time) `escalation_policy_id` uuid `id` uuid `position` integer Zero-based position within the plan. `targets` string[] · nullable Alert channel IDs and on-call rotations written as oncall:. `urgency` string one of`high` `low` `sms` `voice` `wait_seconds` integer Seconds to wait after the previous step. `updated_at` string (date-time) `client_id` uuid | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 402 | [billing_action_unavailable](https://upfour.io/docs/api/errors#code-billing_action_unavailable) | The Company's billing state does not allow this action. **When:** A metered or limited action while the billing state, such as paused or without credit, blocks it; details holds the action and billing_state. **Fix:** Top up the credit or change the plan, then retry. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 503 | [quota_gate_unavailable](https://upfour.io/docs/api/errors#code-quota_gate_unavailable) | The limit check itself failed, so the action was not carried out. **When:** The quota service could not decide while the request ran. **Fix:** Retry in a moment; nothing was changed. | | 403 / 429 | [quota_limit_reached](https://upfour.io/docs/api/errors#code-quota_limit_reached) | A usage or inventory limit of the Company was reached. **When:** Counters and concurrency limits answer 429 with Retry-After, inventory and capacity limits answer 403; details holds purpose, limit and window. **Fix:** On 429 wait Retry-After seconds; on 403 free something up, request a higher limit where details.raise_request is true, or buy an extension. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet an alert plan](https://upfour.io/docs/api/escalation-policies/get-an-alert-plan.md) [NextDelete an alert plan](https://upfour.io/docs/api/escalation-policies/delete-an-alert-plan.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/escalation-policies/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "description": "Email the on-call mailbox, repeat twice.", "name": "Default escalation", "repeat_count": 2, "steps": [ { "targets": [ "00000000-0000-4000-8000-000000000002" ], "urgency": "high", "wait_seconds": 0 } ] }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/escalation-policies/00000000-0000-4000-8000-000000000001", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ description: "Email the on-call mailbox, repeat twice.", name: "Default escalation", repeat_count: 2, steps: [ { targets: ["00000000-0000-4000-8000-000000000002"], urgency: "high", wait_seconds: 0, }, ], }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/escalation-policies/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "description": "Email the on-call mailbox, repeat twice.", "name": "Default escalation", "repeat_count": 2, "steps": [ { "targets": ["00000000-0000-4000-8000-000000000002"], "urgency": "high", "wait_seconds": 0, }, ], }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "description": "Email the on-call mailbox, repeat twice.", "name": "Default escalation", "repeat_count": 2, "steps": [ { "targets": [ "00000000-0000-4000-8000-000000000002" ], "urgency": "high", "wait_seconds": 0 } ] }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/escalation-policies/00000000-0000-4000-8000-000000000001", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "description": "Email the on-call mailbox, repeat twice.", "id": "00000000-0000-4000-8000-000000000001", "monitor_count": 0, "name": "Default escalation", "organization_id": "00000000-0000-4000-8000-000000000003", "repeat_count": 2, "repeat_interval_seconds": 900, "steps": [ { "created_at": "2026-10-27T09:00:00Z", "escalation_policy_id": "00000000-0000-4000-8000-000000000001", "id": "00000000-0000-4000-8000-000000000004", "position": 0, "targets": [ "00000000-0000-4000-8000-000000000002" ], "urgency": "high", "wait_seconds": 0 } ], "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "billing_action_unavailable", "message": "The Company's billing state does not allow this action.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` ### 429 ```json { "error": { "code": "quota_limit_reached", "message": "A usage or inventory limit of the Company was reached.", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "quota_gate_unavailable", "message": "The limit check itself failed, so the action was not carried out.", "request_id": "req_…" } } ``` --- # Delete an alert plan — API reference > Confirms deletion with an empty response body. Source: https://upfour.io/docs/api/escalation-policies/delete-an-alert-plan [Developers](https://upfour.io/docs/api.md)/[Alert plans (escalation policies)](https://upfour.io/docs/api/escalation-policies.md) Confirms deletion with an empty response body. DELETE https://app.upfour.io/v1/escalation-policies/{policyID} scope escalation_policy.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `policyID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpdate an alert plan](https://upfour.io/docs/api/escalation-policies/update-an-alert-plan.md) [NextTest an alert plan](https://upfour.io/docs/api/escalation-policies/test-an-alert-plan.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/escalation-policies/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/escalation-policies/00000000-0000-4000-8000-000000000001", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/escalation-policies/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/escalation-policies/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Test an alert plan — API reference > Creates a test run for one step or the full alert plan. Source: https://upfour.io/docs/api/escalation-policies/test-an-alert-plan [Developers](https://upfour.io/docs/api.md)/[Alert plans (escalation policies)](https://upfour.io/docs/api/escalation-policies.md) Creates a test run for one step or the full alert plan. POST https://app.upfour.io/v1/escalation-policies/{policyID}/test scope escalation_policy.write stable [Idempotency-Key required](https://upfour.io/docs/api/idempotency.md) returns 202 ## Parameters | Field | Type | Description | | --- | --- | --- | | `policyID` required | uuid | path parameter | ## Body `application/json` optional 2 fields All other fields2 fields modestring step sends the step at step_position, full sends every step. Empty means step. stepfull step_positioninteger Zero-based position of the step to test; only used with mode step. ## Returns A successful request returns `202` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | EscalationPolicyTestRun | | | EscalationPolicyTestRun · 22 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `mode` string one of`step` `full` `policy_id` uuid `status` string one of`scheduled` `in_progress` `delivered` `failed` `steps` EscalationPolicyTestStep[] · nullable Each item · 16 fields FieldTypeDescription `position` integer Zero-based position of the step within the plan. `scheduled_at` string (date-time) `status` string one of`scheduled` `in_progress` `delivered` `failed` `targets` EscalationPolicyTestTarget[] · nullable Each item · 10 fields FieldTypeDescription `attempts` integer `channel_id` uuid Nil UUID for a rotation that reached nobody. `channel_name` string `channel_type` string `status` string one of`scheduled` `in_progress` `delivered` `failed` `unresolved` `error` string `person_name` string Who was on call for the rotation when the test started. `reason` string one of`nobody_on_call` `no_alert_channel` `rotation_unavailable` `rotation_id` uuid The rotation this channel was reached through, if any. `rotation_name` string `urgency` string one of`high` `low` `sms` `voice` `wait_seconds` integer | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousDelete an alert plan](https://upfour.io/docs/api/escalation-policies/delete-an-alert-plan.md) [NextGet the latest alert plan test run](https://upfour.io/docs/api/escalation-policies/get-the-latest-alert-plan-test-run.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/escalation-policies/00000000-0000-4000-8000-000000000001/test" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "mode": "full" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/escalation-policies/00000000-0000-4000-8000-000000000001/test", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ mode: "full", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/escalation-policies/00000000-0000-4000-8000-000000000001/test", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "mode": "full", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "mode": "full" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/escalation-policies/00000000-0000-4000-8000-000000000001/test", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 202 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000002", "mode": "full", "policy_id": "00000000-0000-4000-8000-000000000001", "status": "scheduled", "steps": [ { "position": 0, "scheduled_at": "2026-10-27T09:00:00Z", "status": "scheduled", "targets": [ { "attempts": 0, "channel_id": "00000000-0000-4000-8000-000000000003", "channel_name": "On-call email", "channel_type": "email", "status": "scheduled" } ], "urgency": "high", "wait_seconds": 0 } ] } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Get the latest alert plan test run — API reference > Returns the latest test run, or no content when the plan has not been tested. Source: https://upfour.io/docs/api/escalation-policies/get-the-latest-alert-plan-test-run [Developers](https://upfour.io/docs/api.md)/[Alert plans (escalation policies)](https://upfour.io/docs/api/escalation-policies.md) Returns the latest test run, or no content when the plan has not been tested. GET https://app.upfour.io/v1/escalation-policies/{policyID}/test-runs/latest scope escalation_policy.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `policyID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | EscalationPolicyTestRun | | | EscalationPolicyTestRun · 22 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `mode` string one of`step` `full` `policy_id` uuid `status` string one of`scheduled` `in_progress` `delivered` `failed` `steps` EscalationPolicyTestStep[] · nullable Each item · 16 fields FieldTypeDescription `position` integer Zero-based position of the step within the plan. `scheduled_at` string (date-time) `status` string one of`scheduled` `in_progress` `delivered` `failed` `targets` EscalationPolicyTestTarget[] · nullable Each item · 10 fields FieldTypeDescription `attempts` integer `channel_id` uuid Nil UUID for a rotation that reached nobody. `channel_name` string `channel_type` string `status` string one of`scheduled` `in_progress` `delivered` `failed` `unresolved` `error` string `person_name` string Who was on call for the rotation when the test started. `reason` string one of`nobody_on_call` `no_alert_channel` `rotation_unavailable` `rotation_id` uuid The rotation this channel was reached through, if any. `rotation_name` string `urgency` string one of`high` `low` `sms` `voice` `wait_seconds` integer | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousTest an alert plan](https://upfour.io/docs/api/escalation-policies/test-an-alert-plan.md) [NextGet an alert plan test run](https://upfour.io/docs/api/escalation-policies/get-an-alert-plan-test-run.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/escalation-policies/00000000-0000-4000-8000-000000000001/test-runs/latest" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/escalation-policies/00000000-0000-4000-8000-000000000001/test-runs/latest", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/escalation-policies/00000000-0000-4000-8000-000000000001/test-runs/latest", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/escalation-policies/00000000-0000-4000-8000-000000000001/test-runs/latest", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000002", "mode": "full", "policy_id": "00000000-0000-4000-8000-000000000001", "status": "scheduled", "steps": [ { "position": 0, "scheduled_at": "2026-10-27T09:00:00Z", "status": "scheduled", "targets": [ { "attempts": 0, "channel_id": "00000000-0000-4000-8000-000000000003", "channel_name": "On-call email", "channel_type": "email", "status": "scheduled" } ], "urgency": "high", "wait_seconds": 0 } ] } } ``` ### 204 ``` The policy has no test run yet. ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Get an alert plan test run — API reference > Returns the selected alert plan test run, or no content when none is available. Source: https://upfour.io/docs/api/escalation-policies/get-an-alert-plan-test-run [Developers](https://upfour.io/docs/api.md)/[Alert plans (escalation policies)](https://upfour.io/docs/api/escalation-policies.md) Returns the selected alert plan test run, or no content when none is available. GET https://app.upfour.io/v1/escalation-policies/{policyID}/test-runs/{runID} scope escalation_policy.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `policyID` required | uuid | path parameter | | `runID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | EscalationPolicyTestRun | | | EscalationPolicyTestRun · 22 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `mode` string one of`step` `full` `policy_id` uuid `status` string one of`scheduled` `in_progress` `delivered` `failed` `steps` EscalationPolicyTestStep[] · nullable Each item · 16 fields FieldTypeDescription `position` integer Zero-based position of the step within the plan. `scheduled_at` string (date-time) `status` string one of`scheduled` `in_progress` `delivered` `failed` `targets` EscalationPolicyTestTarget[] · nullable Each item · 10 fields FieldTypeDescription `attempts` integer `channel_id` uuid Nil UUID for a rotation that reached nobody. `channel_name` string `channel_type` string `status` string one of`scheduled` `in_progress` `delivered` `failed` `unresolved` `error` string `person_name` string Who was on call for the rotation when the test started. `reason` string one of`nobody_on_call` `no_alert_channel` `rotation_unavailable` `rotation_id` uuid The rotation this channel was reached through, if any. `rotation_name` string `urgency` string one of`high` `low` `sms` `voice` `wait_seconds` integer | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet the latest alert plan test run](https://upfour.io/docs/api/escalation-policies/get-the-latest-alert-plan-test-run.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/escalation-policies/00000000-0000-4000-8000-000000000001/test-runs/00000000-0000-4000-8000-000000000002" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/escalation-policies/00000000-0000-4000-8000-000000000001/test-runs/00000000-0000-4000-8000-000000000002", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/escalation-policies/00000000-0000-4000-8000-000000000001/test-runs/00000000-0000-4000-8000-000000000002", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/escalation-policies/00000000-0000-4000-8000-000000000001/test-runs/00000000-0000-4000-8000-000000000002", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000002", "mode": "full", "policy_id": "00000000-0000-4000-8000-000000000001", "status": "scheduled", "steps": [ { "position": 0, "scheduled_at": "2026-10-27T09:00:00Z", "status": "scheduled", "targets": [ { "attempts": 0, "channel_id": "00000000-0000-4000-8000-000000000003", "channel_name": "On-call email", "channel_type": "email", "status": "scheduled" } ], "urgency": "high", "wait_seconds": 0 } ] } } ``` ### 204 ``` No test run to return. ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # On-call rotations — API reference > Rotations, overrides, and "who is on call right now". REST endpoints with request and response fields, examples in curl, JavaScript, Python, Go and PHP, and an in-browser Try it console. Source: https://upfour.io/docs/api/oncall-schedules [Developers](https://upfour.io/docs/api.md)/Alerting & response Rotations, overrides, and "who is on call right now". Base URL `https://app.upfour.io/v1`. Send your key as `Authorization: Bearer `; every key acts on your real Company, so [test without paging anyone](https://upfour.io/docs/api/testing.md). New here? Start with the [quickstart](https://upfour.io/docs/api/quickstart.md). The examples read the key from `UP4_API_KEY`. ## Everyday [GET **List on-call rotations** /oncall-schedules](https://upfour.io/docs/api/oncall-schedules/list-on-call-rotations.md)[POST **Create an on-call rotation** /oncall-schedules](https://upfour.io/docs/api/oncall-schedules/create-an-on-call-rotation.md)[GET **Get the current on-call person** /oncall-schedules/current](https://upfour.io/docs/api/oncall-schedules/get-the-current-on-call-person.md)[GET **Get an on-call rotation** /oncall-schedules/{scheduleID}](https://upfour.io/docs/api/oncall-schedules/get-an-on-call-rotation.md)[PUT **Update an on-call rotation** /oncall-schedules/{scheduleID}](https://upfour.io/docs/api/oncall-schedules/update-an-on-call-rotation.md) ## Control [DEL **Delete an on-call rotation** /oncall-schedules/{scheduleID}](https://upfour.io/docs/api/oncall-schedules/delete-an-on-call-rotation.md)[GET **List on-call overrides** /oncall-schedules/{scheduleID}/overrides](https://upfour.io/docs/api/oncall-schedules/list-on-call-overrides.md)[POST **Create an on-call override** /oncall-schedules/{scheduleID}/overrides](https://upfour.io/docs/api/oncall-schedules/create-an-on-call-override.md)[DEL **Delete an on-call override** /oncall-schedules/{scheduleID}/overrides/{overrideID}](https://upfour.io/docs/api/oncall-schedules/delete-an-on-call-override.md) ## Results and history [GET **Get on-call coverage** /oncall-schedules/{scheduleID}/coverage](https://upfour.io/docs/api/oncall-schedules/get-on-call-coverage.md)[GET **List on-call events** /oncall-schedules/{scheduleID}/events](https://upfour.io/docs/api/oncall-schedules/list-on-call-events.md)[GET **List on-call rotation members** /oncall-schedules/{scheduleID}/rotations](https://upfour.io/docs/api/oncall-schedules/list-on-call-rotation-members.md) --- # List on-call rotations — API reference > Returns rotations with members, handoff settings, the current on-call person, and pagination. Source: https://upfour.io/docs/api/oncall-schedules/list-on-call-rotations [Developers](https://upfour.io/docs/api.md)/[On-call rotations](https://upfour.io/docs/api/oncall-schedules.md) Returns rotations with members, handoff settings, the current on-call person, and pagination. GET https://app.upfour.io/v1/oncall-schedules scope oncall.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `limit` optional | integer 1–250 default `50` | query parameter | | `cursor` optional | string | query parameter | | `client_id` optional | uuid | query parameter | | `all_clients` optional | boolean | query parameter | | `q` optional | string | query parameter | | `sort` optional | string | query parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | OnCallSchedule[] · nullable | | | Each item · 28 fields FieldTypeDescription `created_at` string (date-time) `handoff_day` integer Weekday of the weekly handoff, 0 = Sunday to 6 = Saturday. `handoff_time` string HH:MM:SS in the schedule's time zone. `id` uuid `members` OnCallMember[] · nullable Each item · 8 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `schedule_id` uuid `sort_order` integer `user_id` uuid `email` string `first_name` string `last_name` string `name` string `organization_id` uuid `rotation_type` string one of`daily` `weekly` `custom` `timezone` string `updated_at` string (date-time) `client_id` uuid `current_oncall` OnCallMember The person on call right now, an active override first; absent while nobody is. OnCallMember · 8 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `schedule_id` uuid `sort_order` integer `user_id` uuid `email` string `first_name` string `last_name` string | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [NextCreate an on-call rotation](https://upfour.io/docs/api/oncall-schedules/create-an-on-call-rotation.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/oncall-schedules" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/oncall-schedules", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/oncall-schedules", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/oncall-schedules", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "created_at": "2026-10-27T09:00:00Z", "current_oncall": { "created_at": "2026-10-27T09:00:00Z", "email": "anna@example.com", "id": "00000000-0000-4000-8000-000000000001", "schedule_id": "00000000-0000-4000-8000-000000000002", "sort_order": 0, "user_id": "00000000-0000-4000-8000-000000000003" }, "handoff_day": 1, "handoff_time": "09:00:00", "id": "00000000-0000-4000-8000-000000000002", "members": [ { "created_at": "2026-10-27T09:00:00Z", "email": "anna@example.com", "id": "00000000-0000-4000-8000-000000000001", "schedule_id": "00000000-0000-4000-8000-000000000002", "sort_order": 0, "user_id": "00000000-0000-4000-8000-000000000003" } ], "name": "Platform on-call", "organization_id": "00000000-0000-4000-8000-000000000004", "rotation_type": "weekly", "timezone": "Europe/Berlin", "updated_at": "2026-10-27T09:00:00Z" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` --- # Create an on-call rotation — API reference > Stores the rotation name, members, handoff settings, time zone, and optional client scope. Source: https://upfour.io/docs/api/oncall-schedules/create-an-on-call-rotation [Developers](https://upfour.io/docs/api.md)/[On-call rotations](https://upfour.io/docs/api/oncall-schedules.md) Stores the rotation name, members, handoff settings, time zone, and optional client scope. POST https://app.upfour.io/v1/oncall-schedules scope oncall.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Body `application/json` required 7 fields namestring (required) Unique within the company. In the app: **Name** All other fields6 fields client_iduuid · nullable handoff_dayinteger Weekday of the weekly handoff, 0 = Sunday to 6 = Saturday, in the schedule's time zone. Daily rotations ignore it. A value outside 0 to 6 is stored as 1 (Monday). In the app: **Handoff day** handoff_timestring Time of day of the handoff as HH:MM or HH:MM:SS in the schedule's time zone; empty means 09:00:00. In the app: **Handoff time** member_idsuuid[] User IDs of the company members in rotation order. On update they replace the current members. In the app: **Team members** rotation_typestring daily hands off every day at handoff_time, weekly every week on handoff_day at handoff_time. custom currently behaves like weekly; there is no custom shift length yet. Required on PUT; POST defaults to weekly. dailyweeklycustom In the app: **Rotation frequency** timezonestring IANA time zone name; empty means UTC. In the app: **Timezone** ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | OnCallSchedule | | | OnCallSchedule · 28 fields FieldTypeDescription `created_at` string (date-time) `handoff_day` integer Weekday of the weekly handoff, 0 = Sunday to 6 = Saturday. `handoff_time` string HH:MM:SS in the schedule's time zone. `id` uuid `members` OnCallMember[] · nullable Each item · 8 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `schedule_id` uuid `sort_order` integer `user_id` uuid `email` string `first_name` string `last_name` string `name` string `organization_id` uuid `rotation_type` string one of`daily` `weekly` `custom` `timezone` string `updated_at` string (date-time) `client_id` uuid `current_oncall` OnCallMember The person on call right now, an active override first; absent while nobody is. OnCallMember · 8 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `schedule_id` uuid `sort_order` integer `user_id` uuid `email` string `first_name` string `last_name` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList on-call rotations](https://upfour.io/docs/api/oncall-schedules/list-on-call-rotations.md) [NextGet the current on-call person](https://upfour.io/docs/api/oncall-schedules/get-the-current-on-call-person.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/oncall-schedules" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "handoff_day": 1, "handoff_time": "09:00", "member_ids": [ "00000000-0000-4000-8000-000000000001" ], "name": "Platform on-call", "rotation_type": "weekly", "timezone": "Europe/Berlin" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/oncall-schedules", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ handoff_day: 1, handoff_time: "09:00", member_ids: ["00000000-0000-4000-8000-000000000001"], name: "Platform on-call", rotation_type: "weekly", timezone: "Europe/Berlin", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/oncall-schedules", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "handoff_day": 1, "handoff_time": "09:00", "member_ids": ["00000000-0000-4000-8000-000000000001"], "name": "Platform on-call", "rotation_type": "weekly", "timezone": "Europe/Berlin", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "handoff_day": 1, "handoff_time": "09:00", "member_ids": [ "00000000-0000-4000-8000-000000000001" ], "name": "Platform on-call", "rotation_type": "weekly", "timezone": "Europe/Berlin" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/oncall-schedules", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "handoff_day": 1, "handoff_time": "09:00:00", "id": "00000000-0000-4000-8000-000000000002", "members": [ { "created_at": "2026-10-27T09:00:00Z", "email": "anna@example.com", "id": "00000000-0000-4000-8000-000000000003", "schedule_id": "00000000-0000-4000-8000-000000000002", "sort_order": 0, "user_id": "00000000-0000-4000-8000-000000000001" } ], "name": "Platform on-call", "organization_id": "00000000-0000-4000-8000-000000000004", "rotation_type": "weekly", "timezone": "Europe/Berlin", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Get the current on-call person — API reference > Returns each current member with its schedule ID and schedule name. Source: https://upfour.io/docs/api/oncall-schedules/get-the-current-on-call-person [Developers](https://upfour.io/docs/api.md)/[On-call rotations](https://upfour.io/docs/api/oncall-schedules.md) Returns each current member with its schedule ID and schedule name. GET https://app.upfour.io/v1/oncall-schedules/current scope oncall.read stable returns 200 ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | CurrentOnCallInfo[] · nullable | | | Each item · 11 fields FieldTypeDescription `member` OnCallMember OnCallMember · 8 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `schedule_id` uuid `sort_order` integer `user_id` uuid `email` string `first_name` string `last_name` string `schedule_id` uuid `schedule_name` string | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousCreate an on-call rotation](https://upfour.io/docs/api/oncall-schedules/create-an-on-call-rotation.md) [NextGet an on-call rotation](https://upfour.io/docs/api/oncall-schedules/get-an-on-call-rotation.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/oncall-schedules/current" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/oncall-schedules/current", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/oncall-schedules/current", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/oncall-schedules/current", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "member": { "created_at": "2026-10-27T09:00:00Z", "email": "anna@example.com", "id": "00000000-0000-4000-8000-000000000001", "schedule_id": "00000000-0000-4000-8000-000000000002", "sort_order": 0, "user_id": "00000000-0000-4000-8000-000000000003" }, "schedule_id": "00000000-0000-4000-8000-000000000002", "schedule_name": "Platform on-call" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` --- # Get an on-call rotation — API reference > Returns the rotation's members, handoff settings, time zone, current on-call person, and client scope. Source: https://upfour.io/docs/api/oncall-schedules/get-an-on-call-rotation [Developers](https://upfour.io/docs/api.md)/[On-call rotations](https://upfour.io/docs/api/oncall-schedules.md) Returns the rotation's members, handoff settings, time zone, current on-call person, and client scope. GET https://app.upfour.io/v1/oncall-schedules/{scheduleID} scope oncall.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `scheduleID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | OnCallSchedule | | | OnCallSchedule · 28 fields FieldTypeDescription `created_at` string (date-time) `handoff_day` integer Weekday of the weekly handoff, 0 = Sunday to 6 = Saturday. `handoff_time` string HH:MM:SS in the schedule's time zone. `id` uuid `members` OnCallMember[] · nullable Each item · 8 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `schedule_id` uuid `sort_order` integer `user_id` uuid `email` string `first_name` string `last_name` string `name` string `organization_id` uuid `rotation_type` string one of`daily` `weekly` `custom` `timezone` string `updated_at` string (date-time) `client_id` uuid `current_oncall` OnCallMember The person on call right now, an active override first; absent while nobody is. OnCallMember · 8 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `schedule_id` uuid `sort_order` integer `user_id` uuid `email` string `first_name` string `last_name` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet the current on-call person](https://upfour.io/docs/api/oncall-schedules/get-the-current-on-call-person.md) [NextUpdate an on-call rotation](https://upfour.io/docs/api/oncall-schedules/update-an-on-call-rotation.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "current_oncall": { "created_at": "2026-10-27T09:00:00Z", "email": "anna@example.com", "id": "00000000-0000-4000-8000-000000000002", "schedule_id": "00000000-0000-4000-8000-000000000001", "sort_order": 0, "user_id": "00000000-0000-4000-8000-000000000003" }, "handoff_day": 1, "handoff_time": "09:00:00", "id": "00000000-0000-4000-8000-000000000001", "members": [ { "created_at": "2026-10-27T09:00:00Z", "email": "anna@example.com", "id": "00000000-0000-4000-8000-000000000002", "schedule_id": "00000000-0000-4000-8000-000000000001", "sort_order": 0, "user_id": "00000000-0000-4000-8000-000000000003" } ], "name": "Platform on-call", "organization_id": "00000000-0000-4000-8000-000000000004", "rotation_type": "weekly", "timezone": "Europe/Berlin", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Update an on-call rotation — API reference > Replaces the rotation name, members, handoff settings, time zone, and client scope. Source: https://upfour.io/docs/api/oncall-schedules/update-an-on-call-rotation [Developers](https://upfour.io/docs/api.md)/[On-call rotations](https://upfour.io/docs/api/oncall-schedules.md) Replaces the rotation name, members, handoff settings, time zone, and client scope. PUT https://app.upfour.io/v1/oncall-schedules/{scheduleID} scope oncall.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `scheduleID` required | uuid | path parameter | ## Body `application/json` required 7 fields namestring (required) Unique within the company. In the app: **Name** rotation_typestring (required) daily hands off every day at handoff_time, weekly every week on handoff_day at handoff_time. custom currently behaves like weekly; there is no custom shift length yet. Required on PUT; POST defaults to weekly. dailyweeklycustom In the app: **Rotation frequency** All other fields5 fields client_iduuid · nullable handoff_dayinteger Weekday of the weekly handoff, 0 = Sunday to 6 = Saturday, in the schedule's time zone. Daily rotations ignore it. A value outside 0 to 6 is stored as 1 (Monday). In the app: **Handoff day** handoff_timestring Time of day of the handoff as HH:MM or HH:MM:SS in the schedule's time zone; empty means 09:00:00. In the app: **Handoff time** member_idsuuid[] User IDs of the company members in rotation order. On update they replace the current members. In the app: **Team members** timezonestring IANA time zone name; empty means UTC. In the app: **Timezone** ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | OnCallSchedule | | | OnCallSchedule · 28 fields FieldTypeDescription `created_at` string (date-time) `handoff_day` integer Weekday of the weekly handoff, 0 = Sunday to 6 = Saturday. `handoff_time` string HH:MM:SS in the schedule's time zone. `id` uuid `members` OnCallMember[] · nullable Each item · 8 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `schedule_id` uuid `sort_order` integer `user_id` uuid `email` string `first_name` string `last_name` string `name` string `organization_id` uuid `rotation_type` string one of`daily` `weekly` `custom` `timezone` string `updated_at` string (date-time) `client_id` uuid `current_oncall` OnCallMember The person on call right now, an active override first; absent while nobody is. OnCallMember · 8 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `schedule_id` uuid `sort_order` integer `user_id` uuid `email` string `first_name` string `last_name` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet an on-call rotation](https://upfour.io/docs/api/oncall-schedules/get-an-on-call-rotation.md) [NextDelete an on-call rotation](https://upfour.io/docs/api/oncall-schedules/delete-an-on-call-rotation.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "handoff_day": 1, "handoff_time": "09:00", "member_ids": [ "00000000-0000-4000-8000-000000000002" ], "name": "Platform on-call", "rotation_type": "weekly", "timezone": "Europe/Berlin" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ handoff_day: 1, handoff_time: "09:00", member_ids: ["00000000-0000-4000-8000-000000000002"], name: "Platform on-call", rotation_type: "weekly", timezone: "Europe/Berlin", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "handoff_day": 1, "handoff_time": "09:00", "member_ids": ["00000000-0000-4000-8000-000000000002"], "name": "Platform on-call", "rotation_type": "weekly", "timezone": "Europe/Berlin", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "handoff_day": 1, "handoff_time": "09:00", "member_ids": [ "00000000-0000-4000-8000-000000000002" ], "name": "Platform on-call", "rotation_type": "weekly", "timezone": "Europe/Berlin" }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "current_oncall": { "created_at": "2026-10-27T09:00:00Z", "email": "anna@example.com", "id": "00000000-0000-4000-8000-000000000003", "schedule_id": "00000000-0000-4000-8000-000000000001", "sort_order": 0, "user_id": "00000000-0000-4000-8000-000000000002" }, "handoff_day": 1, "handoff_time": "09:00:00", "id": "00000000-0000-4000-8000-000000000001", "members": [ { "created_at": "2026-10-27T09:00:00Z", "email": "anna@example.com", "id": "00000000-0000-4000-8000-000000000003", "schedule_id": "00000000-0000-4000-8000-000000000001", "sort_order": 0, "user_id": "00000000-0000-4000-8000-000000000002" } ], "name": "Platform on-call", "organization_id": "00000000-0000-4000-8000-000000000004", "rotation_type": "weekly", "timezone": "Europe/Berlin", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Delete an on-call rotation — API reference > Returns status 204 without a response body after deleting the selected rotation. Source: https://upfour.io/docs/api/oncall-schedules/delete-an-on-call-rotation [Developers](https://upfour.io/docs/api.md)/[On-call rotations](https://upfour.io/docs/api/oncall-schedules.md) Returns status 204 without a response body after deleting the selected rotation. DELETE https://app.upfour.io/v1/oncall-schedules/{scheduleID} scope oncall.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `scheduleID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 409 | [oncall_schedule_in_use](https://upfour.io/docs/api/errors#code-oncall_schedule_in_use) | The on-call rotation is still a target of an alert plan step, so it was not deleted. **When:** Deleting an on-call rotation that an alert plan step targets as `oncall:`. `details.alert_plans` lists those plans. **Fix:** Remove the rotation from the steps of those alert plans first, then delete it. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpdate an on-call rotation](https://upfour.io/docs/api/oncall-schedules/update-an-on-call-rotation.md) [NextList on-call overrides](https://upfour.io/docs/api/oncall-schedules/list-on-call-overrides.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` ### 409 ```json { "error": { "code": "oncall_schedule_in_use", "message": "The on-call rotation is still a target of an alert plan step, so it was not deleted.", "request_id": "req_…" } } ``` --- # List on-call overrides — API reference > Returns each override with its member, start and end timestamps, reason, and creation time. Source: https://upfour.io/docs/api/oncall-schedules/list-on-call-overrides [Developers](https://upfour.io/docs/api.md)/[On-call rotations](https://upfour.io/docs/api/oncall-schedules.md) Returns each override with its member, start and end timestamps, reason, and creation time. GET https://app.upfour.io/v1/oncall-schedules/{scheduleID}/overrides scope oncall.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `scheduleID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | OnCallOverride[] · nullable | | | Each item · 10 fields FieldTypeDescription `created_at` string (date-time) `ends_at` string (date-time) `id` uuid `reason` string `schedule_id` uuid `starts_at` string (date-time) `user_id` uuid `email` string `first_name` string `last_name` string | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousDelete an on-call rotation](https://upfour.io/docs/api/oncall-schedules/delete-an-on-call-rotation.md) [NextCreate an on-call override](https://upfour.io/docs/api/oncall-schedules/create-an-on-call-override.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001/overrides" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001/overrides", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001/overrides", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001/overrides", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "created_at": "2026-10-27T09:00:00Z", "email": "anna@example.com", "ends_at": "2026-10-30T09:00:00Z", "id": "00000000-0000-4000-8000-000000000002", "reason": "Covering for a colleague", "schedule_id": "00000000-0000-4000-8000-000000000001", "starts_at": "2026-10-29T09:00:00Z", "user_id": "00000000-0000-4000-8000-000000000003" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Create an on-call override — API reference > Creates an override for a user between two RFC 3339 timestamps, with an optional reason. Source: https://upfour.io/docs/api/oncall-schedules/create-an-on-call-override [Developers](https://upfour.io/docs/api.md)/[On-call rotations](https://upfour.io/docs/api/oncall-schedules.md) Creates an override for a user between two RFC 3339 timestamps, with an optional reason. POST https://app.upfour.io/v1/oncall-schedules/{scheduleID}/overrides scope oncall.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Parameters | Field | Type | Description | | --- | --- | --- | | `scheduleID` required | uuid | path parameter | ## Body `application/json` required 4 fields ends_atstring (date-time) (required) RFC 3339, after starts_at. In the app: **Until** starts_atstring (date-time) (required) RFC 3339. In the app: **From** user_iduuid (required) In the app: **Who covers** All other fields1 fields reasonstring In the app: **Reason (optional)** ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | OnCallOverride | | | OnCallOverride · 10 fields FieldTypeDescription `created_at` string (date-time) `ends_at` string (date-time) `id` uuid `reason` string `schedule_id` uuid `starts_at` string (date-time) `user_id` uuid `email` string `first_name` string `last_name` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList on-call overrides](https://upfour.io/docs/api/oncall-schedules/list-on-call-overrides.md) [NextDelete an on-call override](https://upfour.io/docs/api/oncall-schedules/delete-an-on-call-override.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001/overrides" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "ends_at": "2026-10-30T09:00:00Z", "reason": "Covering for a colleague", "starts_at": "2026-10-29T09:00:00Z", "user_id": "00000000-0000-4000-8000-000000000002" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001/overrides", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ ends_at: "2026-10-30T09:00:00Z", reason: "Covering for a colleague", starts_at: "2026-10-29T09:00:00Z", user_id: "00000000-0000-4000-8000-000000000002", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001/overrides", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "ends_at": "2026-10-30T09:00:00Z", "reason": "Covering for a colleague", "starts_at": "2026-10-29T09:00:00Z", "user_id": "00000000-0000-4000-8000-000000000002", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "ends_at": "2026-10-30T09:00:00Z", "reason": "Covering for a colleague", "starts_at": "2026-10-29T09:00:00Z", "user_id": "00000000-0000-4000-8000-000000000002" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001/overrides", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "email": "anna@example.com", "ends_at": "2026-10-30T09:00:00Z", "id": "00000000-0000-4000-8000-000000000003", "reason": "Covering for a colleague", "schedule_id": "00000000-0000-4000-8000-000000000001", "starts_at": "2026-10-29T09:00:00Z", "user_id": "00000000-0000-4000-8000-000000000002" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Delete an on-call override — API reference > Returns status 204 without a response body after deleting the selected override. Source: https://upfour.io/docs/api/oncall-schedules/delete-an-on-call-override [Developers](https://upfour.io/docs/api.md)/[On-call rotations](https://upfour.io/docs/api/oncall-schedules.md) Returns status 204 without a response body after deleting the selected override. DELETE https://app.upfour.io/v1/oncall-schedules/{scheduleID}/overrides/{overrideID} scope oncall.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `scheduleID` required | uuid | path parameter | | `overrideID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousCreate an on-call override](https://upfour.io/docs/api/oncall-schedules/create-an-on-call-override.md) [NextGet on-call coverage](https://upfour.io/docs/api/oncall-schedules/get-on-call-coverage.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001/overrides/00000000-0000-4000-8000-000000000002" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001/overrides/00000000-0000-4000-8000-000000000002", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001/overrides/00000000-0000-4000-8000-000000000002", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001/overrides/00000000-0000-4000-8000-000000000002", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Get on-call coverage — API reference > Returns the schedule ID, current on-call member, scheduled members, and echoed `from` and `to` values. Source: https://upfour.io/docs/api/oncall-schedules/get-on-call-coverage [Developers](https://upfour.io/docs/api.md)/[On-call rotations](https://upfour.io/docs/api/oncall-schedules.md) Returns the schedule ID, current on-call member, scheduled members, and echoed `from` and `to` values. GET https://app.upfour.io/v1/oncall-schedules/{scheduleID}/coverage scope oncall.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `scheduleID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | OnCallCoverageResponse | | | OnCallCoverageResponse · 21 fields FieldTypeDescription `current_on_call` OnCallMember · nullable The person on call right now, an active override first; null while nobody is. OnCallMember · 8 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `schedule_id` uuid `sort_order` integer `user_id` uuid `email` string `first_name` string `last_name` string `from` string · nullable The from query parameter as sent, null when omitted. It does not filter anything. `members` OnCallMember[] · nullable Each item · 8 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `schedule_id` uuid `sort_order` integer `user_id` uuid `email` string `first_name` string `last_name` string `schedule_id` uuid `to` string · nullable The to query parameter as sent, null when omitted. It does not filter anything. | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousDelete an on-call override](https://upfour.io/docs/api/oncall-schedules/delete-an-on-call-override.md) [NextList on-call events](https://upfour.io/docs/api/oncall-schedules/list-on-call-events.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001/coverage" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001/coverage", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001/coverage", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001/coverage", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "current_on_call": { "created_at": "2026-10-27T09:00:00Z", "email": "anna@example.com", "id": "00000000-0000-4000-8000-000000000002", "schedule_id": "00000000-0000-4000-8000-000000000001", "sort_order": 0, "user_id": "00000000-0000-4000-8000-000000000003" }, "from": null, "members": [ { "created_at": "2026-10-27T09:00:00Z", "email": "anna@example.com", "id": "00000000-0000-4000-8000-000000000002", "schedule_id": "00000000-0000-4000-8000-000000000001", "sort_order": 0, "user_id": "00000000-0000-4000-8000-000000000003" } ], "schedule_id": "00000000-0000-4000-8000-000000000001", "to": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # List on-call events — API reference > Returns rotation and override events with members, timestamps, sources, and rotation positions. Source: https://upfour.io/docs/api/oncall-schedules/list-on-call-events [Developers](https://upfour.io/docs/api.md)/[On-call rotations](https://upfour.io/docs/api/oncall-schedules.md) Returns rotation and override events with members, timestamps, sources, and rotation positions. GET https://app.upfour.io/v1/oncall-schedules/{scheduleID}/events scope oncall.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `scheduleID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | OnCallEvent[] · nullable | | | Each item · 11 fields FieldTypeDescription `email` string `ends_at` string (date-time) `first_name` string `last_name` string `rotation_index` integer `schedule_id` uuid `source` string one of`rotation` `override` `starts_at` string (date-time) `user_id` uuid `override_id` uuid Set for events with source override. `reason` string | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet on-call coverage](https://upfour.io/docs/api/oncall-schedules/get-on-call-coverage.md) [NextList on-call rotation members](https://upfour.io/docs/api/oncall-schedules/list-on-call-rotation-members.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001/events" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001/events", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001/events", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001/events", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "email": "anna@example.com", "ends_at": "2026-11-03T00:55:00Z", "first_name": "", "last_name": "", "rotation_index": 0, "schedule_id": "00000000-0000-4000-8000-000000000001", "source": "rotation", "starts_at": "2026-10-27T09:00:00Z", "user_id": "00000000-0000-4000-8000-000000000002" }, { "email": "anna@example.com", "ends_at": "2026-11-10T00:55:00Z", "first_name": "", "last_name": "", "rotation_index": 0, "schedule_id": "00000000-0000-4000-8000-000000000001", "source": "rotation", "starts_at": "2026-11-03T00:55:00Z", "user_id": "00000000-0000-4000-8000-000000000002" }, { "email": "anna@example.com", "ends_at": "2026-11-17T00:55:00Z", "first_name": "", "last_name": "", "rotation_index": 0, "schedule_id": "00000000-0000-4000-8000-000000000001", "source": "rotation", "starts_at": "2026-11-10T00:55:00Z", "user_id": "00000000-0000-4000-8000-000000000002" }, { "email": "anna@example.com", "ends_at": "2026-11-24T00:55:00Z", "first_name": "", "last_name": "", "rotation_index": 0, "schedule_id": "00000000-0000-4000-8000-000000000001", "source": "rotation", "starts_at": "2026-11-17T00:55:00Z", "user_id": "00000000-0000-4000-8000-000000000002" }, { "email": "anna@example.com", "ends_at": "2026-11-26T09:00:00Z", "first_name": "", "last_name": "", "rotation_index": 0, "schedule_id": "00000000-0000-4000-8000-000000000001", "source": "rotation", "starts_at": "2026-11-24T00:55:00Z", "user_id": "00000000-0000-4000-8000-000000000002" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # List on-call rotation members — API reference > Returns rotation members with handoff settings, ordering, time zone, and rotation type. Source: https://upfour.io/docs/api/oncall-schedules/list-on-call-rotation-members [Developers](https://upfour.io/docs/api.md)/[On-call rotations](https://upfour.io/docs/api/oncall-schedules.md) Returns rotation members with handoff settings, ordering, time zone, and rotation type. GET https://app.upfour.io/v1/oncall-schedules/{scheduleID}/rotations scope oncall.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `scheduleID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | OnCallRotation[] · nullable | | | Each item · 15 fields FieldTypeDescription `handoff_day` integer `handoff_time` string `member` OnCallMember OnCallMember · 8 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `schedule_id` uuid `sort_order` integer `user_id` uuid `email` string `first_name` string `last_name` string `rotation_type` string one of`daily` `weekly` `custom` `schedule_id` uuid `sort_order` integer `timezone` string | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList on-call events](https://upfour.io/docs/api/oncall-schedules/list-on-call-events.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001/rotations" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001/rotations", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001/rotations", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/oncall-schedules/00000000-0000-4000-8000-000000000001/rotations", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "handoff_day": 1, "handoff_time": "09:00:00", "member": { "created_at": "2026-10-27T09:00:00Z", "email": "anna@example.com", "id": "00000000-0000-4000-8000-000000000002", "schedule_id": "00000000-0000-4000-8000-000000000001", "sort_order": 0, "user_id": "00000000-0000-4000-8000-000000000003" }, "rotation_type": "weekly", "schedule_id": "00000000-0000-4000-8000-000000000001", "sort_order": 0, "timezone": "Europe/Berlin" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Playbooks (runbooks) — API reference > The steps you take when something breaks, attached to monitors, with shared progress. REST endpoints with request and response fields, examples in curl, JavaScript, Python, Go and PHP, and an in-browser Try it console. Source: https://upfour.io/docs/api/runbooks [Developers](https://upfour.io/docs/api.md)/Alerting & response The steps you take when something breaks, attached to monitors, with shared progress. Base URL `https://app.upfour.io/v1`. Send your key as `Authorization: Bearer `; every key acts on your real Company, so [test without paging anyone](https://upfour.io/docs/api/testing.md). New here? Start with the [quickstart](https://upfour.io/docs/api/quickstart.md). The examples read the key from `UP4_API_KEY`. ## Everyday [GET **List playbooks** /runbooks](https://upfour.io/docs/api/runbooks/list-playbooks.md)[POST **Create a playbook** /runbooks](https://upfour.io/docs/api/runbooks/create-a-playbook.md)[GET **Get a playbook** /runbooks/{runbookID}](https://upfour.io/docs/api/runbooks/get-a-playbook.md)[PUT **Update a playbook** /runbooks/{runbookID}](https://upfour.io/docs/api/runbooks/update-a-playbook.md) ## Control [DEL **Delete a playbook** /runbooks/{runbookID}](https://upfour.io/docs/api/runbooks/delete-a-playbook.md)[PUT **Set a playbook step completion** /runbooks/{runbookID}/steps/{stepID}/completion](https://upfour.io/docs/api/runbooks/set-a-playbook-step-completion.md) ## Results and history [GET **List playbook step completions** /runbooks/{runbookID}/step-completions](https://upfour.io/docs/api/runbooks/list-playbook-step-completions.md) --- # List playbooks — API reference > Returns playbooks with owners, descriptions, attached monitor IDs, steps, and pagination. Source: https://upfour.io/docs/api/runbooks/list-playbooks [Developers](https://upfour.io/docs/api.md)/[Playbooks (runbooks)](https://upfour.io/docs/api/runbooks.md) Returns playbooks with owners, descriptions, attached monitor IDs, steps, and pagination. GET https://app.upfour.io/v1/runbooks scope runbook.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `limit` optional | integer 1–250 default `50` | query parameter | | `cursor` optional | string | query parameter | | `client_id` optional | uuid | query parameter | | `all_clients` optional | boolean | query parameter | | `q` optional | string | query parameter | | `sort` optional | string | query parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | Runbook[] · nullable | | | Each item · 17 fields FieldTypeDescription `created_at` string (date-time) `description` string `id` uuid `monitor_ids` uuid[] · nullable `org_id` uuid `owner` string `step_count` integer `steps` RunbookStep[] · nullable Each item · 6 fields FieldTypeDescription `created_at` string (date-time) `description` string `id` uuid `position` integer `runbook_id` uuid `title` string `title` string `updated_at` string (date-time) `client_id` uuid | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [NextCreate a playbook](https://upfour.io/docs/api/runbooks/create-a-playbook.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/runbooks" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/runbooks", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/runbooks", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/runbooks", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "created_at": "2026-10-27T09:00:00Z", "description": "What to do when the checkout stops answering.", "id": "00000000-0000-4000-8000-000000000001", "monitor_ids": [ "00000000-0000-4000-8000-000000000002" ], "org_id": "00000000-0000-4000-8000-000000000003", "owner": "Platform team", "step_count": 2, "steps": null, "title": "Checkout outage", "updated_at": "2026-10-27T09:00:00Z" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` --- # Create a playbook — API reference > Stores a title, description, owner, attached monitor IDs, client scope, and ordered steps. Source: https://upfour.io/docs/api/runbooks/create-a-playbook [Developers](https://upfour.io/docs/api.md)/[Playbooks (runbooks)](https://upfour.io/docs/api/runbooks.md) Stores a title, description, owner, attached monitor IDs, client scope, and ordered steps. POST https://app.upfour.io/v1/runbooks scope runbook.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Body `application/json` required 6 fields titlestring (required) In the app: **Title** All other fields7 fields client_iduuid · nullable descriptionstring In the app: **Description** monitor_idsuuid[] The monitors the runbook is attached to. On update they replace the stored list. In the app: **Linked monitors** ownerstring In the app: **Owner** stepsRunbookStepInput[] The steps in order. On update they replace the stored steps. In the app: **Steps** Nested fields 2 fields | Field | Type | Description | | --- | --- | --- | | `description` optional | string | | | `title` optional | string | | ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | Runbook | | | Runbook · 17 fields FieldTypeDescription `created_at` string (date-time) `description` string `id` uuid `monitor_ids` uuid[] · nullable `org_id` uuid `owner` string `step_count` integer `steps` RunbookStep[] · nullable Each item · 6 fields FieldTypeDescription `created_at` string (date-time) `description` string `id` uuid `position` integer `runbook_id` uuid `title` string `title` string `updated_at` string (date-time) `client_id` uuid | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList playbooks](https://upfour.io/docs/api/runbooks/list-playbooks.md) [NextGet a playbook](https://upfour.io/docs/api/runbooks/get-a-playbook.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/runbooks" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "description": "What to do when the checkout stops answering.", "monitor_ids": [ "00000000-0000-4000-8000-000000000001" ], "owner": "Platform team", "steps": [ { "title": "Check the payment provider status page" }, { "title": "Roll back the last deploy" } ], "title": "Checkout outage" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/runbooks", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ description: "What to do when the checkout stops answering.", monitor_ids: ["00000000-0000-4000-8000-000000000001"], owner: "Platform team", steps: [ { title: "Check the payment provider status page", }, { title: "Roll back the last deploy", }, ], title: "Checkout outage", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/runbooks", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "description": "What to do when the checkout stops answering.", "monitor_ids": ["00000000-0000-4000-8000-000000000001"], "owner": "Platform team", "steps": [ { "title": "Check the payment provider status page", }, { "title": "Roll back the last deploy", }, ], "title": "Checkout outage", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "description": "What to do when the checkout stops answering.", "monitor_ids": [ "00000000-0000-4000-8000-000000000001" ], "owner": "Platform team", "steps": [ { "title": "Check the payment provider status page" }, { "title": "Roll back the last deploy" } ], "title": "Checkout outage" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/runbooks", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "description": "What to do when the checkout stops answering.", "id": "00000000-0000-4000-8000-000000000002", "monitor_ids": [ "00000000-0000-4000-8000-000000000001" ], "org_id": "00000000-0000-4000-8000-000000000003", "owner": "Platform team", "step_count": 2, "steps": [ { "created_at": "2026-10-27T09:00:00Z", "description": "", "id": "00000000-0000-4000-8000-000000000004", "position": 0, "runbook_id": "00000000-0000-4000-8000-000000000002", "title": "Check the payment provider status page" }, { "created_at": "2026-10-27T09:00:00Z", "description": "", "id": "00000000-0000-4000-8000-000000000005", "position": 1, "runbook_id": "00000000-0000-4000-8000-000000000002", "title": "Roll back the last deploy" } ], "title": "Checkout outage", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Get a playbook — API reference > Returns a playbook with its owner, attached monitors, ordered steps, and step count. Source: https://upfour.io/docs/api/runbooks/get-a-playbook [Developers](https://upfour.io/docs/api.md)/[Playbooks (runbooks)](https://upfour.io/docs/api/runbooks.md) Returns a playbook with its owner, attached monitors, ordered steps, and step count. GET https://app.upfour.io/v1/runbooks/{runbookID} scope runbook.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `runbookID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | Runbook | | | Runbook · 17 fields FieldTypeDescription `created_at` string (date-time) `description` string `id` uuid `monitor_ids` uuid[] · nullable `org_id` uuid `owner` string `step_count` integer `steps` RunbookStep[] · nullable Each item · 6 fields FieldTypeDescription `created_at` string (date-time) `description` string `id` uuid `position` integer `runbook_id` uuid `title` string `title` string `updated_at` string (date-time) `client_id` uuid | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousCreate a playbook](https://upfour.io/docs/api/runbooks/create-a-playbook.md) [NextUpdate a playbook](https://upfour.io/docs/api/runbooks/update-a-playbook.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/runbooks/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/runbooks/00000000-0000-4000-8000-000000000001", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/runbooks/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/runbooks/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "description": "What to do when the checkout stops answering.", "id": "00000000-0000-4000-8000-000000000001", "monitor_ids": [ "00000000-0000-4000-8000-000000000002" ], "org_id": "00000000-0000-4000-8000-000000000003", "owner": "Platform team", "step_count": 2, "steps": [ { "created_at": "2026-10-27T09:00:00Z", "description": "", "id": "00000000-0000-4000-8000-000000000004", "position": 0, "runbook_id": "00000000-0000-4000-8000-000000000001", "title": "Check the payment provider status page" }, { "created_at": "2026-10-27T09:00:00Z", "description": "", "id": "00000000-0000-4000-8000-000000000005", "position": 1, "runbook_id": "00000000-0000-4000-8000-000000000001", "title": "Roll back the last deploy" } ], "title": "Checkout outage", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Update a playbook — API reference > Replaces the title, description, owner, attached monitor IDs, client scope, and ordered steps. Source: https://upfour.io/docs/api/runbooks/update-a-playbook [Developers](https://upfour.io/docs/api.md)/[Playbooks (runbooks)](https://upfour.io/docs/api/runbooks.md) Replaces the title, description, owner, attached monitor IDs, client scope, and ordered steps. PUT https://app.upfour.io/v1/runbooks/{runbookID} scope runbook.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `runbookID` required | uuid | path parameter | ## Body `application/json` required 6 fields titlestring (required) In the app: **Title** All other fields7 fields client_iduuid · nullable descriptionstring In the app: **Description** monitor_idsuuid[] The monitors the runbook is attached to. On update they replace the stored list. In the app: **Linked monitors** ownerstring In the app: **Owner** stepsRunbookStepInput[] The steps in order. On update they replace the stored steps. In the app: **Steps** Nested fields 2 fields | Field | Type | Description | | --- | --- | --- | | `description` optional | string | | | `title` optional | string | | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | Runbook | | | Runbook · 17 fields FieldTypeDescription `created_at` string (date-time) `description` string `id` uuid `monitor_ids` uuid[] · nullable `org_id` uuid `owner` string `step_count` integer `steps` RunbookStep[] · nullable Each item · 6 fields FieldTypeDescription `created_at` string (date-time) `description` string `id` uuid `position` integer `runbook_id` uuid `title` string `title` string `updated_at` string (date-time) `client_id` uuid | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet a playbook](https://upfour.io/docs/api/runbooks/get-a-playbook.md) [NextDelete a playbook](https://upfour.io/docs/api/runbooks/delete-a-playbook.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/runbooks/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "description": "What to do when the checkout stops answering.", "monitor_ids": [ "00000000-0000-4000-8000-000000000002" ], "owner": "Platform team", "steps": [ { "title": "Check the payment provider status page" }, { "title": "Roll back the last deploy" }, { "title": "Post an update on the status page" } ], "title": "Checkout outage" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/runbooks/00000000-0000-4000-8000-000000000001", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ description: "What to do when the checkout stops answering.", monitor_ids: ["00000000-0000-4000-8000-000000000002"], owner: "Platform team", steps: [ { title: "Check the payment provider status page", }, { title: "Roll back the last deploy", }, { title: "Post an update on the status page", }, ], title: "Checkout outage", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/runbooks/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "description": "What to do when the checkout stops answering.", "monitor_ids": ["00000000-0000-4000-8000-000000000002"], "owner": "Platform team", "steps": [ { "title": "Check the payment provider status page", }, { "title": "Roll back the last deploy", }, { "title": "Post an update on the status page", }, ], "title": "Checkout outage", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "description": "What to do when the checkout stops answering.", "monitor_ids": [ "00000000-0000-4000-8000-000000000002" ], "owner": "Platform team", "steps": [ { "title": "Check the payment provider status page" }, { "title": "Roll back the last deploy" }, { "title": "Post an update on the status page" } ], "title": "Checkout outage" }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/runbooks/00000000-0000-4000-8000-000000000001", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "description": "What to do when the checkout stops answering.", "id": "00000000-0000-4000-8000-000000000001", "monitor_ids": [ "00000000-0000-4000-8000-000000000002" ], "org_id": "00000000-0000-4000-8000-000000000003", "owner": "Platform team", "step_count": 3, "steps": [ { "created_at": "2026-10-27T09:00:00Z", "description": "", "id": "00000000-0000-4000-8000-000000000004", "position": 0, "runbook_id": "00000000-0000-4000-8000-000000000001", "title": "Check the payment provider status page" }, { "created_at": "2026-10-27T09:00:00Z", "description": "", "id": "00000000-0000-4000-8000-000000000005", "position": 1, "runbook_id": "00000000-0000-4000-8000-000000000001", "title": "Roll back the last deploy" }, { "created_at": "2026-10-27T09:00:00Z", "description": "", "id": "00000000-0000-4000-8000-000000000006", "position": 2, "runbook_id": "00000000-0000-4000-8000-000000000001", "title": "Post an update on the status page" } ], "title": "Checkout outage", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Delete a playbook — API reference > Returns status 204 without a response body after deleting the selected playbook. Source: https://upfour.io/docs/api/runbooks/delete-a-playbook [Developers](https://upfour.io/docs/api.md)/[Playbooks (runbooks)](https://upfour.io/docs/api/runbooks.md) Returns status 204 without a response body after deleting the selected playbook. DELETE https://app.upfour.io/v1/runbooks/{runbookID} scope runbook.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `runbookID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpdate a playbook](https://upfour.io/docs/api/runbooks/update-a-playbook.md) [NextSet a playbook step completion](https://upfour.io/docs/api/runbooks/set-a-playbook-step-completion.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/runbooks/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/runbooks/00000000-0000-4000-8000-000000000001", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/runbooks/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/runbooks/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Set a playbook step completion — API reference > Updates whether a playbook step is marked complete and returns a success flag. Source: https://upfour.io/docs/api/runbooks/set-a-playbook-step-completion [Developers](https://upfour.io/docs/api.md)/[Playbooks (runbooks)](https://upfour.io/docs/api/runbooks.md) Updates whether a playbook step is marked complete and returns a success flag. PUT https://app.upfour.io/v1/runbooks/{runbookID}/steps/{stepID}/completion scope runbook.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `runbookID` required | uuid | path parameter | | `stepID` required | uuid | path parameter | ## Body `application/json` required 1 field All other fields1 fields completedboolean ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | RunbookStepCompletionResponse | | | RunbookStepCompletionResponse · 1 fields FieldTypeDescription `success` boolean | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousDelete a playbook](https://upfour.io/docs/api/runbooks/delete-a-playbook.md) [NextList playbook step completions](https://upfour.io/docs/api/runbooks/list-playbook-step-completions.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/runbooks/00000000-0000-4000-8000-000000000001/steps/00000000-0000-4000-8000-000000000002/completion" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "completed": true }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/runbooks/00000000-0000-4000-8000-000000000001/steps/00000000-0000-4000-8000-000000000002/completion", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ completed: true, }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/runbooks/00000000-0000-4000-8000-000000000001/steps/00000000-0000-4000-8000-000000000002/completion", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "completed": True, }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "completed": true }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/runbooks/00000000-0000-4000-8000-000000000001/steps/00000000-0000-4000-8000-000000000002/completion", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "success": true } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # List playbook step completions — API reference > Returns step IDs the caller ticked off on the playbook, in step order. Source: https://upfour.io/docs/api/runbooks/list-playbook-step-completions [Developers](https://upfour.io/docs/api.md)/[Playbooks (runbooks)](https://upfour.io/docs/api/runbooks.md) Returns step IDs the caller ticked off on the playbook, in step order. GET https://app.upfour.io/v1/runbooks/{runbookID}/step-completions scope runbook.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `runbookID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | RunbookStepIDsResponse | | | RunbookStepIDsResponse · 1 fields FieldTypeDescription `step_ids` uuid[] · nullable The steps the calling user ticked off on the runbook itself, in step order. Progress on an incident is kept per incident. | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousSet a playbook step completion](https://upfour.io/docs/api/runbooks/set-a-playbook-step-completion.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/runbooks/00000000-0000-4000-8000-000000000001/step-completions" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/runbooks/00000000-0000-4000-8000-000000000001/step-completions", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/runbooks/00000000-0000-4000-8000-000000000001/step-completions", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/runbooks/00000000-0000-4000-8000-000000000001/step-completions", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "step_ids": [ "00000000-0000-4000-8000-000000000002" ] } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Health advisor — API reference > Findings about your setup, such as monitors that alert nobody, and one call to apply a suggested fix. REST endpoints with request and response fields, examples in curl, JavaScript, Python, Go and PHP, and an in-browser Try it console. Source: https://upfour.io/docs/api/advisor [Developers](https://upfour.io/docs/api.md)/Alerting & response Findings about your setup, such as monitors that alert nobody, and one call to apply a suggested fix. Base URL `https://app.upfour.io/v1`. Send your key as `Authorization: Bearer `; every key acts on your real Company, so [test without paging anyone](https://upfour.io/docs/api/testing.md). New here? Start with the [quickstart](https://upfour.io/docs/api/quickstart.md). The examples read the key from `UP4_API_KEY`. ## Everyday [GET **Get the health advisor report** /advisor](https://upfour.io/docs/api/advisor/get-the-health-advisor-report.md) ## Control [POST **Apply an advisor fix** /advisor/apply](https://upfour.io/docs/api/advisor/apply-an-advisor-fix.md) --- # Get the health advisor report — API reference > Returns findings, severity counts and the evaluated scope. Source: https://upfour.io/docs/api/advisor/get-the-health-advisor-report [Developers](https://upfour.io/docs/api.md)/[Health advisor](https://upfour.io/docs/api/advisor.md) Returns findings, severity counts and the evaluated scope. GET https://app.upfour.io/v1/advisor scope report.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `limit` optional | integer 1–250 default `50` | query parameter | | `cursor` optional | string | query parameter | | `client_id` optional | uuid | query parameter | | `all_clients` optional | boolean | query parameter | | `q` optional | string | query parameter | | `sort` optional | string | query parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | AdvisorReport | | | AdvisorReport · 19 fields FieldTypeDescription `critical_count` integer `findings` AdvisorFinding[] · nullable Each item · 12 fields FieldTypeDescription `rule_id` string Identifier of the rule, e.g. monitor_without_recipient, interval_below_plan_minimum, ssl_expiring_soon, single_region. New rules can appear. `severity` string one of`critical` `warning` `hint` `target_type` string one of`monitor` `channel` `escalation_policy` `heartbeat` `organization` `title` string `why` string `fix_action` string attach_default_channel or set_interval when POST /advisor/apply can fix the finding; absent for advice that only links to fix_href. `fix_href` string `fix_label` string `message_params` object free-form keys Values for localizing the finding's prose; the keys depend on the rule. `message_variant` string `target_id` uuid `target_name` string `generated_at` string (date-time) `hint_count` integer `monitors_with_findings` map of string · nullable one of`critical` `warning` `hint` Highest severity per monitor ID with findings. `scope` string one of`organization` `monitor` `warning_count` integer | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [NextApply an advisor fix](https://upfour.io/docs/api/advisor/apply-an-advisor-fix.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/advisor" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/advisor", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/advisor", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/advisor", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "critical_count": 3, "findings": [ { "fix_action": "attach_default_channel", "fix_href": "/monitors/00000000-0000-4000-8000-000000000001/edit", "fix_label": "Alert me by email", "rule_id": "monitor_without_recipient", "severity": "critical", "target_id": "00000000-0000-4000-8000-000000000001", "target_name": "Status page", "target_type": "monitor", "title": "Nobody is alerted when this goes down", "why": "This monitor has no alert channel and no alert plan. If it fails, up4 checks it, records the outage — and tells no one." }, { "fix_action": "attach_default_channel", "fix_href": "/monitors/00000000-0000-4000-8000-000000000002/edit", "fix_label": "Alert me by email", "rule_id": "monitor_without_recipient", "severity": "critical", "target_id": "00000000-0000-4000-8000-000000000002", "target_name": "Docs site", "target_type": "monitor", "title": "Nobody is alerted when this goes down", "why": "This monitor has no alert channel and no alert plan. If it fails, up4 checks it, records the outage — and tells no one." }, { "fix_action": "attach_default_channel", "fix_href": "/monitors/00000000-0000-4000-8000-000000000003/edit", "fix_label": "Alert me by email", "rule_id": "monitor_without_recipient", "severity": "critical", "target_id": "00000000-0000-4000-8000-000000000003", "target_name": "Marketing website", "target_type": "monitor", "title": "Nobody is alerted when this goes down", "why": "This monitor has no alert channel and no alert plan. If it fails, up4 checks it, records the outage — and tells no one." }, { "fix_href": "/escalation-policies/00000000-0000-4000-8000-000000000004", "fix_label": "Attach to monitors", "rule_id": "alert_plan_without_monitors", "severity": "hint", "target_id": "00000000-0000-4000-8000-000000000004", "target_name": "Default escalation", "target_type": "escalation_policy", "title": "This alert plan is not attached to anything", "why": "No monitor uses this plan, so its escalation steps never run." } ], "generated_at": "2026-10-27T09:00:00Z", "hint_count": 1, "monitors_with_findings": { "00000000-0000-4000-8000-000000000001": "critical", "00000000-0000-4000-8000-000000000002": "critical", "00000000-0000-4000-8000-000000000003": "critical" }, "scope": "organization", "warning_count": 0 } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` --- # Apply an advisor fix — API reference > Applies the supported one-click fix for a finding to its target monitor. Source: https://upfour.io/docs/api/advisor/apply-an-advisor-fix [Developers](https://upfour.io/docs/api.md)/[Health advisor](https://upfour.io/docs/api/advisor.md) Applies the supported one-click fix for a finding to its target monitor. POST https://app.upfour.io/v1/advisor/apply scope report.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Body `application/json` required 4 fields rule_idstring (required) The rule of the finding to fix. Only the rules listed have a one-click fix; findings without a fix_action link to a page instead. monitor_without_recipientinterval_below_plan_minimum target_iduuid (required) The monitor to fix. All other fields2 fields paramsobjectfree-form keys monitor_without_recipient: optional channel_id (UUID of the alert channel to attach; without it the caller's own email channel is used or created). interval_below_plan_minimum: optional interval_seconds (a positive number; without it the default check interval is set). target_typestring Recorded in the audit log only; both fixes act on the monitor in target_id. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | AdvisorApplyResponse | | | AdvisorApplyResponse · 1 fields FieldTypeDescription `applied` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet the health advisor report](https://upfour.io/docs/api/advisor/get-the-health-advisor-report.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/advisor/apply" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "rule_id": "monitor_without_recipient", "target_id": "00000000-0000-4000-8000-000000000001", "target_type": "monitor" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/advisor/apply", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ rule_id: "monitor_without_recipient", target_id: "00000000-0000-4000-8000-000000000001", target_type: "monitor", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/advisor/apply", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "rule_id": "monitor_without_recipient", "target_id": "00000000-0000-4000-8000-000000000001", "target_type": "monitor", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "rule_id": "monitor_without_recipient", "target_id": "00000000-0000-4000-8000-000000000001", "target_type": "monitor" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/advisor/apply", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "applied": "channel_attached" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Status pages — API reference > Public and client-facing uptime pages: structure, updates, subscribers, translations. REST endpoints with request and response fields, examples in curl, JavaScript, Python, Go and PHP, and an in-browser Try it console. Source: https://upfour.io/docs/api/status-pages [Developers](https://upfour.io/docs/api.md)/Pages & reporting Public and client-facing uptime pages: structure, updates, subscribers, translations. Base URL `https://app.upfour.io/v1`. Send your key as `Authorization: Bearer `; every key acts on your real Company, so [test without paging anyone](https://upfour.io/docs/api/testing.md). New here? Start with the [quickstart](https://upfour.io/docs/api/quickstart.md). The examples read the key from `UP4_API_KEY`. ## Everyday [GET **List status pages** /status-pages](https://upfour.io/docs/api/status-pages/list-status-pages.md)[POST **Create a status page** /status-pages](https://upfour.io/docs/api/status-pages/create-a-status-page.md)[POST **Preview a status feed** /status-pages/feed-preview](https://upfour.io/docs/api/status-pages/preview-a-status-feed.md)[GET **Get platform details** /status-pages/platform](https://upfour.io/docs/api/status-pages/get-platform-details.md)[DEL **Delete a status page** /status-pages/{pageID}](https://upfour.io/docs/api/status-pages/delete-a-status-page.md)[GET **Get a status page** /status-pages/{pageID}](https://upfour.io/docs/api/status-pages/get-a-status-page.md)[PUT **Update a status page** /status-pages/{pageID}](https://upfour.io/docs/api/status-pages/update-a-status-page.md) ## Page management [GET **Get status page access** /status-pages/{pageID}/access](https://upfour.io/docs/api/status-pages/get-status-page-access.md)[PUT **Update status page access** /status-pages/{pageID}/access](https://upfour.io/docs/api/status-pages/update-status-page-access.md)[DEL **Remove a status page access token** /status-pages/{pageID}/access-token](https://upfour.io/docs/api/status-pages/remove-a-status-page-access-token.md)[POST **Create a status page access token** /status-pages/{pageID}/access-token](https://upfour.io/docs/api/status-pages/create-a-status-page-access-token.md)[DEL **Delete a status page asset** /status-pages/{pageID}/assets/{kind}](https://upfour.io/docs/api/status-pages/delete-a-status-page-asset.md)[PUT **Upload a status page asset** /status-pages/{pageID}/assets/{kind}](https://upfour.io/docs/api/status-pages/upload-a-status-page-asset.md)[DEL **Remove a custom domain** /status-pages/{pageID}/custom-domain](https://upfour.io/docs/api/status-pages/remove-a-custom-domain.md)[PUT **Set a custom domain** /status-pages/{pageID}/custom-domain](https://upfour.io/docs/api/status-pages/set-a-custom-domain.md)[POST **Recheck a custom domain** /status-pages/{pageID}/custom-domain/recheck](https://upfour.io/docs/api/status-pages/recheck-a-custom-domain.md)[GET **Get custom domain details** /status-pages/{pageID}/domain](https://upfour.io/docs/api/status-pages/get-custom-domain-details.md)[GET **List custom domain events** /status-pages/{pageID}/domain/events](https://upfour.io/docs/api/status-pages/list-custom-domain-events.md)[POST **Duplicate a status page** /status-pages/{pageID}/duplicate](https://upfour.io/docs/api/status-pages/duplicate-a-status-page.md)[GET **Preview subscriber deliveries** /status-pages/{pageID}/mail-preview](https://upfour.io/docs/api/status-pages/preview-subscriber-deliveries.md)[GET **List report updates** /status-pages/{pageID}/report-updates](https://upfour.io/docs/api/status-pages/list-report-updates.md)[GET **List status page reports** /status-pages/{pageID}/reports](https://upfour.io/docs/api/status-pages/list-status-page-reports.md)[POST **Create a status page report** /status-pages/{pageID}/reports](https://upfour.io/docs/api/status-pages/create-a-status-page-report.md) ## Structure [GET **List status page sections** /status-pages/{pageID}/sections](https://upfour.io/docs/api/status-pages/list-status-page-sections.md)[POST **Create a status page section** /status-pages/{pageID}/sections](https://upfour.io/docs/api/status-pages/create-a-status-page-section.md)[DEL **Delete a status page section** /status-pages/{pageID}/sections/{sectionID}](https://upfour.io/docs/api/status-pages/delete-a-status-page-section.md)[PUT **Update a status page section** /status-pages/{pageID}/sections/{sectionID}](https://upfour.io/docs/api/status-pages/update-a-status-page-section.md)[GET **List section resources** /status-pages/{pageID}/sections/{sectionID}/resources](https://upfour.io/docs/api/status-pages/list-section-resources.md)[POST **Add a section resource** /status-pages/{pageID}/sections/{sectionID}/resources](https://upfour.io/docs/api/status-pages/add-a-section-resource.md)[DEL **Delete a section resource** /status-pages/{pageID}/sections/{sectionID}/resources/{resourceID}](https://upfour.io/docs/api/status-pages/delete-a-section-resource.md)[PUT **Update a section resource** /status-pages/{pageID}/sections/{sectionID}/resources/{resourceID}](https://upfour.io/docs/api/status-pages/update-a-section-resource.md)[GET **Get status page structure** /status-pages/{pageID}/structure](https://upfour.io/docs/api/status-pages/get-status-page-structure.md)[PUT **Replace status page structure** /status-pages/{pageID}/structure](https://upfour.io/docs/api/status-pages/replace-status-page-structure.md) ## Subscribers and translations [GET **List status page subscribers** /status-pages/{pageID}/subscribers](https://upfour.io/docs/api/status-pages/list-status-page-subscribers.md)[POST **Add a status page subscriber** /status-pages/{pageID}/subscribers](https://upfour.io/docs/api/status-pages/add-a-status-page-subscriber.md)[GET **Count status page subscribers** /status-pages/{pageID}/subscribers/count](https://upfour.io/docs/api/status-pages/count-status-page-subscribers.md)[POST **Resend pending subscriber messages** /status-pages/{pageID}/subscribers/resend-pending](https://upfour.io/docs/api/status-pages/resend-pending-subscriber-messages.md)[POST **Import status page subscribers** /status-pages/{pageID}/subscribers/upload](https://upfour.io/docs/api/status-pages/import-status-page-subscribers.md)[DEL **Delete a status page subscriber** /status-pages/{pageID}/subscribers/{subscriberID}](https://upfour.io/docs/api/status-pages/delete-a-status-page-subscriber.md)[POST **Resend subscriber confirmation** /status-pages/{pageID}/subscribers/{subscriberID}/resend](https://upfour.io/docs/api/status-pages/resend-subscriber-confirmation.md)[GET **List status page translations** /status-pages/{pageID}/translations](https://upfour.io/docs/api/status-pages/list-status-page-translations.md)[POST **Create a status page translation** /status-pages/{pageID}/translations](https://upfour.io/docs/api/status-pages/create-a-status-page-translation.md)[DEL **Delete a status page translation** /status-pages/{pageID}/translations/{translationID}](https://upfour.io/docs/api/status-pages/delete-a-status-page-translation.md)[PUT **Update a status page translation** /status-pages/{pageID}/translations/{translationID}](https://upfour.io/docs/api/status-pages/update-a-status-page-translation.md) ## Updates and deliveries [GET **List status page updates** /status-pages/{pageID}/updates](https://upfour.io/docs/api/status-pages/list-status-page-updates.md)[POST **Create a status page update** /status-pages/{pageID}/updates](https://upfour.io/docs/api/status-pages/create-a-status-page-update.md)[DEL **Delete a status page update** /status-pages/{pageID}/updates/{updateID}](https://upfour.io/docs/api/status-pages/delete-a-status-page-update.md)[GET **Get a status page update** /status-pages/{pageID}/updates/{updateID}](https://upfour.io/docs/api/status-pages/get-a-status-page-update.md)[PUT **Update a status page update** /status-pages/{pageID}/updates/{updateID}](https://upfour.io/docs/api/status-pages/update-a-status-page-update.md)[GET **List update deliveries** /status-pages/{pageID}/updates/{updateID}/deliveries](https://upfour.io/docs/api/status-pages/list-update-deliveries.md)[POST **Add an update entry** /status-pages/{pageID}/updates/{updateID}/entries](https://upfour.io/docs/api/status-pages/add-an-update-entry.md)[DEL **Delete an update entry** /status-pages/{pageID}/updates/{updateID}/entries/{entryID}](https://upfour.io/docs/api/status-pages/delete-an-update-entry.md)[PUT **Update a status page entry** /status-pages/{pageID}/updates/{updateID}/entries/{entryID}](https://upfour.io/docs/api/status-pages/update-a-status-page-entry.md) --- # List status pages — API reference > Returns status pages in the selected client scope, with optional search, sorting, cursor and page-size parameters. Source: https://upfour.io/docs/api/status-pages/list-status-pages [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Returns status pages in the selected client scope, with optional search, sorting, cursor and page-size parameters. GET https://app.upfour.io/v1/status-pages scope status_page.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `limit` optional | integer 1–250 default `50` | query parameter | | `cursor` optional | string | query parameter | | `client_id` optional | uuid | query parameter | | `all_clients` optional | boolean | query parameter | | `q` optional | string | query parameter | | `sort` optional | string | query parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPage[] · nullable | | | Each item · 61 fields FieldTypeDescription `accent_color` string `announcement` string `appearance` StatusPageAppearanceResponse · nullable null until a layout was chosen; the page then renders the default design (essential layout, paper ground). StatusPageAppearanceResponse · 5 fields FieldTypeDescription `ground` string one of`paper` `white` `cool` Empty is stored as paper. `header_dark` string #RRGGBB or empty for the platform default. `header_light` string #RRGGBB or empty for the platform default. `layout` string one of`essential` `brand` `overview` `version` integer Must be 1. `automatic_reports` boolean `automatic_reports_notify` boolean `company_name` string `company_url` string `contact_url` string `created_at` string (date-time) `custom_css` string `custom_domain` string `custom_domain_certificate_status` string one of`none` `pending` `issued` `failed` `unknown` `custom_domain_status` string one of`none` `pending_dns` `queued` `provisioning` `active` `failed` `disabled` `conflict_requires_review` `custom_js` string Not supported: always empty. `default_language` string · nullable Language of the page's own communication; null inherits from the client, then the company default. `design` string No effect since Plan 82; accepted and stored only. `google_tag_id` string Not supported: always empty. `header_layout` string No effect since Plan 82; accepted and stored only. `hide_from_search_engines` boolean `history_days` integer `id` uuid `is_published` boolean `logo_url` string `minimum_incident_length` integer `organization_id` uuid `password_protected` boolean `remove_branding` boolean `show_not_monitored_as_green` boolean `subdomain` string `subscribable` boolean `theme` string `timezone` string `updated_at` string (date-time) `uptime_decimals` integer `access_restricted` boolean Whether any visitor gate (password, e-mail codes, IP allowlist or access token) is on. Only in GET /status-pages. `auto_report_open_template_id` uuid `auto_report_resolve_template_id` uuid `client_id` uuid `custom_domain_attempts` integer `custom_domain_cleanup_requested_at` string (date-time) `custom_domain_dns_target` string `custom_domain_error` string `custom_domain_last_checked_at` string (date-time) `custom_domain_next_retry_at` string (date-time) `custom_domain_pending_expires_at` string (date-time) `custom_domain_ploi_tenant` string `custom_domain_provisioned_at` string (date-time) `custom_domain_verification_token` string Value of the up4-status-verification TXT record that proves ownership of the domain. `custom_domain_verified_at` string (date-time) `favicon_url` string `logo_asset_url` string `logo_dark_asset_url` string `platform_url` string `public_url` string Where the page is reachable: the custom domain once active, else the platform address. `reply_to_email` string `subscriber_count` integer Confirmed, active subscribers. Only in GET /status-pages. | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [NextCreate a status page](https://upfour.io/docs/api/status-pages/create-a-status-page.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/status-pages" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/status-pages", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/status-pages", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "accent_color": "", "announcement": "All systems are monitored by up4.", "appearance": null, "automatic_reports": false, "automatic_reports_notify": false, "company_name": "Acme GmbH", "company_url": "", "contact_url": "", "created_at": "2026-10-27T09:00:00Z", "custom_css": "", "custom_domain": "", "custom_domain_certificate_status": "none", "custom_domain_status": "none", "custom_js": "", "default_language": null, "design": "v2", "google_tag_id": "", "header_layout": "vertical", "hide_from_search_engines": false, "history_days": 90, "id": "00000000-0000-4000-8000-000000000001", "is_published": false, "logo_url": "", "minimum_incident_length": 0, "organization_id": "00000000-0000-4000-8000-000000000002", "password_protected": false, "platform_url": "https://example.com", "public_url": "https://example.com", "remove_branding": false, "show_not_monitored_as_green": false, "subdomain": "acme-status", "subscribable": false, "subscriber_count": 0, "theme": "light", "timezone": "UTC", "updated_at": "2026-10-27T09:00:00Z", "uptime_decimals": 0 } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` --- # Create a status page — API reference > Creates a status page with a company name, public subdomain, branding, visibility and optional initial monitors or Scheduled jobs. Source: https://upfour.io/docs/api/status-pages/create-a-status-page [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Creates a status page with a company name, public subdomain, branding, visibility and optional initial monitors or Scheduled jobs. POST https://app.upfour.io/v1/status-pages scope status_page.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Body The 5 fields most calls use come first; the rest are grouped below. `application/json` required 36 fields company_namestring (required) In the app: **Company name** subdomainstring (required) In the app: **Public page slug** company_urlstring client_iduuid · nullable contact_urlstring Appearance22 fields accent_colorstring appearanceStatusPageAppearance · nullable Layout and brand surface. Omitted leaves the stored value unchanged (on create: none chosen), null resets it to the default design, an object is validated and stored: version must be 1, layout is required, ground defaults to paper, header_light and header_dark are #RRGGBB or empty. In the app: **Layout** Nested fields 5 fields | Field | Type | Description | | --- | --- | --- | | `ground` optional | string one of`paper` `white` `cool` | Empty is stored as paper. | | `header_dark` optional | string | #RRGGBB or empty for the platform default. | | `header_light` optional | string | #RRGGBB or empty for the platform default. | | `layout` optional | string one of`essential` `brand` `overview` | | | `version` optional | integer | Must be 1. | designstring No effect since Plan 82; accepted and stored only. design_template_applicationDesignTemplateApplication · nullable Nested fields 4 fields | Field | Type | Description | | --- | --- | --- | | `apply_assets` optional | boolean | Replaces the page's images (and logo_url) with the template's. | | `apply_css` optional | boolean | Fills custom_css from the template when the request sets none. | | `expected_revision` optional | integer | The template revision the caller looked at (at least 1); a different stored revision is a conflict. | | `template_id` optional | uuid | | header_layoutstring No effect since Plan 82; accepted and stored only. logo_urlstring custom_cssstring In the app: **Custom CSS** custom_jsstring Not supported: a non-empty value is rejected. themestring hide_from_search_enginesboolean remove_brandingboolean show_not_monitored_as_greenboolean uptime_decimalsinteger Publication and notifications9 fields announcementstring In the app: **Announcement** is_publishedboolean · nullable A page starts as a draft unless this is true. In the app: **Publish status page** subscribableboolean automatic_reportsboolean automatic_reports_notifyboolean auto_report_open_template_iduuid · nullable auto_report_resolve_template_iduuid · nullable reply_to_emailstring In the app: **Reply-to address for status e-mails** minimum_incident_lengthinteger Access and history6 fields passwordstring Write-only; stored as a hash when password_protected is true. Responses carry password_protected only. In the app: **Password** password_protectedboolean custom_domainstring Not accepted on create: leave it empty and connect a domain with PUT /status-pages/{pageID}/custom-domain. default_languagestring · nullable Language of the page's own communication; empty or omitted inherits from the client, then the company default. In the app: **Default language** history_daysinteger In the app: **How many days to display?** timezonestring Initial structure20 fields sectionsStructureSectionInput[] The initial structure. Not combinable with selected_monitor_ids or selected_heartbeat_ids. Nested fields 17 fields | Field | Type | Description | | --- | --- | --- | | `auto_group_id` optional | uuid · nullable | Required for monitor_group and job_group, rejected for the other sources. | | `auto_source` optional | string one of`none` `all_monitors` `all_jobs` `monitor_group` `job_group` | none (stored resources), all_monitors, all_jobs, monitor_group or job_group. An automatic section cannot also hold resources. | | `auto_widget_type` optional | string | How the components of an automatic section render; history by default. | | `name` optional | string | | | `resources` optional | StructureResourceInput[] | | | Each item · 12 fields FieldTypeDescription `auto_publish` optional boolean · nullable false keeps the automatic report for this component as an internal draft. Default true. `explanation` optional string `feed_component_id` optional string `feed_url` optional string Address of a vendor's status page (Atlassian Statuspage or Instatus); needs feed_component_id. `heartbeat_id` optional uuid · nullable `is_external` optional boolean Shown for transparency but never counted into the page's own status or availability. Always true for a feed component. `manual_status` optional string one of`""` `operational` `degraded` `down` `maintenance` Only for a component with no monitor, scheduled job or feed. Empty derives the status. `mark_degraded_for` optional string any_incident shows an open incident as degraded instead of down; any non-empty value also shows a slow but up monitor as degraded. Default no_incident. `mark_down_for` optional string any_incident (the default) shows the component as down while an incident is open; any other value shows it as degraded instead. `monitor_id` optional uuid · nullable At most one of monitor_id and heartbeat_id; with neither the component is manual (manual_status) or follows a vendor feed (feed_url). `public_name` optional string `widget_type` optional string How the component renders: history (daily bars, the default), plain, or response_times (monitors only). Not validated by the service. | | | selected_monitor_idsuuid[] Puts these monitors into one section, "Current status by service". Not combinable with sections. selected_heartbeat_idsuuid[] Puts these scheduled jobs into one section, "Current status by service". Not combinable with sections. ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPage | | | StatusPage · 61 fields FieldTypeDescription `accent_color` string `announcement` string `appearance` StatusPageAppearanceResponse · nullable null until a layout was chosen; the page then renders the default design (essential layout, paper ground). StatusPageAppearanceResponse · 5 fields FieldTypeDescription `ground` string one of`paper` `white` `cool` Empty is stored as paper. `header_dark` string #RRGGBB or empty for the platform default. `header_light` string #RRGGBB or empty for the platform default. `layout` string one of`essential` `brand` `overview` `version` integer Must be 1. `automatic_reports` boolean `automatic_reports_notify` boolean `company_name` string `company_url` string `contact_url` string `created_at` string (date-time) `custom_css` string `custom_domain` string `custom_domain_certificate_status` string one of`none` `pending` `issued` `failed` `unknown` `custom_domain_status` string one of`none` `pending_dns` `queued` `provisioning` `active` `failed` `disabled` `conflict_requires_review` `custom_js` string Not supported: always empty. `default_language` string · nullable Language of the page's own communication; null inherits from the client, then the company default. `design` string No effect since Plan 82; accepted and stored only. `google_tag_id` string Not supported: always empty. `header_layout` string No effect since Plan 82; accepted and stored only. `hide_from_search_engines` boolean `history_days` integer `id` uuid `is_published` boolean `logo_url` string `minimum_incident_length` integer `organization_id` uuid `password_protected` boolean `remove_branding` boolean `show_not_monitored_as_green` boolean `subdomain` string `subscribable` boolean `theme` string `timezone` string `updated_at` string (date-time) `uptime_decimals` integer `access_restricted` boolean Whether any visitor gate (password, e-mail codes, IP allowlist or access token) is on. Only in GET /status-pages. `auto_report_open_template_id` uuid `auto_report_resolve_template_id` uuid `client_id` uuid `custom_domain_attempts` integer `custom_domain_cleanup_requested_at` string (date-time) `custom_domain_dns_target` string `custom_domain_error` string `custom_domain_last_checked_at` string (date-time) `custom_domain_next_retry_at` string (date-time) `custom_domain_pending_expires_at` string (date-time) `custom_domain_ploi_tenant` string `custom_domain_provisioned_at` string (date-time) `custom_domain_verification_token` string Value of the up4-status-verification TXT record that proves ownership of the domain. `custom_domain_verified_at` string (date-time) `favicon_url` string `logo_asset_url` string `logo_dark_asset_url` string `platform_url` string `public_url` string Where the page is reachable: the custom domain once active, else the platform address. `reply_to_email` string `subscriber_count` integer Confirmed, active subscribers. Only in GET /status-pages. | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 402 | [billing_action_unavailable](https://upfour.io/docs/api/errors#code-billing_action_unavailable) | The Company's billing state does not allow this action. **When:** A metered or limited action while the billing state, such as paused or without credit, blocks it; details holds the action and billing_state. **Fix:** Top up the credit or change the plan, then retry. | | 409 | [design_template_conflict](https://upfour.io/docs/api/errors#code-design_template_conflict) | The design template was changed after the caller read it. **When:** Updating or applying a design template that was changed after the caller read it. **Fix:** Reload the template and apply the change again. | | 404 | [design_template_not_found](https://upfour.io/docs/api/errors#code-design_template_not_found) | The design template does not exist or is not visible to the caller. **When:** A design template id in the path or body that is unknown or not visible to the caller. **Fix:** List design templates and use an id from the list. | | 402 | [payment_required](https://upfour.io/docs/api/errors#code-payment_required) | The account does not have the credit or plan required for this action. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 503 | [quota_gate_unavailable](https://upfour.io/docs/api/errors#code-quota_gate_unavailable) | The limit check itself failed, so the action was not carried out. **When:** The quota service could not decide while the request ran. **Fix:** Retry in a moment; nothing was changed. | | 403 / 429 | [quota_limit_reached](https://upfour.io/docs/api/errors#code-quota_limit_reached) | A usage or inventory limit of the Company was reached. **When:** Counters and concurrency limits answer 429 with Retry-After, inventory and capacity limits answer 403; details holds purpose, limit and window. **Fix:** On 429 wait Retry-After seconds; on 403 free something up, request a higher limit where details.raise_request is true, or buy an extension. `quota_limit_reached` (window `total`): the Company already holds the most of these it may have. `error.details` carries `purpose` and `limit`; remove one or ask support for a higher limit. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList status pages](https://upfour.io/docs/api/status-pages/list-status-pages.md) [NextPreview a status feed](https://upfour.io/docs/api/status-pages/preview-a-status-feed.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/status-pages" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "announcement": "All systems are monitored by up4.", "company_name": "Acme GmbH", "subdomain": "acme-status" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ announcement: "All systems are monitored by up4.", company_name: "Acme GmbH", subdomain: "acme-status", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/status-pages", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "announcement": "All systems are monitored by up4.", "company_name": "Acme GmbH", "subdomain": "acme-status", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "announcement": "All systems are monitored by up4.", "company_name": "Acme GmbH", "subdomain": "acme-status" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/status-pages", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "accent_color": "", "announcement": "All systems are monitored by up4.", "appearance": null, "automatic_reports": false, "automatic_reports_notify": false, "company_name": "Acme GmbH", "company_url": "", "contact_url": "", "created_at": "2026-10-27T09:00:00Z", "custom_css": "", "custom_domain": "", "custom_domain_certificate_status": "none", "custom_domain_status": "none", "custom_js": "", "default_language": null, "design": "v2", "google_tag_id": "", "header_layout": "vertical", "hide_from_search_engines": false, "history_days": 90, "id": "00000000-0000-4000-8000-000000000001", "is_published": false, "logo_url": "", "minimum_incident_length": 0, "organization_id": "00000000-0000-4000-8000-000000000002", "password_protected": false, "platform_url": "https://example.com", "public_url": "https://example.com", "remove_branding": false, "show_not_monitored_as_green": false, "subdomain": "acme-status", "subscribable": false, "theme": "light", "timezone": "UTC", "updated_at": "2026-10-27T09:00:00Z", "uptime_decimals": 0 } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "billing_action_unavailable", "message": "The Company's billing state does not allow this action.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "design_template_not_found", "message": "The design template does not exist or is not visible to the caller.", "request_id": "req_…" } } ``` ### 409 ```json { "error": { "code": "design_template_conflict", "message": "The design template was changed after the caller read it.", "request_id": "req_…" } } ``` ### 429 ```json { "error": { "code": "quota_limit_reached", "message": "A usage or inventory limit of the Company was reached.", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "quota_gate_unavailable", "message": "The limit check itself failed, so the action was not carried out.", "request_id": "req_…" } } ``` --- # Preview a status feed — API reference > Previews a status feed from a URL and returns its detected page name, format and components. Source: https://upfour.io/docs/api/status-pages/preview-a-status-feed [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Previews a status feed from a URL and returns its detected page name, format and components. POST https://app.upfour.io/v1/status-pages/feed-preview scope status_page.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Body `application/json` required 1 field urlstring (required) ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusFeedPreviewResponse | | | StatusFeedPreviewResponse · 8 fields FieldTypeDescription `components` StatusFeedComponent[] · nullable Each item · 4 fields FieldTypeDescription `group` string `id` string `name` string `status` string `format` string one of`statuspage_v2` `instatus` `page_name` string `url` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousCreate a status page](https://upfour.io/docs/api/status-pages/create-a-status-page.md) [NextGet platform details](https://upfour.io/docs/api/status-pages/get-platform-details.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/status-pages/feed-preview" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/feed-preview", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ url: "https://example.com", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/status-pages/feed-preview", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "url": "https://example.com", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "url": "https://example.com" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/status-pages/feed-preview", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "components": [ { "group": "example", "id": "3f6c1a52-8b1e-4f0d-9a43-2c5d7e8f1a90", "name": "Example", "status": "example" } ], "format": "statuspage_v2", "page_name": "example", "url": "https://example.com" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Get platform details — API reference > Returns the platform domain and public URL scheme used for hosted status pages. Source: https://upfour.io/docs/api/status-pages/get-platform-details [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Returns the platform domain and public URL scheme used for hosted status pages. GET https://app.upfour.io/v1/status-pages/platform scope status_page.read stable returns 200 ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPagePlatformConfigResponse | | | StatusPagePlatformConfigResponse · 2 fields FieldTypeDescription `platform_domain` string `public_scheme` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). The specification does not list additional operation-specific errors. Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousPreview a status feed](https://upfour.io/docs/api/status-pages/preview-a-status-feed.md) [NextDelete a status page](https://upfour.io/docs/api/status-pages/delete-a-status-page.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/status-pages/platform" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/platform", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/status-pages/platform", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/status-pages/platform", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "platform_domain": "pages.example.test", "public_scheme": "https" } } ``` --- # Delete a status page — API reference > Deletes the selected status page and returns status 204 without a response body. Source: https://upfour.io/docs/api/status-pages/delete-a-status-page [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Deletes the selected status page and returns status 204 without a response body. DELETE https://app.upfour.io/v1/status-pages/{pageID} scope status_page.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet platform details](https://upfour.io/docs/api/status-pages/get-platform-details.md) [NextGet a status page](https://upfour.io/docs/api/status-pages/get-a-status-page.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Get a status page — API reference > Returns one status page with its publication, branding, access, domain and subscriber details. Source: https://upfour.io/docs/api/status-pages/get-a-status-page [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Returns one status page with its publication, branding, access, domain and subscriber details. GET https://app.upfour.io/v1/status-pages/{pageID} scope status_page.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPage | | | StatusPage · 61 fields FieldTypeDescription `accent_color` string `announcement` string `appearance` StatusPageAppearanceResponse · nullable null until a layout was chosen; the page then renders the default design (essential layout, paper ground). StatusPageAppearanceResponse · 5 fields FieldTypeDescription `ground` string one of`paper` `white` `cool` Empty is stored as paper. `header_dark` string #RRGGBB or empty for the platform default. `header_light` string #RRGGBB or empty for the platform default. `layout` string one of`essential` `brand` `overview` `version` integer Must be 1. `automatic_reports` boolean `automatic_reports_notify` boolean `company_name` string `company_url` string `contact_url` string `created_at` string (date-time) `custom_css` string `custom_domain` string `custom_domain_certificate_status` string one of`none` `pending` `issued` `failed` `unknown` `custom_domain_status` string one of`none` `pending_dns` `queued` `provisioning` `active` `failed` `disabled` `conflict_requires_review` `custom_js` string Not supported: always empty. `default_language` string · nullable Language of the page's own communication; null inherits from the client, then the company default. `design` string No effect since Plan 82; accepted and stored only. `google_tag_id` string Not supported: always empty. `header_layout` string No effect since Plan 82; accepted and stored only. `hide_from_search_engines` boolean `history_days` integer `id` uuid `is_published` boolean `logo_url` string `minimum_incident_length` integer `organization_id` uuid `password_protected` boolean `remove_branding` boolean `show_not_monitored_as_green` boolean `subdomain` string `subscribable` boolean `theme` string `timezone` string `updated_at` string (date-time) `uptime_decimals` integer `access_restricted` boolean Whether any visitor gate (password, e-mail codes, IP allowlist or access token) is on. Only in GET /status-pages. `auto_report_open_template_id` uuid `auto_report_resolve_template_id` uuid `client_id` uuid `custom_domain_attempts` integer `custom_domain_cleanup_requested_at` string (date-time) `custom_domain_dns_target` string `custom_domain_error` string `custom_domain_last_checked_at` string (date-time) `custom_domain_next_retry_at` string (date-time) `custom_domain_pending_expires_at` string (date-time) `custom_domain_ploi_tenant` string `custom_domain_provisioned_at` string (date-time) `custom_domain_verification_token` string Value of the up4-status-verification TXT record that proves ownership of the domain. `custom_domain_verified_at` string (date-time) `favicon_url` string `logo_asset_url` string `logo_dark_asset_url` string `platform_url` string `public_url` string Where the page is reachable: the custom domain once active, else the platform address. `reply_to_email` string `subscriber_count` integer Confirmed, active subscribers. Only in GET /status-pages. | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousDelete a status page](https://upfour.io/docs/api/status-pages/delete-a-status-page.md) [NextUpdate a status page](https://upfour.io/docs/api/status-pages/update-a-status-page.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "accent_color": "", "announcement": "All systems are monitored by up4.", "appearance": null, "automatic_reports": false, "automatic_reports_notify": false, "company_name": "Acme GmbH", "company_url": "", "contact_url": "", "created_at": "2026-10-27T09:00:00Z", "custom_css": "", "custom_domain": "", "custom_domain_certificate_status": "none", "custom_domain_status": "none", "custom_js": "", "default_language": null, "design": "v2", "google_tag_id": "", "header_layout": "vertical", "hide_from_search_engines": false, "history_days": 90, "id": "00000000-0000-4000-8000-000000000001", "is_published": false, "logo_url": "", "minimum_incident_length": 0, "organization_id": "00000000-0000-4000-8000-000000000002", "password_protected": false, "platform_url": "https://example.com", "public_url": "https://example.com", "remove_branding": false, "show_not_monitored_as_green": false, "subdomain": "acme-status", "subscribable": false, "theme": "light", "timezone": "UTC", "updated_at": "2026-10-27T09:00:00Z", "uptime_decimals": 0 } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Update a status page — API reference > Updates selected status-page settings, including branding, publication, appearance, notifications, history and public address. Source: https://upfour.io/docs/api/status-pages/update-a-status-page [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Updates selected status-page settings, including branding, publication, appearance, notifications, history and public address. PUT https://app.upfour.io/v1/status-pages/{pageID} scope status_page.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Body The 5 fields most calls use come first; the rest are grouped below. `application/json` required 33 fields company_namestring · nullable In the app: **Company name** subdomainstring · nullable In the app: **Public page slug** company_urlstring · nullable contact_urlstring · nullable announcementstring · nullable In the app: **Announcement** Appearance23 fields accent_colorstring · nullable appearanceStatusPageAppearance · nullable Layout and brand surface. Omitted leaves the stored value unchanged (on create: none chosen), null resets it to the default design, an object is validated and stored: version must be 1, layout is required, ground defaults to paper, header_light and header_dark are #RRGGBB or empty. In the app: **Layout** Nested fields 5 fields | Field | Type | Description | | --- | --- | --- | | `ground` optional | string one of`paper` `white` `cool` | Empty is stored as paper. | | `header_dark` optional | string | #RRGGBB or empty for the platform default. | | `header_light` optional | string | #RRGGBB or empty for the platform default. | | `layout` optional | string one of`essential` `brand` `overview` | | | `version` optional | integer | Must be 1. | designstring · nullable No effect since Plan 82; accepted and stored only. design_template_applicationDesignTemplateApplication · nullable Nested fields 4 fields | Field | Type | Description | | --- | --- | --- | | `apply_assets` optional | boolean | Replaces the page's images (and logo_url) with the template's. | | `apply_css` optional | boolean | Fills custom_css from the template when the request sets none. | | `expected_revision` optional | integer | The template revision the caller looked at (at least 1); a different stored revision is a conflict. | | `template_id` optional | uuid | | header_layoutstring · nullable No effect since Plan 82; accepted and stored only. logo_urlstring · nullable custom_cssstring · nullable In the app: **Custom CSS** custom_jsstring · nullable Not supported: a non-empty value is rejected. google_tag_idstring · nullable Not supported: a non-empty value is rejected; empty clears it. themestring · nullable hide_from_search_enginesboolean · nullable remove_brandingboolean · nullable show_not_monitored_as_greenboolean · nullable uptime_decimalsinteger · nullable Publication and notifications8 fields is_publishedboolean · nullable In the app: **Publish status page** subscribableboolean · nullable automatic_reportsboolean · nullable automatic_reports_notifyboolean · nullable auto_report_open_template_iduuid · nullable auto_report_resolve_template_iduuid · nullable reply_to_emailstring · nullable In the app: **Reply-to address for status e-mails** minimum_incident_lengthinteger · nullable Access and history6 fields passwordstring · nullable Write-only; an empty value keeps the current password. Responses carry password_protected only. In the app: **Password** password_protectedboolean · nullable custom_domainstring · nullable Ignored. Connect a domain with PUT /status-pages/{pageID}/custom-domain. default_languagestring · nullable In the app: **Default language** history_daysinteger · nullable In the app: **How many days to display?** timezonestring · nullable ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPage | | | StatusPage · 61 fields FieldTypeDescription `accent_color` string `announcement` string `appearance` StatusPageAppearanceResponse · nullable null until a layout was chosen; the page then renders the default design (essential layout, paper ground). StatusPageAppearanceResponse · 5 fields FieldTypeDescription `ground` string one of`paper` `white` `cool` Empty is stored as paper. `header_dark` string #RRGGBB or empty for the platform default. `header_light` string #RRGGBB or empty for the platform default. `layout` string one of`essential` `brand` `overview` `version` integer Must be 1. `automatic_reports` boolean `automatic_reports_notify` boolean `company_name` string `company_url` string `contact_url` string `created_at` string (date-time) `custom_css` string `custom_domain` string `custom_domain_certificate_status` string one of`none` `pending` `issued` `failed` `unknown` `custom_domain_status` string one of`none` `pending_dns` `queued` `provisioning` `active` `failed` `disabled` `conflict_requires_review` `custom_js` string Not supported: always empty. `default_language` string · nullable Language of the page's own communication; null inherits from the client, then the company default. `design` string No effect since Plan 82; accepted and stored only. `google_tag_id` string Not supported: always empty. `header_layout` string No effect since Plan 82; accepted and stored only. `hide_from_search_engines` boolean `history_days` integer `id` uuid `is_published` boolean `logo_url` string `minimum_incident_length` integer `organization_id` uuid `password_protected` boolean `remove_branding` boolean `show_not_monitored_as_green` boolean `subdomain` string `subscribable` boolean `theme` string `timezone` string `updated_at` string (date-time) `uptime_decimals` integer `access_restricted` boolean Whether any visitor gate (password, e-mail codes, IP allowlist or access token) is on. Only in GET /status-pages. `auto_report_open_template_id` uuid `auto_report_resolve_template_id` uuid `client_id` uuid `custom_domain_attempts` integer `custom_domain_cleanup_requested_at` string (date-time) `custom_domain_dns_target` string `custom_domain_error` string `custom_domain_last_checked_at` string (date-time) `custom_domain_next_retry_at` string (date-time) `custom_domain_pending_expires_at` string (date-time) `custom_domain_ploi_tenant` string `custom_domain_provisioned_at` string (date-time) `custom_domain_verification_token` string Value of the up4-status-verification TXT record that proves ownership of the domain. `custom_domain_verified_at` string (date-time) `favicon_url` string `logo_asset_url` string `logo_dark_asset_url` string `platform_url` string `public_url` string Where the page is reachable: the custom domain once active, else the platform address. `reply_to_email` string `subscriber_count` integer Confirmed, active subscribers. Only in GET /status-pages. | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 409 | [design_template_conflict](https://upfour.io/docs/api/errors#code-design_template_conflict) | The design template was changed after the caller read it. **When:** Updating or applying a design template that was changed after the caller read it. **Fix:** Reload the template and apply the change again. | | 404 | [design_template_not_found](https://upfour.io/docs/api/errors#code-design_template_not_found) | The design template does not exist or is not visible to the caller. **When:** A design template id in the path or body that is unknown or not visible to the caller. **Fix:** List design templates and use an id from the list. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet a status page](https://upfour.io/docs/api/status-pages/get-a-status-page.md) [NextGet status page access](https://upfour.io/docs/api/status-pages/get-status-page-access.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "announcement": "Planned maintenance on Sunday from 02:00 to 03:00 UTC.", "company_name": "Acme GmbH" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ announcement: "Planned maintenance on Sunday from 02:00 to 03:00 UTC.", company_name: "Acme GmbH", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "announcement": "Planned maintenance on Sunday from 02:00 to 03:00 UTC.", "company_name": "Acme GmbH", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "announcement": "Planned maintenance on Sunday from 02:00 to 03:00 UTC.", "company_name": "Acme GmbH" }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "accent_color": "", "announcement": "Planned maintenance on Sunday from 02:00 to 03:00 UTC.", "appearance": null, "automatic_reports": false, "automatic_reports_notify": false, "company_name": "Acme GmbH", "company_url": "", "contact_url": "", "created_at": "2026-10-27T09:00:00Z", "custom_css": "", "custom_domain": "", "custom_domain_certificate_status": "none", "custom_domain_status": "none", "custom_js": "", "default_language": null, "design": "v2", "google_tag_id": "", "header_layout": "vertical", "hide_from_search_engines": false, "history_days": 90, "id": "00000000-0000-4000-8000-000000000001", "is_published": false, "logo_url": "", "minimum_incident_length": 0, "organization_id": "00000000-0000-4000-8000-000000000002", "password_protected": false, "platform_url": "https://example.com", "public_url": "https://example.com", "remove_branding": false, "show_not_monitored_as_green": false, "subdomain": "acme-status", "subscribable": false, "theme": "light", "timezone": "UTC", "updated_at": "2026-10-27T09:00:00Z", "uptime_decimals": 0 } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "design_template_not_found", "message": "The design template does not exist or is not visible to the caller.", "request_id": "req_…" } } ``` ### 409 ```json { "error": { "code": "design_template_conflict", "message": "The design template was changed after the caller read it.", "request_id": "req_…" } } ``` --- # Get status page access — API reference > Returns the page access policy, including e-mail codes, address and IP allowlists, token state and rotation time. Source: https://upfour.io/docs/api/status-pages/get-status-page-access [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Returns the page access policy, including e-mail codes, address and IP allowlists, token state and rotation time. GET https://app.upfour.io/v1/status-pages/{pageID}/access scope status_page.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageAccessPolicy | | | StatusPageAccessPolicy · 6 fields FieldTypeDescription `access_email_allowlist` string[] · nullable Full addresses or @domain.tld entries allowed to request an e-mail access code. `access_email_codes` boolean `access_token_configured` boolean `ip_allowlist` string[] · nullable `status_page_id` uuid `token_rotated_at` string (date-time) · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpdate a status page](https://upfour.io/docs/api/status-pages/update-a-status-page.md) [NextUpdate status page access](https://upfour.io/docs/api/status-pages/update-status-page-access.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/access" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/access", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/access", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/access", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "access_email_allowlist": [], "access_email_codes": false, "access_token_configured": false, "ip_allowlist": [], "status_page_id": "00000000-0000-4000-8000-000000000001", "token_rotated_at": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Update status page access — API reference > Updates e-mail and IP allowlists for a status page and returns the resulting access policy. Source: https://upfour.io/docs/api/status-pages/update-status-page-access [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Updates e-mail and IP allowlists for a status page and returns the resulting access policy. PUT https://app.upfour.io/v1/status-pages/{pageID}/access scope status_page.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Body `application/json` required 3 fields All other fields3 fields access_email_allowliststring[] · nullable Full addresses or @domain.tld entries. Omitted leaves the allowlist as configured. access_email_codesboolean · nullable Omitted leaves the e-mail-code setting as configured. ip_allowliststring[] IPv4, IPv6 or CIDR entries. Always applied: omitting it clears the list. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageAccessPolicy | | | StatusPageAccessPolicy · 6 fields FieldTypeDescription `access_email_allowlist` string[] · nullable Full addresses or @domain.tld entries allowed to request an e-mail access code. `access_email_codes` boolean `access_token_configured` boolean `ip_allowlist` string[] · nullable `status_page_id` uuid `token_rotated_at` string (date-time) · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet status page access](https://upfour.io/docs/api/status-pages/get-status-page-access.md) [NextRemove a status page access token](https://upfour.io/docs/api/status-pages/remove-a-status-page-access-token.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/access" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "access_email_codes": false, "ip_allowlist": [ "203.0.113.0/24" ] }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/access", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ access_email_codes: false, ip_allowlist: ["203.0.113.0/24"], }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/access", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "access_email_codes": False, "ip_allowlist": ["203.0.113.0/24"], }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "access_email_codes": false, "ip_allowlist": [ "203.0.113.0/24" ] }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/access", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "access_email_allowlist": [], "access_email_codes": false, "access_token_configured": false, "ip_allowlist": [ "203.0.113.0/24" ], "status_page_id": "00000000-0000-4000-8000-000000000001", "token_rotated_at": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Remove a status page access token — API reference > Removes the page access token and returns the remaining access policy. Source: https://upfour.io/docs/api/status-pages/remove-a-status-page-access-token [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Removes the page access token and returns the remaining access policy. DELETE https://app.upfour.io/v1/status-pages/{pageID}/access-token scope status_page.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageAccessPolicy | | | StatusPageAccessPolicy · 6 fields FieldTypeDescription `access_email_allowlist` string[] · nullable Full addresses or @domain.tld entries allowed to request an e-mail access code. `access_email_codes` boolean `access_token_configured` boolean `ip_allowlist` string[] · nullable `status_page_id` uuid `token_rotated_at` string (date-time) · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpdate status page access](https://upfour.io/docs/api/status-pages/update-status-page-access.md) [NextCreate a status page access token](https://upfour.io/docs/api/status-pages/create-a-status-page-access-token.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/access-token" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/access-token", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/access-token", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/access-token", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "access_email_allowlist": [], "access_email_codes": false, "access_token_configured": false, "ip_allowlist": [ "203.0.113.0/24" ], "status_page_id": "00000000-0000-4000-8000-000000000001", "token_rotated_at": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Create a status page access token — API reference > Creates or rotates a page access token and returns the token once with its policy. Source: https://upfour.io/docs/api/status-pages/create-a-status-page-access-token [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Creates or rotates a page access token and returns the token once with its policy. POST https://app.upfour.io/v1/status-pages/{pageID}/access-token scope status_page.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageAccessTokenResponse | | | StatusPageAccessTokenResponse · 8 fields FieldTypeDescription `access_token` string Shown once; only its hash is stored. Rotating invalidates the previous token and all access sessions. `policy` StatusPageAccessPolicy StatusPageAccessPolicy · 6 fields FieldTypeDescription `access_email_allowlist` string[] · nullable Full addresses or @domain.tld entries allowed to request an e-mail access code. `access_email_codes` boolean `access_token_configured` boolean `ip_allowlist` string[] · nullable `status_page_id` uuid `token_rotated_at` string (date-time) · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousRemove a status page access token](https://upfour.io/docs/api/status-pages/remove-a-status-page-access-token.md) [NextDelete a status page asset](https://upfour.io/docs/api/status-pages/delete-a-status-page-asset.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/access-token" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/access-token", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/access-token", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/access-token", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "access_token": "••••", "policy": { "access_email_allowlist": [], "access_email_codes": false, "access_token_configured": true, "ip_allowlist": [ "203.0.113.0/24" ], "status_page_id": "00000000-0000-4000-8000-000000000001", "token_rotated_at": "2026-10-27T09:00:00Z" } } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Delete a status page asset — API reference > Deletes the selected status-page asset and returns status 204 without a response body. Source: https://upfour.io/docs/api/status-pages/delete-a-status-page-asset [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Deletes the selected status-page asset and returns status 204 without a response body. DELETE https://app.upfour.io/v1/status-pages/{pageID}/assets/{kind} scope status_page.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | | `kind` required | string one of`logo` `logo_dark` `favicon` | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousCreate a status page access token](https://upfour.io/docs/api/status-pages/create-a-status-page-access-token.md) [NextUpload a status page asset](https://upfour.io/docs/api/status-pages/upload-a-status-page-asset.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/assets/logo" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/assets/logo", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/assets/logo", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/assets/logo", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Upload a status page asset — API reference > Uploads a status-page asset for the selected kind and returns the updated page. Source: https://upfour.io/docs/api/status-pages/upload-a-status-page-asset [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Uploads a status-page asset for the selected kind and returns the updated page. PUT https://app.upfour.io/v1/status-pages/{pageID}/assets/{kind} scope status_page.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | | `kind` required | string one of`logo` `logo_dark` `favicon` | path parameter | ## Body `multipart/form-data` required filefile (required) ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPage | | | StatusPage · 61 fields FieldTypeDescription `accent_color` string `announcement` string `appearance` StatusPageAppearanceResponse · nullable null until a layout was chosen; the page then renders the default design (essential layout, paper ground). StatusPageAppearanceResponse · 5 fields FieldTypeDescription `ground` string one of`paper` `white` `cool` Empty is stored as paper. `header_dark` string #RRGGBB or empty for the platform default. `header_light` string #RRGGBB or empty for the platform default. `layout` string one of`essential` `brand` `overview` `version` integer Must be 1. `automatic_reports` boolean `automatic_reports_notify` boolean `company_name` string `company_url` string `contact_url` string `created_at` string (date-time) `custom_css` string `custom_domain` string `custom_domain_certificate_status` string one of`none` `pending` `issued` `failed` `unknown` `custom_domain_status` string one of`none` `pending_dns` `queued` `provisioning` `active` `failed` `disabled` `conflict_requires_review` `custom_js` string Not supported: always empty. `default_language` string · nullable Language of the page's own communication; null inherits from the client, then the company default. `design` string No effect since Plan 82; accepted and stored only. `google_tag_id` string Not supported: always empty. `header_layout` string No effect since Plan 82; accepted and stored only. `hide_from_search_engines` boolean `history_days` integer `id` uuid `is_published` boolean `logo_url` string `minimum_incident_length` integer `organization_id` uuid `password_protected` boolean `remove_branding` boolean `show_not_monitored_as_green` boolean `subdomain` string `subscribable` boolean `theme` string `timezone` string `updated_at` string (date-time) `uptime_decimals` integer `access_restricted` boolean Whether any visitor gate (password, e-mail codes, IP allowlist or access token) is on. Only in GET /status-pages. `auto_report_open_template_id` uuid `auto_report_resolve_template_id` uuid `client_id` uuid `custom_domain_attempts` integer `custom_domain_cleanup_requested_at` string (date-time) `custom_domain_dns_target` string `custom_domain_error` string `custom_domain_last_checked_at` string (date-time) `custom_domain_next_retry_at` string (date-time) `custom_domain_pending_expires_at` string (date-time) `custom_domain_ploi_tenant` string `custom_domain_provisioned_at` string (date-time) `custom_domain_verification_token` string Value of the up4-status-verification TXT record that proves ownership of the domain. `custom_domain_verified_at` string (date-time) `favicon_url` string `logo_asset_url` string `logo_dark_asset_url` string `platform_url` string `public_url` string Where the page is reachable: the custom domain once active, else the platform address. `reply_to_email` string `subscriber_count` integer Confirmed, active subscribers. Only in GET /status-pages. | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 400 | [file_empty](https://upfour.io/docs/api/errors#code-file_empty) | The uploaded file is empty. **When:** Uploading a logo or image with no content. **Fix:** Upload a file that has content. Stable codes next to the English message when a logo is refused: `file_empty`, `file_type_unsupported` (`error.details.formats` lists the accepted ones), `image_invalid` (damaged or unreadable), `image_dimensions_too_large` (`error.details.max_pixels`), `svg_unsafe`. `upload_no_file` (the form has no `file` part) and `upload_invalid` (the upload could not be read) apply to multipart uploads. | | 413 | [file_too_large](https://upfour.io/docs/api/errors#code-file_too_large) | The uploaded file is over the size limit. **When:** Uploading a logo or image larger than details.max_bytes. **Fix:** Reduce the file size and upload again. `file_too_large`: the logo or favicon is larger than 256 KB (`error.details.max_bytes`). A proxy in front of the API may answer 413 with a plain HTML body and no code. | | 400 | [file_type_unsupported](https://upfour.io/docs/api/errors#code-file_type_unsupported) | The file's type is not accepted for this upload. **When:** Uploading a file whose type is not one of the formats the operation accepts; details.formats lists them. **Fix:** Upload a file in one of details.formats. | | 400 | [image_dimensions_too_large](https://upfour.io/docs/api/errors#code-image_dimensions_too_large) | The image has more pixels than allowed. **When:** Uploading an image with more pixels than details.max_pixels. **Fix:** Scale the image down and upload it again. | | 400 | [image_invalid](https://upfour.io/docs/api/errors#code-image_invalid) | The file is not a readable image. **When:** Uploading a file with an image type that cannot be decoded. **Fix:** Export the image again and upload the new file. | | 400 | [logo_fetch_failed](https://upfour.io/docs/api/errors#code-logo_fetch_failed) | The logo could not be fetched from the address given. **When:** Importing a client logo from a URL that cannot be reached or that does not return an acceptable image. **Fix:** Check the URL, or upload the logo file instead. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 400 | [svg_unsafe](https://upfour.io/docs/api/errors#code-svg_unsafe) | The SVG contains content that is not allowed. **When:** Uploading an SVG that contains a script, a foreignObject, a javascript: URL, an event-handler attribute or an external href. **Fix:** Remove those parts from the SVG, or upload a PNG or JPEG. | | 400 | [upload_invalid](https://upfour.io/docs/api/errors#code-upload_invalid) | The upload could not be read as a multipart form. **When:** A body that is not a valid multipart form, or whose file cannot be parsed. **Fix:** Send a well-formed multipart/form-data request. | | 400 | [upload_no_file](https://upfour.io/docs/api/errors#code-upload_no_file) | The multipart request has no file part. **When:** An upload request without a file field. **Fix:** Send the file as multipart/form-data in the field the operation names. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousDelete a status page asset](https://upfour.io/docs/api/status-pages/delete-a-status-page-asset.md) [NextRemove a custom domain](https://upfour.io/docs/api/status-pages/remove-a-custom-domain.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/assets/logo" \ -H "Authorization: Bearer $UP4_API_KEY" \ -F "file=@./file.png" ``` ### JavaScript ```javascript import { readFile } from "node:fs/promises"; const fileBytes = await readFile("./file.png"); const form = new FormData(); form.append("file", new Blob([fileBytes]), "file.png"); const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/assets/logo", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, body: form, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/assets/logo", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, files={"file": open("./file.png", "rb")}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "bytes" "fmt" "io" "mime/multipart" "net/http" "os" ) func main() { var buf bytes.Buffer w := multipart.NewWriter(&buf) part, _ := w.CreateFormFile("file", "file.png") file, _ := os.Open("./file.png") io.Copy(part, file) w.Close() req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/assets/logo", &buf) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", w.FormDataContentType()) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], CURLOPT_POSTFIELDS => ["file" => new CURLFile("./file.png")], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "accent_color": "", "announcement": "Planned maintenance on Sunday from 02:00 to 03:00 UTC.", "appearance": null, "automatic_reports": false, "automatic_reports_notify": false, "company_name": "Acme GmbH", "company_url": "", "contact_url": "", "created_at": "2026-10-27T09:00:00Z", "custom_css": "", "custom_domain": "", "custom_domain_certificate_status": "none", "custom_domain_dns_target": "pages.example.test", "custom_domain_pending_expires_at": "2026-11-10T09:00:00Z", "custom_domain_status": "none", "custom_js": "", "default_language": null, "design": "v2", "google_tag_id": "", "header_layout": "vertical", "hide_from_search_engines": false, "history_days": 90, "id": "00000000-0000-4000-8000-000000000001", "is_published": false, "logo_asset_url": "https://example.com/v1/public/status/assets/00000000-0000-4000-8000-000000000002?v=c414cd0e204d", "logo_url": "", "minimum_incident_length": 0, "organization_id": "00000000-0000-4000-8000-000000000003", "password_protected": false, "platform_url": "https://example.com", "public_url": "https://example.com", "remove_branding": false, "show_not_monitored_as_green": false, "subdomain": "acme-status", "subscribable": false, "theme": "light", "timezone": "UTC", "updated_at": "2026-10-27T09:00:00Z", "uptime_decimals": 0 } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` ### 413 ```json { "error": { "code": "file_too_large", "message": "The uploaded file is over the size limit.", "request_id": "req_…" } } ``` --- # Remove a custom domain — API reference > Removes the custom domain and returns the page's current domain challenge and health details. Source: https://upfour.io/docs/api/status-pages/remove-a-custom-domain [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Removes the custom domain and returns the page's current domain challenge and health details. DELETE https://app.upfour.io/v1/status-pages/{pageID}/custom-domain scope status_page.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageDomainDetails | | | StatusPageDomainDetails · 101 fields FieldTypeDescription `page` StatusPage · nullable StatusPage · 61 fields FieldTypeDescription `accent_color` string `announcement` string `appearance` StatusPageAppearanceResponse · nullable null until a layout was chosen; the page then renders the default design (essential layout, paper ground). StatusPageAppearanceResponse · 5 fields FieldTypeDescription `ground` string one of`paper` `white` `cool` Empty is stored as paper. `header_dark` string #RRGGBB or empty for the platform default. `header_light` string #RRGGBB or empty for the platform default. `layout` string one of`essential` `brand` `overview` `version` integer Must be 1. `automatic_reports` boolean `automatic_reports_notify` boolean `company_name` string `company_url` string `contact_url` string `created_at` string (date-time) `custom_css` string `custom_domain` string `custom_domain_certificate_status` string one of`none` `pending` `issued` `failed` `unknown` `custom_domain_status` string one of`none` `pending_dns` `queued` `provisioning` `active` `failed` `disabled` `conflict_requires_review` `custom_js` string Not supported: always empty. `default_language` string · nullable Language of the page's own communication; null inherits from the client, then the company default. `design` string No effect since Plan 82; accepted and stored only. `google_tag_id` string Not supported: always empty. `header_layout` string No effect since Plan 82; accepted and stored only. `hide_from_search_engines` boolean `history_days` integer `id` uuid `is_published` boolean `logo_url` string `minimum_incident_length` integer `organization_id` uuid `password_protected` boolean `remove_branding` boolean `show_not_monitored_as_green` boolean `subdomain` string `subscribable` boolean `theme` string `timezone` string `updated_at` string (date-time) `uptime_decimals` integer `access_restricted` boolean Whether any visitor gate (password, e-mail codes, IP allowlist or access token) is on. Only in GET /status-pages. `auto_report_open_template_id` uuid `auto_report_resolve_template_id` uuid `client_id` uuid `custom_domain_attempts` integer `custom_domain_cleanup_requested_at` string (date-time) `custom_domain_dns_target` string `custom_domain_error` string `custom_domain_last_checked_at` string (date-time) `custom_domain_next_retry_at` string (date-time) `custom_domain_pending_expires_at` string (date-time) `custom_domain_ploi_tenant` string `custom_domain_provisioned_at` string (date-time) `custom_domain_verification_token` string Value of the up4-status-verification TXT record that proves ownership of the domain. `custom_domain_verified_at` string (date-time) `favicon_url` string `logo_asset_url` string `logo_dark_asset_url` string `platform_url` string `public_url` string Where the page is reachable: the custom domain once active, else the platform address. `reply_to_email` string `subscriber_count` integer Confirmed, active subscribers. Only in GET /status-pages. `platform_url` string The page's address under the platform domain. `public_url` string `challenge` StatusPageDomainChallenge StatusPageDomainChallenge · 14 fields FieldTypeDescription `created_at` string (date-time) `domain` string `id` uuid `organization_id` uuid `pending_expires_at` string (date-time) `status` string one of`pending_dns` `conflict_requires_review` `status_page_id` uuid `updated_at` string (date-time) `error` string `incumbent_organization_id` uuid `incumbent_status_page_id` uuid `last_checked_at` string (date-time) `verification_token` string `verified_at` string (date-time) `cname_name` string `cname_target` string `entitlement` StatusPageDomainEntitlement StatusPageDomainEntitlement · 10 fields FieldTypeDescription `can_purchase` boolean `limit` integer `metered` boolean `over_limit` boolean `pack_slots` integer `paid_slots` integer `plan_included` integer `unit_price_cents` integer `used` integer `renew_at` string (date-time) `health` StatusPageDomainHealth StatusPageDomainHealth · 5 fields FieldTypeDescription `status` string one of`unknown` `healthy` `unhealthy` `checked_at` string (date-time) `error` string `error_code` string one of`dns_invalid` `routing_unchecked` `routing_changed` `certificate_unchecked` `certificate_not_ready` `https_unreachable` `unknown` `next_check_at` string (date-time) `problem_code` string one of`dns_not_found` `txt_not_found` `hosting_setup_failed` `hosting_routing_failed` `https_pending` `https_unreachable` `public_page_mismatch` `public_page_unreachable` `removal_delayed` `claim_released` `unknown` `txt_name` string `txt_value` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpload a status page asset](https://upfour.io/docs/api/status-pages/upload-a-status-page-asset.md) [NextSet a custom domain](https://upfour.io/docs/api/status-pages/set-a-custom-domain.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/custom-domain" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/custom-domain", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/custom-domain", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/custom-domain", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "entitlement": { "can_purchase": false, "limit": 10, "metered": false, "over_limit": false, "pack_slots": 0, "paid_slots": 0, "plan_included": 10, "unit_price_cents": 200, "used": 0 }, "page": { "accent_color": "", "announcement": "Planned maintenance on Sunday from 02:00 to 03:00 UTC.", "appearance": null, "automatic_reports": false, "automatic_reports_notify": false, "company_name": "Acme GmbH", "company_url": "", "contact_url": "", "created_at": "2026-10-27T09:00:00Z", "custom_css": "", "custom_domain": "", "custom_domain_certificate_status": "none", "custom_domain_dns_target": "pages.example.test", "custom_domain_pending_expires_at": "2026-11-10T09:00:00Z", "custom_domain_status": "none", "custom_js": "", "default_language": null, "design": "v2", "google_tag_id": "", "header_layout": "vertical", "hide_from_search_engines": false, "history_days": 90, "id": "00000000-0000-4000-8000-000000000001", "is_published": false, "logo_url": "", "minimum_incident_length": 0, "organization_id": "00000000-0000-4000-8000-000000000002", "password_protected": false, "platform_url": "https://example.com", "public_url": "https://example.com", "remove_branding": false, "show_not_monitored_as_green": false, "subdomain": "acme-status", "subscribable": false, "theme": "light", "timezone": "UTC", "updated_at": "2026-10-27T09:00:00Z", "uptime_decimals": 0 }, "platform_url": "https://example.com", "public_url": "https://example.com" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Set a custom domain — API reference > Assigns a custom domain, optionally moving it from another page, and returns verification and health details. Source: https://upfour.io/docs/api/status-pages/set-a-custom-domain [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Assigns a custom domain, optionally moving it from another page, and returns verification and health details. PUT https://app.upfour.io/v1/status-pages/{pageID}/custom-domain scope status_page.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Body `application/json` required 2 fields domainstring (required) All other fields1 fields move_from_page_iduuid Take the domain over from another status page of the same company. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageDomainDetails | | | StatusPageDomainDetails · 101 fields FieldTypeDescription `page` StatusPage · nullable StatusPage · 61 fields FieldTypeDescription `accent_color` string `announcement` string `appearance` StatusPageAppearanceResponse · nullable null until a layout was chosen; the page then renders the default design (essential layout, paper ground). StatusPageAppearanceResponse · 5 fields FieldTypeDescription `ground` string one of`paper` `white` `cool` Empty is stored as paper. `header_dark` string #RRGGBB or empty for the platform default. `header_light` string #RRGGBB or empty for the platform default. `layout` string one of`essential` `brand` `overview` `version` integer Must be 1. `automatic_reports` boolean `automatic_reports_notify` boolean `company_name` string `company_url` string `contact_url` string `created_at` string (date-time) `custom_css` string `custom_domain` string `custom_domain_certificate_status` string one of`none` `pending` `issued` `failed` `unknown` `custom_domain_status` string one of`none` `pending_dns` `queued` `provisioning` `active` `failed` `disabled` `conflict_requires_review` `custom_js` string Not supported: always empty. `default_language` string · nullable Language of the page's own communication; null inherits from the client, then the company default. `design` string No effect since Plan 82; accepted and stored only. `google_tag_id` string Not supported: always empty. `header_layout` string No effect since Plan 82; accepted and stored only. `hide_from_search_engines` boolean `history_days` integer `id` uuid `is_published` boolean `logo_url` string `minimum_incident_length` integer `organization_id` uuid `password_protected` boolean `remove_branding` boolean `show_not_monitored_as_green` boolean `subdomain` string `subscribable` boolean `theme` string `timezone` string `updated_at` string (date-time) `uptime_decimals` integer `access_restricted` boolean Whether any visitor gate (password, e-mail codes, IP allowlist or access token) is on. Only in GET /status-pages. `auto_report_open_template_id` uuid `auto_report_resolve_template_id` uuid `client_id` uuid `custom_domain_attempts` integer `custom_domain_cleanup_requested_at` string (date-time) `custom_domain_dns_target` string `custom_domain_error` string `custom_domain_last_checked_at` string (date-time) `custom_domain_next_retry_at` string (date-time) `custom_domain_pending_expires_at` string (date-time) `custom_domain_ploi_tenant` string `custom_domain_provisioned_at` string (date-time) `custom_domain_verification_token` string Value of the up4-status-verification TXT record that proves ownership of the domain. `custom_domain_verified_at` string (date-time) `favicon_url` string `logo_asset_url` string `logo_dark_asset_url` string `platform_url` string `public_url` string Where the page is reachable: the custom domain once active, else the platform address. `reply_to_email` string `subscriber_count` integer Confirmed, active subscribers. Only in GET /status-pages. `platform_url` string The page's address under the platform domain. `public_url` string `challenge` StatusPageDomainChallenge StatusPageDomainChallenge · 14 fields FieldTypeDescription `created_at` string (date-time) `domain` string `id` uuid `organization_id` uuid `pending_expires_at` string (date-time) `status` string one of`pending_dns` `conflict_requires_review` `status_page_id` uuid `updated_at` string (date-time) `error` string `incumbent_organization_id` uuid `incumbent_status_page_id` uuid `last_checked_at` string (date-time) `verification_token` string `verified_at` string (date-time) `cname_name` string `cname_target` string `entitlement` StatusPageDomainEntitlement StatusPageDomainEntitlement · 10 fields FieldTypeDescription `can_purchase` boolean `limit` integer `metered` boolean `over_limit` boolean `pack_slots` integer `paid_slots` integer `plan_included` integer `unit_price_cents` integer `used` integer `renew_at` string (date-time) `health` StatusPageDomainHealth StatusPageDomainHealth · 5 fields FieldTypeDescription `status` string one of`unknown` `healthy` `unhealthy` `checked_at` string (date-time) `error` string `error_code` string one of`dns_invalid` `routing_unchecked` `routing_changed` `certificate_unchecked` `certificate_not_ready` `https_unreachable` `unknown` `next_check_at` string (date-time) `problem_code` string one of`dns_not_found` `txt_not_found` `hosting_setup_failed` `hosting_routing_failed` `https_pending` `https_unreachable` `public_page_mismatch` `public_page_unreachable` `removal_delayed` `claim_released` `unknown` `txt_name` string `txt_value` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 409 | [domain_claimed](https://upfour.io/docs/api/errors#code-domain_claimed) | The domain is in use by another Company. **When:** Setting a custom domain on a status page when another Company already holds it; details.domain names it. **Fix:** Prove control of the domain with the TXT record shown for the page, or use a different domain. | | 409 | [domain_move_stale](https://upfour.io/docs/api/errors#code-domain_move_stale) | The page named as the domain's current holder is not the holder any more. **When:** Moving a domain with move_from_page_id set to a page that does not hold it; details.page names the real holder. **Fix:** Read the domain's current holder and send the move again with that page. | | 409 | [domain_on_other_page](https://upfour.io/docs/api/errors#code-domain_on_other_page) | The domain is used by another status page in the same Company. **When:** Setting a custom domain that another page of the Company already holds; details.page names that page. **Fix:** Move the domain on purpose by sending the holder's page id as the page to move from, or choose another domain. | | 409 | [domain_removal_pending](https://upfour.io/docs/api/errors#code-domain_removal_pending) | The domain is still being disconnected from its previous status page. **When:** Setting a domain right after it was removed from another page, before the disconnect finished. **Fix:** Try again in a few minutes. | | 409 | [domain_used_in_company](https://upfour.io/docs/api/errors#code-domain_used_in_company) | The domain is used by a status page in the Company that the caller cannot see. **When:** Setting a custom domain held by a page outside the caller's client scope; the page is not named. **Fix:** Ask someone with access to that page to move or remove the domain, or choose another domain. | | 400 | [invalid_move_source](https://upfour.io/docs/api/errors#code-invalid_move_source) | The page to move the domain from is not valid. **When:** Setting a domain with a move-from page id that is not a status page the caller can use. **Fix:** Send the id of the status page that holds the domain. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 402 | [payment_required](https://upfour.io/docs/api/errors#code-payment_required) | The account does not have the credit or plan required for this action. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousRemove a custom domain](https://upfour.io/docs/api/status-pages/remove-a-custom-domain.md) [NextRecheck a custom domain](https://upfour.io/docs/api/status-pages/recheck-a-custom-domain.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/custom-domain" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "domain": "status.example.com" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/custom-domain", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ domain: "status.example.com", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/custom-domain", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "domain": "status.example.com", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "domain": "status.example.com" }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/custom-domain", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "cname_name": "status.example.com", "cname_target": "pages.example.test", "entitlement": { "can_purchase": false, "limit": 10, "metered": false, "over_limit": false, "pack_slots": 0, "paid_slots": 0, "plan_included": 10, "unit_price_cents": 200, "used": 1 }, "page": { "accent_color": "", "announcement": "Planned maintenance on Sunday from 02:00 to 03:00 UTC.", "appearance": null, "automatic_reports": false, "automatic_reports_notify": false, "company_name": "Acme GmbH", "company_url": "", "contact_url": "", "created_at": "2026-10-27T09:00:00Z", "custom_css": "", "custom_domain": "status.example.com", "custom_domain_certificate_status": "none", "custom_domain_dns_target": "pages.example.test", "custom_domain_next_retry_at": "2026-10-27T09:00:00Z", "custom_domain_pending_expires_at": "2026-11-10T09:00:00Z", "custom_domain_status": "pending_dns", "custom_domain_verification_token": "••••", "custom_js": "", "default_language": null, "design": "v2", "google_tag_id": "", "header_layout": "vertical", "hide_from_search_engines": false, "history_days": 90, "id": "00000000-0000-4000-8000-000000000001", "is_published": false, "logo_url": "", "minimum_incident_length": 0, "organization_id": "00000000-0000-4000-8000-000000000002", "password_protected": false, "platform_url": "https://example.com", "public_url": "https://example.com", "remove_branding": false, "show_not_monitored_as_green": false, "subdomain": "acme-status", "subscribable": false, "theme": "light", "timezone": "UTC", "updated_at": "2026-10-27T09:00:00Z", "uptime_decimals": 0 }, "platform_url": "https://example.com", "public_url": "https://example.com", "txt_name": "_up4-status.status.example.com", "txt_value": "up4-status-verification=721378bc96ef2ddece4a66b447ba60d6a1266860d9cb23f4" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "payment_required", "message": "The account does not have the credit or plan required for this action.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` ### 409 ```json { "error": { "code": "domain_claimed", "message": "The domain is in use by another Company.", "request_id": "req_…" } } ``` --- # Recheck a custom domain — API reference > Rechecks custom-domain status and returns the current challenge, health and public addresses. Source: https://upfour.io/docs/api/status-pages/recheck-a-custom-domain [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Rechecks custom-domain status and returns the current challenge, health and public addresses. POST https://app.upfour.io/v1/status-pages/{pageID}/custom-domain/recheck scope status_page.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageDomainDetails | | | StatusPageDomainDetails · 101 fields FieldTypeDescription `page` StatusPage · nullable StatusPage · 61 fields FieldTypeDescription `accent_color` string `announcement` string `appearance` StatusPageAppearanceResponse · nullable null until a layout was chosen; the page then renders the default design (essential layout, paper ground). StatusPageAppearanceResponse · 5 fields FieldTypeDescription `ground` string one of`paper` `white` `cool` Empty is stored as paper. `header_dark` string #RRGGBB or empty for the platform default. `header_light` string #RRGGBB or empty for the platform default. `layout` string one of`essential` `brand` `overview` `version` integer Must be 1. `automatic_reports` boolean `automatic_reports_notify` boolean `company_name` string `company_url` string `contact_url` string `created_at` string (date-time) `custom_css` string `custom_domain` string `custom_domain_certificate_status` string one of`none` `pending` `issued` `failed` `unknown` `custom_domain_status` string one of`none` `pending_dns` `queued` `provisioning` `active` `failed` `disabled` `conflict_requires_review` `custom_js` string Not supported: always empty. `default_language` string · nullable Language of the page's own communication; null inherits from the client, then the company default. `design` string No effect since Plan 82; accepted and stored only. `google_tag_id` string Not supported: always empty. `header_layout` string No effect since Plan 82; accepted and stored only. `hide_from_search_engines` boolean `history_days` integer `id` uuid `is_published` boolean `logo_url` string `minimum_incident_length` integer `organization_id` uuid `password_protected` boolean `remove_branding` boolean `show_not_monitored_as_green` boolean `subdomain` string `subscribable` boolean `theme` string `timezone` string `updated_at` string (date-time) `uptime_decimals` integer `access_restricted` boolean Whether any visitor gate (password, e-mail codes, IP allowlist or access token) is on. Only in GET /status-pages. `auto_report_open_template_id` uuid `auto_report_resolve_template_id` uuid `client_id` uuid `custom_domain_attempts` integer `custom_domain_cleanup_requested_at` string (date-time) `custom_domain_dns_target` string `custom_domain_error` string `custom_domain_last_checked_at` string (date-time) `custom_domain_next_retry_at` string (date-time) `custom_domain_pending_expires_at` string (date-time) `custom_domain_ploi_tenant` string `custom_domain_provisioned_at` string (date-time) `custom_domain_verification_token` string Value of the up4-status-verification TXT record that proves ownership of the domain. `custom_domain_verified_at` string (date-time) `favicon_url` string `logo_asset_url` string `logo_dark_asset_url` string `platform_url` string `public_url` string Where the page is reachable: the custom domain once active, else the platform address. `reply_to_email` string `subscriber_count` integer Confirmed, active subscribers. Only in GET /status-pages. `platform_url` string The page's address under the platform domain. `public_url` string `challenge` StatusPageDomainChallenge StatusPageDomainChallenge · 14 fields FieldTypeDescription `created_at` string (date-time) `domain` string `id` uuid `organization_id` uuid `pending_expires_at` string (date-time) `status` string one of`pending_dns` `conflict_requires_review` `status_page_id` uuid `updated_at` string (date-time) `error` string `incumbent_organization_id` uuid `incumbent_status_page_id` uuid `last_checked_at` string (date-time) `verification_token` string `verified_at` string (date-time) `cname_name` string `cname_target` string `entitlement` StatusPageDomainEntitlement StatusPageDomainEntitlement · 10 fields FieldTypeDescription `can_purchase` boolean `limit` integer `metered` boolean `over_limit` boolean `pack_slots` integer `paid_slots` integer `plan_included` integer `unit_price_cents` integer `used` integer `renew_at` string (date-time) `health` StatusPageDomainHealth StatusPageDomainHealth · 5 fields FieldTypeDescription `status` string one of`unknown` `healthy` `unhealthy` `checked_at` string (date-time) `error` string `error_code` string one of`dns_invalid` `routing_unchecked` `routing_changed` `certificate_unchecked` `certificate_not_ready` `https_unreachable` `unknown` `next_check_at` string (date-time) `problem_code` string one of`dns_not_found` `txt_not_found` `hosting_setup_failed` `hosting_routing_failed` `https_pending` `https_unreachable` `public_page_mismatch` `public_page_unreachable` `removal_delayed` `claim_released` `unknown` `txt_name` string `txt_value` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousSet a custom domain](https://upfour.io/docs/api/status-pages/set-a-custom-domain.md) [NextGet custom domain details](https://upfour.io/docs/api/status-pages/get-custom-domain-details.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/custom-domain/recheck" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/custom-domain/recheck", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/custom-domain/recheck", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/custom-domain/recheck", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "cname_name": "status.example.com", "cname_target": "pages.example.test", "entitlement": { "can_purchase": false, "limit": 10, "metered": false, "over_limit": false, "pack_slots": 0, "paid_slots": 0, "plan_included": 10, "unit_price_cents": 200, "used": 1 }, "page": { "accent_color": "", "announcement": "Planned maintenance on Sunday from 02:00 to 03:00 UTC.", "appearance": null, "automatic_reports": false, "automatic_reports_notify": false, "company_name": "Acme GmbH", "company_url": "", "contact_url": "", "created_at": "2026-10-27T09:00:00Z", "custom_css": "", "custom_domain": "status.example.com", "custom_domain_certificate_status": "none", "custom_domain_dns_target": "pages.example.test", "custom_domain_next_retry_at": "2026-10-27T09:00:00Z", "custom_domain_pending_expires_at": "2026-11-10T09:00:00Z", "custom_domain_status": "pending_dns", "custom_domain_verification_token": "••••", "custom_js": "", "default_language": null, "design": "v2", "google_tag_id": "", "header_layout": "vertical", "hide_from_search_engines": false, "history_days": 90, "id": "00000000-0000-4000-8000-000000000001", "is_published": false, "logo_url": "", "minimum_incident_length": 0, "organization_id": "00000000-0000-4000-8000-000000000002", "password_protected": false, "platform_url": "https://example.com", "public_url": "https://example.com", "remove_branding": false, "show_not_monitored_as_green": false, "subdomain": "acme-status", "subscribable": false, "theme": "light", "timezone": "UTC", "updated_at": "2026-10-27T09:00:00Z", "uptime_decimals": 0 }, "platform_url": "https://example.com", "public_url": "https://example.com", "txt_name": "_up4-status.status.example.com", "txt_value": "up4-status-verification=721378bc96ef2ddece4a66b447ba60d6a1266860d9cb23f4" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Get custom domain details — API reference > Returns the custom-domain challenge, health, entitlement, problem and public-address details. Source: https://upfour.io/docs/api/status-pages/get-custom-domain-details [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Returns the custom-domain challenge, health, entitlement, problem and public-address details. GET https://app.upfour.io/v1/status-pages/{pageID}/domain scope status_page.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageDomainDetails | | | StatusPageDomainDetails · 101 fields FieldTypeDescription `page` StatusPage · nullable StatusPage · 61 fields FieldTypeDescription `accent_color` string `announcement` string `appearance` StatusPageAppearanceResponse · nullable null until a layout was chosen; the page then renders the default design (essential layout, paper ground). StatusPageAppearanceResponse · 5 fields FieldTypeDescription `ground` string one of`paper` `white` `cool` Empty is stored as paper. `header_dark` string #RRGGBB or empty for the platform default. `header_light` string #RRGGBB or empty for the platform default. `layout` string one of`essential` `brand` `overview` `version` integer Must be 1. `automatic_reports` boolean `automatic_reports_notify` boolean `company_name` string `company_url` string `contact_url` string `created_at` string (date-time) `custom_css` string `custom_domain` string `custom_domain_certificate_status` string one of`none` `pending` `issued` `failed` `unknown` `custom_domain_status` string one of`none` `pending_dns` `queued` `provisioning` `active` `failed` `disabled` `conflict_requires_review` `custom_js` string Not supported: always empty. `default_language` string · nullable Language of the page's own communication; null inherits from the client, then the company default. `design` string No effect since Plan 82; accepted and stored only. `google_tag_id` string Not supported: always empty. `header_layout` string No effect since Plan 82; accepted and stored only. `hide_from_search_engines` boolean `history_days` integer `id` uuid `is_published` boolean `logo_url` string `minimum_incident_length` integer `organization_id` uuid `password_protected` boolean `remove_branding` boolean `show_not_monitored_as_green` boolean `subdomain` string `subscribable` boolean `theme` string `timezone` string `updated_at` string (date-time) `uptime_decimals` integer `access_restricted` boolean Whether any visitor gate (password, e-mail codes, IP allowlist or access token) is on. Only in GET /status-pages. `auto_report_open_template_id` uuid `auto_report_resolve_template_id` uuid `client_id` uuid `custom_domain_attempts` integer `custom_domain_cleanup_requested_at` string (date-time) `custom_domain_dns_target` string `custom_domain_error` string `custom_domain_last_checked_at` string (date-time) `custom_domain_next_retry_at` string (date-time) `custom_domain_pending_expires_at` string (date-time) `custom_domain_ploi_tenant` string `custom_domain_provisioned_at` string (date-time) `custom_domain_verification_token` string Value of the up4-status-verification TXT record that proves ownership of the domain. `custom_domain_verified_at` string (date-time) `favicon_url` string `logo_asset_url` string `logo_dark_asset_url` string `platform_url` string `public_url` string Where the page is reachable: the custom domain once active, else the platform address. `reply_to_email` string `subscriber_count` integer Confirmed, active subscribers. Only in GET /status-pages. `platform_url` string The page's address under the platform domain. `public_url` string `challenge` StatusPageDomainChallenge StatusPageDomainChallenge · 14 fields FieldTypeDescription `created_at` string (date-time) `domain` string `id` uuid `organization_id` uuid `pending_expires_at` string (date-time) `status` string one of`pending_dns` `conflict_requires_review` `status_page_id` uuid `updated_at` string (date-time) `error` string `incumbent_organization_id` uuid `incumbent_status_page_id` uuid `last_checked_at` string (date-time) `verification_token` string `verified_at` string (date-time) `cname_name` string `cname_target` string `entitlement` StatusPageDomainEntitlement StatusPageDomainEntitlement · 10 fields FieldTypeDescription `can_purchase` boolean `limit` integer `metered` boolean `over_limit` boolean `pack_slots` integer `paid_slots` integer `plan_included` integer `unit_price_cents` integer `used` integer `renew_at` string (date-time) `health` StatusPageDomainHealth StatusPageDomainHealth · 5 fields FieldTypeDescription `status` string one of`unknown` `healthy` `unhealthy` `checked_at` string (date-time) `error` string `error_code` string one of`dns_invalid` `routing_unchecked` `routing_changed` `certificate_unchecked` `certificate_not_ready` `https_unreachable` `unknown` `next_check_at` string (date-time) `problem_code` string one of`dns_not_found` `txt_not_found` `hosting_setup_failed` `hosting_routing_failed` `https_pending` `https_unreachable` `public_page_mismatch` `public_page_unreachable` `removal_delayed` `claim_released` `unknown` `txt_name` string `txt_value` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousRecheck a custom domain](https://upfour.io/docs/api/status-pages/recheck-a-custom-domain.md) [NextList custom domain events](https://upfour.io/docs/api/status-pages/list-custom-domain-events.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/domain" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/domain", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/domain", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/domain", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "cname_name": "status.example.com", "cname_target": "pages.example.test", "entitlement": { "can_purchase": false, "limit": 10, "metered": false, "over_limit": false, "pack_slots": 0, "paid_slots": 0, "plan_included": 10, "unit_price_cents": 200, "used": 1 }, "page": { "accent_color": "", "announcement": "Planned maintenance on Sunday from 02:00 to 03:00 UTC.", "appearance": null, "automatic_reports": false, "automatic_reports_notify": false, "company_name": "Acme GmbH", "company_url": "", "contact_url": "", "created_at": "2026-10-27T09:00:00Z", "custom_css": "", "custom_domain": "status.example.com", "custom_domain_certificate_status": "none", "custom_domain_dns_target": "pages.example.test", "custom_domain_next_retry_at": "2026-10-27T09:00:00Z", "custom_domain_pending_expires_at": "2026-11-10T09:00:00Z", "custom_domain_status": "pending_dns", "custom_domain_verification_token": "••••", "custom_js": "", "default_language": null, "design": "v2", "google_tag_id": "", "header_layout": "vertical", "hide_from_search_engines": false, "history_days": 90, "id": "00000000-0000-4000-8000-000000000001", "is_published": false, "logo_url": "", "minimum_incident_length": 0, "organization_id": "00000000-0000-4000-8000-000000000002", "password_protected": false, "platform_url": "https://example.com", "public_url": "https://example.com", "remove_branding": false, "show_not_monitored_as_green": false, "subdomain": "acme-status", "subscribable": false, "theme": "light", "timezone": "UTC", "updated_at": "2026-10-27T09:00:00Z", "uptime_decimals": 0 }, "platform_url": "https://example.com", "public_url": "https://example.com", "txt_name": "_up4-status.status.example.com", "txt_value": "up4-status-verification=721378bc96ef2ddece4a66b447ba60d6a1266860d9cb23f4" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # List custom domain events — API reference > Returns events recorded for the selected status-page domain. Source: https://upfour.io/docs/api/status-pages/list-custom-domain-events [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Returns events recorded for the selected status-page domain. GET https://app.upfour.io/v1/status-pages/{pageID}/domain/events scope status_page.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageDomainEvent[] · nullable | | | Each item · 8 fields FieldTypeDescription `created_at` string (date-time) `domain` string `event_type` string `id` uuid `message` string `metadata` object · nullable free-form keys `organization_id` uuid `status_page_id` uuid | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet custom domain details](https://upfour.io/docs/api/status-pages/get-custom-domain-details.md) [NextDuplicate a status page](https://upfour.io/docs/api/status-pages/duplicate-a-status-page.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/domain/events" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/domain/events", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/domain/events", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/domain/events", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "created_at": "2026-10-27T09:00:00Z", "domain": "status.example.com", "event_type": "domain_requested", "id": "00000000-0000-4000-8000-000000000002", "message": "Waiting for DNS verification", "metadata": {}, "organization_id": "00000000-0000-4000-8000-000000000003", "status_page_id": "00000000-0000-4000-8000-000000000001" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Duplicate a status page — API reference > Duplicates a status page with optional Client assignment, company name and subdomain, and reports resources skipped during copying. Source: https://upfour.io/docs/api/status-pages/duplicate-a-status-page [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Duplicates a status page with optional Client assignment, company name and subdomain, and reports resources skipped during copying. POST https://app.upfour.io/v1/status-pages/{pageID}/duplicate scope status_page.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Body `application/json` optional 3 fields All other fields3 fields client_iduuid · nullable Omitted keeps the source page's client; null moves the copy to company level. company_namestring Defaults to the source name followed by (copy). subdomainstring Defaults to a generated slug. ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | DuplicateStatusPageResult | | | DuplicateStatusPageResult · 63 fields FieldTypeDescription `page` StatusPage · nullable StatusPage · 61 fields FieldTypeDescription `accent_color` string `announcement` string `appearance` StatusPageAppearanceResponse · nullable null until a layout was chosen; the page then renders the default design (essential layout, paper ground). StatusPageAppearanceResponse · 5 fields FieldTypeDescription `ground` string one of`paper` `white` `cool` Empty is stored as paper. `header_dark` string #RRGGBB or empty for the platform default. `header_light` string #RRGGBB or empty for the platform default. `layout` string one of`essential` `brand` `overview` `version` integer Must be 1. `automatic_reports` boolean `automatic_reports_notify` boolean `company_name` string `company_url` string `contact_url` string `created_at` string (date-time) `custom_css` string `custom_domain` string `custom_domain_certificate_status` string one of`none` `pending` `issued` `failed` `unknown` `custom_domain_status` string one of`none` `pending_dns` `queued` `provisioning` `active` `failed` `disabled` `conflict_requires_review` `custom_js` string Not supported: always empty. `default_language` string · nullable Language of the page's own communication; null inherits from the client, then the company default. `design` string No effect since Plan 82; accepted and stored only. `google_tag_id` string Not supported: always empty. `header_layout` string No effect since Plan 82; accepted and stored only. `hide_from_search_engines` boolean `history_days` integer `id` uuid `is_published` boolean `logo_url` string `minimum_incident_length` integer `organization_id` uuid `password_protected` boolean `remove_branding` boolean `show_not_monitored_as_green` boolean `subdomain` string `subscribable` boolean `theme` string `timezone` string `updated_at` string (date-time) `uptime_decimals` integer `access_restricted` boolean Whether any visitor gate (password, e-mail codes, IP allowlist or access token) is on. Only in GET /status-pages. `auto_report_open_template_id` uuid `auto_report_resolve_template_id` uuid `client_id` uuid `custom_domain_attempts` integer `custom_domain_cleanup_requested_at` string (date-time) `custom_domain_dns_target` string `custom_domain_error` string `custom_domain_last_checked_at` string (date-time) `custom_domain_next_retry_at` string (date-time) `custom_domain_pending_expires_at` string (date-time) `custom_domain_ploi_tenant` string `custom_domain_provisioned_at` string (date-time) `custom_domain_verification_token` string Value of the up4-status-verification TXT record that proves ownership of the domain. `custom_domain_verified_at` string (date-time) `favicon_url` string `logo_asset_url` string `logo_dark_asset_url` string `platform_url` string `public_url` string Where the page is reachable: the custom domain once active, else the platform address. `reply_to_email` string `subscriber_count` integer Confirmed, active subscribers. Only in GET /status-pages. `skipped_resources` integer Stored components not carried over because their monitor or scheduled job is not visible in the target client. | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 402 | [payment_required](https://upfour.io/docs/api/errors#code-payment_required) | The account does not have the credit or plan required for this action. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList custom domain events](https://upfour.io/docs/api/status-pages/list-custom-domain-events.md) [NextPreview subscriber deliveries](https://upfour.io/docs/api/status-pages/preview-subscriber-deliveries.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/duplicate" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "company_name": "Acme GmbH (staging)", "subdomain": "acme-status-staging" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/duplicate", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ company_name: "Acme GmbH (staging)", subdomain: "acme-status-staging", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/duplicate", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "company_name": "Acme GmbH (staging)", "subdomain": "acme-status-staging", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "company_name": "Acme GmbH (staging)", "subdomain": "acme-status-staging" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/duplicate", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "page": { "accent_color": "", "announcement": "Planned maintenance on Sunday from 02:00 to 03:00 UTC.", "appearance": null, "automatic_reports": false, "automatic_reports_notify": false, "company_name": "Acme GmbH (staging)", "company_url": "", "contact_url": "", "created_at": "2026-10-27T09:00:00Z", "custom_css": "", "custom_domain": "", "custom_domain_certificate_status": "none", "custom_domain_status": "none", "custom_js": "", "default_language": null, "design": "v2", "google_tag_id": "", "header_layout": "vertical", "hide_from_search_engines": false, "history_days": 90, "id": "00000000-0000-4000-8000-000000000002", "is_published": false, "logo_asset_url": "https://example.com/v1/public/status/assets/00000000-0000-4000-8000-000000000003?v=c414cd0e204d", "logo_url": "", "minimum_incident_length": 0, "organization_id": "00000000-0000-4000-8000-000000000004", "password_protected": false, "platform_url": "https://example.com", "public_url": "https://example.com", "remove_branding": false, "show_not_monitored_as_green": false, "subdomain": "acme-status-staging", "subscribable": false, "theme": "light", "timezone": "UTC", "updated_at": "2026-10-27T09:00:00Z", "uptime_decimals": 0 }, "skipped_resources": 0 } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "payment_required", "message": "The account does not have the credit or plan required for this action.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Preview subscriber deliveries — API reference > Previews e-mail and webhook deliveries for a status-page update without queueing messages or consuming page limits. Source: https://upfour.io/docs/api/status-pages/preview-subscriber-deliveries [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Previews e-mail and webhook deliveries for a status-page update without queueing messages or consuming page limits. GET https://app.upfour.io/v1/status-pages/{pageID}/mail-preview scope status_page.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | | `kind` required | string one of`start` `interim` `resolution` | query parameter · The entry about to be published: `start` (first entry of a new incident or maintenance, also publishing a draft), `interim` (an update in between) or `resolution` (resolved, completed, cancelled). | | `update_id` optional | uuid | query parameter · The thread the entry belongs to. Its type and components win over `type` and `resource_ids`. Without it the thread is new and nobody has been mailed about it yet. | | `type` optional | string one of`incident` `maintenance` `report` | query parameter · Type of a thread that does not exist yet (default `incident`). | | `resource_ids` optional | string | query parameter · Comma-separated affected components of a thread that does not exist yet; empty means all subscribers. | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | MailPreview | | | MailPreview · 28 fields FieldTypeDescription `billing_paused` boolean `email` MailPreviewEmail MailPreviewEmail · 11 fields FieldTypeDescription `folded` integer `groups` MailPreviewGroup[] · nullable Each item · 5 fields FieldTypeDescription `count` integer `earliest` string (date-time) · nullable `latest` string (date-time) · nullable `outcome` string one of`folded` `not_sent` `reason` string `immediate` integer `next_mail_at` string (date-time) · nullable `not_sent` integer `recipients` integer `generated_at` string (date-time) `kind` string one of`start` `interim` `resolution` `limits` MailPreviewLimits MailPreviewLimits · 5 fields FieldTypeDescription `brake_mails` integer `brake_window_seconds` integer `interim_window_seconds` integer `page_burst` integer `page_entries_per_hour` integer `quota_level` string one of`ok` `warn` `bundle` `hold` `stop` `update_id` uuid · nullable null when the preview is for a thread that does not exist yet. `update_type` string one of`incident` `maintenance` `report` `webhook` MailPreviewWebhook MailPreviewWebhook · 3 fields FieldTypeDescription `immediate` integer `not_sent` integer `recipients` integer | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousDuplicate a status page](https://upfour.io/docs/api/status-pages/duplicate-a-status-page.md) [NextList report updates](https://upfour.io/docs/api/status-pages/list-report-updates.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/mail-preview?kind=start" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/mail-preview?kind=start", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/mail-preview?kind=start", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/mail-preview?kind=start", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "billing_paused": false, "email": { "folded": 0, "groups": [], "immediate": 0, "next_mail_at": null, "not_sent": 0, "recipients": 0 }, "generated_at": "2026-10-27T09:00:00Z", "kind": "start", "limits": { "brake_mails": 20, "brake_window_seconds": 86400, "interim_window_seconds": 1800, "page_burst": 5, "page_entries_per_hour": 10 }, "quota_level": "ok", "update_id": null, "update_type": "incident", "webhook": { "immediate": 0, "not_sent": 0, "recipients": 0 } } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # List report updates — API reference > Returns report updates for the selected status page. Source: https://upfour.io/docs/api/status-pages/list-report-updates [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Returns report updates for the selected status page. GET https://app.upfour.io/v1/status-pages/{pageID}/report-updates scope status_page.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageUpdate[] · nullable | | | Each item · 38 fields FieldTypeDescription `affected_monitor_ids` uuid[] · nullable Monitors behind the affected components. `affected_resource_ids` uuid[] · nullable `auto_end` boolean `auto_start` boolean `created_at` string (date-time) `ends_at` string (date-time) · nullable `entries` StatusPageUpdateEntry[] · nullable Each item · 9 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `message` string `notify_subscribers` boolean `published_at` string (date-time) `source` string one of`manual` `automatic` `status` string one of`investigating` `identified` `monitoring` `resolved` `scheduled` `in_progress` `completed` `cancelled` `published` `update_id` uuid `updated_at` string (date-time) `id` uuid `message` string Mirrors the latest entry. `monitor_mode` string one of`none` `pause_checks` `notify_subscribers` boolean `published_at` string (date-time) `recurrence` string one of`none` `weekly` `monthly` `recurrence_until` string (date) · nullable Last day of a recurring series (YYYY-MM-DD); null for no end. `reminder_minutes` integer · nullable `source` string one of`manual` `automatic` `starts_at` string (date-time) · nullable `status` string one of`investigating` `identified` `monitoring` `resolved` `scheduled` `in_progress` `completed` `cancelled` `published` Mirrors the latest entry. `status_page_id` uuid `timezone` string IANA time zone of a planned maintenance window; empty for other types. `title` string `update_type` string one of`incident` `maintenance` `report` `updated_at` string (date-time) `visibility` string one of`public` `draft` `closed_unmeasured` boolean Resolved, but the incident ended because monitoring resumed after a billing pause: nobody measured the recovery. `incident_group_id` uuid `incident_id` uuid `reminder_sent_at` string (date-time) `series_id` uuid | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousPreview subscriber deliveries](https://upfour.io/docs/api/status-pages/preview-subscriber-deliveries.md) [NextList status page reports](https://upfour.io/docs/api/status-pages/list-status-page-reports.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/report-updates" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/report-updates", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/report-updates", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/report-updates", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "affected_monitor_ids": [], "affected_resource_ids": [], "auto_end": false, "auto_start": false, "created_at": "2026-10-27T09:00:00Z", "ends_at": null, "entries": [ { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000002", "message": "The checkout was unavailable for 12 minutes.", "notify_subscribers": false, "published_at": "2026-10-27T09:00:00Z", "source": "manual", "status": "published", "update_id": "00000000-0000-4000-8000-000000000003", "updated_at": "2026-10-27T09:00:00Z" } ], "id": "00000000-0000-4000-8000-000000000003", "message": "The checkout was unavailable for 12 minutes.", "monitor_mode": "none", "notify_subscribers": false, "published_at": "2026-10-27T09:00:00Z", "recurrence": "none", "recurrence_until": null, "reminder_minutes": null, "source": "manual", "starts_at": null, "status": "published", "status_page_id": "00000000-0000-4000-8000-000000000001", "timezone": "", "title": "Checkout outage", "update_type": "report", "updated_at": "2026-10-27T09:00:00Z", "visibility": "public" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # List status page reports — API reference > Returns report records for the selected status page. Source: https://upfour.io/docs/api/status-pages/list-status-page-reports [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Returns report records for the selected status page. GET https://app.upfour.io/v1/status-pages/{pageID}/reports scope status_page.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageUpdate[] · nullable | | | Each item · 38 fields FieldTypeDescription `affected_monitor_ids` uuid[] · nullable Monitors behind the affected components. `affected_resource_ids` uuid[] · nullable `auto_end` boolean `auto_start` boolean `created_at` string (date-time) `ends_at` string (date-time) · nullable `entries` StatusPageUpdateEntry[] · nullable Each item · 9 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `message` string `notify_subscribers` boolean `published_at` string (date-time) `source` string one of`manual` `automatic` `status` string one of`investigating` `identified` `monitoring` `resolved` `scheduled` `in_progress` `completed` `cancelled` `published` `update_id` uuid `updated_at` string (date-time) `id` uuid `message` string Mirrors the latest entry. `monitor_mode` string one of`none` `pause_checks` `notify_subscribers` boolean `published_at` string (date-time) `recurrence` string one of`none` `weekly` `monthly` `recurrence_until` string (date) · nullable Last day of a recurring series (YYYY-MM-DD); null for no end. `reminder_minutes` integer · nullable `source` string one of`manual` `automatic` `starts_at` string (date-time) · nullable `status` string one of`investigating` `identified` `monitoring` `resolved` `scheduled` `in_progress` `completed` `cancelled` `published` Mirrors the latest entry. `status_page_id` uuid `timezone` string IANA time zone of a planned maintenance window; empty for other types. `title` string `update_type` string one of`incident` `maintenance` `report` `updated_at` string (date-time) `visibility` string one of`public` `draft` `closed_unmeasured` boolean Resolved, but the incident ended because monitoring resumed after a billing pause: nobody measured the recovery. `incident_group_id` uuid `incident_id` uuid `reminder_sent_at` string (date-time) `series_id` uuid | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList report updates](https://upfour.io/docs/api/status-pages/list-report-updates.md) [NextCreate a status page report](https://upfour.io/docs/api/status-pages/create-a-status-page-report.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/reports" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/reports", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/reports", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/reports", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "affected_monitor_ids": [], "affected_resource_ids": [], "auto_end": false, "auto_start": false, "created_at": "2026-10-27T09:00:00Z", "ends_at": null, "entries": [ { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000002", "message": "The checkout was unavailable for 12 minutes.", "notify_subscribers": false, "published_at": "2026-10-27T09:00:00Z", "source": "manual", "status": "published", "update_id": "00000000-0000-4000-8000-000000000003", "updated_at": "2026-10-27T09:00:00Z" } ], "id": "00000000-0000-4000-8000-000000000003", "message": "The checkout was unavailable for 12 minutes.", "monitor_mode": "none", "notify_subscribers": false, "published_at": "2026-10-27T09:00:00Z", "recurrence": "none", "recurrence_until": null, "reminder_minutes": null, "source": "manual", "starts_at": null, "status": "published", "status_page_id": "00000000-0000-4000-8000-000000000001", "timezone": "", "title": "Checkout outage", "update_type": "report", "updated_at": "2026-10-27T09:00:00Z", "visibility": "public" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Create a status page report — API reference > Creates a status-page report with a title, message, affected resources and optional subscriber notification. Source: https://upfour.io/docs/api/status-pages/create-a-status-page-report [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Creates a status-page report with a title, message, affected resources and optional subscriber notification. POST https://app.upfour.io/v1/status-pages/{pageID}/reports scope status_page.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Body The 5 fields most calls use come first; the rest are grouped below. `application/json` required 18 fields titlestring (required) messagestring statusstring Valid values depend on update_type: investigating, identified, monitoring or resolved for incidents; scheduled, in_progress, completed or cancelled for maintenance; published for reports. Defaults to the first of those. investigatingidentifiedmonitoringresolvedscheduledin_progresscompletedcancelledpublished update_typestring incident (the default), maintenance or report. Ignored by POST /status-pages/{pageID}/reports, which always creates a report. The service does not validate it; any other value behaves like incident. incidentmaintenancereport visibilitystring public (the default) or draft for a thread still being prepared. publicdraft Affected services3 fields affected_monitor_idsuuid[] Monitors the update affects; read only when affected_resource_ids is empty. affected_resource_idsuuid[] Components (not monitors) the update affects. Wins over affected_monitor_ids when both are given. monitor_modestring · nullable pause_checks pauses the affected monitors for the window and needs starts_at and ends_at. Only for maintenance. nonepause_checks Timing6 fields starts_atstring (date-time) · nullable ends_atstring (date-time) · nullable timezonestring · nullable IANA time zone of the planned window. Only for maintenance. recurrencestring · nullable Needs starts_at and ends_at. Only for maintenance. noneweeklymonthly recurrence_untilstring (date) · nullable Last day of the series (YYYY-MM-DD); empty means no end. reminder_minutesinteger · nullable 5 to 20160 minutes before the window starts; 0 switches the reminder off. Needs starts_at. Only for maintenance. Automation4 fields auto_startboolean auto_endboolean notify_subscribersboolean published_atstring (date-time) · nullable Backdates the display time of an incident or report; not in the future. Ignored for maintenance. ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageUpdate | | | StatusPageUpdate · 38 fields FieldTypeDescription `affected_monitor_ids` uuid[] · nullable Monitors behind the affected components. `affected_resource_ids` uuid[] · nullable `auto_end` boolean `auto_start` boolean `created_at` string (date-time) `ends_at` string (date-time) · nullable `entries` StatusPageUpdateEntry[] · nullable Each item · 9 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `message` string `notify_subscribers` boolean `published_at` string (date-time) `source` string one of`manual` `automatic` `status` string one of`investigating` `identified` `monitoring` `resolved` `scheduled` `in_progress` `completed` `cancelled` `published` `update_id` uuid `updated_at` string (date-time) `id` uuid `message` string Mirrors the latest entry. `monitor_mode` string one of`none` `pause_checks` `notify_subscribers` boolean `published_at` string (date-time) `recurrence` string one of`none` `weekly` `monthly` `recurrence_until` string (date) · nullable Last day of a recurring series (YYYY-MM-DD); null for no end. `reminder_minutes` integer · nullable `source` string one of`manual` `automatic` `starts_at` string (date-time) · nullable `status` string one of`investigating` `identified` `monitoring` `resolved` `scheduled` `in_progress` `completed` `cancelled` `published` Mirrors the latest entry. `status_page_id` uuid `timezone` string IANA time zone of a planned maintenance window; empty for other types. `title` string `update_type` string one of`incident` `maintenance` `report` `updated_at` string (date-time) `visibility` string one of`public` `draft` `closed_unmeasured` boolean Resolved, but the incident ended because monitoring resumed after a billing pause: nobody measured the recovery. `incident_group_id` uuid `incident_id` uuid `reminder_sent_at` string (date-time) `series_id` uuid | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 409 | documented response | `idempotency_key_conflict`: the `Idempotency-Key` was already used with a different request body; `idempotency_replay_unavailable`: the stored response can no longer be replayed for this credential or resource, refresh the state instead of retrying with a new key. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList status page reports](https://upfour.io/docs/api/status-pages/list-status-page-reports.md) [NextList status page sections](https://upfour.io/docs/api/status-pages/list-status-page-sections.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/reports" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "message": "The checkout was unavailable for 12 minutes.", "notify_subscribers": false, "status": "published", "title": "Checkout outage", "update_type": "report" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/reports", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ message: "The checkout was unavailable for 12 minutes.", notify_subscribers: false, status: "published", title: "Checkout outage", update_type: "report", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/reports", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "message": "The checkout was unavailable for 12 minutes.", "notify_subscribers": False, "status": "published", "title": "Checkout outage", "update_type": "report", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "message": "The checkout was unavailable for 12 minutes.", "notify_subscribers": false, "status": "published", "title": "Checkout outage", "update_type": "report" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/reports", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "affected_monitor_ids": [], "affected_resource_ids": [], "auto_end": false, "auto_start": false, "created_at": "2026-10-27T09:00:00Z", "ends_at": null, "entries": [ { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000002", "message": "The checkout was unavailable for 12 minutes.", "notify_subscribers": false, "published_at": "2026-10-27T09:00:00Z", "source": "manual", "status": "published", "update_id": "00000000-0000-4000-8000-000000000003", "updated_at": "2026-10-27T09:00:00Z" } ], "id": "00000000-0000-4000-8000-000000000003", "message": "The checkout was unavailable for 12 minutes.", "monitor_mode": "none", "notify_subscribers": false, "published_at": "2026-10-27T09:00:00Z", "recurrence": "none", "recurrence_until": null, "reminder_minutes": null, "source": "manual", "starts_at": null, "status": "published", "status_page_id": "00000000-0000-4000-8000-000000000001", "timezone": "", "title": "Checkout outage", "update_type": "report", "updated_at": "2026-10-27T09:00:00Z", "visibility": "public" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # List status page sections — API reference > Returns the sections and their resources for the selected status page. Source: https://upfour.io/docs/api/status-pages/list-status-page-sections [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Returns the sections and their resources for the selected status page. GET https://app.upfour.io/v1/status-pages/{pageID}/sections scope status_page.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageSection[] · nullable | | | Each item · 54 fields FieldTypeDescription `auto_source` string one of`none` `all_monitors` `all_jobs` `monitor_group` `job_group` `auto_widget_type` string `created_at` string (date-time) `id` uuid `name` string `position` integer `resources` StatusPageResource[] · nullable Each item · 45 fields FieldTypeDescription `auto_publish` boolean `explanation` string `heartbeat_id` uuid · nullable `id` uuid `mark_degraded_for` string any_incident shows an open incident as degraded instead of down; any non-empty value also shows a slow but up monitor as degraded. Default no_incident. `mark_down_for` string any_incident (the default) shows the component as down while an incident is open; any other value shows it as degraded instead. `monitor_id` uuid · nullable `position` integer `public_name` string `section_id` uuid `widget_type` string `active_incident_ids` uuid[] `feed_component_id` string `feed_component_name` string `feed_format` string one of`statuspage_v2` `instatus` `feed_url` string `freshness_timeout_seconds` integer `history` PublicStatusHistory PublicStatusHistory · 12 fields FieldTypeDescription `covered` boolean `days` integer `ends_at` string (date-time) `segments` PublicStatusHistoryDay[] · nullable Each item · 4 fields FieldTypeDescription `covered` boolean `date` string `status` string one of`up` `degraded` `down` `no_data` `uptime_percentage` number `starts_at` string (date-time) `uptime_by_range` map of number `uptime_percentage` number `watching_since` string (date-time) `is_external` boolean `last_checked_at` string (date-time) `manual_status` string one of`operational` `degraded` `down` `maintenance` `monitor_state` string one of`UP` `DOWN` `PAUSED` `response_times` PublicResponseTimeHistory PublicResponseTimeHistory · 5 fields FieldTypeDescription `days` PublicResponseTimeDay[] · nullable Each item · 3 fields FieldTypeDescription `avg_ms` number `date` string `p95_ms` number `current_avg_ms` number `runtime_status` string Current status of the component, derived from its monitor, scheduled job, manual status or vendor feed. `source` StatusPageResourceSource StatusPageResourceSource · 3 fields FieldTypeDescription `checked_at` string (date-time) · nullable `name` string `url` string `status_page_id` uuid `auto_group_id` uuid | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousCreate a status page report](https://upfour.io/docs/api/status-pages/create-a-status-page-report.md) [NextCreate a status page section](https://upfour.io/docs/api/status-pages/create-a-status-page-section.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "auto_source": "none", "auto_widget_type": "history", "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000002", "name": "Website", "position": 0, "resources": [], "status_page_id": "00000000-0000-4000-8000-000000000001" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Create a status page section — API reference > Creates a status-page section with a name, position and optional automatic source and widget settings. Source: https://upfour.io/docs/api/status-pages/create-a-status-page-section [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Creates a status-page section with a name, position and optional automatic source and widget settings. POST https://app.upfour.io/v1/status-pages/{pageID}/sections scope status_page.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Body `application/json` required 5 fields namestring (required) All other fields4 fields auto_group_iduuid · nullable Required for monitor_group and job_group, rejected for the other sources. auto_sourcestring noneall_monitorsall_jobsmonitor_groupjob_group auto_widget_typestring How the components of an automatic section render; history by default. positioninteger · nullable Omitted appends the section at the end. ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageSection | | | StatusPageSection · 54 fields FieldTypeDescription `auto_source` string one of`none` `all_monitors` `all_jobs` `monitor_group` `job_group` `auto_widget_type` string `created_at` string (date-time) `id` uuid `name` string `position` integer `resources` StatusPageResource[] · nullable Each item · 45 fields FieldTypeDescription `auto_publish` boolean `explanation` string `heartbeat_id` uuid · nullable `id` uuid `mark_degraded_for` string any_incident shows an open incident as degraded instead of down; any non-empty value also shows a slow but up monitor as degraded. Default no_incident. `mark_down_for` string any_incident (the default) shows the component as down while an incident is open; any other value shows it as degraded instead. `monitor_id` uuid · nullable `position` integer `public_name` string `section_id` uuid `widget_type` string `active_incident_ids` uuid[] `feed_component_id` string `feed_component_name` string `feed_format` string one of`statuspage_v2` `instatus` `feed_url` string `freshness_timeout_seconds` integer `history` PublicStatusHistory PublicStatusHistory · 12 fields FieldTypeDescription `covered` boolean `days` integer `ends_at` string (date-time) `segments` PublicStatusHistoryDay[] · nullable Each item · 4 fields FieldTypeDescription `covered` boolean `date` string `status` string one of`up` `degraded` `down` `no_data` `uptime_percentage` number `starts_at` string (date-time) `uptime_by_range` map of number `uptime_percentage` number `watching_since` string (date-time) `is_external` boolean `last_checked_at` string (date-time) `manual_status` string one of`operational` `degraded` `down` `maintenance` `monitor_state` string one of`UP` `DOWN` `PAUSED` `response_times` PublicResponseTimeHistory PublicResponseTimeHistory · 5 fields FieldTypeDescription `days` PublicResponseTimeDay[] · nullable Each item · 3 fields FieldTypeDescription `avg_ms` number `date` string `p95_ms` number `current_avg_ms` number `runtime_status` string Current status of the component, derived from its monitor, scheduled job, manual status or vendor feed. `source` StatusPageResourceSource StatusPageResourceSource · 3 fields FieldTypeDescription `checked_at` string (date-time) · nullable `name` string `url` string `status_page_id` uuid `auto_group_id` uuid | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList status page sections](https://upfour.io/docs/api/status-pages/list-status-page-sections.md) [NextDelete a status page section](https://upfour.io/docs/api/status-pages/delete-a-status-page-section.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Website" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "Website", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "name": "Website", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "name": "Website" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "auto_source": "none", "auto_widget_type": "history", "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000002", "name": "Website", "position": 0, "resources": [], "status_page_id": "00000000-0000-4000-8000-000000000001" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Delete a status page section — API reference > Deletes a section from the selected status page and returns status 204 without a response body. Source: https://upfour.io/docs/api/status-pages/delete-a-status-page-section [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Deletes a section from the selected status page and returns status 204 without a response body. DELETE https://app.upfour.io/v1/status-pages/{pageID}/sections/{sectionID} scope status_page.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | | `sectionID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousCreate a status page section](https://upfour.io/docs/api/status-pages/create-a-status-page-section.md) [NextUpdate a status page section](https://upfour.io/docs/api/status-pages/update-a-status-page-section.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections/00000000-0000-4000-8000-000000000002" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections/00000000-0000-4000-8000-000000000002", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections/00000000-0000-4000-8000-000000000002", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections/00000000-0000-4000-8000-000000000002", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Update a status page section — API reference > Updates a status-page section's name, position or automatic source and widget settings. Source: https://upfour.io/docs/api/status-pages/update-a-status-page-section [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Updates a status-page section's name, position or automatic source and widget settings. PUT https://app.upfour.io/v1/status-pages/{pageID}/sections/{sectionID} scope status_page.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | | `sectionID` required | uuid | path parameter | ## Body `application/json` required 5 fields namestring (required) All other fields4 fields auto_group_iduuid · nullable Required for monitor_group and job_group, rejected for the other sources. auto_sourcestring noneall_monitorsall_jobsmonitor_groupjob_group auto_widget_typestring How the components of an automatic section render; history by default. positioninteger · nullable Omitted appends the section at the end. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageSection | | | StatusPageSection · 54 fields FieldTypeDescription `auto_source` string one of`none` `all_monitors` `all_jobs` `monitor_group` `job_group` `auto_widget_type` string `created_at` string (date-time) `id` uuid `name` string `position` integer `resources` StatusPageResource[] · nullable Each item · 45 fields FieldTypeDescription `auto_publish` boolean `explanation` string `heartbeat_id` uuid · nullable `id` uuid `mark_degraded_for` string any_incident shows an open incident as degraded instead of down; any non-empty value also shows a slow but up monitor as degraded. Default no_incident. `mark_down_for` string any_incident (the default) shows the component as down while an incident is open; any other value shows it as degraded instead. `monitor_id` uuid · nullable `position` integer `public_name` string `section_id` uuid `widget_type` string `active_incident_ids` uuid[] `feed_component_id` string `feed_component_name` string `feed_format` string one of`statuspage_v2` `instatus` `feed_url` string `freshness_timeout_seconds` integer `history` PublicStatusHistory PublicStatusHistory · 12 fields FieldTypeDescription `covered` boolean `days` integer `ends_at` string (date-time) `segments` PublicStatusHistoryDay[] · nullable Each item · 4 fields FieldTypeDescription `covered` boolean `date` string `status` string one of`up` `degraded` `down` `no_data` `uptime_percentage` number `starts_at` string (date-time) `uptime_by_range` map of number `uptime_percentage` number `watching_since` string (date-time) `is_external` boolean `last_checked_at` string (date-time) `manual_status` string one of`operational` `degraded` `down` `maintenance` `monitor_state` string one of`UP` `DOWN` `PAUSED` `response_times` PublicResponseTimeHistory PublicResponseTimeHistory · 5 fields FieldTypeDescription `days` PublicResponseTimeDay[] · nullable Each item · 3 fields FieldTypeDescription `avg_ms` number `date` string `p95_ms` number `current_avg_ms` number `runtime_status` string Current status of the component, derived from its monitor, scheduled job, manual status or vendor feed. `source` StatusPageResourceSource StatusPageResourceSource · 3 fields FieldTypeDescription `checked_at` string (date-time) · nullable `name` string `url` string `status_page_id` uuid `auto_group_id` uuid | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousDelete a status page section](https://upfour.io/docs/api/status-pages/delete-a-status-page-section.md) [NextList section resources](https://upfour.io/docs/api/status-pages/list-section-resources.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections/00000000-0000-4000-8000-000000000002" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Website and checkout" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections/00000000-0000-4000-8000-000000000002", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "Website and checkout", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections/00000000-0000-4000-8000-000000000002", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "name": "Website and checkout", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "name": "Website and checkout" }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections/00000000-0000-4000-8000-000000000002", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "auto_source": "none", "auto_widget_type": "history", "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000002", "name": "Website and checkout", "position": 1, "resources": [], "status_page_id": "00000000-0000-4000-8000-000000000001" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # List section resources — API reference > Returns section resources with their source, current status, display settings and positions. Source: https://upfour.io/docs/api/status-pages/list-section-resources [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Returns section resources with their source, current status, display settings and positions. GET https://app.upfour.io/v1/status-pages/{pageID}/sections/{sectionID}/resources scope status_page.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | | `sectionID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageResource[] · nullable | | | Each item · 45 fields FieldTypeDescription `auto_publish` boolean `explanation` string `heartbeat_id` uuid · nullable `id` uuid `mark_degraded_for` string any_incident shows an open incident as degraded instead of down; any non-empty value also shows a slow but up monitor as degraded. Default no_incident. `mark_down_for` string any_incident (the default) shows the component as down while an incident is open; any other value shows it as degraded instead. `monitor_id` uuid · nullable `position` integer `public_name` string `section_id` uuid `widget_type` string `active_incident_ids` uuid[] `feed_component_id` string `feed_component_name` string `feed_format` string one of`statuspage_v2` `instatus` `feed_url` string `freshness_timeout_seconds` integer `history` PublicStatusHistory PublicStatusHistory · 12 fields FieldTypeDescription `covered` boolean `days` integer `ends_at` string (date-time) `segments` PublicStatusHistoryDay[] · nullable Each item · 4 fields FieldTypeDescription `covered` boolean `date` string `status` string one of`up` `degraded` `down` `no_data` `uptime_percentage` number `starts_at` string (date-time) `uptime_by_range` map of number `uptime_percentage` number `watching_since` string (date-time) `is_external` boolean `last_checked_at` string (date-time) `manual_status` string one of`operational` `degraded` `down` `maintenance` `monitor_state` string one of`UP` `DOWN` `PAUSED` `response_times` PublicResponseTimeHistory PublicResponseTimeHistory · 5 fields FieldTypeDescription `days` PublicResponseTimeDay[] · nullable Each item · 3 fields FieldTypeDescription `avg_ms` number `date` string `p95_ms` number `current_avg_ms` number `runtime_status` string Current status of the component, derived from its monitor, scheduled job, manual status or vendor feed. `source` StatusPageResourceSource StatusPageResourceSource · 3 fields FieldTypeDescription `checked_at` string (date-time) · nullable `name` string `url` string | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpdate a status page section](https://upfour.io/docs/api/status-pages/update-a-status-page-section.md) [NextAdd a section resource](https://upfour.io/docs/api/status-pages/add-a-section-resource.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections/00000000-0000-4000-8000-000000000002/resources" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections/00000000-0000-4000-8000-000000000002/resources", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections/00000000-0000-4000-8000-000000000002/resources", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections/00000000-0000-4000-8000-000000000002/resources", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "auto_publish": true, "explanation": "", "heartbeat_id": null, "id": "00000000-0000-4000-8000-000000000003", "mark_degraded_for": "", "mark_down_for": "", "monitor_id": "00000000-0000-4000-8000-000000000004", "position": 0, "public_name": "Marketing website", "section_id": "00000000-0000-4000-8000-000000000002", "widget_type": "" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Add a section resource — API reference > Adds a monitor, Scheduled job, manual component or vendor feed to a section with display and status rules. Source: https://upfour.io/docs/api/status-pages/add-a-section-resource [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Adds a monitor, Scheduled job, manual component or vendor feed to a section with display and status rules. POST https://app.upfour.io/v1/status-pages/{pageID}/sections/{sectionID}/resources scope status_page.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | | `sectionID` required | uuid | path parameter | ## Body The 5 fields most calls use come first; the rest are grouped below. `application/json` required 13 fields public_namestring monitor_iduuid · nullable At most one of monitor_id and heartbeat_id; with neither the component is manual (manual_status) or follows a vendor feed (feed_url). heartbeat_iduuid · nullable feed_urlstring Address of a vendor's status page (Atlassian Statuspage or Instatus); needs feed_component_id. feed_component_idstring Display4 fields widget_typestring How the component renders: history (daily bars, the default), plain, or response_times (monitors only). Not validated by the service. explanationstring positioninteger · nullable Omitted appends the component at the end. is_externalboolean Shown for transparency but never counted into the page's own status or availability. Always true for a feed component. Status rules4 fields manual_statusstring Only for a component with no monitor, scheduled job or feed. Empty derives the status. ""operationaldegradeddownmaintenance mark_degraded_forstring any_incident shows an open incident as degraded instead of down; any non-empty value also shows a slow but up monitor as degraded. Default no_incident. mark_down_forstring any_incident (the default) shows the component as down while an incident is open; any other value shows it as degraded instead. auto_publishboolean · nullable false keeps the automatic report for this component as an internal draft. Default true. ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageResource | | | StatusPageResource · 45 fields FieldTypeDescription `auto_publish` boolean `explanation` string `heartbeat_id` uuid · nullable `id` uuid `mark_degraded_for` string any_incident shows an open incident as degraded instead of down; any non-empty value also shows a slow but up monitor as degraded. Default no_incident. `mark_down_for` string any_incident (the default) shows the component as down while an incident is open; any other value shows it as degraded instead. `monitor_id` uuid · nullable `position` integer `public_name` string `section_id` uuid `widget_type` string `active_incident_ids` uuid[] `feed_component_id` string `feed_component_name` string `feed_format` string one of`statuspage_v2` `instatus` `feed_url` string `freshness_timeout_seconds` integer `history` PublicStatusHistory PublicStatusHistory · 12 fields FieldTypeDescription `covered` boolean `days` integer `ends_at` string (date-time) `segments` PublicStatusHistoryDay[] · nullable Each item · 4 fields FieldTypeDescription `covered` boolean `date` string `status` string one of`up` `degraded` `down` `no_data` `uptime_percentage` number `starts_at` string (date-time) `uptime_by_range` map of number `uptime_percentage` number `watching_since` string (date-time) `is_external` boolean `last_checked_at` string (date-time) `manual_status` string one of`operational` `degraded` `down` `maintenance` `monitor_state` string one of`UP` `DOWN` `PAUSED` `response_times` PublicResponseTimeHistory PublicResponseTimeHistory · 5 fields FieldTypeDescription `days` PublicResponseTimeDay[] · nullable Each item · 3 fields FieldTypeDescription `avg_ms` number `date` string `p95_ms` number `current_avg_ms` number `runtime_status` string Current status of the component, derived from its monitor, scheduled job, manual status or vendor feed. `source` StatusPageResourceSource StatusPageResourceSource · 3 fields FieldTypeDescription `checked_at` string (date-time) · nullable `name` string `url` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 402 | [billing_action_unavailable](https://upfour.io/docs/api/errors#code-billing_action_unavailable) | The Company's billing state does not allow this action. **When:** A metered or limited action while the billing state, such as paused or without credit, blocks it; details holds the action and billing_state. **Fix:** Top up the credit or change the plan, then retry. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 503 | [quota_gate_unavailable](https://upfour.io/docs/api/errors#code-quota_gate_unavailable) | The limit check itself failed, so the action was not carried out. **When:** The quota service could not decide while the request ran. **Fix:** Retry in a moment; nothing was changed. | | 403 / 429 | [quota_limit_reached](https://upfour.io/docs/api/errors#code-quota_limit_reached) | A usage or inventory limit of the Company was reached. **When:** Counters and concurrency limits answer 429 with Retry-After, inventory and capacity limits answer 403; details holds purpose, limit and window. **Fix:** On 429 wait Retry-After seconds; on 403 free something up, request a higher limit where details.raise_request is true, or buy an extension. `quota_limit_reached` (window `total`): the Company already holds the most of these it may have. `error.details` carries `purpose` and `limit`; remove one or ask support for a higher limit. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList section resources](https://upfour.io/docs/api/status-pages/list-section-resources.md) [NextDelete a section resource](https://upfour.io/docs/api/status-pages/delete-a-section-resource.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections/00000000-0000-4000-8000-000000000002/resources" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "monitor_id": "00000000-0000-4000-8000-000000000003", "public_name": "Marketing website" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections/00000000-0000-4000-8000-000000000002/resources", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ monitor_id: "00000000-0000-4000-8000-000000000003", public_name: "Marketing website", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections/00000000-0000-4000-8000-000000000002/resources", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "monitor_id": "00000000-0000-4000-8000-000000000003", "public_name": "Marketing website", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "monitor_id": "00000000-0000-4000-8000-000000000003", "public_name": "Marketing website" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections/00000000-0000-4000-8000-000000000002/resources", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "auto_publish": true, "explanation": "", "heartbeat_id": null, "id": "00000000-0000-4000-8000-000000000004", "mark_degraded_for": "", "mark_down_for": "", "monitor_id": "00000000-0000-4000-8000-000000000003", "position": 0, "public_name": "Marketing website", "section_id": "00000000-0000-4000-8000-000000000002", "widget_type": "" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "billing_action_unavailable", "message": "The Company's billing state does not allow this action.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` ### 429 ```json { "error": { "code": "quota_limit_reached", "message": "A usage or inventory limit of the Company was reached.", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "quota_gate_unavailable", "message": "The limit check itself failed, so the action was not carried out.", "request_id": "req_…" } } ``` --- # Delete a section resource — API reference > Removes a resource from a status-page section and returns status 204 without a response body. Source: https://upfour.io/docs/api/status-pages/delete-a-section-resource [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Removes a resource from a status-page section and returns status 204 without a response body. DELETE https://app.upfour.io/v1/status-pages/{pageID}/sections/{sectionID}/resources/{resourceID} scope status_page.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | | `sectionID` required | uuid | path parameter | | `resourceID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousAdd a section resource](https://upfour.io/docs/api/status-pages/add-a-section-resource.md) [NextUpdate a section resource](https://upfour.io/docs/api/status-pages/update-a-section-resource.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections/00000000-0000-4000-8000-000000000002/resources/00000000-0000-4000-8000-000000000003" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections/00000000-0000-4000-8000-000000000002/resources/00000000-0000-4000-8000-000000000003", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections/00000000-0000-4000-8000-000000000002/resources/00000000-0000-4000-8000-000000000003", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections/00000000-0000-4000-8000-000000000002/resources/00000000-0000-4000-8000-000000000003", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Update a section resource — API reference > Updates a section resource's source, display name, widget and status rules. Source: https://upfour.io/docs/api/status-pages/update-a-section-resource [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Updates a section resource's source, display name, widget and status rules. PUT https://app.upfour.io/v1/status-pages/{pageID}/sections/{sectionID}/resources/{resourceID} scope status_page.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | | `sectionID` required | uuid | path parameter | | `resourceID` required | uuid | path parameter | ## Body The 5 fields most calls use come first; the rest are grouped below. `application/json` required 13 fields public_namestring monitor_iduuid · nullable At most one of monitor_id and heartbeat_id; with neither the component is manual (manual_status) or follows a vendor feed (feed_url). heartbeat_iduuid · nullable feed_urlstring Address of a vendor's status page (Atlassian Statuspage or Instatus); needs feed_component_id. feed_component_idstring Display4 fields widget_typestring How the component renders: history (daily bars, the default), plain, or response_times (monitors only). Not validated by the service. explanationstring positioninteger · nullable Omitted appends the component at the end. is_externalboolean Shown for transparency but never counted into the page's own status or availability. Always true for a feed component. Status rules4 fields manual_statusstring Only for a component with no monitor, scheduled job or feed. Empty derives the status. ""operationaldegradeddownmaintenance mark_degraded_forstring any_incident shows an open incident as degraded instead of down; any non-empty value also shows a slow but up monitor as degraded. Default no_incident. mark_down_forstring any_incident (the default) shows the component as down while an incident is open; any other value shows it as degraded instead. auto_publishboolean · nullable false keeps the automatic report for this component as an internal draft. Default true. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageResource | | | StatusPageResource · 45 fields FieldTypeDescription `auto_publish` boolean `explanation` string `heartbeat_id` uuid · nullable `id` uuid `mark_degraded_for` string any_incident shows an open incident as degraded instead of down; any non-empty value also shows a slow but up monitor as degraded. Default no_incident. `mark_down_for` string any_incident (the default) shows the component as down while an incident is open; any other value shows it as degraded instead. `monitor_id` uuid · nullable `position` integer `public_name` string `section_id` uuid `widget_type` string `active_incident_ids` uuid[] `feed_component_id` string `feed_component_name` string `feed_format` string one of`statuspage_v2` `instatus` `feed_url` string `freshness_timeout_seconds` integer `history` PublicStatusHistory PublicStatusHistory · 12 fields FieldTypeDescription `covered` boolean `days` integer `ends_at` string (date-time) `segments` PublicStatusHistoryDay[] · nullable Each item · 4 fields FieldTypeDescription `covered` boolean `date` string `status` string one of`up` `degraded` `down` `no_data` `uptime_percentage` number `starts_at` string (date-time) `uptime_by_range` map of number `uptime_percentage` number `watching_since` string (date-time) `is_external` boolean `last_checked_at` string (date-time) `manual_status` string one of`operational` `degraded` `down` `maintenance` `monitor_state` string one of`UP` `DOWN` `PAUSED` `response_times` PublicResponseTimeHistory PublicResponseTimeHistory · 5 fields FieldTypeDescription `days` PublicResponseTimeDay[] · nullable Each item · 3 fields FieldTypeDescription `avg_ms` number `date` string `p95_ms` number `current_avg_ms` number `runtime_status` string Current status of the component, derived from its monitor, scheduled job, manual status or vendor feed. `source` StatusPageResourceSource StatusPageResourceSource · 3 fields FieldTypeDescription `checked_at` string (date-time) · nullable `name` string `url` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 402 | [billing_action_unavailable](https://upfour.io/docs/api/errors#code-billing_action_unavailable) | The Company's billing state does not allow this action. **When:** A metered or limited action while the billing state, such as paused or without credit, blocks it; details holds the action and billing_state. **Fix:** Top up the credit or change the plan, then retry. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 503 | [quota_gate_unavailable](https://upfour.io/docs/api/errors#code-quota_gate_unavailable) | The limit check itself failed, so the action was not carried out. **When:** The quota service could not decide while the request ran. **Fix:** Retry in a moment; nothing was changed. | | 403 / 429 | [quota_limit_reached](https://upfour.io/docs/api/errors#code-quota_limit_reached) | A usage or inventory limit of the Company was reached. **When:** Counters and concurrency limits answer 429 with Retry-After, inventory and capacity limits answer 403; details holds purpose, limit and window. **Fix:** On 429 wait Retry-After seconds; on 403 free something up, request a higher limit where details.raise_request is true, or buy an extension. `quota_limit_reached` (window `total`): the Company already holds the most of these it may have. `error.details` carries `purpose` and `limit`; remove one or ask support for a higher limit. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousDelete a section resource](https://upfour.io/docs/api/status-pages/delete-a-section-resource.md) [NextGet status page structure](https://upfour.io/docs/api/status-pages/get-status-page-structure.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections/00000000-0000-4000-8000-000000000002/resources/00000000-0000-4000-8000-000000000003" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "explanation": "The public website.", "monitor_id": "00000000-0000-4000-8000-000000000004", "public_name": "Marketing website" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections/00000000-0000-4000-8000-000000000002/resources/00000000-0000-4000-8000-000000000003", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ explanation: "The public website.", monitor_id: "00000000-0000-4000-8000-000000000004", public_name: "Marketing website", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections/00000000-0000-4000-8000-000000000002/resources/00000000-0000-4000-8000-000000000003", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "explanation": "The public website.", "monitor_id": "00000000-0000-4000-8000-000000000004", "public_name": "Marketing website", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "explanation": "The public website.", "monitor_id": "00000000-0000-4000-8000-000000000004", "public_name": "Marketing website" }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/sections/00000000-0000-4000-8000-000000000002/resources/00000000-0000-4000-8000-000000000003", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "auto_publish": true, "explanation": "The public website.", "heartbeat_id": null, "id": "00000000-0000-4000-8000-000000000003", "mark_degraded_for": "", "mark_down_for": "", "monitor_id": "00000000-0000-4000-8000-000000000004", "position": 1, "public_name": "Marketing website", "section_id": "00000000-0000-4000-8000-000000000002", "widget_type": "" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "billing_action_unavailable", "message": "The Company's billing state does not allow this action.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` ### 429 ```json { "error": { "code": "quota_limit_reached", "message": "A usage or inventory limit of the Company was reached.", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "quota_gate_unavailable", "message": "The limit check itself failed, so the action was not carried out.", "request_id": "req_…" } } ``` --- # Get status page structure — API reference > Returns the complete section and resource structure for a status page. Source: https://upfour.io/docs/api/status-pages/get-status-page-structure [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Returns the complete section and resource structure for a status page. GET https://app.upfour.io/v1/status-pages/{pageID}/structure scope status_page.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageSection[] · nullable | | | Each item · 54 fields FieldTypeDescription `auto_source` string one of`none` `all_monitors` `all_jobs` `monitor_group` `job_group` `auto_widget_type` string `created_at` string (date-time) `id` uuid `name` string `position` integer `resources` StatusPageResource[] · nullable Each item · 45 fields FieldTypeDescription `auto_publish` boolean `explanation` string `heartbeat_id` uuid · nullable `id` uuid `mark_degraded_for` string any_incident shows an open incident as degraded instead of down; any non-empty value also shows a slow but up monitor as degraded. Default no_incident. `mark_down_for` string any_incident (the default) shows the component as down while an incident is open; any other value shows it as degraded instead. `monitor_id` uuid · nullable `position` integer `public_name` string `section_id` uuid `widget_type` string `active_incident_ids` uuid[] `feed_component_id` string `feed_component_name` string `feed_format` string one of`statuspage_v2` `instatus` `feed_url` string `freshness_timeout_seconds` integer `history` PublicStatusHistory PublicStatusHistory · 12 fields FieldTypeDescription `covered` boolean `days` integer `ends_at` string (date-time) `segments` PublicStatusHistoryDay[] · nullable Each item · 4 fields FieldTypeDescription `covered` boolean `date` string `status` string one of`up` `degraded` `down` `no_data` `uptime_percentage` number `starts_at` string (date-time) `uptime_by_range` map of number `uptime_percentage` number `watching_since` string (date-time) `is_external` boolean `last_checked_at` string (date-time) `manual_status` string one of`operational` `degraded` `down` `maintenance` `monitor_state` string one of`UP` `DOWN` `PAUSED` `response_times` PublicResponseTimeHistory PublicResponseTimeHistory · 5 fields FieldTypeDescription `days` PublicResponseTimeDay[] · nullable Each item · 3 fields FieldTypeDescription `avg_ms` number `date` string `p95_ms` number `current_avg_ms` number `runtime_status` string Current status of the component, derived from its monitor, scheduled job, manual status or vendor feed. `source` StatusPageResourceSource StatusPageResourceSource · 3 fields FieldTypeDescription `checked_at` string (date-time) · nullable `name` string `url` string `status_page_id` uuid `auto_group_id` uuid | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpdate a section resource](https://upfour.io/docs/api/status-pages/update-a-section-resource.md) [NextReplace status page structure](https://upfour.io/docs/api/status-pages/replace-status-page-structure.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/structure" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/structure", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/structure", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/structure", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "auto_source": "none", "auto_widget_type": "history", "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000002", "name": "Website", "position": 0, "resources": [ { "auto_publish": true, "explanation": "", "heartbeat_id": null, "id": "00000000-0000-4000-8000-000000000003", "mark_degraded_for": "", "mark_down_for": "", "monitor_id": "00000000-0000-4000-8000-000000000004", "position": 0, "public_name": "Marketing website", "section_id": "00000000-0000-4000-8000-000000000002", "widget_type": "" } ], "status_page_id": "00000000-0000-4000-8000-000000000001" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Replace status page structure — API reference > Replaces the complete status-page structure with the supplied sections and resources. Source: https://upfour.io/docs/api/status-pages/replace-status-page-structure [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Replaces the complete status-page structure with the supplied sections and resources. PUT https://app.upfour.io/v1/status-pages/{pageID}/structure scope status_page.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Body `application/json` required 1 field All other fields18 fields sectionsStructureSectionInput[] The whole structure: sections not listed are deleted, and omitting the field removes all of them. In the app: **Structure** Nested fields 17 fields | Field | Type | Description | | --- | --- | --- | | `auto_group_id` optional | uuid · nullable | Required for monitor_group and job_group, rejected for the other sources. | | `auto_source` optional | string one of`none` `all_monitors` `all_jobs` `monitor_group` `job_group` | none (stored resources), all_monitors, all_jobs, monitor_group or job_group. An automatic section cannot also hold resources. | | `auto_widget_type` optional | string | How the components of an automatic section render; history by default. | | `name` optional | string | | | `resources` optional | StructureResourceInput[] | | | Each item · 12 fields FieldTypeDescription `auto_publish` optional boolean · nullable false keeps the automatic report for this component as an internal draft. Default true. `explanation` optional string `feed_component_id` optional string `feed_url` optional string Address of a vendor's status page (Atlassian Statuspage or Instatus); needs feed_component_id. `heartbeat_id` optional uuid · nullable `is_external` optional boolean Shown for transparency but never counted into the page's own status or availability. Always true for a feed component. `manual_status` optional string one of`""` `operational` `degraded` `down` `maintenance` Only for a component with no monitor, scheduled job or feed. Empty derives the status. `mark_degraded_for` optional string any_incident shows an open incident as degraded instead of down; any non-empty value also shows a slow but up monitor as degraded. Default no_incident. `mark_down_for` optional string any_incident (the default) shows the component as down while an incident is open; any other value shows it as degraded instead. `monitor_id` optional uuid · nullable At most one of monitor_id and heartbeat_id; with neither the component is manual (manual_status) or follows a vendor feed (feed_url). `public_name` optional string `widget_type` optional string How the component renders: history (daily bars, the default), plain, or response_times (monitors only). Not validated by the service. | | | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageSection[] · nullable | | | Each item · 54 fields FieldTypeDescription `auto_source` string one of`none` `all_monitors` `all_jobs` `monitor_group` `job_group` `auto_widget_type` string `created_at` string (date-time) `id` uuid `name` string `position` integer `resources` StatusPageResource[] · nullable Each item · 45 fields FieldTypeDescription `auto_publish` boolean `explanation` string `heartbeat_id` uuid · nullable `id` uuid `mark_degraded_for` string any_incident shows an open incident as degraded instead of down; any non-empty value also shows a slow but up monitor as degraded. Default no_incident. `mark_down_for` string any_incident (the default) shows the component as down while an incident is open; any other value shows it as degraded instead. `monitor_id` uuid · nullable `position` integer `public_name` string `section_id` uuid `widget_type` string `active_incident_ids` uuid[] `feed_component_id` string `feed_component_name` string `feed_format` string one of`statuspage_v2` `instatus` `feed_url` string `freshness_timeout_seconds` integer `history` PublicStatusHistory PublicStatusHistory · 12 fields FieldTypeDescription `covered` boolean `days` integer `ends_at` string (date-time) `segments` PublicStatusHistoryDay[] · nullable Each item · 4 fields FieldTypeDescription `covered` boolean `date` string `status` string one of`up` `degraded` `down` `no_data` `uptime_percentage` number `starts_at` string (date-time) `uptime_by_range` map of number `uptime_percentage` number `watching_since` string (date-time) `is_external` boolean `last_checked_at` string (date-time) `manual_status` string one of`operational` `degraded` `down` `maintenance` `monitor_state` string one of`UP` `DOWN` `PAUSED` `response_times` PublicResponseTimeHistory PublicResponseTimeHistory · 5 fields FieldTypeDescription `days` PublicResponseTimeDay[] · nullable Each item · 3 fields FieldTypeDescription `avg_ms` number `date` string `p95_ms` number `current_avg_ms` number `runtime_status` string Current status of the component, derived from its monitor, scheduled job, manual status or vendor feed. `source` StatusPageResourceSource StatusPageResourceSource · 3 fields FieldTypeDescription `checked_at` string (date-time) · nullable `name` string `url` string `status_page_id` uuid `auto_group_id` uuid | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 402 | [billing_action_unavailable](https://upfour.io/docs/api/errors#code-billing_action_unavailable) | The Company's billing state does not allow this action. **When:** A metered or limited action while the billing state, such as paused or without credit, blocks it; details holds the action and billing_state. **Fix:** Top up the credit or change the plan, then retry. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 503 | [quota_gate_unavailable](https://upfour.io/docs/api/errors#code-quota_gate_unavailable) | The limit check itself failed, so the action was not carried out. **When:** The quota service could not decide while the request ran. **Fix:** Retry in a moment; nothing was changed. | | 403 / 429 | [quota_limit_reached](https://upfour.io/docs/api/errors#code-quota_limit_reached) | A usage or inventory limit of the Company was reached. **When:** Counters and concurrency limits answer 429 with Retry-After, inventory and capacity limits answer 403; details holds purpose, limit and window. **Fix:** On 429 wait Retry-After seconds; on 403 free something up, request a higher limit where details.raise_request is true, or buy an extension. `quota_limit_reached` (window `total`): the Company already holds the most of these it may have. `error.details` carries `purpose` and `limit`; remove one or ask support for a higher limit. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet status page structure](https://upfour.io/docs/api/status-pages/get-status-page-structure.md) [NextList status page subscribers](https://upfour.io/docs/api/status-pages/list-status-page-subscribers.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/structure" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "sections": [ { "name": "Website and checkout", "resources": [ { "monitor_id": "00000000-0000-4000-8000-000000000002", "public_name": "Marketing website" } ] } ] }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/structure", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ sections: [ { name: "Website and checkout", resources: [ { monitor_id: "00000000-0000-4000-8000-000000000002", public_name: "Marketing website", }, ], }, ], }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/structure", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "sections": [ { "name": "Website and checkout", "resources": [ { "monitor_id": "00000000-0000-4000-8000-000000000002", "public_name": "Marketing website", }, ], }, ], }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "sections": [ { "name": "Website and checkout", "resources": [ { "monitor_id": "00000000-0000-4000-8000-000000000002", "public_name": "Marketing website" } ] } ] }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/structure", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "auto_source": "none", "auto_widget_type": "history", "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000003", "name": "Website and checkout", "position": 0, "resources": [ { "auto_publish": true, "explanation": "", "heartbeat_id": null, "id": "00000000-0000-4000-8000-000000000004", "mark_degraded_for": "", "mark_down_for": "", "monitor_id": "00000000-0000-4000-8000-000000000002", "position": 0, "public_name": "Marketing website", "section_id": "00000000-0000-4000-8000-000000000003", "widget_type": "" } ], "status_page_id": "00000000-0000-4000-8000-000000000001" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "billing_action_unavailable", "message": "The Company's billing state does not allow this action.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` ### 429 ```json { "error": { "code": "quota_limit_reached", "message": "A usage or inventory limit of the Company was reached.", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "quota_gate_unavailable", "message": "The limit check itself failed, so the action was not carried out.", "request_id": "req_…" } } ``` --- # List status page subscribers — API reference > Returns subscriber addresses, channels, filters and delivery status for a status page. Source: https://upfour.io/docs/api/status-pages/list-status-page-subscribers [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Returns subscriber addresses, channels, filters and delivery status for a status page. GET https://app.upfour.io/v1/status-pages/{pageID}/subscribers scope status_page.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageSubscriber[] · nullable | | | Each item · 15 fields FieldTypeDescription `channel` string one of`email` `webhook` `confirmed` boolean `created_at` string (date-time) `email` string `failure_count` integer `id` uuid `resource_ids` uuid[] · nullable `status_page_id` uuid `subscribed_to` string one of`all` `incident` `maintenance` `disabled_at` string (date-time) `last_delivery_at` string (date-time) `last_failure_at` string (date-time) `suppression` string one of`hard_bounce` `complaint` Why nothing is mailed to the address; empty when it is not suppressed. `suppression_liftable` boolean True for a hard bounce, which an owner or admin may release; a complaint stays with up4. `webhook_target` string The webhook URL in masked form; never the full URL or its secret. | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousReplace status page structure](https://upfour.io/docs/api/status-pages/replace-status-page-structure.md) [NextAdd a status page subscriber](https://upfour.io/docs/api/status-pages/add-a-status-page-subscriber.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/subscribers" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/subscribers", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/subscribers", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/subscribers", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "channel": "email", "confirmed": false, "created_at": "2026-10-27T09:00:00Z", "email": "carla@example.com", "failure_count": 0, "id": "00000000-0000-4000-8000-000000000002", "resource_ids": [], "status_page_id": "00000000-0000-4000-8000-000000000001", "subscribed_to": "all" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Add a status page subscriber — API reference > Adds an e-mail subscriber with optional resource and update-type filters. Source: https://upfour.io/docs/api/status-pages/add-a-status-page-subscriber [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Adds an e-mail subscriber with optional resource and update-type filters. POST https://app.upfour.io/v1/status-pages/{pageID}/subscribers scope status_page.subscriber.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Body `application/json` required 3 fields emailstring (required) All other fields2 fields resource_idsuuid[] Components of this page to follow; empty follows all. subscribed_tostring all (the default), incident or maintenance. allincidentmaintenance ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageSubscriber | | | StatusPageSubscriber · 15 fields FieldTypeDescription `channel` string one of`email` `webhook` `confirmed` boolean `created_at` string (date-time) `email` string `failure_count` integer `id` uuid `resource_ids` uuid[] · nullable `status_page_id` uuid `subscribed_to` string one of`all` `incident` `maintenance` `disabled_at` string (date-time) `last_delivery_at` string (date-time) `last_failure_at` string (date-time) `suppression` string one of`hard_bounce` `complaint` Why nothing is mailed to the address; empty when it is not suppressed. `suppression_liftable` boolean True for a hard bounce, which an owner or admin may release; a complaint stays with up4. `webhook_target` string The webhook URL in masked form; never the full URL or its secret. | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 402 | [billing_action_unavailable](https://upfour.io/docs/api/errors#code-billing_action_unavailable) | The Company's billing state does not allow this action. **When:** A metered or limited action while the billing state, such as paused or without credit, blocks it; details holds the action and billing_state. **Fix:** Top up the credit or change the plan, then retry. | | 429 | [confirmation_rate_limited](https://upfour.io/docs/api/errors#code-confirmation_rate_limited) | Subscriber confirmation emails are being sent faster than the limits allow. **When:** Adding subscribers faster than the confirmation limits allow; details.reason says which limit, and details.retry_at and Retry-After say when to try again. **Fix:** Wait until details.retry_at, then add the remaining addresses. `confirmation_rate_limited`: no further confirmation mail can go to this address right now. `error.details.reason` is `too_soon` (2 minutes between mails per page and address), `daily` (3 per 24 hours) or `open` (5 open confirmations per address); `error.details.retry_at` and the `Retry-After` header say when the same request can succeed. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 503 | [quota_gate_unavailable](https://upfour.io/docs/api/errors#code-quota_gate_unavailable) | The limit check itself failed, so the action was not carried out. **When:** The quota service could not decide while the request ran. **Fix:** Retry in a moment; nothing was changed. | | 403 / 429 | [quota_limit_reached](https://upfour.io/docs/api/errors#code-quota_limit_reached) | A usage or inventory limit of the Company was reached. **When:** Counters and concurrency limits answer 429 with Retry-After, inventory and capacity limits answer 403; details holds purpose, limit and window. **Fix:** On 429 wait Retry-After seconds; on 403 free something up, request a higher limit where details.raise_request is true, or buy an extension. | | 422 | [subscriber_confirmations_full](https://upfour.io/docs/api/errors#code-subscriber_confirmations_full) | Too many addresses are waiting for confirmation. **When:** An import or sign-up that would exceed the number of unconfirmed addresses allowed; nothing was added, details gives new_addresses and free_places. **Fix:** Wait for confirmations or remove unconfirmed addresses, then add fewer addresses. | | 402 | [subscriber_import_requires_payment](https://upfour.io/docs/api/errors#code-subscriber_import_requires_payment) | Subscriber import is locked while the Company is on its starting credit. **When:** Importing subscribers before the first payment. **Fix:** Top up the credit, then import. | | 409 | [subscriber_import_running](https://upfour.io/docs/api/errors#code-subscriber_import_running) | Another subscriber import of this Company is still running. **When:** Starting an import while a previous one has not finished. **Fix:** Wait for the running import to finish, then start the next. | | 413 | [subscriber_import_too_large](https://upfour.io/docs/api/errors#code-subscriber_import_too_large) | The subscriber file is over the size limit. **When:** Uploading a subscriber file larger than details.max_bytes. **Fix:** Split the file into smaller ones. | | 422 | [subscriber_import_too_many_rows](https://upfour.io/docs/api/errors#code-subscriber_import_too_many_rows) | The subscriber import has too many rows. **When:** An import with more rows than details.max_rows; nothing was imported. **Fix:** Split the import into several files. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList status page subscribers](https://upfour.io/docs/api/status-pages/list-status-page-subscribers.md) [NextCount status page subscribers](https://upfour.io/docs/api/status-pages/count-status-page-subscribers.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/subscribers" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "email": "carla@example.com", "subscribed_to": "all" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/subscribers", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ email: "carla@example.com", subscribed_to: "all", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/subscribers", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "email": "carla@example.com", "subscribed_to": "all", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "email": "carla@example.com", "subscribed_to": "all" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/subscribers", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "channel": "email", "confirmed": false, "created_at": "2026-10-27T09:00:00Z", "email": "carla@example.com", "failure_count": 0, "id": "00000000-0000-4000-8000-000000000002", "resource_ids": null, "status_page_id": "00000000-0000-4000-8000-000000000001", "subscribed_to": "all" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "billing_action_unavailable", "message": "The Company's billing state does not allow this action.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` ### 409 ```json { "error": { "code": "subscriber_import_running", "message": "Another subscriber import of this Company is still running.", "request_id": "req_…" } } ``` ### 413 ```json { "error": { "code": "subscriber_import_too_large", "message": "The subscriber file is over the size limit.", "request_id": "req_…" } } ``` ### 422 ```json { "error": { "code": "subscriber_confirmations_full", "message": "Too many addresses are waiting for confirmation.", "request_id": "req_…" } } ``` ### 429 ```json { "error": { "code": "confirmation_rate_limited", "message": "Subscriber confirmation emails are being sent faster than the limits allow.", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "quota_gate_unavailable", "message": "The limit check itself failed, so the action was not carried out.", "request_id": "req_…" } } ``` --- # Count status page subscribers — API reference > Returns subscriber counts for e-mail and webhook channels. Source: https://upfour.io/docs/api/status-pages/count-status-page-subscribers [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Returns subscriber counts for e-mail and webhook channels. GET https://app.upfour.io/v1/status-pages/{pageID}/subscribers/count scope status_page.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | SubscriberCounts | | | SubscriberCounts · 2 fields FieldTypeDescription `email` integer `webhook` integer | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousAdd a status page subscriber](https://upfour.io/docs/api/status-pages/add-a-status-page-subscriber.md) [NextResend pending subscriber messages](https://upfour.io/docs/api/status-pages/resend-pending-subscriber-messages.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/subscribers/count" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/subscribers/count", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/subscribers/count", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/subscribers/count", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "email": 0, "webhook": 0 } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Resend pending subscriber messages — API reference > Retries pending subscriber deliveries and reports queued and skipped counts. Source: https://upfour.io/docs/api/status-pages/resend-pending-subscriber-messages [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Retries pending subscriber deliveries and reports queued and skipped counts. POST https://app.upfour.io/v1/status-pages/{pageID}/subscribers/resend-pending scope status_page.subscriber.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | PendingResendResult | | | PendingResendResult · 2 fields FieldTypeDescription `queued` integer `skipped` integer | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousCount status page subscribers](https://upfour.io/docs/api/status-pages/count-status-page-subscribers.md) [NextImport status page subscribers](https://upfour.io/docs/api/status-pages/import-status-page-subscribers.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/subscribers/resend-pending" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/subscribers/resend-pending", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/subscribers/resend-pending", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/subscribers/resend-pending", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "queued": 0, "skipped": 1 } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Import status page subscribers — API reference > Imports subscriber e-mail addresses from text or a multipart file and reports imported, invited and skipped counts. Source: https://upfour.io/docs/api/status-pages/import-status-page-subscribers [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Imports subscriber e-mail addresses from text or a multipart file and reports imported, invited and skipped counts. POST https://app.upfour.io/v1/status-pages/{pageID}/subscribers/upload scope status_page.subscriber.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Body `application/json` required 1 field emailsstring (required) The addresses as text ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageSubscriberUploadResponse | | | StatusPageSubscriberUploadResponse · 3 fields FieldTypeDescription `imported` integer Same number as invited, kept for older API clients. `invited` integer `skipped` integer Invalid or duplicate addresses and addresses that unsubscribed before. | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 402 | [billing_action_unavailable](https://upfour.io/docs/api/errors#code-billing_action_unavailable) | The Company's billing state does not allow this action. **When:** A metered or limited action while the billing state, such as paused or without credit, blocks it; details holds the action and billing_state. **Fix:** Top up the credit or change the plan, then retry. | | 429 | [confirmation_rate_limited](https://upfour.io/docs/api/errors#code-confirmation_rate_limited) | Subscriber confirmation emails are being sent faster than the limits allow. **When:** Adding subscribers faster than the confirmation limits allow; details.reason says which limit, and details.retry_at and Retry-After say when to try again. **Fix:** Wait until details.retry_at, then add the remaining addresses. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 503 | [quota_gate_unavailable](https://upfour.io/docs/api/errors#code-quota_gate_unavailable) | The limit check itself failed, so the action was not carried out. **When:** The quota service could not decide while the request ran. **Fix:** Retry in a moment; nothing was changed. | | 403 / 429 | [quota_limit_reached](https://upfour.io/docs/api/errors#code-quota_limit_reached) | A usage or inventory limit of the Company was reached. **When:** Counters and concurrency limits answer 429 with Retry-After, inventory and capacity limits answer 403; details holds purpose, limit and window. **Fix:** On 429 wait Retry-After seconds; on 403 free something up, request a higher limit where details.raise_request is true, or buy an extension. | | 200 / 410 / 413 / 422 | [request_failed](https://upfour.io/docs/api/errors#code-request_failed) | The request could not be completed. The list is larger than 2 MiB (`error.details.max_bytes`). The code is `subscriber_import_too_large`, or `request_failed` when the upload is cut off in transit. Nothing is imported. | | 422 | [subscriber_confirmations_full](https://upfour.io/docs/api/errors#code-subscriber_confirmations_full) | Too many addresses are waiting for confirmation. **When:** An import or sign-up that would exceed the number of unconfirmed addresses allowed; nothing was added, details gives new_addresses and free_places. **Fix:** Wait for confirmations or remove unconfirmed addresses, then add fewer addresses. | | 402 | [subscriber_import_requires_payment](https://upfour.io/docs/api/errors#code-subscriber_import_requires_payment) | Subscriber import is locked while the Company is on its starting credit. **When:** Importing subscribers before the first payment. **Fix:** Top up the credit, then import. `subscriber_import_requires_payment`: importing subscriber lists is locked while the Company is on its starting credit. | | 409 | [subscriber_import_running](https://upfour.io/docs/api/errors#code-subscriber_import_running) | Another subscriber import of this Company is still running. **When:** Starting an import while a previous one has not finished. **Fix:** Wait for the running import to finish, then start the next. `subscriber_import_running`: another subscriber import of this Company is still running; retry when it has finished. | | 413 | [subscriber_import_too_large](https://upfour.io/docs/api/errors#code-subscriber_import_too_large) | The subscriber file is over the size limit. **When:** Uploading a subscriber file larger than details.max_bytes. **Fix:** Split the file into smaller ones. | | 422 | [subscriber_import_too_many_rows](https://upfour.io/docs/api/errors#code-subscriber_import_too_many_rows) | The subscriber import has too many rows. **When:** An import with more rows than details.max_rows; nothing was imported. **Fix:** Split the import into several files. `subscriber_import_too_many_rows`: the list has more than 10,000 rows (`error.details.max_rows`). Nothing is imported. | | 400 | [upload_invalid](https://upfour.io/docs/api/errors#code-upload_invalid) | The upload could not be read as a multipart form. **When:** A body that is not a valid multipart form, or whose file cannot be parsed. **Fix:** Send a well-formed multipart/form-data request. Stable codes next to the English message: `upload_no_file` (the form has no `file` part), `upload_invalid` (the upload could not be read). | | 400 | [upload_no_file](https://upfour.io/docs/api/errors#code-upload_no_file) | The multipart request has no file part. **When:** An upload request without a file field. **Fix:** Send the file as multipart/form-data in the field the operation names. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousResend pending subscriber messages](https://upfour.io/docs/api/status-pages/resend-pending-subscriber-messages.md) [NextDelete a status page subscriber](https://upfour.io/docs/api/status-pages/delete-a-status-page-subscriber.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/subscribers/upload" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "emails": "anna@example.com" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/subscribers/upload", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ emails: "anna@example.com", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/subscribers/upload", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "emails": "anna@example.com", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "emails": "anna@example.com" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/subscribers/upload", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "imported": 2, "invited": 2, "skipped": 0 } } ``` ### 200 ```json { "error": { "code": "request_failed", "message": "The request could not be completed.", "request_id": "req_…" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "billing_action_unavailable", "message": "The Company's billing state does not allow this action.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` ### 409 ```json { "error": { "code": "subscriber_import_running", "message": "Another subscriber import of this Company is still running.", "request_id": "req_…" } } ``` ### 410 ```json { "error": { "code": "request_failed", "message": "The request could not be completed.", "request_id": "req_…" } } ``` ### 413 ```json { "error": { "code": "request_failed", "message": "The request could not be completed.", "request_id": "req_…" } } ``` ### 422 ```json { "error": { "code": "request_failed", "message": "The request could not be completed.", "request_id": "req_…" } } ``` ### 429 ```json { "error": { "code": "confirmation_rate_limited", "message": "Subscriber confirmation emails are being sent faster than the limits allow.", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "quota_gate_unavailable", "message": "The limit check itself failed, so the action was not carried out.", "request_id": "req_…" } } ``` --- # Delete a status page subscriber — API reference > Removes a subscriber from the status page and returns status 204 without a response body. Source: https://upfour.io/docs/api/status-pages/delete-a-status-page-subscriber [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Removes a subscriber from the status page and returns status 204 without a response body. DELETE https://app.upfour.io/v1/status-pages/{pageID}/subscribers/{subscriberID} scope status_page.subscriber.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | | `subscriberID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousImport status page subscribers](https://upfour.io/docs/api/status-pages/import-status-page-subscribers.md) [NextResend subscriber confirmation](https://upfour.io/docs/api/status-pages/resend-subscriber-confirmation.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/subscribers/00000000-0000-4000-8000-000000000002" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/subscribers/00000000-0000-4000-8000-000000000002", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/subscribers/00000000-0000-4000-8000-000000000002", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/subscribers/00000000-0000-4000-8000-000000000002", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Resend subscriber confirmation — API reference > Resends a confirmation message to the selected subscriber. Source: https://upfour.io/docs/api/status-pages/resend-subscriber-confirmation [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Resends a confirmation message to the selected subscriber. POST https://app.upfour.io/v1/status-pages/{pageID}/subscribers/{subscriberID}/resend scope status_page.subscriber.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | | `subscriberID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageMessageResponse | | | StatusPageMessageResponse · 1 fields FieldTypeDescription `message` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 402 | [billing_action_unavailable](https://upfour.io/docs/api/errors#code-billing_action_unavailable) | The Company's billing state does not allow this action. **When:** A metered or limited action while the billing state, such as paused or without credit, blocks it; details holds the action and billing_state. **Fix:** Top up the credit or change the plan, then retry. | | 429 | [confirmation_rate_limited](https://upfour.io/docs/api/errors#code-confirmation_rate_limited) | Subscriber confirmation emails are being sent faster than the limits allow. **When:** Adding subscribers faster than the confirmation limits allow; details.reason says which limit, and details.retry_at and Retry-After say when to try again. **Fix:** Wait until details.retry_at, then add the remaining addresses. `confirmation_rate_limited`: no further confirmation mail can go to this address right now. `error.details.reason` is `too_soon` (2 minutes between mails per page and address), `daily` (3 per 24 hours) or `open` (5 open confirmations per address); `error.details.retry_at` and the `Retry-After` header say when the same request can succeed. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 503 | [quota_gate_unavailable](https://upfour.io/docs/api/errors#code-quota_gate_unavailable) | The limit check itself failed, so the action was not carried out. **When:** The quota service could not decide while the request ran. **Fix:** Retry in a moment; nothing was changed. | | 403 / 429 | [quota_limit_reached](https://upfour.io/docs/api/errors#code-quota_limit_reached) | A usage or inventory limit of the Company was reached. **When:** Counters and concurrency limits answer 429 with Retry-After, inventory and capacity limits answer 403; details holds purpose, limit and window. **Fix:** On 429 wait Retry-After seconds; on 403 free something up, request a higher limit where details.raise_request is true, or buy an extension. | | 422 | [subscriber_confirmations_full](https://upfour.io/docs/api/errors#code-subscriber_confirmations_full) | Too many addresses are waiting for confirmation. **When:** An import or sign-up that would exceed the number of unconfirmed addresses allowed; nothing was added, details gives new_addresses and free_places. **Fix:** Wait for confirmations or remove unconfirmed addresses, then add fewer addresses. | | 402 | [subscriber_import_requires_payment](https://upfour.io/docs/api/errors#code-subscriber_import_requires_payment) | Subscriber import is locked while the Company is on its starting credit. **When:** Importing subscribers before the first payment. **Fix:** Top up the credit, then import. | | 409 | [subscriber_import_running](https://upfour.io/docs/api/errors#code-subscriber_import_running) | Another subscriber import of this Company is still running. **When:** Starting an import while a previous one has not finished. **Fix:** Wait for the running import to finish, then start the next. | | 413 | [subscriber_import_too_large](https://upfour.io/docs/api/errors#code-subscriber_import_too_large) | The subscriber file is over the size limit. **When:** Uploading a subscriber file larger than details.max_bytes. **Fix:** Split the file into smaller ones. | | 422 | [subscriber_import_too_many_rows](https://upfour.io/docs/api/errors#code-subscriber_import_too_many_rows) | The subscriber import has too many rows. **When:** An import with more rows than details.max_rows; nothing was imported. **Fix:** Split the import into several files. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousDelete a status page subscriber](https://upfour.io/docs/api/status-pages/delete-a-status-page-subscriber.md) [NextList status page translations](https://upfour.io/docs/api/status-pages/list-status-page-translations.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/subscribers/00000000-0000-4000-8000-000000000002/resend" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/subscribers/00000000-0000-4000-8000-000000000002/resend", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/subscribers/00000000-0000-4000-8000-000000000002/resend", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/subscribers/00000000-0000-4000-8000-000000000002/resend", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "message": "Confirmation email resent" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "billing_action_unavailable", "message": "The Company's billing state does not allow this action.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` ### 409 ```json { "error": { "code": "subscriber_import_running", "message": "Another subscriber import of this Company is still running.", "request_id": "req_…" } } ``` ### 413 ```json { "error": { "code": "subscriber_import_too_large", "message": "The subscriber file is over the size limit.", "request_id": "req_…" } } ``` ### 422 ```json { "error": { "code": "subscriber_confirmations_full", "message": "Too many addresses are waiting for confirmation.", "request_id": "req_…" } } ``` ### 429 ```json { "error": { "code": "confirmation_rate_limited", "message": "Subscriber confirmation emails are being sent faster than the limits allow.", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "quota_gate_unavailable", "message": "The limit check itself failed, so the action was not carried out.", "request_id": "req_…" } } ``` --- # List status page translations — API reference > Returns each configured language and its translated texts. Source: https://upfour.io/docs/api/status-pages/list-status-page-translations [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Returns each configured language and its translated texts. GET https://app.upfour.io/v1/status-pages/{pageID}/translations scope status_page.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageTranslation[] · nullable | | | Each item · 7 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `is_published` boolean `language` string `status_page_id` uuid `translations` map of string · nullable Translated texts by key. `updated_at` string (date-time) | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousResend subscriber confirmation](https://upfour.io/docs/api/status-pages/resend-subscriber-confirmation.md) [NextCreate a status page translation](https://upfour.io/docs/api/status-pages/create-a-status-page-translation.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/translations" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/translations", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/translations", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/translations", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000002", "is_published": false, "language": "de", "status_page_id": "00000000-0000-4000-8000-000000000001", "translations": { "announcement": "Alle Systeme werden von up4 überwacht." }, "updated_at": "2026-10-27T09:00:00Z" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Create a status page translation — API reference > Creates a language translation with translated texts keyed by page content. Source: https://upfour.io/docs/api/status-pages/create-a-status-page-translation [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Creates a language translation with translated texts keyed by page content. POST https://app.upfour.io/v1/status-pages/{pageID}/translations scope status_page.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Body `application/json` required 2 fields languagestring (required) Language code or name, e.g. de or German; one translation per language. All other fields1 fields translationsmap of string Translated texts by key. ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageTranslation | | | StatusPageTranslation · 7 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `is_published` boolean `language` string `status_page_id` uuid `translations` map of string · nullable Translated texts by key. `updated_at` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList status page translations](https://upfour.io/docs/api/status-pages/list-status-page-translations.md) [NextDelete a status page translation](https://upfour.io/docs/api/status-pages/delete-a-status-page-translation.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/translations" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "language": "de", "translations": { "announcement": "Alle Systeme werden von up4 überwacht." } }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/translations", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ language: "de", translations: { announcement: "Alle Systeme werden von up4 überwacht.", }, }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/translations", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "language": "de", "translations": { "announcement": "Alle Systeme werden von up4 überwacht.", }, }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "language": "de", "translations": { "announcement": "Alle Systeme werden von up4 überwacht." } }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/translations", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000002", "is_published": false, "language": "de", "status_page_id": "00000000-0000-4000-8000-000000000001", "translations": { "announcement": "Alle Systeme werden von up4 überwacht." }, "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Delete a status page translation — API reference > Deletes a status-page translation and returns a confirmation message. Source: https://upfour.io/docs/api/status-pages/delete-a-status-page-translation [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Deletes a status-page translation and returns a confirmation message. DELETE https://app.upfour.io/v1/status-pages/{pageID}/translations/{translationID} scope status_page.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | | `translationID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageMessageResponse | | | StatusPageMessageResponse · 1 fields FieldTypeDescription `message` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousCreate a status page translation](https://upfour.io/docs/api/status-pages/create-a-status-page-translation.md) [NextUpdate a status page translation](https://upfour.io/docs/api/status-pages/update-a-status-page-translation.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/translations/00000000-0000-4000-8000-000000000002" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/translations/00000000-0000-4000-8000-000000000002", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/translations/00000000-0000-4000-8000-000000000002", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/translations/00000000-0000-4000-8000-000000000002", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "message": "Translation deleted" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Update a status page translation — API reference > Replaces the translated texts or publication state for a language. Source: https://upfour.io/docs/api/status-pages/update-a-status-page-translation [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Replaces the translated texts or publication state for a language. PUT https://app.upfour.io/v1/status-pages/{pageID}/translations/{translationID} scope status_page.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | | `translationID` required | uuid | path parameter | ## Body `application/json` required 2 fields All other fields2 fields is_publishedboolean · nullable translationsmap of string · nullable Replaces all translated texts of the language, not a merge. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageTranslation | | | StatusPageTranslation · 7 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `is_published` boolean `language` string `status_page_id` uuid `translations` map of string · nullable Translated texts by key. `updated_at` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousDelete a status page translation](https://upfour.io/docs/api/status-pages/delete-a-status-page-translation.md) [NextList status page updates](https://upfour.io/docs/api/status-pages/list-status-page-updates.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/translations/00000000-0000-4000-8000-000000000002" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "is_published": true, "translations": { "announcement": "Alle Systeme werden von up4 überwacht." } }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/translations/00000000-0000-4000-8000-000000000002", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ is_published: true, translations: { announcement: "Alle Systeme werden von up4 überwacht.", }, }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/translations/00000000-0000-4000-8000-000000000002", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "is_published": True, "translations": { "announcement": "Alle Systeme werden von up4 überwacht.", }, }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "is_published": true, "translations": { "announcement": "Alle Systeme werden von up4 überwacht." } }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/translations/00000000-0000-4000-8000-000000000002", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000002", "is_published": true, "language": "de", "status_page_id": "00000000-0000-4000-8000-000000000001", "translations": { "announcement": "Alle Systeme werden von up4 überwacht." }, "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # List status page updates — API reference > Returns incidents, maintenance windows and reports configured as updates on a status page. Source: https://upfour.io/docs/api/status-pages/list-status-page-updates [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Returns incidents, maintenance windows and reports configured as updates on a status page. GET https://app.upfour.io/v1/status-pages/{pageID}/updates scope status_page.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageUpdate[] · nullable | | | Each item · 38 fields FieldTypeDescription `affected_monitor_ids` uuid[] · nullable Monitors behind the affected components. `affected_resource_ids` uuid[] · nullable `auto_end` boolean `auto_start` boolean `created_at` string (date-time) `ends_at` string (date-time) · nullable `entries` StatusPageUpdateEntry[] · nullable Each item · 9 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `message` string `notify_subscribers` boolean `published_at` string (date-time) `source` string one of`manual` `automatic` `status` string one of`investigating` `identified` `monitoring` `resolved` `scheduled` `in_progress` `completed` `cancelled` `published` `update_id` uuid `updated_at` string (date-time) `id` uuid `message` string Mirrors the latest entry. `monitor_mode` string one of`none` `pause_checks` `notify_subscribers` boolean `published_at` string (date-time) `recurrence` string one of`none` `weekly` `monthly` `recurrence_until` string (date) · nullable Last day of a recurring series (YYYY-MM-DD); null for no end. `reminder_minutes` integer · nullable `source` string one of`manual` `automatic` `starts_at` string (date-time) · nullable `status` string one of`investigating` `identified` `monitoring` `resolved` `scheduled` `in_progress` `completed` `cancelled` `published` Mirrors the latest entry. `status_page_id` uuid `timezone` string IANA time zone of a planned maintenance window; empty for other types. `title` string `update_type` string one of`incident` `maintenance` `report` `updated_at` string (date-time) `visibility` string one of`public` `draft` `closed_unmeasured` boolean Resolved, but the incident ended because monitoring resumed after a billing pause: nobody measured the recovery. `incident_group_id` uuid `incident_id` uuid `reminder_sent_at` string (date-time) `series_id` uuid | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpdate a status page translation](https://upfour.io/docs/api/status-pages/update-a-status-page-translation.md) [NextCreate a status page update](https://upfour.io/docs/api/status-pages/create-a-status-page-update.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "affected_monitor_ids": [], "affected_resource_ids": [], "auto_end": false, "auto_start": false, "created_at": "2026-10-27T09:00:00Z", "ends_at": null, "entries": [ { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000002", "message": "We are looking into slow responses on the checkout.", "notify_subscribers": false, "published_at": "2026-10-27T09:00:00Z", "source": "manual", "status": "investigating", "update_id": "00000000-0000-4000-8000-000000000003", "updated_at": "2026-10-27T09:00:00Z" } ], "id": "00000000-0000-4000-8000-000000000003", "message": "We are looking into slow responses on the checkout.", "monitor_mode": "none", "notify_subscribers": false, "published_at": "2026-10-27T09:00:00Z", "recurrence": "none", "recurrence_until": null, "reminder_minutes": null, "source": "manual", "starts_at": null, "status": "investigating", "status_page_id": "00000000-0000-4000-8000-000000000001", "timezone": "", "title": "Checkout is slow", "update_type": "incident", "updated_at": "2026-10-27T09:00:00Z", "visibility": "public" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Create a status page update — API reference > Creates an incident, maintenance window or report with a title, status, timing, affected resources and optional notifications. Source: https://upfour.io/docs/api/status-pages/create-a-status-page-update [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Creates an incident, maintenance window or report with a title, status, timing, affected resources and optional notifications. POST https://app.upfour.io/v1/status-pages/{pageID}/updates scope status_page.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | ## Body The 5 fields most calls use come first; the rest are grouped below. `application/json` required 18 fields titlestring (required) In the app: **Title** messagestring statusstring Valid values depend on update_type: investigating, identified, monitoring or resolved for incidents; scheduled, in_progress, completed or cancelled for maintenance; published for reports. Defaults to the first of those. investigatingidentifiedmonitoringresolvedscheduledin_progresscompletedcancelledpublished update_typestring incident (the default), maintenance or report. Ignored by POST /status-pages/{pageID}/reports, which always creates a report. The service does not validate it; any other value behaves like incident. incidentmaintenancereport visibilitystring public (the default) or draft for a thread still being prepared. publicdraft Affected services3 fields affected_monitor_idsuuid[] Monitors the update affects; read only when affected_resource_ids is empty. affected_resource_idsuuid[] Components (not monitors) the update affects. Wins over affected_monitor_ids when both are given. In the app: **Affected components** monitor_modestring · nullable pause_checks pauses the affected monitors for the window and needs starts_at and ends_at. Only for maintenance. nonepause_checks In the app: **During the window** Timing6 fields starts_atstring (date-time) · nullable In the app: **Start date** ends_atstring (date-time) · nullable In the app: **End date** timezonestring · nullable IANA time zone of the planned window. Only for maintenance. recurrencestring · nullable Needs starts_at and ends_at. Only for maintenance. noneweeklymonthly In the app: **Repeat** recurrence_untilstring (date) · nullable Last day of the series (YYYY-MM-DD); empty means no end. reminder_minutesinteger · nullable 5 to 20160 minutes before the window starts; 0 switches the reminder off. Needs starts_at. Only for maintenance. Automation4 fields auto_startboolean auto_endboolean notify_subscribersboolean In the app: **Notify subscribers** published_atstring (date-time) · nullable Backdates the display time of an incident or report; not in the future. Ignored for maintenance. ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageUpdate | | | StatusPageUpdate · 38 fields FieldTypeDescription `affected_monitor_ids` uuid[] · nullable Monitors behind the affected components. `affected_resource_ids` uuid[] · nullable `auto_end` boolean `auto_start` boolean `created_at` string (date-time) `ends_at` string (date-time) · nullable `entries` StatusPageUpdateEntry[] · nullable Each item · 9 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `message` string `notify_subscribers` boolean `published_at` string (date-time) `source` string one of`manual` `automatic` `status` string one of`investigating` `identified` `monitoring` `resolved` `scheduled` `in_progress` `completed` `cancelled` `published` `update_id` uuid `updated_at` string (date-time) `id` uuid `message` string Mirrors the latest entry. `monitor_mode` string one of`none` `pause_checks` `notify_subscribers` boolean `published_at` string (date-time) `recurrence` string one of`none` `weekly` `monthly` `recurrence_until` string (date) · nullable Last day of a recurring series (YYYY-MM-DD); null for no end. `reminder_minutes` integer · nullable `source` string one of`manual` `automatic` `starts_at` string (date-time) · nullable `status` string one of`investigating` `identified` `monitoring` `resolved` `scheduled` `in_progress` `completed` `cancelled` `published` Mirrors the latest entry. `status_page_id` uuid `timezone` string IANA time zone of a planned maintenance window; empty for other types. `title` string `update_type` string one of`incident` `maintenance` `report` `updated_at` string (date-time) `visibility` string one of`public` `draft` `closed_unmeasured` boolean Resolved, but the incident ended because monitoring resumed after a billing pause: nobody measured the recovery. `incident_group_id` uuid `incident_id` uuid `reminder_sent_at` string (date-time) `series_id` uuid | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 409 | documented response | `idempotency_key_conflict`: the `Idempotency-Key` was already used with a different request body; `idempotency_replay_unavailable`: the stored response can no longer be replayed for this credential or resource, refresh the state instead of retrying with a new key. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList status page updates](https://upfour.io/docs/api/status-pages/list-status-page-updates.md) [NextDelete a status page update](https://upfour.io/docs/api/status-pages/delete-a-status-page-update.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "message": "We are looking into slow responses on the checkout.", "notify_subscribers": false, "status": "investigating", "title": "Checkout is slow", "update_type": "incident" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ message: "We are looking into slow responses on the checkout.", notify_subscribers: false, status: "investigating", title: "Checkout is slow", update_type: "incident", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "message": "We are looking into slow responses on the checkout.", "notify_subscribers": False, "status": "investigating", "title": "Checkout is slow", "update_type": "incident", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "message": "We are looking into slow responses on the checkout.", "notify_subscribers": false, "status": "investigating", "title": "Checkout is slow", "update_type": "incident" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "affected_monitor_ids": [], "affected_resource_ids": [], "auto_end": false, "auto_start": false, "created_at": "2026-10-27T09:00:00Z", "ends_at": null, "entries": [ { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000002", "message": "We are looking into slow responses on the checkout.", "notify_subscribers": false, "published_at": "2026-10-27T09:00:00Z", "source": "manual", "status": "investigating", "update_id": "00000000-0000-4000-8000-000000000003", "updated_at": "2026-10-27T09:00:00Z" } ], "id": "00000000-0000-4000-8000-000000000003", "message": "We are looking into slow responses on the checkout.", "monitor_mode": "none", "notify_subscribers": false, "published_at": "2026-10-27T09:00:00Z", "recurrence": "none", "recurrence_until": null, "reminder_minutes": null, "source": "manual", "starts_at": null, "status": "investigating", "status_page_id": "00000000-0000-4000-8000-000000000001", "timezone": "", "title": "Checkout is slow", "update_type": "incident", "updated_at": "2026-10-27T09:00:00Z", "visibility": "public" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Delete a status page update — API reference > Deletes a status-page update and returns status 204 without a response body. Source: https://upfour.io/docs/api/status-pages/delete-a-status-page-update [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Deletes a status-page update and returns status 204 without a response body. DELETE https://app.upfour.io/v1/status-pages/{pageID}/updates/{updateID} scope status_page.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | | `updateID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 409 | [conflict](https://upfour.io/docs/api/errors#code-conflict) | The request conflicts with the current state. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousCreate a status page update](https://upfour.io/docs/api/status-pages/create-a-status-page-update.md) [NextGet a status page update](https://upfour.io/docs/api/status-pages/get-a-status-page-update.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates/00000000-0000-4000-8000-000000000002" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates/00000000-0000-4000-8000-000000000002", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates/00000000-0000-4000-8000-000000000002", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates/00000000-0000-4000-8000-000000000002", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` ### 409 ```json { "error": { "code": "conflict", "message": "The request conflicts with the current state.", "request_id": "req_…" } } ``` --- # Get a status page update — API reference > Returns one status-page update with its entries, timing, status, affected resources and delivery settings. Source: https://upfour.io/docs/api/status-pages/get-a-status-page-update [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Returns one status-page update with its entries, timing, status, affected resources and delivery settings. GET https://app.upfour.io/v1/status-pages/{pageID}/updates/{updateID} scope status_page.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | | `updateID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageUpdate | | | StatusPageUpdate · 38 fields FieldTypeDescription `affected_monitor_ids` uuid[] · nullable Monitors behind the affected components. `affected_resource_ids` uuid[] · nullable `auto_end` boolean `auto_start` boolean `created_at` string (date-time) `ends_at` string (date-time) · nullable `entries` StatusPageUpdateEntry[] · nullable Each item · 9 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `message` string `notify_subscribers` boolean `published_at` string (date-time) `source` string one of`manual` `automatic` `status` string one of`investigating` `identified` `monitoring` `resolved` `scheduled` `in_progress` `completed` `cancelled` `published` `update_id` uuid `updated_at` string (date-time) `id` uuid `message` string Mirrors the latest entry. `monitor_mode` string one of`none` `pause_checks` `notify_subscribers` boolean `published_at` string (date-time) `recurrence` string one of`none` `weekly` `monthly` `recurrence_until` string (date) · nullable Last day of a recurring series (YYYY-MM-DD); null for no end. `reminder_minutes` integer · nullable `source` string one of`manual` `automatic` `starts_at` string (date-time) · nullable `status` string one of`investigating` `identified` `monitoring` `resolved` `scheduled` `in_progress` `completed` `cancelled` `published` Mirrors the latest entry. `status_page_id` uuid `timezone` string IANA time zone of a planned maintenance window; empty for other types. `title` string `update_type` string one of`incident` `maintenance` `report` `updated_at` string (date-time) `visibility` string one of`public` `draft` `closed_unmeasured` boolean Resolved, but the incident ended because monitoring resumed after a billing pause: nobody measured the recovery. `incident_group_id` uuid `incident_id` uuid `reminder_sent_at` string (date-time) `series_id` uuid | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousDelete a status page update](https://upfour.io/docs/api/status-pages/delete-a-status-page-update.md) [NextUpdate a status page update](https://upfour.io/docs/api/status-pages/update-a-status-page-update.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates/00000000-0000-4000-8000-000000000002" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates/00000000-0000-4000-8000-000000000002", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates/00000000-0000-4000-8000-000000000002", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates/00000000-0000-4000-8000-000000000002", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "affected_monitor_ids": [], "affected_resource_ids": [], "auto_end": false, "auto_start": false, "created_at": "2026-10-27T09:00:00Z", "ends_at": null, "entries": [ { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000003", "message": "We are looking into slow responses on the checkout.", "notify_subscribers": false, "published_at": "2026-10-27T09:00:00Z", "source": "manual", "status": "investigating", "update_id": "00000000-0000-4000-8000-000000000002", "updated_at": "2026-10-27T09:00:00Z" } ], "id": "00000000-0000-4000-8000-000000000002", "message": "We are looking into slow responses on the checkout.", "monitor_mode": "none", "notify_subscribers": false, "published_at": "2026-10-27T09:00:00Z", "recurrence": "none", "recurrence_until": null, "reminder_minutes": null, "source": "manual", "starts_at": null, "status": "investigating", "status_page_id": "00000000-0000-4000-8000-000000000001", "timezone": "", "title": "Checkout is slow", "update_type": "incident", "updated_at": "2026-10-27T09:00:00Z", "visibility": "public" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Update a status page update — API reference > Updates an existing status-page update's timing, recurrence, visibility, affected resources or publication behavior. Source: https://upfour.io/docs/api/status-pages/update-a-status-page-update [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Updates an existing status-page update's timing, recurrence, visibility, affected resources or publication behavior. PUT https://app.upfour.io/v1/status-pages/{pageID}/updates/{updateID} scope status_page.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | | `updateID` required | uuid | path parameter | ## Body The 3 fields most calls use come first; the rest are grouped below. `application/json` required 14 fields titlestring · nullable visibilitystring · nullable publicdraft notify_on_publishboolean When this request turns a draft public, sends its latest entry to subscribers. Affected services3 fields affected_monitor_idsuuid[] · nullable affected_resource_idsuuid[] · nullable monitor_modestring · nullable pause_checks pauses the affected monitors for the window and needs starts_at and ends_at. Only for maintenance. nonepause_checks Timing6 fields starts_atstring (date-time) · nullable Omitted leaves it unchanged; the zero time (0001-01-01T00:00:00Z) clears it. ends_atstring (date-time) · nullable Omitted leaves it unchanged; the zero time (0001-01-01T00:00:00Z) clears it. timezonestring · nullable IANA time zone of the planned window. Only for maintenance. recurrencestring · nullable Needs starts_at and ends_at. Only for maintenance. noneweeklymonthly recurrence_untilstring (date) · nullable Last day of the series (YYYY-MM-DD); empty means no end. reminder_minutesinteger · nullable 5 to 20160 minutes before the window starts; 0 switches the reminder off. Needs starts_at. Only for maintenance. Automation2 fields auto_startboolean · nullable auto_endboolean · nullable ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageUpdate | | | StatusPageUpdate · 38 fields FieldTypeDescription `affected_monitor_ids` uuid[] · nullable Monitors behind the affected components. `affected_resource_ids` uuid[] · nullable `auto_end` boolean `auto_start` boolean `created_at` string (date-time) `ends_at` string (date-time) · nullable `entries` StatusPageUpdateEntry[] · nullable Each item · 9 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `message` string `notify_subscribers` boolean `published_at` string (date-time) `source` string one of`manual` `automatic` `status` string one of`investigating` `identified` `monitoring` `resolved` `scheduled` `in_progress` `completed` `cancelled` `published` `update_id` uuid `updated_at` string (date-time) `id` uuid `message` string Mirrors the latest entry. `monitor_mode` string one of`none` `pause_checks` `notify_subscribers` boolean `published_at` string (date-time) `recurrence` string one of`none` `weekly` `monthly` `recurrence_until` string (date) · nullable Last day of a recurring series (YYYY-MM-DD); null for no end. `reminder_minutes` integer · nullable `source` string one of`manual` `automatic` `starts_at` string (date-time) · nullable `status` string one of`investigating` `identified` `monitoring` `resolved` `scheduled` `in_progress` `completed` `cancelled` `published` Mirrors the latest entry. `status_page_id` uuid `timezone` string IANA time zone of a planned maintenance window; empty for other types. `title` string `update_type` string one of`incident` `maintenance` `report` `updated_at` string (date-time) `visibility` string one of`public` `draft` `closed_unmeasured` boolean Resolved, but the incident ended because monitoring resumed after a billing pause: nobody measured the recovery. `incident_group_id` uuid `incident_id` uuid `reminder_sent_at` string (date-time) `series_id` uuid | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet a status page update](https://upfour.io/docs/api/status-pages/get-a-status-page-update.md) [NextList update deliveries](https://upfour.io/docs/api/status-pages/list-update-deliveries.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates/00000000-0000-4000-8000-000000000002" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Checkout is slow" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates/00000000-0000-4000-8000-000000000002", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ title: "Checkout is slow", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates/00000000-0000-4000-8000-000000000002", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "title": "Checkout is slow", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "title": "Checkout is slow" }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates/00000000-0000-4000-8000-000000000002", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "affected_monitor_ids": [], "affected_resource_ids": [], "auto_end": false, "auto_start": false, "created_at": "2026-10-27T09:00:00Z", "ends_at": null, "entries": [ { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000003", "message": "We are looking into slow responses on the checkout.", "notify_subscribers": false, "published_at": "2026-10-27T09:00:00Z", "source": "manual", "status": "investigating", "update_id": "00000000-0000-4000-8000-000000000002", "updated_at": "2026-10-27T09:00:00Z" } ], "id": "00000000-0000-4000-8000-000000000002", "message": "We are looking into slow responses on the checkout.", "monitor_mode": "none", "notify_subscribers": false, "published_at": "2026-10-27T09:00:00Z", "recurrence": "none", "recurrence_until": null, "reminder_minutes": null, "source": "manual", "starts_at": null, "status": "investigating", "status_page_id": "00000000-0000-4000-8000-000000000001", "timezone": "", "title": "Checkout is slow", "update_type": "incident", "updated_at": "2026-10-27T09:00:00Z", "visibility": "public" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # List update deliveries — API reference > Returns delivery counts and recent failures for a status-page update. Source: https://upfour.io/docs/api/status-pages/list-update-deliveries [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Returns delivery counts and recent failures for a status-page update. GET https://app.upfour.io/v1/status-pages/{pageID}/updates/{updateID}/deliveries scope status_page.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | | `updateID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | DeliverySummary | | | DeliverySummary · 9 fields FieldTypeDescription `address_suppressed` integer `billing_paused` integer `counts` map of integer · nullable `held` map of integer · nullable `recent_failures` DeliveryFailure[] · nullable Each item · 4 fields FieldTypeDescription `attempts` integer `kind` string `last_error` string `updated_at` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpdate a status page update](https://upfour.io/docs/api/status-pages/update-a-status-page-update.md) [NextAdd an update entry](https://upfour.io/docs/api/status-pages/add-an-update-entry.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates/00000000-0000-4000-8000-000000000002/deliveries" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates/00000000-0000-4000-8000-000000000002/deliveries", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates/00000000-0000-4000-8000-000000000002/deliveries", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates/00000000-0000-4000-8000-000000000002/deliveries", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "address_suppressed": 0, "billing_paused": 0, "counts": { "bundled": 0, "cancelled": 0, "expired": 0, "failed": 0, "pending": 0, "sent": 0, "unclear": 0 }, "held": { "address_suppressed": 0, "billing_paused": 0, "brake": 0, "broadcast_stop": 0, "complaint_throttle": 0, "fair_use_window": 0, "page_limit": 0, "quota_stage": 0 }, "recent_failures": [] } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Add an update entry — API reference > Adds an entry to a status-page update with a status, message and optional subscriber notification. Source: https://upfour.io/docs/api/status-pages/add-an-update-entry [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Adds an entry to a status-page update with a status, message and optional subscriber notification. POST https://app.upfour.io/v1/status-pages/{pageID}/updates/{updateID}/entries scope status_page.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | | `updateID` required | uuid | path parameter | ## Body `application/json` required 5 fields statusstring (required) investigatingidentifiedmonitoringresolvedscheduledin_progresscompletedcancelledpublished All other fields4 fields cancel_seriesboolean With a cancelled entry, also stops the recurring series instead of only skipping this window. messagestring notify_subscribersboolean published_atstring (date-time) · nullable Backdates the entry; not in the future. Ignored for maintenance. ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageUpdateEntry | | | StatusPageUpdateEntry · 9 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `message` string `notify_subscribers` boolean `published_at` string (date-time) `source` string one of`manual` `automatic` `status` string one of`investigating` `identified` `monitoring` `resolved` `scheduled` `in_progress` `completed` `cancelled` `published` `update_id` uuid `updated_at` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 409 | documented response | `idempotency_key_conflict`: the `Idempotency-Key` was already used with a different request body; `idempotency_replay_unavailable`: the stored response can no longer be replayed for this credential or resource, refresh the state instead of retrying with a new key. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList update deliveries](https://upfour.io/docs/api/status-pages/list-update-deliveries.md) [NextDelete an update entry](https://upfour.io/docs/api/status-pages/delete-an-update-entry.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates/00000000-0000-4000-8000-000000000002/entries" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "message": "The payment provider reports a partial outage.", "notify_subscribers": false, "status": "identified" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates/00000000-0000-4000-8000-000000000002/entries", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ message: "The payment provider reports a partial outage.", notify_subscribers: false, status: "identified", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates/00000000-0000-4000-8000-000000000002/entries", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "message": "The payment provider reports a partial outage.", "notify_subscribers": False, "status": "identified", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "message": "The payment provider reports a partial outage.", "notify_subscribers": false, "status": "identified" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates/00000000-0000-4000-8000-000000000002/entries", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000003", "message": "The payment provider reports a partial outage.", "notify_subscribers": false, "published_at": "2026-10-27T09:00:00Z", "source": "manual", "status": "identified", "update_id": "00000000-0000-4000-8000-000000000002", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Delete an update entry — API reference > Deletes an entry from a status-page update and returns status 204 without a response body. Source: https://upfour.io/docs/api/status-pages/delete-an-update-entry [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Deletes an entry from a status-page update and returns status 204 without a response body. DELETE https://app.upfour.io/v1/status-pages/{pageID}/updates/{updateID}/entries/{entryID} scope status_page.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | | `updateID` required | uuid | path parameter | | `entryID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousAdd an update entry](https://upfour.io/docs/api/status-pages/add-an-update-entry.md) [NextUpdate a status page entry](https://upfour.io/docs/api/status-pages/update-a-status-page-entry.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates/00000000-0000-4000-8000-000000000002/entries/00000000-0000-4000-8000-000000000003" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates/00000000-0000-4000-8000-000000000002/entries/00000000-0000-4000-8000-000000000003", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates/00000000-0000-4000-8000-000000000002/entries/00000000-0000-4000-8000-000000000003", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates/00000000-0000-4000-8000-000000000002/entries/00000000-0000-4000-8000-000000000003", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Update a status page entry — API reference > Updates an entry's message, publication time or status. Source: https://upfour.io/docs/api/status-pages/update-a-status-page-entry [Developers](https://upfour.io/docs/api.md)/[Status pages](https://upfour.io/docs/api/status-pages.md) Updates an entry's message, publication time or status. PUT https://app.upfour.io/v1/status-pages/{pageID}/updates/{updateID}/entries/{entryID} scope status_page.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `pageID` required | uuid | path parameter | | `updateID` required | uuid | path parameter | | `entryID` required | uuid | path parameter | ## Body `application/json` required 3 fields All other fields3 fields messagestring · nullable published_atstring (date-time) · nullable statusstring · nullable Valid values depend on the update's type, as for POST /status-pages/{pageID}/updates. investigatingidentifiedmonitoringresolvedscheduledin_progresscompletedcancelledpublished ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageUpdateEntry | | | StatusPageUpdateEntry · 9 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `message` string `notify_subscribers` boolean `published_at` string (date-time) `source` string one of`manual` `automatic` `status` string one of`investigating` `identified` `monitoring` `resolved` `scheduled` `in_progress` `completed` `cancelled` `published` `update_id` uuid `updated_at` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousDelete an update entry](https://upfour.io/docs/api/status-pages/delete-an-update-entry.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates/00000000-0000-4000-8000-000000000002/entries/00000000-0000-4000-8000-000000000003" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "message": "The payment provider confirmed a partial outage.", "status": "identified" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates/00000000-0000-4000-8000-000000000002/entries/00000000-0000-4000-8000-000000000003", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ message: "The payment provider confirmed a partial outage.", status: "identified", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates/00000000-0000-4000-8000-000000000002/entries/00000000-0000-4000-8000-000000000003", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "message": "The payment provider confirmed a partial outage.", "status": "identified", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "message": "The payment provider confirmed a partial outage.", "status": "identified" }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/status-pages/00000000-0000-4000-8000-000000000001/updates/00000000-0000-4000-8000-000000000002/entries/00000000-0000-4000-8000-000000000003", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000003", "message": "The payment provider confirmed a partial outage.", "notify_subscribers": false, "published_at": "2026-10-27T09:00:00Z", "source": "manual", "status": "identified", "update_id": "00000000-0000-4000-8000-000000000002", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Status page update templates — API reference > Saved wording for status page updates: title, status and message to reuse. REST endpoints with request and response fields, examples in curl, JavaScript, Python, Go and PHP, and an in-browser Try it console. Source: https://upfour.io/docs/api/status-page-templates [Developers](https://upfour.io/docs/api.md)/Pages & reporting Saved wording for status page updates: title, status and message to reuse. Base URL `https://app.upfour.io/v1`. Send your key as `Authorization: Bearer `; every key acts on your real Company, so [test without paging anyone](https://upfour.io/docs/api/testing.md). New here? Start with the [quickstart](https://upfour.io/docs/api/quickstart.md). The examples read the key from `UP4_API_KEY`. ## Everyday [GET **List status page update templates** /status-page-templates](https://upfour.io/docs/api/status-page-templates/list-status-page-update-templates.md)[POST **Create a status page update template** /status-page-templates](https://upfour.io/docs/api/status-page-templates/create-a-status-page-update-template.md)[PUT **Update a status page update template** /status-page-templates/{templateID}](https://upfour.io/docs/api/status-page-templates/update-a-status-page-update-template.md) ## Control [DEL **Delete a status page update template** /status-page-templates/{templateID}](https://upfour.io/docs/api/status-page-templates/delete-a-status-page-update-template.md) --- # List status page update templates — API reference > Lists status page update templates and returns pagination metadata. Source: https://upfour.io/docs/api/status-page-templates/list-status-page-update-templates [Developers](https://upfour.io/docs/api.md)/[Status page update templates](https://upfour.io/docs/api/status-page-templates.md) Lists status page update templates and returns pagination metadata. GET https://app.upfour.io/v1/status-page-templates scope status_page.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `limit` optional | integer 1–250 default `50` | query parameter | | `cursor` optional | string | query parameter | | `client_id` optional | uuid | query parameter | | `all_clients` optional | boolean | query parameter | | `q` optional | string | query parameter | | `sort` optional | string | query parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageUpdateTemplate[] · nullable | | | Each item · 10 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `message` string `name` string `notify_subscribers` boolean `organization_id` uuid `status` string Empty when the template sets no status. `title` string `update_type` string one of`incident` `maintenance` `report` `updated_at` string (date-time) | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [NextCreate a status page update template](https://upfour.io/docs/api/status-page-templates/create-a-status-page-update-template.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/status-page-templates" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-page-templates", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/status-page-templates", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/status-page-templates", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000001", "message": "We are looking into reports of slow or failing requests.", "name": "Investigating", "notify_subscribers": false, "organization_id": "00000000-0000-4000-8000-000000000002", "status": "investigating", "title": "We are investigating an issue", "update_type": "incident", "updated_at": "2026-10-27T09:00:00Z" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` --- # Create a status page update template — API reference > Creates a status page update template from a name and optional message, title, status, and notification settings. Source: https://upfour.io/docs/api/status-page-templates/create-a-status-page-update-template [Developers](https://upfour.io/docs/api.md)/[Status page update templates](https://upfour.io/docs/api/status-page-templates.md) Creates a status page update template from a name and optional message, title, status, and notification settings. POST https://app.upfour.io/v1/status-page-templates scope status_page.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Body `application/json` required 6 fields namestring (required) Unique within the company. All other fields5 fields messagestring notify_subscribersboolean · nullable Defaults to true. statusstring Status the template sets. For incident: investigating, identified, monitoring or resolved; for maintenance: scheduled, in_progress, completed or cancelled; for report: published. Empty sets none. titlestring update_typestring Defaults to incident. It cannot be changed afterwards. incidentmaintenancereport ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageUpdateTemplate | | | StatusPageUpdateTemplate · 10 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `message` string `name` string `notify_subscribers` boolean `organization_id` uuid `status` string Empty when the template sets no status. `title` string `update_type` string one of`incident` `maintenance` `report` `updated_at` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList status page update templates](https://upfour.io/docs/api/status-page-templates/list-status-page-update-templates.md) [NextUpdate a status page update template](https://upfour.io/docs/api/status-page-templates/update-a-status-page-update-template.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/status-page-templates" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "message": "We are looking into reports of slow or failing requests.", "name": "Investigating", "notify_subscribers": false, "status": "investigating", "title": "We are investigating an issue", "update_type": "incident" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-page-templates", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ message: "We are looking into reports of slow or failing requests.", name: "Investigating", notify_subscribers: false, status: "investigating", title: "We are investigating an issue", update_type: "incident", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/status-page-templates", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "message": "We are looking into reports of slow or failing requests.", "name": "Investigating", "notify_subscribers": False, "status": "investigating", "title": "We are investigating an issue", "update_type": "incident", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "message": "We are looking into reports of slow or failing requests.", "name": "Investigating", "notify_subscribers": false, "status": "investigating", "title": "We are investigating an issue", "update_type": "incident" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/status-page-templates", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000001", "message": "We are looking into reports of slow or failing requests.", "name": "Investigating", "notify_subscribers": false, "organization_id": "00000000-0000-4000-8000-000000000002", "status": "investigating", "title": "We are investigating an issue", "update_type": "incident", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Update a status page update template — API reference > Updates selected message, name, notification, status, or title fields. Source: https://upfour.io/docs/api/status-page-templates/update-a-status-page-update-template [Developers](https://upfour.io/docs/api.md)/[Status page update templates](https://upfour.io/docs/api/status-page-templates.md) Updates selected message, name, notification, status, or title fields. PUT https://app.upfour.io/v1/status-page-templates/{templateID} scope status_page.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `templateID` required | uuid | path parameter | ## Body `application/json` required 5 fields All other fields5 fields messagestring · nullable namestring · nullable Unique within the company. notify_subscribersboolean · nullable statusstring · nullable Must fit the template's update_type, see the create request. Empty removes the status. titlestring · nullable Fields left out keep their value. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageUpdateTemplate | | | StatusPageUpdateTemplate · 10 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `message` string `name` string `notify_subscribers` boolean `organization_id` uuid `status` string Empty when the template sets no status. `title` string `update_type` string one of`incident` `maintenance` `report` `updated_at` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousCreate a status page update template](https://upfour.io/docs/api/status-page-templates/create-a-status-page-update-template.md) [NextDelete a status page update template](https://upfour.io/docs/api/status-page-templates/delete-a-status-page-update-template.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/status-page-templates/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Investigating an issue" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-page-templates/00000000-0000-4000-8000-000000000001", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "Investigating an issue", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/status-page-templates/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "name": "Investigating an issue", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "name": "Investigating an issue" }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/status-page-templates/00000000-0000-4000-8000-000000000001", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000001", "message": "We are looking into reports of slow or failing requests.", "name": "Investigating an issue", "notify_subscribers": false, "organization_id": "00000000-0000-4000-8000-000000000002", "status": "investigating", "title": "We are investigating an issue", "update_type": "incident", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Delete a status page update template — API reference > Returns an empty 204 response after deleting the status page update template. Source: https://upfour.io/docs/api/status-page-templates/delete-a-status-page-update-template [Developers](https://upfour.io/docs/api.md)/[Status page update templates](https://upfour.io/docs/api/status-page-templates.md) Returns an empty 204 response after deleting the status page update template. DELETE https://app.upfour.io/v1/status-page-templates/{templateID} scope status_page.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `templateID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpdate a status page update template](https://upfour.io/docs/api/status-page-templates/update-a-status-page-update-template.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/status-page-templates/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-page-templates/00000000-0000-4000-8000-000000000001", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/status-page-templates/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/status-page-templates/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Status page design templates — API reference > Reusable status page designs: save the look of a page once and apply it to other pages. REST endpoints with request and response fields, examples in curl, JavaScript, Python, Go and PHP, and an in-browser Try it console. Source: https://upfour.io/docs/api/status-page-design-templates [Developers](https://upfour.io/docs/api.md)/Pages & reporting Reusable status page designs: save the look of a page once and apply it to other pages. Base URL `https://app.upfour.io/v1`. Send your key as `Authorization: Bearer `; every key acts on your real Company, so [test without paging anyone](https://upfour.io/docs/api/testing.md). New here? Start with the [quickstart](https://upfour.io/docs/api/quickstart.md). The examples read the key from `UP4_API_KEY`. ## Everyday [GET **List status page design templates** /status-page-design-templates](https://upfour.io/docs/api/status-page-design-templates/list-status-page-design-templates.md)[POST **Create a status page design template** /status-page-design-templates](https://upfour.io/docs/api/status-page-design-templates/create-a-status-page-design-template.md)[GET **Get a status page design template** /status-page-design-templates/{templateID}](https://upfour.io/docs/api/status-page-design-templates/get-a-status-page-design-template.md)[PUT **Update a status page design template** /status-page-design-templates/{templateID}](https://upfour.io/docs/api/status-page-design-templates/update-a-status-page-design-template.md)[GET **Get a status page design template asset** /status-page-design-templates/{templateID}/assets/{kind}](https://upfour.io/docs/api/status-page-design-templates/get-a-status-page-design-template-asset.md) ## Control [DEL **Delete a status page design template** /status-page-design-templates/{templateID}](https://upfour.io/docs/api/status-page-design-templates/delete-a-status-page-design-template.md) --- # List status page design templates — API reference > Lists status page design templates and returns page, page-size, and total-count metadata. Source: https://upfour.io/docs/api/status-page-design-templates/list-status-page-design-templates [Developers](https://upfour.io/docs/api.md)/[Status page design templates](https://upfour.io/docs/api/status-page-design-templates.md) Lists status page design templates and returns page, page-size, and total-count metadata. GET https://app.upfour.io/v1/status-page-design-templates scope status_page.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `limit` optional | integer 1–250 default `50` | query parameter | | `cursor` optional | string | query parameter | | `client_id` optional | uuid | query parameter | | `all_clients` optional | boolean | query parameter | | `q` optional | string | query parameter | | `sort` optional | string | query parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | DesignTemplateListResponse | | | DesignTemplateListResponse · 18 fields FieldTypeDescription `items` StatusPageDesignTemplateSummary[] · nullable Each item · 14 fields FieldTypeDescription `accent_color` string `asset_kinds` string[] · nullable one of`logo` `logo_dark` `favicon` The images the template stores, in the order logo, logo_dark, favicon. `created_at` string (date-time) `description` string `ground` string one of`paper` `white` `cool` `header_dark` string `header_light` string `id` uuid `includes_css` boolean `layout` string one of`essential` `brand` `overview` `name` string `revision` integer Starts at 1 and rises with every update. `theme` string one of`system` `light` `dark` `updated_at` string (date-time) `page` integer The page served; the service clamps it to 1..100000. `per_page` integer `total` integer | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 409 | [design_template_conflict](https://upfour.io/docs/api/errors#code-design_template_conflict) | The design template was changed after the caller read it. **When:** Updating or applying a design template that was changed after the caller read it. **Fix:** Reload the template and apply the change again. | | 404 | [design_template_not_found](https://upfour.io/docs/api/errors#code-design_template_not_found) | The design template does not exist or is not visible to the caller. **When:** A design template id in the path or body that is unknown or not visible to the caller. **Fix:** List design templates and use an id from the list. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [NextCreate a status page design template](https://upfour.io/docs/api/status-page-design-templates/create-a-status-page-design-template.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/status-page-design-templates" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-page-design-templates", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/status-page-design-templates", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/status-page-design-templates", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "items": [ { "accent_color": "", "asset_kinds": [ "logo" ], "created_at": "2026-10-27T09:00:00Z", "description": "The look of the Acme status pages.", "ground": "paper", "header_dark": "#7a9bff", "header_light": "#294e9e", "id": "00000000-0000-4000-8000-000000000001", "includes_css": false, "layout": "brand", "name": "Acme blue", "revision": 1, "theme": "system", "updated_at": "2026-10-27T09:00:00Z" } ], "page": 1, "per_page": 24, "total": 1 } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "design_template_not_found", "message": "The design template does not exist or is not visible to the caller.", "request_id": "req_…" } } ``` ### 409 ```json { "error": { "code": "design_template_conflict", "message": "The design template was changed after the caller read it.", "request_id": "req_…" } } ``` --- # Create a status page design template — API reference > Creates a status page design template with a name and design specification, optionally copying assets from a page or another template. Source: https://upfour.io/docs/api/status-page-design-templates/create-a-status-page-design-template [Developers](https://upfour.io/docs/api.md)/[Status page design templates](https://upfour.io/docs/api/status-page-design-templates.md) Creates a status page design template with a name and design specification, optionally copying assets from a page or another template. POST https://app.upfour.io/v1/status-page-design-templates scope status_page.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Body `application/json` optional 6 fields namestring (required) At most 80 characters. specStatusPageDesignTemplateSpec (required) The look of a page, at most 80 KiB. Unknown keys are refused; version must be 1 and appearance is required. Nested fields 12 fields | Field | Type | Description | | --- | --- | --- | | `accent_color` optional | string | #RRGGBB or empty for the platform default. | | `appearance` optional | StatusPageAppearance | | | StatusPageAppearance · 5 fields FieldTypeDescription `ground` optional string one of`paper` `white` `cool` Empty is stored as paper. `header_dark` optional string #RRGGBB or empty for the platform default. `header_light` optional string #RRGGBB or empty for the platform default. `layout` optional string one of`essential` `brand` `overview` `version` optional integer Must be 1. | | | | `custom_css` optional | string | At most 64 KiB; must not contain "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "accent_color": "", "asset_kinds": [ "logo" ], "assets": [ { "content_type": "image/png", "kind": "logo", "sha256": "721378bc96ef2ddece4a66b447ba60d6a1266860d9cb23f4e0e6c152f30279cb", "size_bytes": 70 } ], "created_at": "2026-10-27T09:00:00Z", "description": "The look of the Acme status pages.", "ground": "paper", "header_dark": "#7a9bff", "header_light": "#294e9e", "id": "00000000-0000-4000-8000-000000000002", "includes_css": false, "layout": "brand", "name": "Acme blue", "revision": 1, "spec": { "accent_color": "", "appearance": { "ground": "paper", "header_dark": "#7a9bff", "header_light": "#294e9e", "layout": "brand", "version": 1 }, "custom_css": "", "includes_css": false, "logo_url": "", "theme": "system", "version": 1 }, "theme": "system", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 200 ```json { "error": { "code": "request_failed", "message": "The request could not be completed.", "request_id": "req_…" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "design_template_not_found", "message": "The design template does not exist or is not visible to the caller.", "request_id": "req_…" } } ``` ### 409 ```json { "error": { "code": "design_template_conflict", "message": "The design template was changed after the caller read it.", "request_id": "req_…" } } ``` ### 410 ```json { "error": { "code": "request_failed", "message": "The request could not be completed.", "request_id": "req_…" } } ``` ### 413 ```json { "error": { "code": "request_failed", "message": "The request could not be completed.", "request_id": "req_…" } } ``` ### 422 ```json { "error": { "code": "request_failed", "message": "The request could not be completed.", "request_id": "req_…" } } ``` --- # Get a status page design template — API reference > Returns the design specification, stored asset information, and current revision. Source: https://upfour.io/docs/api/status-page-design-templates/get-a-status-page-design-template [Developers](https://upfour.io/docs/api.md)/[Status page design templates](https://upfour.io/docs/api/status-page-design-templates.md) Returns the design specification, stored asset information, and current revision. GET https://app.upfour.io/v1/status-page-design-templates/{templateID} scope status_page.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `templateID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | StatusPageDesignTemplate | | | StatusPageDesignTemplate · 32 fields FieldTypeDescription `accent_color` string `asset_kinds` string[] · nullable one of`logo` `logo_dark` `favicon` The images the template stores, in the order logo, logo_dark, favicon. `assets` StatusPageDesignTemplateAssetInfo[] · nullable Each item · 4 fields FieldTypeDescription `content_type` string `kind` string one of`logo` `logo_dark` `favicon` `sha256` string `size_bytes` integer `created_at` string (date-time) `description` string `ground` string one of`paper` `white` `cool` `header_dark` string `header_light` string `id` uuid `includes_css` boolean `layout` string one of`essential` `brand` `overview` `name` string `revision` integer Starts at 1 and rises with every update. `spec` StatusPageDesignTemplateSpecResponse StatusPageDesignTemplateSpecResponse · 12 fields FieldTypeDescription `accent_color` string #RRGGBB or empty for the platform default. `appearance` StatusPageAppearanceResponse StatusPageAppearanceResponse · 5 fields FieldTypeDescription `ground` string one of`paper` `white` `cool` Empty is stored as paper. `header_dark` string #RRGGBB or empty for the platform default. `header_light` string #RRGGBB or empty for the platform default. `layout` string one of`essential` `brand` `overview` `version` integer Must be 1. `custom_css` string At most 64 KiB; must not contain true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "accent_color": "", "asset_kinds": [ "logo" ], "assets": [ { "content_type": "image/png", "kind": "logo", "sha256": "721378bc96ef2ddece4a66b447ba60d6a1266860d9cb23f4e0e6c152f30279cb", "size_bytes": 70 } ], "created_at": "2026-10-27T09:00:00Z", "description": "The look of the Acme status pages.", "ground": "paper", "header_dark": "#7a9bff", "header_light": "#294e9e", "id": "00000000-0000-4000-8000-000000000001", "includes_css": false, "layout": "brand", "name": "Acme blue", "revision": 1, "spec": { "accent_color": "", "appearance": { "ground": "paper", "header_dark": "#7a9bff", "header_light": "#294e9e", "layout": "brand", "version": 1 }, "custom_css": "", "includes_css": false, "logo_url": "", "theme": "system", "version": 1 }, "theme": "system", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "design_template_not_found", "message": "The design template does not exist or is not visible to the caller.", "request_id": "req_…" } } ``` ### 409 ```json { "error": { "code": "design_template_conflict", "message": "The design template was changed after the caller read it.", "request_id": "req_…" } } ``` --- # Update a status page design template — API reference > Updates a status page design template with an expected revision and design specification. Source: https://upfour.io/docs/api/status-page-design-templates/update-a-status-page-design-template [Developers](https://upfour.io/docs/api.md)/[Status page design templates](https://upfour.io/docs/api/status-page-design-templates.md) Updates a status page design template with an expected revision and design specification. PUT https://app.upfour.io/v1/status-page-design-templates/{templateID} scope status_page.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `templateID` required | uuid | path parameter | ## Body `application/json` optional 7 fields expected_revisioninteger (required) The revision the caller last read. A template that changed since answers 409 with code design_template_conflict. namestring (required) At most 80 characters. specStatusPageDesignTemplateSpec (required) The look of a page, at most 80 KiB. Unknown keys are refused; version must be 1 and appearance is required. Nested fields 12 fields | Field | Type | Description | | --- | --- | --- | | `accent_color` optional | string | #RRGGBB or empty for the platform default. | | `appearance` optional | StatusPageAppearance | | | StatusPageAppearance · 5 fields FieldTypeDescription `ground` optional string one of`paper` `white` `cool` Empty is stored as paper. `header_dark` optional string #RRGGBB or empty for the platform default. `header_light` optional string #RRGGBB or empty for the platform default. `layout` optional string one of`essential` `brand` `overview` `version` optional integer Must be 1. | | | | `custom_css` optional | string | At most 64 KiB; must not contain "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "accent_color": "", "asset_kinds": [ "logo" ], "assets": [ { "content_type": "image/png", "kind": "logo", "sha256": "721378bc96ef2ddece4a66b447ba60d6a1266860d9cb23f4e0e6c152f30279cb", "size_bytes": 70 } ], "created_at": "2026-10-27T09:00:00Z", "description": "", "ground": "paper", "header_dark": "#7a9bff", "header_light": "#294e9e", "id": "00000000-0000-4000-8000-000000000001", "includes_css": false, "layout": "brand", "name": "Acme blue", "revision": 2, "spec": { "accent_color": "", "appearance": { "ground": "paper", "header_dark": "#7a9bff", "header_light": "#294e9e", "layout": "brand", "version": 1 }, "custom_css": "", "includes_css": false, "logo_url": "", "theme": "system", "version": 1 }, "theme": "system", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 200 ```json { "error": { "code": "request_failed", "message": "The request could not be completed.", "request_id": "req_…" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "design_template_not_found", "message": "The design template does not exist or is not visible to the caller.", "request_id": "req_…" } } ``` ### 409 ```json { "error": { "code": "design_template_conflict", "message": "The design template was changed after the caller read it.", "request_id": "req_…" } } ``` ### 410 ```json { "error": { "code": "request_failed", "message": "The request could not be completed.", "request_id": "req_…" } } ``` ### 413 ```json { "error": { "code": "request_failed", "message": "The request could not be completed.", "request_id": "req_…" } } ``` ### 422 ```json { "error": { "code": "request_failed", "message": "The request could not be completed.", "request_id": "req_…" } } ``` --- # Get a status page design template asset — API reference > Returns a stored status page design template asset by templateID and kind. Source: https://upfour.io/docs/api/status-page-design-templates/get-a-status-page-design-template-asset [Developers](https://upfour.io/docs/api.md)/[Status page design templates](https://upfour.io/docs/api/status-page-design-templates.md) Returns a stored status page design template asset by templateID and kind. GET https://app.upfour.io/v1/status-page-design-templates/{templateID}/assets/{kind} scope status_page.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `templateID` required | uuid | path parameter | | `kind` required | string one of`logo` `logo_dark` `favicon` | path parameter | ## Returns A successful request returns `200` with image/\*. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 409 | [design_template_conflict](https://upfour.io/docs/api/errors#code-design_template_conflict) | The design template was changed after the caller read it. **When:** Updating or applying a design template that was changed after the caller read it. **Fix:** Reload the template and apply the change again. | | 404 | [design_template_not_found](https://upfour.io/docs/api/errors#code-design_template_not_found) | The design template does not exist or is not visible to the caller. **When:** A design template id in the path or body that is unknown or not visible to the caller. **Fix:** List design templates and use an id from the list. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpdate a status page design template](https://upfour.io/docs/api/status-page-design-templates/update-a-status-page-design-template.md) [NextDelete a status page design template](https://upfour.io/docs/api/status-page-design-templates/delete-a-status-page-design-template.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/status-page-design-templates/3f6c1a52-8b1e-4f0d-9a43-2c5d7e8f1a90/assets/logo" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-page-design-templates/3f6c1a52-8b1e-4f0d-9a43-2c5d7e8f1a90/assets/logo", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.text()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/status-page-design-templates/3f6c1a52-8b1e-4f0d-9a43-2c5d7e8f1a90/assets/logo", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.text) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/status-page-design-templates/3f6c1a52-8b1e-4f0d-9a43-2c5d7e8f1a90/assets/logo", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ``` Successful response ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "design_template_not_found", "message": "The design template does not exist or is not visible to the caller.", "request_id": "req_…" } } ``` ### 409 ```json { "error": { "code": "design_template_conflict", "message": "The design template was changed after the caller read it.", "request_id": "req_…" } } ``` --- # Delete a status page design template — API reference > Returns an empty 204 response after deleting the status page design template. Source: https://upfour.io/docs/api/status-page-design-templates/delete-a-status-page-design-template [Developers](https://upfour.io/docs/api.md)/[Status page design templates](https://upfour.io/docs/api/status-page-design-templates.md) Returns an empty 204 response after deleting the status page design template. DELETE https://app.upfour.io/v1/status-page-design-templates/{templateID} scope status_page.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `templateID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 409 | [design_template_conflict](https://upfour.io/docs/api/errors#code-design_template_conflict) | The design template was changed after the caller read it. **When:** Updating or applying a design template that was changed after the caller read it. **Fix:** Reload the template and apply the change again. | | 404 | [design_template_not_found](https://upfour.io/docs/api/errors#code-design_template_not_found) | The design template does not exist or is not visible to the caller. **When:** A design template id in the path or body that is unknown or not visible to the caller. **Fix:** List design templates and use an id from the list. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet a status page design template asset](https://upfour.io/docs/api/status-page-design-templates/get-a-status-page-design-template-asset.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/status-page-design-templates/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/status-page-design-templates/00000000-0000-4000-8000-000000000001", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/status-page-design-templates/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/status-page-design-templates/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "design_template_not_found", "message": "The design template does not exist or is not visible to the caller.", "request_id": "req_…" } } ``` ### 409 ```json { "error": { "code": "design_template_conflict", "message": "The design template was changed after the caller read it.", "request_id": "req_…" } } ``` --- # Reports — API reference > Availability and SLA reporting: summaries, trends, exports, schedules, share links. REST endpoints with request and response fields, examples in curl, JavaScript, Python, Go and PHP, and an in-browser Try it console. Source: https://upfour.io/docs/api/reports [Developers](https://upfour.io/docs/api.md)/Pages & reporting Availability and SLA reporting: summaries, trends, exports, schedules, share links. Base URL `https://app.upfour.io/v1`. Send your key as `Authorization: Bearer `; every key acts on your real Company, so [test without paging anyone](https://upfour.io/docs/api/testing.md). New here? Start with the [quickstart](https://upfour.io/docs/api/quickstart.md). The examples read the key from `UP4_API_KEY`. ## Everyday [GET **Get the report summary** /reports/summary](https://upfour.io/docs/api/reports/get-the-report-summary.md)[GET **Get the monitor report** /reports/monitors](https://upfour.io/docs/api/reports/get-the-monitor-report.md)[GET **Get the incident report** /reports/incidents](https://upfour.io/docs/api/reports/get-the-incident-report.md)[GET **Get the response-time report** /reports/response-time](https://upfour.io/docs/api/reports/get-the-response-time-report.md)[GET **Get the service-level report** /reports/sla](https://upfour.io/docs/api/reports/get-the-service-level-report.md)[GET **Get the downtime ledger** /reports/downtime-ledger](https://upfour.io/docs/api/reports/get-the-downtime-ledger.md)[GET **Get report trends** /reports/trends](https://upfour.io/docs/api/reports/get-report-trends.md)[GET **Get the monitor ribbon** /reports/monitor-ribbon](https://upfour.io/docs/api/reports/get-the-monitor-ribbon.md)[GET **Get the agency ribbon** /reports/agency-ribbon](https://upfour.io/docs/api/reports/get-the-agency-ribbon.md)[GET **Export a report** /reports/export](https://upfour.io/docs/api/reports/export-a-report.md) ## Schedules [GET **List report schedules** /reports/schedules](https://upfour.io/docs/api/reports/list-report-schedules.md)[POST **Create a report schedule** /reports/schedules](https://upfour.io/docs/api/reports/create-a-report-schedule.md)[POST **Preview a report schedule** /reports/schedules/preview](https://upfour.io/docs/api/reports/preview-a-report-schedule.md)[GET **Get a report schedule** /reports/schedules/{scheduleID}](https://upfour.io/docs/api/reports/get-a-report-schedule.md)[PUT **Update a report schedule** /reports/schedules/{scheduleID}](https://upfour.io/docs/api/reports/update-a-report-schedule.md)[DEL **Delete a report schedule** /reports/schedules/{scheduleID}](https://upfour.io/docs/api/reports/delete-a-report-schedule.md)[POST **Run a report schedule now** /reports/schedules/{scheduleID}/run-now](https://upfour.io/docs/api/reports/run-a-report-schedule-now.md) ## Results and history [GET **List report runs** /reports/runs](https://upfour.io/docs/api/reports/list-report-runs.md) ## Sharing [GET **List report share links** /reports/share-links](https://upfour.io/docs/api/reports/list-report-share-links.md)[POST **Create a report share link** /reports/{runID}/share](https://upfour.io/docs/api/reports/create-a-report-share-link.md)[DEL **Delete a report share link** /reports/share-links/{linkID}](https://upfour.io/docs/api/reports/delete-a-report-share-link.md) --- # Get the report summary — API reference > Returns check, uptime, downtime, response-time and incident totals. Source: https://upfour.io/docs/api/reports/get-the-report-summary [Developers](https://upfour.io/docs/api.md)/[Reports](https://upfour.io/docs/api/reports.md) Returns check, uptime, downtime, response-time and incident totals. GET https://app.upfour.io/v1/reports/summary scope report.read stable returns 200 ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ReportSummary | | | ReportSummary · 12 fields FieldTypeDescription `avg_response_evaluated` boolean `avg_response_ms` number 0 when avg_response_evaluated is false: no check recorded a latency. `evaluated` boolean `failed_checks` integer `incident_count` integer Incidents that started inside the window. `monitor_count` integer `mttr_seconds` number `open_incidents` integer Unresolved incidents right now, the number the dashboard shows; not limited to the window. `resolved_incidents` integer `total_checks` integer `total_downtime_seconds` number `uptime_percentage` number Absent when no check ran in the window (evaluated is false). | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [NextGet the monitor report](https://upfour.io/docs/api/reports/get-the-monitor-report.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/reports/summary" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/reports/summary", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/reports/summary", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/reports/summary", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "avg_response_evaluated": true, "avg_response_ms": 97.5, "evaluated": true, "failed_checks": 0, "incident_count": 1, "monitor_count": 3, "mttr_seconds": 720, "open_incidents": 0, "resolved_incidents": 1, "total_checks": 6, "total_downtime_seconds": 720, "uptime_percentage": 100 } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Get the monitor report — API reference > Returns monitor states, check totals, average and P95 response times, incident counts and pagination. Source: https://upfour.io/docs/api/reports/get-the-monitor-report [Developers](https://upfour.io/docs/api.md)/[Reports](https://upfour.io/docs/api/reports.md) Returns monitor states, check totals, average and P95 response times, incident counts and pagination. GET https://app.upfour.io/v1/reports/monitors scope report.read stable returns 200 ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | MonitorReportRow[] · nullable | | | Each item · 10 fields FieldTypeDescription `avg_response_ms` number `evaluated` boolean `incident_count` integer `monitor_id` uuid `name` string `p95_response_ms` number `state` string one of`UP` `DOWN` `PAUSED` `PENDING` `total_checks` integer `url` string `uptime_percentage` number Absent when no check ran in the window (evaluated is false). | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet the report summary](https://upfour.io/docs/api/reports/get-the-report-summary.md) [NextGet the incident report](https://upfour.io/docs/api/reports/get-the-incident-report.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/reports/monitors" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/reports/monitors", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/reports/monitors", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/reports/monitors", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "avg_response_ms": 0, "evaluated": false, "incident_count": 0, "monitor_id": "00000000-0000-4000-8000-000000000001", "name": "Docs site", "p95_response_ms": 0, "state": "PENDING", "total_checks": 0, "url": "https://example.com" }, { "avg_response_ms": 97.5, "evaluated": true, "incident_count": 1, "monitor_id": "00000000-0000-4000-8000-000000000002", "name": "Marketing website", "p95_response_ms": 113.25, "state": "UP", "total_checks": 6, "uptime_percentage": 100, "url": "https://example.org" }, { "avg_response_ms": 0, "evaluated": false, "incident_count": 0, "monitor_id": "00000000-0000-4000-8000-000000000003", "name": "Status page", "p95_response_ms": 0, "state": "PENDING", "total_checks": 0, "url": "https://example.net" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Get the incident report — API reference > Returns incident KPIs, timelines and coverage for the selected window, or downloads the report in a requested format. Source: https://upfour.io/docs/api/reports/get-the-incident-report [Developers](https://upfour.io/docs/api.md)/[Reports](https://upfour.io/docs/api/reports.md) Returns incident KPIs, timelines and coverage for the selected window, or downloads the report in a requested format. GET https://app.upfour.io/v1/reports/incidents scope report.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `format` optional | string one of`csv` `json` `pdf` `xlsx` | query parameter · Without it the answer is the JSON envelope; with it the answer is a file download of that format. | | `preset` optional | string one of`24h` `7d` `30d` `90d` `mtd` `last_month` `qtd` `ytd` | query parameter · Reporting window; `period` is accepted as an alias. Without `preset`, `from` and `to` the window is the last 30 days. The window is cut to the plan's history. | | `from` optional | string | query parameter · Start of the window: an RFC 3339 timestamp or a date (`YYYY-MM-DD`, in `tz`). Replaces `preset`. | | `to` optional | string | query parameter · End of the window, same formats as `from`; a date means the end of that day. Defaults to now. | | `tz` optional | string | query parameter · IANA time zone for calendar presets and dates. Defaults to the Company's report time zone. | | `lang` optional | string one of`en` `de` default `"en"` | query parameter · Language of the report text. | | `monitor_id` optional | uuid | query parameter · Only this monitor; repeat the parameter or send a comma-separated list for several. | | `severity` optional | string | query parameter · Incident reports only: keep incidents of this severity; repeat the parameter or send a comma-separated list for several. | | `q` optional | string | query parameter · Case-insensitive text: keeps monitors whose name, or incidents whose title, contains it. | | `business_hours` optional | string | query parameter · Count only these hours in SLA and response-time reports, for example `mon-fri,09:00-17:00` (several groups separated by `;`). The hours are read in `tz`, or in UTC when `tz` is not sent. | | `client_id` optional | uuid | query parameter · Report on one client. | | `all_clients` optional | boolean | query parameter · `true` includes all clients; without `client_id` the report covers the Company level. | ## Returns A successful request returns `200` with application/json, application/pdf, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, text/csv. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ReportDocument | | | ReportDocument · 89 fields FieldTypeDescription `branding` ReportDocumentBranding ReportDocumentBranding · 8 fields FieldTypeDescription `accent_color_hex` string `footer_text` string `logo_data_url` string `logo_url` string `org_name` string `primary_color_hex` string `watermark` string `website_url` string `content_hash` string Fingerprint of the content; GET /reports/verify/{hash} confirms it. `generated_at` string (date-time) `kpis` ReportKPI[] · nullable Each item · 10 fields FieldTypeDescription `label` string `value` string Formatted for display, for example "99.94 %". `delta` ReportKPIDelta ReportKPIDelta · 4 fields FieldTypeDescription `abs` number `direction` string one of`up` `down` `flat` `good` boolean `pct` number `key` string `raw` any The machine-readable value (a number, mostly); absent when there is no data. `unit` string `org` ReportCompany ReportCompany · 2 fields FieldTypeDescription `id` uuid `name` string `range` ReportRange ReportRange · 5 fields FieldTypeDescription `from` string (date-time) `to` string (date-time) `clamped` boolean true when the window was cut to the plan's history. `preset` string one of`24h` `1d` `7d` `30d` `90d` `mtd` `last_month` `qtd` `ytd` `timezone` string `sections` ReportSection[] · nullable Each item · 20 fields FieldTypeDescription `heading` string `charts` ReportChart[] Each item · 9 fields FieldTypeDescription `categories` string[] · nullable `kind` string one of`line` `bar` `donut` `stacked_bar` `heatmap` `series` ReportChartSeries[] · nullable Each item · 4 fields FieldTypeDescription `data` number[] · nullable `name` string `color` string `dash` boolean `title` string `y_unit` string `note` string `tables` ReportTable[] Each item · 7 fields FieldTypeDescription `columns` ReportColumn[] · nullable Each item · 4 fields FieldTypeDescription `header` string `align` string one of`left` `right` `center` `key` string `kind` string one of`text` `number` `percent` `duration` `timestamp` `rows` ReportCell[][] · nullable `title` string `title` string `type` string one of`executive_summary` `monitor_report` `monitor_sla` `downtime_ledger` `incident_analytics` `response_time` `trends` `client` ReportClient ReportClient · 3 fields FieldTypeDescription `id` uuid `name` string `logo_data_url` string `coverage` ReportCoverage ReportCoverage · 4 fields FieldTypeDescription `observed_days` integer `total_days` integer `partial_buckets` boolean `unavailable` boolean `date_format` string one of`day_month_year` `month_day_year` `iso` `lang` string one of`en` `de` `narrative` string `period` string `provenance` ReportProvenance ReportProvenance · 13 fields FieldTypeDescription `data_sources` string[] · nullable Where the figures come from, for example hot:check_results or cold:check_result_archive_daily_stats. `tool` string `hot_window` ReportRange ReportRange · 5 fields FieldTypeDescription `from` string (date-time) `to` string (date-time) `clamped` boolean true when the window was cut to the plan's history. `preset` string one of`24h` `1d` `7d` `30d` `90d` `mtd` `last_month` `qtd` `ytd` `timezone` string `manifests` ReportArchiveManifest[] Each item · 4 fields FieldTypeDescription `max_created_at` string (date-time) `min_created_at` string (date-time) `sha256` string `storage_key` string `sla` ReportSLAAssessment ReportSLAAssessment · 6 fields FieldTypeDescription `evaluated` integer `inconclusive` integer `met` integer `monitors` integer `no_data` integer `partial` integer `subtitle` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet the monitor report](https://upfour.io/docs/api/reports/get-the-monitor-report.md) [NextGet the response-time report](https://upfour.io/docs/api/reports/get-the-response-time-report.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/reports/incidents" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/reports/incidents", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/reports/incidents", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/reports/incidents", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "branding": { "accent_color_hex": "#294E9E", "footer_text": "Acme GmbH", "org_name": "Acme GmbH", "primary_color_hex": "#294E9E" }, "content_hash": "", "coverage": { "observed_days": 1, "total_days": 31 }, "date_format": "day_month_year", "generated_at": "1734-07-18T09:15:00Z", "kpis": [ { "delta": { "abs": 1, "direction": "up", "good": false, "pct": 0 }, "key": "incidents", "label": "Incidents", "raw": 1, "value": "1" }, { "key": "open_at_period_end", "label": "Open at period end", "raw": 0, "value": "0" }, { "delta": { "abs": 100, "direction": "up", "good": true, "pct": 0 }, "key": "acknowledged", "label": "Acknowledged", "raw": 100, "unit": "%", "value": "100 %" }, { "delta": { "abs": 240, "direction": "up", "good": false, "pct": 0 }, "key": "mtta", "label": "MTTA", "raw": 240, "unit": "s", "value": "4m" }, { "delta": { "abs": 720, "direction": "up", "good": false, "pct": 0 }, "key": "mttr", "label": "MTTR", "raw": 720, "unit": "s", "value": "12m" }, { "key": "resolved", "label": "Resolved", "raw": 1, "value": "1" } ], "lang": "en", "org": { "id": "00000000-0000-4000-8000-000000000001", "name": "Acme GmbH" }, "period": "27 Sep 2026 09:00:00 - 27 Oct 2026 09:00:00 (end exclusive) · UTC", "range": { "from": "2026-09-27T09:00:00Z", "preset": "30d", "timezone": "UTC", "to": "2026-10-27T09:00:00Z" }, "sections": [ { "charts": [ { "categories": [ "warning" ], "kind": "donut", "series": [ { "data": [ 1 ], "name": "Incidents" } ], "title": "Incidents by severity" } ], "heading": "By severity", "tables": [ { "columns": [ { "header": "Severity", "key": "severity", "kind": "text" }, { "align": "right", "header": "Incidents", "key": "incidents", "kind": "number" } ], "rows": [ [ { "text": "warning" }, { "raw": 1, "text": "1" } ] ] } ] }, { "heading": "Most-affected monitors", "tables": [ { "columns": [ { "header": "Monitor", "key": "monitor", "kind": "text" }, { "align": "right", "header": "Incidents", "key": "incidents", "kind": "number" }, { "align": "right", "header": "Total downtime", "key": "total_downtime", "kind": "duration" } ], "rows": [ [ { "text": "Marketing website" }, { "raw": 1, "text": "1" }, { "raw": 720, "text": "12m" } ] ] } ] }, { "charts": [ { "categories": [ "2026-10-24" ], "kind": "bar", "series": [ { "data": [ 1 ], "name": "Incidents" } ], "title": "Incidents per day" } ], "heading": "Incident volume", "tables": [ { "columns": [ { "header": "Date", "key": "date", "kind": "text" }, { "align": "right", "header": "Incidents", "key": "incidents", "kind": "number" } ], "rows": [ [ { "text": "2026-10-24" }, { "raw": 1, "text": "1" } ] ] } ] }, { "charts": [ { "categories": [ "2026-10-24" ], "kind": "line", "series": [ { "data": [ 12 ], "name": "MTTR (min)" } ], "title": "MTTR (minutes)", "y_unit": "min" } ], "heading": "MTTR trend", "note": "Mean time to resolve, in minutes, for incidents started each day." } ], "subtitle": "Acknowledgement, resolution time & impact · last 30 days", "title": "Incident analytics — Acme GmbH", "type": "incident_analytics" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Get the response-time report — API reference > Returns response-time KPIs, coverage and report provenance for the selected window, with optional file formats. Source: https://upfour.io/docs/api/reports/get-the-response-time-report [Developers](https://upfour.io/docs/api.md)/[Reports](https://upfour.io/docs/api/reports.md) Returns response-time KPIs, coverage and report provenance for the selected window, with optional file formats. GET https://app.upfour.io/v1/reports/response-time scope report.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `format` optional | string one of`csv` `json` `pdf` `xlsx` | query parameter · Without it the answer is the JSON envelope; with it the answer is a file download of that format. | | `preset` optional | string one of`24h` `7d` `30d` `90d` `mtd` `last_month` `qtd` `ytd` | query parameter · Reporting window; `period` is accepted as an alias. Without `preset`, `from` and `to` the window is the last 30 days. The window is cut to the plan's history. | | `from` optional | string | query parameter · Start of the window: an RFC 3339 timestamp or a date (`YYYY-MM-DD`, in `tz`). Replaces `preset`. | | `to` optional | string | query parameter · End of the window, same formats as `from`; a date means the end of that day. Defaults to now. | | `tz` optional | string | query parameter · IANA time zone for calendar presets and dates. Defaults to the Company's report time zone. | | `lang` optional | string one of`en` `de` default `"en"` | query parameter · Language of the report text. | | `monitor_id` optional | uuid | query parameter · Only this monitor; repeat the parameter or send a comma-separated list for several. | | `severity` optional | string | query parameter · Incident reports only: keep incidents of this severity; repeat the parameter or send a comma-separated list for several. | | `q` optional | string | query parameter · Case-insensitive text: keeps monitors whose name, or incidents whose title, contains it. | | `business_hours` optional | string | query parameter · Count only these hours in SLA and response-time reports, for example `mon-fri,09:00-17:00` (several groups separated by `;`). The hours are read in `tz`, or in UTC when `tz` is not sent. | | `client_id` optional | uuid | query parameter · Report on one client. | | `all_clients` optional | boolean | query parameter · `true` includes all clients; without `client_id` the report covers the Company level. | ## Returns A successful request returns `200` with application/json, application/pdf, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, text/csv. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ReportDocument | | | ReportDocument · 89 fields FieldTypeDescription `branding` ReportDocumentBranding ReportDocumentBranding · 8 fields FieldTypeDescription `accent_color_hex` string `footer_text` string `logo_data_url` string `logo_url` string `org_name` string `primary_color_hex` string `watermark` string `website_url` string `content_hash` string Fingerprint of the content; GET /reports/verify/{hash} confirms it. `generated_at` string (date-time) `kpis` ReportKPI[] · nullable Each item · 10 fields FieldTypeDescription `label` string `value` string Formatted for display, for example "99.94 %". `delta` ReportKPIDelta ReportKPIDelta · 4 fields FieldTypeDescription `abs` number `direction` string one of`up` `down` `flat` `good` boolean `pct` number `key` string `raw` any The machine-readable value (a number, mostly); absent when there is no data. `unit` string `org` ReportCompany ReportCompany · 2 fields FieldTypeDescription `id` uuid `name` string `range` ReportRange ReportRange · 5 fields FieldTypeDescription `from` string (date-time) `to` string (date-time) `clamped` boolean true when the window was cut to the plan's history. `preset` string one of`24h` `1d` `7d` `30d` `90d` `mtd` `last_month` `qtd` `ytd` `timezone` string `sections` ReportSection[] · nullable Each item · 20 fields FieldTypeDescription `heading` string `charts` ReportChart[] Each item · 9 fields FieldTypeDescription `categories` string[] · nullable `kind` string one of`line` `bar` `donut` `stacked_bar` `heatmap` `series` ReportChartSeries[] · nullable Each item · 4 fields FieldTypeDescription `data` number[] · nullable `name` string `color` string `dash` boolean `title` string `y_unit` string `note` string `tables` ReportTable[] Each item · 7 fields FieldTypeDescription `columns` ReportColumn[] · nullable Each item · 4 fields FieldTypeDescription `header` string `align` string one of`left` `right` `center` `key` string `kind` string one of`text` `number` `percent` `duration` `timestamp` `rows` ReportCell[][] · nullable `title` string `title` string `type` string one of`executive_summary` `monitor_report` `monitor_sla` `downtime_ledger` `incident_analytics` `response_time` `trends` `client` ReportClient ReportClient · 3 fields FieldTypeDescription `id` uuid `name` string `logo_data_url` string `coverage` ReportCoverage ReportCoverage · 4 fields FieldTypeDescription `observed_days` integer `total_days` integer `partial_buckets` boolean `unavailable` boolean `date_format` string one of`day_month_year` `month_day_year` `iso` `lang` string one of`en` `de` `narrative` string `period` string `provenance` ReportProvenance ReportProvenance · 13 fields FieldTypeDescription `data_sources` string[] · nullable Where the figures come from, for example hot:check_results or cold:check_result_archive_daily_stats. `tool` string `hot_window` ReportRange ReportRange · 5 fields FieldTypeDescription `from` string (date-time) `to` string (date-time) `clamped` boolean true when the window was cut to the plan's history. `preset` string one of`24h` `1d` `7d` `30d` `90d` `mtd` `last_month` `qtd` `ytd` `timezone` string `manifests` ReportArchiveManifest[] Each item · 4 fields FieldTypeDescription `max_created_at` string (date-time) `min_created_at` string (date-time) `sha256` string `storage_key` string `sla` ReportSLAAssessment ReportSLAAssessment · 6 fields FieldTypeDescription `evaluated` integer `inconclusive` integer `met` integer `monitors` integer `no_data` integer `partial` integer `subtitle` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet the incident report](https://upfour.io/docs/api/reports/get-the-incident-report.md) [NextGet the service-level report](https://upfour.io/docs/api/reports/get-the-service-level-report.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/reports/response-time" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/reports/response-time", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/reports/response-time", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/reports/response-time", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "branding": { "accent_color_hex": "#294E9E", "footer_text": "Acme GmbH", "org_name": "Acme GmbH", "primary_color_hex": "#294E9E" }, "content_hash": "", "coverage": { "observed_days": 1, "total_days": 31 }, "date_format": "day_month_year", "generated_at": "1734-07-18T09:15:00Z", "kpis": [ { "key": "average", "label": "Average", "raw": 97.5, "unit": "ms", "value": "98 ms" }, { "key": "median_p50", "label": "Median (P50)", "raw": 97.5, "unit": "ms", "value": "98 ms*" }, { "key": "p95", "label": "P95", "raw": 113.25, "unit": "ms", "value": "113 ms*" }, { "key": "p99", "label": "P99", "unit": "ms", "value": "n/a" }, { "key": "slowest_sample", "label": "Slowest sample", "raw": 115, "unit": "ms", "value": "115 ms" }, { "key": "successful_checks", "label": "Successful checks", "raw": 6, "value": "6" } ], "lang": "en", "org": { "id": "00000000-0000-4000-8000-000000000001", "name": "Acme GmbH" }, "period": "27 Sep 2026 09:00:00 - 27 Oct 2026 09:00:00 (end exclusive) · UTC", "range": { "from": "2026-09-27T09:00:00Z", "preset": "30d", "timezone": "UTC", "to": "2026-10-27T09:00:00Z" }, "sections": [ { "heading": "Coverage", "note": "Values marked * (p50/p95/p99, connection timing) use available full-resolution data within the last 7d. With no such samples in this period, they are shown as n/a. Older days retain daily averages, p95 and maxima. The window average and check counts span the whole period. Daily and regional charts use 300-second rollups for recent data and daily archives for older data. Their p95 is the maximum of bucket percentiles, not an exact whole-period percentile. p99 is unavailable at this granularity." }, { "charts": [ { "categories": [], "kind": "line", "series": [ { "data": [], "name": "Avg" }, { "dash": true, "data": [], "name": "P95" } ], "title": "Daily latency · UTC", "y_unit": "ms" } ], "heading": "Latency over time", "tables": [ { "columns": [ { "header": "Date (UTC)", "key": "date_utc", "kind": "text" }, { "align": "right", "header": "Avg (ms)", "key": "avg_ms", "kind": "number" }, { "align": "right", "header": "P95 (ms)", "key": "p95_ms", "kind": "number" }, { "header": "Source", "key": "source", "kind": "text" } ], "rows": null } ] }, { "heading": "Slowest endpoints", "note": "Top monitors by p95 latency over the full-resolution window.", "tables": [ { "columns": [ { "header": "Monitor", "key": "monitor", "kind": "text" }, { "align": "right", "header": "Checks", "key": "checks", "kind": "number" }, { "align": "right", "header": "Avg (ms)", "key": "avg_ms", "kind": "number" }, { "align": "right", "header": "P95 (ms)", "key": "p95_ms", "kind": "number" }, { "align": "right", "header": "Max (ms)", "key": "max_ms", "kind": "number" } ], "rows": [ [ { "text": "Marketing website" }, { "raw": 6, "text": "6" }, { "raw": 97.5, "text": "97.5" }, { "raw": 113.25, "text": "113.2" }, { "raw": 115, "text": "115" } ] ] } ] } ], "subtitle": "Latency distribution & timing breakdown · last 30 days", "title": "Response-time report — Acme GmbH", "type": "response_time" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Get the service-level report — API reference > Returns SLA KPIs, monitor coverage and report provenance for the selected window, with optional file formats. Source: https://upfour.io/docs/api/reports/get-the-service-level-report [Developers](https://upfour.io/docs/api.md)/[Reports](https://upfour.io/docs/api/reports.md) Returns SLA KPIs, monitor coverage and report provenance for the selected window, with optional file formats. GET https://app.upfour.io/v1/reports/sla scope report.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `format` optional | string one of`csv` `json` `pdf` `xlsx` | query parameter · Without it the answer is the JSON envelope; with it the answer is a file download of that format. | | `preset` optional | string one of`24h` `7d` `30d` `90d` `mtd` `last_month` `qtd` `ytd` | query parameter · Reporting window; `period` is accepted as an alias. Without `preset`, `from` and `to` the window is the last 30 days. The window is cut to the plan's history. | | `from` optional | string | query parameter · Start of the window: an RFC 3339 timestamp or a date (`YYYY-MM-DD`, in `tz`). Replaces `preset`. | | `to` optional | string | query parameter · End of the window, same formats as `from`; a date means the end of that day. Defaults to now. | | `tz` optional | string | query parameter · IANA time zone for calendar presets and dates. Defaults to the Company's report time zone. | | `lang` optional | string one of`en` `de` default `"en"` | query parameter · Language of the report text. | | `monitor_id` optional | uuid | query parameter · Only this monitor; repeat the parameter or send a comma-separated list for several. | | `severity` optional | string | query parameter · Incident reports only: keep incidents of this severity; repeat the parameter or send a comma-separated list for several. | | `q` optional | string | query parameter · Case-insensitive text: keeps monitors whose name, or incidents whose title, contains it. | | `business_hours` optional | string | query parameter · Count only these hours in SLA and response-time reports, for example `mon-fri,09:00-17:00` (several groups separated by `;`). The hours are read in `tz`, or in UTC when `tz` is not sent. | | `client_id` optional | uuid | query parameter · Report on one client. | | `all_clients` optional | boolean | query parameter · `true` includes all clients; without `client_id` the report covers the Company level. | | `target` optional | number 0–100 | query parameter · Replaces every monitor's SLA target (percent, 0 to 100). | ## Returns A successful request returns `200` with application/json, application/pdf, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, text/csv. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ReportDocument | | | ReportDocument · 89 fields FieldTypeDescription `branding` ReportDocumentBranding ReportDocumentBranding · 8 fields FieldTypeDescription `accent_color_hex` string `footer_text` string `logo_data_url` string `logo_url` string `org_name` string `primary_color_hex` string `watermark` string `website_url` string `content_hash` string Fingerprint of the content; GET /reports/verify/{hash} confirms it. `generated_at` string (date-time) `kpis` ReportKPI[] · nullable Each item · 10 fields FieldTypeDescription `label` string `value` string Formatted for display, for example "99.94 %". `delta` ReportKPIDelta ReportKPIDelta · 4 fields FieldTypeDescription `abs` number `direction` string one of`up` `down` `flat` `good` boolean `pct` number `key` string `raw` any The machine-readable value (a number, mostly); absent when there is no data. `unit` string `org` ReportCompany ReportCompany · 2 fields FieldTypeDescription `id` uuid `name` string `range` ReportRange ReportRange · 5 fields FieldTypeDescription `from` string (date-time) `to` string (date-time) `clamped` boolean true when the window was cut to the plan's history. `preset` string one of`24h` `1d` `7d` `30d` `90d` `mtd` `last_month` `qtd` `ytd` `timezone` string `sections` ReportSection[] · nullable Each item · 20 fields FieldTypeDescription `heading` string `charts` ReportChart[] Each item · 9 fields FieldTypeDescription `categories` string[] · nullable `kind` string one of`line` `bar` `donut` `stacked_bar` `heatmap` `series` ReportChartSeries[] · nullable Each item · 4 fields FieldTypeDescription `data` number[] · nullable `name` string `color` string `dash` boolean `title` string `y_unit` string `note` string `tables` ReportTable[] Each item · 7 fields FieldTypeDescription `columns` ReportColumn[] · nullable Each item · 4 fields FieldTypeDescription `header` string `align` string one of`left` `right` `center` `key` string `kind` string one of`text` `number` `percent` `duration` `timestamp` `rows` ReportCell[][] · nullable `title` string `title` string `type` string one of`executive_summary` `monitor_report` `monitor_sla` `downtime_ledger` `incident_analytics` `response_time` `trends` `client` ReportClient ReportClient · 3 fields FieldTypeDescription `id` uuid `name` string `logo_data_url` string `coverage` ReportCoverage ReportCoverage · 4 fields FieldTypeDescription `observed_days` integer `total_days` integer `partial_buckets` boolean `unavailable` boolean `date_format` string one of`day_month_year` `month_day_year` `iso` `lang` string one of`en` `de` `narrative` string `period` string `provenance` ReportProvenance ReportProvenance · 13 fields FieldTypeDescription `data_sources` string[] · nullable Where the figures come from, for example hot:check_results or cold:check_result_archive_daily_stats. `tool` string `hot_window` ReportRange ReportRange · 5 fields FieldTypeDescription `from` string (date-time) `to` string (date-time) `clamped` boolean true when the window was cut to the plan's history. `preset` string one of`24h` `1d` `7d` `30d` `90d` `mtd` `last_month` `qtd` `ytd` `timezone` string `manifests` ReportArchiveManifest[] Each item · 4 fields FieldTypeDescription `max_created_at` string (date-time) `min_created_at` string (date-time) `sha256` string `storage_key` string `sla` ReportSLAAssessment ReportSLAAssessment · 6 fields FieldTypeDescription `evaluated` integer `inconclusive` integer `met` integer `monitors` integer `no_data` integer `partial` integer `subtitle` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet the response-time report](https://upfour.io/docs/api/reports/get-the-response-time-report.md) [NextGet the downtime ledger](https://upfour.io/docs/api/reports/get-the-downtime-ledger.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/reports/sla" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/reports/sla", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/reports/sla", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/reports/sla", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "branding": { "accent_color_hex": "#294E9E", "footer_text": "Acme GmbH", "org_name": "Acme GmbH", "primary_color_hex": "#294E9E" }, "content_hash": "", "coverage": { "observed_days": 1, "total_days": 31 }, "date_format": "day_month_year", "generated_at": "1734-07-18T09:15:00Z", "kpis": [ { "key": "monitors_meeting_sla", "label": "Monitors meeting SLA", "raw": 1, "value": "1 / 1" }, { "key": "average_sla", "label": "Average SLA", "raw": 100, "unit": "%", "value": "100.00 %" }, { "key": "worst_sla", "label": "Worst SLA", "raw": 100, "unit": "%", "value": "100.00 %" }, { "key": "incident_downtime", "label": "Incident downtime", "raw": 12, "unit": "min", "value": "12m" }, { "key": "no_data", "label": "No data", "raw": 2, "value": "2" }, { "key": "partial_observation", "label": "Partial observation", "raw": 1, "value": "1" } ], "lang": "en", "narrative": "1 of 3 monitors evaluated using verified checks. See each monitor's observed dates and days below. Days without measurements are not counted as uptime. Results labelled observed data apply only to those observations, not the full selected period.", "org": { "id": "00000000-0000-4000-8000-000000000001", "name": "Acme GmbH" }, "period": "27 Sep 2026 09:00:00 - 27 Oct 2026 09:00:00 (end exclusive) · UTC", "range": { "from": "2026-09-27T09:00:00Z", "preset": "30d", "timezone": "UTC", "to": "2026-10-27T09:00:00Z" }, "sections": [ { "heading": "Service levels", "note": "Achieved % = (1 − failed/total) using verified observations within the selected report range. Days without measurements are not counted as uptime. An observed-data result does not certify the entire selected period. A policy window narrows that range per monitor when it is shorter, but never expands it beyond the period shown in this report. Paused or new monitors without checks are shown as No data and excluded from the headline. Incident downtime is a separate incident-record total and does not alter the check-based achieved percentage.", "tables": [ { "columns": [ { "header": "Monitor", "key": "monitor", "kind": "text" }, { "align": "right", "header": "Target", "key": "target", "kind": "percent" }, { "align": "right", "header": "Achieved", "key": "achieved", "kind": "percent" }, { "align": "right", "header": "Budget used", "key": "budget_used", "kind": "percent" }, { "align": "right", "header": "Downtime", "key": "downtime", "kind": "duration" }, { "align": "right", "header": "Outages", "key": "outages", "kind": "number" }, { "align": "right", "header": "Status", "key": "status", "kind": "text" } ], "rows": [ [ { "text": "Docs site" }, { "raw": 99.9, "text": "99.9 %" }, { "emphasis": "muted", "text": "—" }, { "emphasis": "muted", "text": "—" }, { "raw": 0, "text": "—" }, { "raw": 0, "text": "0" }, { "emphasis": "muted", "text": "No data" } ], [ { "text": "Status page" }, { "raw": 99.9, "text": "99.9 %" }, { "emphasis": "muted", "text": "—" }, { "emphasis": "muted", "text": "—" }, { "raw": 0, "text": "—" }, { "raw": 0, "text": "0" }, { "emphasis": "muted", "text": "No data" } ], [ { "text": "Marketing website" }, { "raw": 99.9, "text": "99.9 %" }, { "emphasis": "good", "raw": 100, "text": "100.00 %" }, { "raw": 0, "text": "0.0 %" }, { "raw": 12, "text": "12m" }, { "raw": 1, "text": "1" }, { "emphasis": "good", "text": "Met (observed data)" } ], [ { "emphasis": "muted", "text": "Average (evaluated monitors)" }, { "text": "—" }, { "emphasis": "muted", "raw": 100, "text": "100.00 %" }, { "text": "—" }, { "text": "—" }, { "text": "—" }, { "text": "—" } ] ], "title": "Service levels by monitor" } ] }, { "heading": "Observation coverage", "note": "The dates bound the available data, not a continuous observation period. Each counted UTC day contains at least one verified check after applying the monitor's policy and business hours.", "tables": [ { "columns": [ { "header": "Monitor", "key": "monitor", "kind": "text" }, { "header": "Data from (UTC)", "key": "data_from_utc", "kind": "text" }, { "header": "Data through (UTC)", "key": "data_through_utc", "kind": "text" }, { "header": "Days with checks", "key": "days_with_checks", "kind": "text" }, { "header": "Assessment basis", "key": "assessment_basis", "kind": "text" } ], "rows": [ [ { "text": "Docs site" }, { "text": "—" }, { "text": "—" }, { "text": "0 / 31" }, { "text": "No checks in the selected period or business hours" } ], [ { "text": "Status page" }, { "text": "—" }, { "text": "—" }, { "text": "0 / 31" }, { "text": "No checks in the selected period or business hours" } ], [ { "text": "Marketing website" }, { "text": "2026-10-27" }, { "text": "2026-10-27" }, { "text": "1 / 31" }, { "text": "Observed data only; fewer days with checks than requested" } ] ], "title": "Observation coverage by monitor" } ] }, { "heading": "Outage detail", "tables": [ { "columns": [ { "header": "Monitor", "key": "monitor", "kind": "text" }, { "header": "Started", "key": "started", "kind": "timestamp" }, { "header": "Resolved", "key": "resolved", "kind": "timestamp" }, { "align": "right", "header": "Duration", "key": "duration", "kind": "duration" }, { "header": "Severity", "key": "severity", "kind": "text" } ], "rows": [ [ { "text": "Marketing website" }, { "raw": "2026-10-24T09:00:00Z", "text": "2026-10-24 09:00 UTC" }, { "raw": "2026-10-24T09:10:00Z", "text": "2026-10-24 09:10 UTC" }, { "raw": 720, "text": "12m" }, { "emphasis": "muted", "text": "warning" } ] ], "title": "Outages in the window" } ] } ], "sla": { "evaluated": 1, "inconclusive": 0, "met": 1, "monitors": 3, "no_data": 2, "partial": 1 }, "subtitle": "Targets from SLA policies, with a 99.9 % default where no policy applies · last 30 days", "title": "Service-level report — Acme GmbH", "type": "monitor_sla" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Get the downtime ledger — API reference > Returns outage timelines, evidence coverage and report provenance for the selected window, with optional file formats. Source: https://upfour.io/docs/api/reports/get-the-downtime-ledger [Developers](https://upfour.io/docs/api.md)/[Reports](https://upfour.io/docs/api/reports.md) Returns outage timelines, evidence coverage and report provenance for the selected window, with optional file formats. GET https://app.upfour.io/v1/reports/downtime-ledger scope report.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `format` optional | string one of`csv` `json` `pdf` `xlsx` | query parameter · Without it the answer is the JSON envelope; with it the answer is a file download of that format. | | `preset` optional | string one of`24h` `7d` `30d` `90d` `mtd` `last_month` `qtd` `ytd` | query parameter · Reporting window; `period` is accepted as an alias. Without `preset`, `from` and `to` the window is the last 30 days. The window is cut to the plan's history. | | `from` optional | string | query parameter · Start of the window: an RFC 3339 timestamp or a date (`YYYY-MM-DD`, in `tz`). Replaces `preset`. | | `to` optional | string | query parameter · End of the window, same formats as `from`; a date means the end of that day. Defaults to now. | | `tz` optional | string | query parameter · IANA time zone for calendar presets and dates. Defaults to the Company's report time zone. | | `lang` optional | string one of`en` `de` default `"en"` | query parameter · Language of the report text. | | `monitor_id` optional | uuid | query parameter · Only this monitor; repeat the parameter or send a comma-separated list for several. | | `severity` optional | string | query parameter · Incident reports only: keep incidents of this severity; repeat the parameter or send a comma-separated list for several. | | `q` optional | string | query parameter · Case-insensitive text: keeps monitors whose name, or incidents whose title, contains it. | | `business_hours` optional | string | query parameter · Count only these hours in SLA and response-time reports, for example `mon-fri,09:00-17:00` (several groups separated by `;`). The hours are read in `tz`, or in UTC when `tz` is not sent. | | `client_id` optional | uuid | query parameter · Report on one client. | | `all_clients` optional | boolean | query parameter · `true` includes all clients; without `client_id` the report covers the Company level. | ## Returns A successful request returns `200` with application/json, application/pdf, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, text/csv. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ReportDocument | | | ReportDocument · 89 fields FieldTypeDescription `branding` ReportDocumentBranding ReportDocumentBranding · 8 fields FieldTypeDescription `accent_color_hex` string `footer_text` string `logo_data_url` string `logo_url` string `org_name` string `primary_color_hex` string `watermark` string `website_url` string `content_hash` string Fingerprint of the content; GET /reports/verify/{hash} confirms it. `generated_at` string (date-time) `kpis` ReportKPI[] · nullable Each item · 10 fields FieldTypeDescription `label` string `value` string Formatted for display, for example "99.94 %". `delta` ReportKPIDelta ReportKPIDelta · 4 fields FieldTypeDescription `abs` number `direction` string one of`up` `down` `flat` `good` boolean `pct` number `key` string `raw` any The machine-readable value (a number, mostly); absent when there is no data. `unit` string `org` ReportCompany ReportCompany · 2 fields FieldTypeDescription `id` uuid `name` string `range` ReportRange ReportRange · 5 fields FieldTypeDescription `from` string (date-time) `to` string (date-time) `clamped` boolean true when the window was cut to the plan's history. `preset` string one of`24h` `1d` `7d` `30d` `90d` `mtd` `last_month` `qtd` `ytd` `timezone` string `sections` ReportSection[] · nullable Each item · 20 fields FieldTypeDescription `heading` string `charts` ReportChart[] Each item · 9 fields FieldTypeDescription `categories` string[] · nullable `kind` string one of`line` `bar` `donut` `stacked_bar` `heatmap` `series` ReportChartSeries[] · nullable Each item · 4 fields FieldTypeDescription `data` number[] · nullable `name` string `color` string `dash` boolean `title` string `y_unit` string `note` string `tables` ReportTable[] Each item · 7 fields FieldTypeDescription `columns` ReportColumn[] · nullable Each item · 4 fields FieldTypeDescription `header` string `align` string one of`left` `right` `center` `key` string `kind` string one of`text` `number` `percent` `duration` `timestamp` `rows` ReportCell[][] · nullable `title` string `title` string `type` string one of`executive_summary` `monitor_report` `monitor_sla` `downtime_ledger` `incident_analytics` `response_time` `trends` `client` ReportClient ReportClient · 3 fields FieldTypeDescription `id` uuid `name` string `logo_data_url` string `coverage` ReportCoverage ReportCoverage · 4 fields FieldTypeDescription `observed_days` integer `total_days` integer `partial_buckets` boolean `unavailable` boolean `date_format` string one of`day_month_year` `month_day_year` `iso` `lang` string one of`en` `de` `narrative` string `period` string `provenance` ReportProvenance ReportProvenance · 13 fields FieldTypeDescription `data_sources` string[] · nullable Where the figures come from, for example hot:check_results or cold:check_result_archive_daily_stats. `tool` string `hot_window` ReportRange ReportRange · 5 fields FieldTypeDescription `from` string (date-time) `to` string (date-time) `clamped` boolean true when the window was cut to the plan's history. `preset` string one of`24h` `1d` `7d` `30d` `90d` `mtd` `last_month` `qtd` `ytd` `timezone` string `manifests` ReportArchiveManifest[] Each item · 4 fields FieldTypeDescription `max_created_at` string (date-time) `min_created_at` string (date-time) `sha256` string `storage_key` string `sla` ReportSLAAssessment ReportSLAAssessment · 6 fields FieldTypeDescription `evaluated` integer `inconclusive` integer `met` integer `monitors` integer `no_data` integer `partial` integer `subtitle` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet the service-level report](https://upfour.io/docs/api/reports/get-the-service-level-report.md) [NextGet report trends](https://upfour.io/docs/api/reports/get-report-trends.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/reports/downtime-ledger" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/reports/downtime-ledger", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/reports/downtime-ledger", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/reports/downtime-ledger", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "branding": { "accent_color_hex": "#294E9E", "footer_text": "Acme GmbH", "org_name": "Acme GmbH", "primary_color_hex": "#294E9E" }, "content_hash": "721378bc96ef2ddece4a66b447ba60d6a1266860d9cb23f4e0e6c152f30279cb", "coverage": { "observed_days": 1, "total_days": 31 }, "date_format": "day_month_year", "generated_at": "2026-10-27T09:00:00Z", "kpis": [ { "key": "outages_full_fidelity", "label": "Outages (full-fidelity)", "raw": 0, "value": "0" }, { "key": "downtime_full_fidelity", "label": "Downtime (full-fidelity)", "raw": 0, "unit": "s", "value": "—" }, { "key": "failed_checks", "label": "Failed checks", "raw": 0, "value": "0" }, { "key": "archive_manifests", "label": "Archive manifests", "raw": 0, "value": "0" } ], "lang": "en", "org": { "id": "00000000-0000-4000-8000-000000000001", "name": "Acme GmbH" }, "period": "27 Sep 2026 09:00:00 - 27 Oct 2026 09:00:00 (end exclusive) · UTC", "provenance": { "data_sources": [ "hot:check_results", "cold:check_result_archive_daily_stats" ], "hot_window": { "from": "2026-10-19T16:55:00Z", "to": "2026-10-27T09:00:00Z" }, "tool": "up4-reporting/0.1.0" }, "range": { "from": "2026-09-27T09:00:00Z", "preset": "30d", "timezone": "UTC", "to": "2026-10-27T09:00:00Z" }, "sections": [ { "heading": "Outage timeline", "note": "Each full-fidelity row is a maximal run of consecutive failed checks (gap-and-island over check_results.was_successful). Cold rows are reconstructed from the daily archive — exact timestamps and error samples are no longer retained for those days; verify them against the archive manifest below.", "tables": [ { "columns": [ { "header": "Monitor", "key": "monitor", "kind": "text" }, { "header": "Start (UTC)", "key": "start_utc", "kind": "timestamp" }, { "header": "End (UTC)", "key": "end_utc", "kind": "timestamp" }, { "align": "right", "header": "Duration", "key": "duration", "kind": "duration" }, { "header": "Regions", "key": "regions", "kind": "text" }, { "align": "right", "header": "Failed checks", "key": "failed_checks", "kind": "number" }, { "header": "Sample errors", "key": "sample_errors", "kind": "text" }, { "header": "Data source", "key": "data_source", "kind": "text" } ], "rows": null, "title": "Outage timeline" } ] }, { "heading": "Evidence summary", "note": "No archive manifests cover this window yet — the cold portion is summarised from daily aggregates only. The full-fidelity portion is backed directly by the live check_results store." } ], "subtitle": "Timestamped outage evidence · last 30 days", "title": "Downtime ledger — Acme GmbH", "type": "downtime_ledger" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Get report trends — API reference > Returns daily uptime, response-time and incident trend points. Source: https://upfour.io/docs/api/reports/get-report-trends [Developers](https://upfour.io/docs/api.md)/[Reports](https://upfour.io/docs/api/reports.md) Returns daily uptime, response-time and incident trend points. GET https://app.upfour.io/v1/reports/trends scope report.read stable returns 200 ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ReportTrends | | | ReportTrends · 12 fields FieldTypeDescription `incidents` IncidentTrendPoint[] · nullable Each item · 2 fields FieldTypeDescription `count` integer `date` string `response_time` ResponseTimeTrendPoint[] · nullable Each item · 3 fields FieldTypeDescription `avg_ms` number `date` string `p95_ms` number `uptime` UptimeTrendPoint[] · nullable Each item · 4 fields FieldTypeDescription `date` string `failed_checks` integer `total_checks` integer `uptime_percentage` number | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet the downtime ledger](https://upfour.io/docs/api/reports/get-the-downtime-ledger.md) [NextGet the monitor ribbon](https://upfour.io/docs/api/reports/get-the-monitor-ribbon.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/reports/trends" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/reports/trends", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/reports/trends", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/reports/trends", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "incidents": [ { "count": 1, "date": "2026-10-24" } ], "response_time": [ { "avg_ms": 97.5, "date": "2026-10-27", "p95_ms": 113.25 } ], "uptime": [ { "date": "2026-10-27", "failed_checks": 0, "total_checks": 6, "uptime_percentage": 100 } ] } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Get the monitor ribbon — API reference > Returns monitor lanes with state, check totals, uptime percentages and the selected time window. Source: https://upfour.io/docs/api/reports/get-the-monitor-ribbon [Developers](https://upfour.io/docs/api.md)/[Reports](https://upfour.io/docs/api/reports.md) Returns monitor lanes with state, check totals, uptime percentages and the selected time window. GET https://app.upfour.io/v1/reports/monitor-ribbon scope report.read stable returns 200 ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ReportMonitorRibbon | | | ReportMonitorRibbon · 14 fields FieldTypeDescription `monitors` ReportMonitorRibbonLane[] · nullable Each item · 10 fields FieldTypeDescription `checks_total` integer `id` uuid `name` string `segments` ReportRibbonSegment[] · nullable Each item · 4 fields FieldTypeDescription `end` string (date-time) `start` string (date-time) `state` string one of`down` `degraded` `monitor_name` string `state` string one of`UP` `DOWN` `PAUSED` `PENDING` `uptime_pct` number · nullable null when the window holds no checks. `window` string one of`6h` `24h` `7d` `30d` `window_end` string (date-time) `window_start` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet report trends](https://upfour.io/docs/api/reports/get-report-trends.md) [NextGet the agency ribbon](https://upfour.io/docs/api/reports/get-the-agency-ribbon.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/reports/monitor-ribbon" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/reports/monitor-ribbon", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/reports/monitor-ribbon", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/reports/monitor-ribbon", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "monitors": [ { "checks_total": 6, "id": "00000000-0000-4000-8000-000000000001", "name": "Marketing website", "segments": [], "state": "UP", "uptime_pct": 100 }, { "checks_total": 0, "id": "00000000-0000-4000-8000-000000000002", "name": "Docs site", "segments": [], "state": "PENDING", "uptime_pct": null }, { "checks_total": 0, "id": "00000000-0000-4000-8000-000000000003", "name": "Status page", "segments": [], "state": "PENDING", "uptime_pct": null } ], "window": "24h", "window_end": "2026-10-27T09:00:00Z", "window_start": "2026-10-26T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Get the agency ribbon — API reference > Returns client lanes with check totals, monitor counts, uptime percentages and the selected time window. Source: https://upfour.io/docs/api/reports/get-the-agency-ribbon [Developers](https://upfour.io/docs/api.md)/[Reports](https://upfour.io/docs/api/reports.md) Returns client lanes with check totals, monitor counts, uptime percentages and the selected time window. GET https://app.upfour.io/v1/reports/agency-ribbon scope report.read stable returns 200 ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ReportAgencyRibbon | | | ReportAgencyRibbon · 15 fields FieldTypeDescription `clients` ReportRibbonLane[] · nullable Each item · 11 fields FieldTypeDescription `checks_total` integer `id` uuid `monitors` integer `name` string `segments` ReportRibbonSegment[] · nullable Each item · 4 fields FieldTypeDescription `end` string (date-time) `start` string (date-time) `state` string one of`down` `degraded` `monitor_name` string `uptime_pct` number · nullable null when the window holds no checks. `kind` string one of`company` `client` `window` string one of`6h` `24h` `7d` `30d` `window_end` string (date-time) `window_start` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet the monitor ribbon](https://upfour.io/docs/api/reports/get-the-monitor-ribbon.md) [NextExport a report](https://upfour.io/docs/api/reports/export-a-report.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/reports/agency-ribbon" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/reports/agency-ribbon", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/reports/agency-ribbon", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/reports/agency-ribbon", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "clients": [ { "checks_total": 6, "id": "00000000-0000-4000-8000-000000000001", "kind": "company", "monitors": 3, "name": "Acme GmbH", "segments": [], "uptime_pct": 100 } ], "window": "24h", "window_end": "2026-10-27T09:00:00Z", "window_start": "2026-10-26T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Export a report — API reference > Downloads the selected report as CSV, JSON, PDF or XLSX for a chosen reporting window. Source: https://upfour.io/docs/api/reports/export-a-report [Developers](https://upfour.io/docs/api.md)/[Reports](https://upfour.io/docs/api/reports.md) Downloads the selected report as CSV, JSON, PDF or XLSX for a chosen reporting window. GET https://app.upfour.io/v1/reports/export scope report.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `type` optional | string one of`monitors` `summary` `trends` `incidents` `sla` `response-time` `downtime-ledger` default `"monitors"` | query parameter · Which report to render. | | `format` optional | string one of`csv` `json` `pdf` `xlsx` default `"csv"` | query parameter · File format of the download (default `csv`). | | `preset` optional | string one of`24h` `7d` `30d` `90d` `mtd` `last_month` `qtd` `ytd` | query parameter · Reporting window; `period` is accepted as an alias. Without `preset`, `from` and `to` the window is the last 30 days. The window is cut to the plan's history. | | `from` optional | string | query parameter · Start of the window: an RFC 3339 timestamp or a date (`YYYY-MM-DD`, in `tz`). Replaces `preset`. | | `to` optional | string | query parameter · End of the window, same formats as `from`; a date means the end of that day. Defaults to now. | | `tz` optional | string | query parameter · IANA time zone for calendar presets and dates. Defaults to the Company's report time zone. | | `lang` optional | string one of`en` `de` default `"en"` | query parameter · Language of the report text. | | `monitor_id` optional | uuid | query parameter · Only this monitor; repeat the parameter or send a comma-separated list for several. | | `severity` optional | string | query parameter · Incident reports only: keep incidents of this severity; repeat the parameter or send a comma-separated list for several. | | `q` optional | string | query parameter · Case-insensitive text: keeps monitors whose name, or incidents whose title, contains it. | | `business_hours` optional | string | query parameter · Count only these hours in SLA and response-time reports, for example `mon-fri,09:00-17:00` (several groups separated by `;`). The hours are read in `tz`, or in UTC when `tz` is not sent. | | `client_id` optional | uuid | query parameter · Report on one client. | | `all_clients` optional | boolean | query parameter · `true` includes all clients; without `client_id` the report covers the Company level. | | `target` optional | number 0–100 | query parameter · Only for `type=sla`: replaces every monitor's SLA target (percent, 0 to 100). | ## Returns A successful request returns `200` with application/json, application/pdf, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, text/csv. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `(body)` | object free-form keys | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet the agency ribbon](https://upfour.io/docs/api/reports/get-the-agency-ribbon.md) [NextList report schedules](https://upfour.io/docs/api/reports/list-report-schedules.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/reports/export?format=json" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/reports/export?format=json", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/reports/export?format=json", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/reports/export?format=json", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json {} ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # List report schedules — API reference > Returns report schedules with cadence, recipients, report type, next-run timestamps and pagination. Source: https://upfour.io/docs/api/reports/list-report-schedules [Developers](https://upfour.io/docs/api.md)/[Reports](https://upfour.io/docs/api/reports.md) Returns report schedules with cadence, recipients, report type, next-run timestamps and pagination. GET https://app.upfour.io/v1/reports/schedules scope report.read stable returns 200 ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ReportSchedule[] · nullable | | | Each item · 21 fields FieldTypeDescription `cadence` string one of`weekly` `monthly` `created_at` string (date-time) `enabled` boolean `format` string one of`csv` `json` `pdf` `xlsx` `id` uuid `language` string · nullable one of`en` `de` null when the schedule inherits. `name` string `next_run_at` string (date-time) `organization_id` uuid `params` object · nullable free-form keys The frozen report parameters; see the request. `recipients` string[] · nullable `report_type` string one of`executive_summary` `summary` `monitor_report` `monitors` `monitor_sla` `sla` `incident_analytics` `incidents` `response_time` `response-time` `downtime_ledger` `downtime-ledger` `trends` `send_hour` integer `timezone` string `updated_at` string (date-time) `client_id` uuid `created_by` uuid `day_of_month` integer `day_of_week` integer `effective_language` string one of`en` `de` The language the schedule renders in now. `last_run_at` string (date-time) | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousExport a report](https://upfour.io/docs/api/reports/export-a-report.md) [NextCreate a report schedule](https://upfour.io/docs/api/reports/create-a-report-schedule.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/reports/schedules" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/reports/schedules", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/reports/schedules", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/reports/schedules", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "cadence": "weekly", "created_at": "2026-10-27T09:00:00Z", "created_by": "00000000-0000-4000-8000-000000000001", "day_of_week": 1, "effective_language": "en", "enabled": true, "format": "pdf", "id": "00000000-0000-4000-8000-000000000002", "language": "en", "name": "Weekly summary", "next_run_at": "2026-11-01T23:55:00Z", "organization_id": "00000000-0000-4000-8000-000000000003", "params": { "preset": "7d" }, "recipients": [ "anna@example.com" ], "report_type": "summary", "send_hour": 8, "timezone": "Europe/Berlin", "updated_at": "2026-10-27T09:00:00Z" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Create a report schedule — API reference > Creates a report schedule with cadence, recipients, report type, language, timezone and frozen report parameters. Source: https://upfour.io/docs/api/reports/create-a-report-schedule [Developers](https://upfour.io/docs/api.md)/[Reports](https://upfour.io/docs/api/reports.md) Creates a report schedule with cadence, recipients, report type, language, timezone and frozen report parameters. POST https://app.upfour.io/v1/reports/schedules scope report.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Body The 4 fields most calls use come first; the rest are grouped below. `application/json` required 13 fields namestring (required) 1 to 200 characters without line breaks. In the app: **Name** report_typestring (required) executive_summarysummarymonitor_reportmonitorsmonitor_slaslaincident_analyticsincidentsresponse_timeresponse-timedowntime_ledgerdowntime-ledgertrends In the app: **Report** cadencestring (required) weeklymonthly In the app: **Cadence** recipientsstring (email)[] (required) 1 to 50 distinct email addresses. In the app: **Recipients** Timing4 fields day_of_weekinteger · nullable Required for weekly: 1 (Monday) to 7 (Sunday). day_of_monthinteger · nullable Required for monthly: 1 to 28. In the app: **Day of month** send_hourinteger Hour of the day, 0 to 23, in timezone. 0 when omitted. In the app: **Send at (hour)** timezonestring IANA time zone; UTC when omitted. In the app: **Timezone** Delivery3 fields formatstring Defaults to pdf. csvjsonpdfxlsx In the app: **Format** languagestring · nullable en or de (a region such as de-DE or the language name is accepted); null or empty inherits from the client, then the Company. In the app: **Report language** enabledboolean · nullable true when omitted, on update too. Scope and parameters2 fields client_iduuid · nullable Report on one client; omitted means the Company level. On update, omitting it keeps the schedule's client. In the app: **Scope to client (optional)** paramsobjectfree-form keys Frozen report parameters: preset, from and to (the window, as in the query of the report endpoints), target (SLA target in percent, above 0 up to 100) and filters (monitor, severity and search narrowing; region, tag and group filters are refused). Without a window a weekly schedule reports the last 7 days and a monthly one the previous month. ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ReportSchedule | | | ReportSchedule · 21 fields FieldTypeDescription `cadence` string one of`weekly` `monthly` `created_at` string (date-time) `enabled` boolean `format` string one of`csv` `json` `pdf` `xlsx` `id` uuid `language` string · nullable one of`en` `de` null when the schedule inherits. `name` string `next_run_at` string (date-time) `organization_id` uuid `params` object · nullable free-form keys The frozen report parameters; see the request. `recipients` string[] · nullable `report_type` string one of`executive_summary` `summary` `monitor_report` `monitors` `monitor_sla` `sla` `incident_analytics` `incidents` `response_time` `response-time` `downtime_ledger` `downtime-ledger` `trends` `send_hour` integer `timezone` string `updated_at` string (date-time) `client_id` uuid `created_by` uuid `day_of_month` integer `day_of_week` integer `effective_language` string one of`en` `de` The language the schedule renders in now. `last_run_at` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 402 | [billing_action_unavailable](https://upfour.io/docs/api/errors#code-billing_action_unavailable) | The Company's billing state does not allow this action. **When:** A metered or limited action while the billing state, such as paused or without credit, blocks it; details holds the action and billing_state. **Fix:** Top up the credit or change the plan, then retry. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 503 | [quota_gate_unavailable](https://upfour.io/docs/api/errors#code-quota_gate_unavailable) | The limit check itself failed, so the action was not carried out. **When:** The quota service could not decide while the request ran. **Fix:** Retry in a moment; nothing was changed. | | 403 / 429 | [quota_limit_reached](https://upfour.io/docs/api/errors#code-quota_limit_reached) | A usage or inventory limit of the Company was reached. **When:** Counters and concurrency limits answer 429 with Retry-After, inventory and capacity limits answer 403; details holds purpose, limit and window. **Fix:** On 429 wait Retry-After seconds; on 403 free something up, request a higher limit where details.raise_request is true, or buy an extension. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList report schedules](https://upfour.io/docs/api/reports/list-report-schedules.md) [NextPreview a report schedule](https://upfour.io/docs/api/reports/preview-a-report-schedule.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/reports/schedules" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "cadence": "weekly", "day_of_week": 1, "enabled": true, "format": "pdf", "language": "en", "name": "Weekly summary", "recipients": [ "anna@example.com" ], "report_type": "summary", "send_hour": 8, "timezone": "Europe/Berlin" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/reports/schedules", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ cadence: "weekly", day_of_week: 1, enabled: true, format: "pdf", language: "en", name: "Weekly summary", recipients: ["anna@example.com"], report_type: "summary", send_hour: 8, timezone: "Europe/Berlin", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/reports/schedules", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "cadence": "weekly", "day_of_week": 1, "enabled": True, "format": "pdf", "language": "en", "name": "Weekly summary", "recipients": ["anna@example.com"], "report_type": "summary", "send_hour": 8, "timezone": "Europe/Berlin", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "cadence": "weekly", "day_of_week": 1, "enabled": true, "format": "pdf", "language": "en", "name": "Weekly summary", "recipients": [ "anna@example.com" ], "report_type": "summary", "send_hour": 8, "timezone": "Europe/Berlin" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/reports/schedules", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "cadence": "weekly", "created_at": "2026-10-27T09:00:00Z", "created_by": "00000000-0000-4000-8000-000000000001", "day_of_week": 1, "effective_language": "en", "enabled": true, "format": "pdf", "id": "00000000-0000-4000-8000-000000000002", "language": "en", "name": "Weekly summary", "next_run_at": "2026-11-01T23:55:00Z", "organization_id": "00000000-0000-4000-8000-000000000003", "params": { "preset": "7d" }, "recipients": [ "anna@example.com" ], "report_type": "summary", "send_hour": 8, "timezone": "Europe/Berlin", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "billing_action_unavailable", "message": "The Company's billing state does not allow this action.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 429 ```json { "error": { "code": "quota_limit_reached", "message": "A usage or inventory limit of the Company was reached.", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "quota_gate_unavailable", "message": "The limit check itself failed, so the action was not carried out.", "request_id": "req_…" } } ``` --- # Preview a report schedule — API reference > Calculates the next run and reporting range for the supplied schedule settings. Source: https://upfour.io/docs/api/reports/preview-a-report-schedule [Developers](https://upfour.io/docs/api.md)/[Reports](https://upfour.io/docs/api/reports.md) Calculates the next run and reporting range for the supplied schedule settings. POST https://app.upfour.io/v1/reports/schedules/preview scope report.read stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Body `application/json` required 13 fields cadencestring (required) weeklymonthly namestring (required) 1 to 200 characters without line breaks. recipientsstring (email)[] (required) 1 to 50 distinct email addresses. report_typestring (required) executive_summarysummarymonitor_reportmonitorsmonitor_slaslaincident_analyticsincidentsresponse_timeresponse-timedowntime_ledgerdowntime-ledgertrends All other fields9 fields client_iduuid · nullable Report on one client; omitted means the Company level. On update, omitting it keeps the schedule's client. day_of_monthinteger · nullable Required for monthly: 1 to 28. day_of_weekinteger · nullable Required for weekly: 1 (Monday) to 7 (Sunday). enabledboolean · nullable true when omitted, on update too. formatstring Defaults to pdf. csvjsonpdfxlsx languagestring · nullable en or de (a region such as de-DE or the language name is accepted); null or empty inherits from the client, then the Company. paramsobjectfree-form keys Frozen report parameters: preset, from and to (the window, as in the query of the report endpoints), target (SLA target in percent, above 0 up to 100) and filters (monitor, severity and search narrowing; region, tag and group filters are refused). Without a window a weekly schedule reports the last 7 days and a monthly one the previous month. send_hourinteger Hour of the day, 0 to 23, in timezone. 0 when omitted. timezonestring IANA time zone; UTC when omitted. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ReportSchedulePreview | | | ReportSchedulePreview · 8 fields FieldTypeDescription `next_run_at` string (date-time) `period` string The window the next run would report, as text. `range` ReportRange ReportRange · 5 fields FieldTypeDescription `from` string (date-time) `to` string (date-time) `clamped` boolean true when the window was cut to the plan's history. `preset` string one of`24h` `1d` `7d` `30d` `90d` `mtd` `last_month` `qtd` `ytd` `timezone` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousCreate a report schedule](https://upfour.io/docs/api/reports/create-a-report-schedule.md) [NextGet a report schedule](https://upfour.io/docs/api/reports/get-a-report-schedule.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/reports/schedules/preview" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "cadence": "weekly", "day_of_week": 1, "enabled": true, "format": "pdf", "language": "en", "name": "Weekly summary", "recipients": [ "anna@example.com" ], "report_type": "summary", "send_hour": 8, "timezone": "Europe/Berlin" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/reports/schedules/preview", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ cadence: "weekly", day_of_week: 1, enabled: true, format: "pdf", language: "en", name: "Weekly summary", recipients: ["anna@example.com"], report_type: "summary", send_hour: 8, timezone: "Europe/Berlin", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/reports/schedules/preview", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "cadence": "weekly", "day_of_week": 1, "enabled": True, "format": "pdf", "language": "en", "name": "Weekly summary", "recipients": ["anna@example.com"], "report_type": "summary", "send_hour": 8, "timezone": "Europe/Berlin", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "cadence": "weekly", "day_of_week": 1, "enabled": true, "format": "pdf", "language": "en", "name": "Weekly summary", "recipients": [ "anna@example.com" ], "report_type": "summary", "send_hour": 8, "timezone": "Europe/Berlin" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/reports/schedules/preview", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "next_run_at": "2026-11-01T23:55:00Z", "period": "26 Oct 2026 00:55:00 - 2 Nov 2026 00:55:00 (end exclusive) · Europe/Berlin", "range": { "from": "2026-10-25T23:55:00Z", "preset": "7d", "timezone": "Europe/Berlin", "to": "2026-11-01T23:55:00Z" } } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Get a report schedule — API reference > Returns one report schedule with cadence, recipients, report type, next-run timestamps and frozen parameters. Source: https://upfour.io/docs/api/reports/get-a-report-schedule [Developers](https://upfour.io/docs/api.md)/[Reports](https://upfour.io/docs/api/reports.md) Returns one report schedule with cadence, recipients, report type, next-run timestamps and frozen parameters. GET https://app.upfour.io/v1/reports/schedules/{scheduleID} scope report.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `scheduleID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ReportSchedule | | | ReportSchedule · 21 fields FieldTypeDescription `cadence` string one of`weekly` `monthly` `created_at` string (date-time) `enabled` boolean `format` string one of`csv` `json` `pdf` `xlsx` `id` uuid `language` string · nullable one of`en` `de` null when the schedule inherits. `name` string `next_run_at` string (date-time) `organization_id` uuid `params` object · nullable free-form keys The frozen report parameters; see the request. `recipients` string[] · nullable `report_type` string one of`executive_summary` `summary` `monitor_report` `monitors` `monitor_sla` `sla` `incident_analytics` `incidents` `response_time` `response-time` `downtime_ledger` `downtime-ledger` `trends` `send_hour` integer `timezone` string `updated_at` string (date-time) `client_id` uuid `created_by` uuid `day_of_month` integer `day_of_week` integer `effective_language` string one of`en` `de` The language the schedule renders in now. `last_run_at` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousPreview a report schedule](https://upfour.io/docs/api/reports/preview-a-report-schedule.md) [NextUpdate a report schedule](https://upfour.io/docs/api/reports/update-a-report-schedule.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/reports/schedules/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/reports/schedules/00000000-0000-4000-8000-000000000001", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/reports/schedules/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/reports/schedules/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "cadence": "weekly", "created_at": "2026-10-27T09:00:00Z", "created_by": "00000000-0000-4000-8000-000000000002", "day_of_week": 1, "effective_language": "en", "enabled": true, "format": "pdf", "id": "00000000-0000-4000-8000-000000000001", "language": "en", "name": "Weekly summary", "next_run_at": "2026-11-01T23:55:00Z", "organization_id": "00000000-0000-4000-8000-000000000003", "params": { "preset": "7d" }, "recipients": [ "anna@example.com" ], "report_type": "summary", "send_hour": 8, "timezone": "Europe/Berlin", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Update a report schedule — API reference > Replaces a report schedule's cadence, recipients, report type, language, timezone and frozen report parameters. Source: https://upfour.io/docs/api/reports/update-a-report-schedule [Developers](https://upfour.io/docs/api.md)/[Reports](https://upfour.io/docs/api/reports.md) Replaces a report schedule's cadence, recipients, report type, language, timezone and frozen report parameters. PUT https://app.upfour.io/v1/reports/schedules/{scheduleID} scope report.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `scheduleID` required | uuid | path parameter | ## Body The 4 fields most calls use come first; the rest are grouped below. `application/json` required 13 fields namestring (required) 1 to 200 characters without line breaks. In the app: **Name** report_typestring (required) executive_summarysummarymonitor_reportmonitorsmonitor_slaslaincident_analyticsincidentsresponse_timeresponse-timedowntime_ledgerdowntime-ledgertrends In the app: **Report** cadencestring (required) weeklymonthly In the app: **Cadence** recipientsstring (email)[] (required) 1 to 50 distinct email addresses. In the app: **Recipients** Timing4 fields day_of_weekinteger · nullable Required for weekly: 1 (Monday) to 7 (Sunday). day_of_monthinteger · nullable Required for monthly: 1 to 28. In the app: **Day of month** send_hourinteger Hour of the day, 0 to 23, in timezone. 0 when omitted. In the app: **Send at (hour)** timezonestring IANA time zone; UTC when omitted. In the app: **Timezone** Delivery3 fields formatstring Defaults to pdf. csvjsonpdfxlsx In the app: **Format** languagestring · nullable en or de (a region such as de-DE or the language name is accepted); null or empty inherits from the client, then the Company. In the app: **Report language** enabledboolean · nullable true when omitted, on update too. Scope and parameters2 fields client_iduuid · nullable Report on one client; omitted means the Company level. On update, omitting it keeps the schedule's client. In the app: **Scope to client (optional)** paramsobjectfree-form keys Frozen report parameters: preset, from and to (the window, as in the query of the report endpoints), target (SLA target in percent, above 0 up to 100) and filters (monitor, severity and search narrowing; region, tag and group filters are refused). Without a window a weekly schedule reports the last 7 days and a monthly one the previous month. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ReportSchedule | | | ReportSchedule · 21 fields FieldTypeDescription `cadence` string one of`weekly` `monthly` `created_at` string (date-time) `enabled` boolean `format` string one of`csv` `json` `pdf` `xlsx` `id` uuid `language` string · nullable one of`en` `de` null when the schedule inherits. `name` string `next_run_at` string (date-time) `organization_id` uuid `params` object · nullable free-form keys The frozen report parameters; see the request. `recipients` string[] · nullable `report_type` string one of`executive_summary` `summary` `monitor_report` `monitors` `monitor_sla` `sla` `incident_analytics` `incidents` `response_time` `response-time` `downtime_ledger` `downtime-ledger` `trends` `send_hour` integer `timezone` string `updated_at` string (date-time) `client_id` uuid `created_by` uuid `day_of_month` integer `day_of_week` integer `effective_language` string one of`en` `de` The language the schedule renders in now. `last_run_at` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 402 | [billing_action_unavailable](https://upfour.io/docs/api/errors#code-billing_action_unavailable) | The Company's billing state does not allow this action. **When:** A metered or limited action while the billing state, such as paused or without credit, blocks it; details holds the action and billing_state. **Fix:** Top up the credit or change the plan, then retry. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 503 | [quota_gate_unavailable](https://upfour.io/docs/api/errors#code-quota_gate_unavailable) | The limit check itself failed, so the action was not carried out. **When:** The quota service could not decide while the request ran. **Fix:** Retry in a moment; nothing was changed. | | 403 / 429 | [quota_limit_reached](https://upfour.io/docs/api/errors#code-quota_limit_reached) | A usage or inventory limit of the Company was reached. **When:** Counters and concurrency limits answer 429 with Retry-After, inventory and capacity limits answer 403; details holds purpose, limit and window. **Fix:** On 429 wait Retry-After seconds; on 403 free something up, request a higher limit where details.raise_request is true, or buy an extension. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet a report schedule](https://upfour.io/docs/api/reports/get-a-report-schedule.md) [NextDelete a report schedule](https://upfour.io/docs/api/reports/delete-a-report-schedule.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/reports/schedules/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "cadence": "weekly", "day_of_week": 1, "enabled": true, "format": "pdf", "language": "en", "name": "Weekly summary", "recipients": [ "anna@example.com" ], "report_type": "summary", "send_hour": 9, "timezone": "Europe/Berlin" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/reports/schedules/00000000-0000-4000-8000-000000000001", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ cadence: "weekly", day_of_week: 1, enabled: true, format: "pdf", language: "en", name: "Weekly summary", recipients: ["anna@example.com"], report_type: "summary", send_hour: 9, timezone: "Europe/Berlin", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/reports/schedules/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "cadence": "weekly", "day_of_week": 1, "enabled": True, "format": "pdf", "language": "en", "name": "Weekly summary", "recipients": ["anna@example.com"], "report_type": "summary", "send_hour": 9, "timezone": "Europe/Berlin", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "cadence": "weekly", "day_of_week": 1, "enabled": true, "format": "pdf", "language": "en", "name": "Weekly summary", "recipients": [ "anna@example.com" ], "report_type": "summary", "send_hour": 9, "timezone": "Europe/Berlin" }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/reports/schedules/00000000-0000-4000-8000-000000000001", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "cadence": "weekly", "created_at": "2026-10-27T09:00:00Z", "created_by": "00000000-0000-4000-8000-000000000002", "day_of_week": 1, "effective_language": "en", "enabled": true, "format": "pdf", "id": "00000000-0000-4000-8000-000000000001", "language": "en", "name": "Weekly summary", "next_run_at": "2026-11-02T00:55:00Z", "organization_id": "00000000-0000-4000-8000-000000000003", "params": { "preset": "7d" }, "recipients": [ "anna@example.com" ], "report_type": "summary", "send_hour": 9, "timezone": "Europe/Berlin", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "billing_action_unavailable", "message": "The Company's billing state does not allow this action.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` ### 429 ```json { "error": { "code": "quota_limit_reached", "message": "A usage or inventory limit of the Company was reached.", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "quota_gate_unavailable", "message": "The limit check itself failed, so the action was not carried out.", "request_id": "req_…" } } ``` --- # Delete a report schedule — API reference > Deletes one report schedule and returns status 204 without a response body. Source: https://upfour.io/docs/api/reports/delete-a-report-schedule [Developers](https://upfour.io/docs/api.md)/[Reports](https://upfour.io/docs/api/reports.md) Deletes one report schedule and returns status 204 without a response body. DELETE https://app.upfour.io/v1/reports/schedules/{scheduleID} scope report.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `scheduleID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpdate a report schedule](https://upfour.io/docs/api/reports/update-a-report-schedule.md) [NextRun a report schedule now](https://upfour.io/docs/api/reports/run-a-report-schedule-now.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/reports/schedules/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/reports/schedules/00000000-0000-4000-8000-000000000001", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/reports/schedules/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/reports/schedules/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Run a report schedule now — API reference > Starts a report run immediately and returns its status, output details and delivery state. Source: https://upfour.io/docs/api/reports/run-a-report-schedule-now [Developers](https://upfour.io/docs/api.md)/[Reports](https://upfour.io/docs/api/reports.md) Starts a report run immediately and returns its status, output details and delivery state. POST https://app.upfour.io/v1/reports/schedules/{scheduleID}/run-now scope report.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 202 ## Parameters | Field | Type | Description | | --- | --- | --- | | `scheduleID` required | uuid | path parameter | ## Returns A successful request returns `202` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ReportRun | | | ReportRun · 23 fields FieldTypeDescription `created_at` string (date-time) `format` string one of`csv` `json` `pdf` `xlsx` `id` uuid `organization_id` uuid `params` object · nullable free-form keys The parameters the report was rendered with. `range_from` string (date-time) `range_to` string (date-time) `report_type` string one of`executive_summary` `summary` `monitor_report` `monitors` `monitor_sla` `sla` `incident_analytics` `incidents` `response_time` `response-time` `downtime_ledger` `downtime-ledger` `trends` `status` string one of`pending` `running` `retrying` `succeeded` `failed` `client_id` uuid `completed_at` string (date-time) `content_hash` string `delivery` ReportDeliveryStatus Mail delivery of a scheduled run; absent for downloads. ReportDeliveryStatus · 4 fields FieldTypeDescription `attempts` integer `sent` integer `total` integer `next_attempt_at` string (date-time) `error` string Why a run failed or was skipped. `output_bytes` integer `output_sha256` string `requested_by` uuid `schedule_id` uuid `storage_key` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 402 | [billing_action_unavailable](https://upfour.io/docs/api/errors#code-billing_action_unavailable) | The Company's billing state does not allow this action. **When:** A metered or limited action while the billing state, such as paused or without credit, blocks it; details holds the action and billing_state. **Fix:** Top up the credit or change the plan, then retry. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 503 | [quota_gate_unavailable](https://upfour.io/docs/api/errors#code-quota_gate_unavailable) | The limit check itself failed, so the action was not carried out. **When:** The quota service could not decide while the request ran. **Fix:** Retry in a moment; nothing was changed. | | 403 / 429 | [quota_limit_reached](https://upfour.io/docs/api/errors#code-quota_limit_reached) | A usage or inventory limit of the Company was reached. **When:** Counters and concurrency limits answer 429 with Retry-After, inventory and capacity limits answer 403; details holds purpose, limit and window. **Fix:** On 429 wait Retry-After seconds; on 403 free something up, request a higher limit where details.raise_request is true, or buy an extension. | | 503 | [service_unavailable](https://upfour.io/docs/api/errors#code-service_unavailable) | The service or a required dependency is temporarily unavailable. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousDelete a report schedule](https://upfour.io/docs/api/reports/delete-a-report-schedule.md) [NextList report runs](https://upfour.io/docs/api/reports/list-report-runs.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/reports/schedules/00000000-0000-4000-8000-000000000001/run-now" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/reports/schedules/00000000-0000-4000-8000-000000000001/run-now", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/reports/schedules/00000000-0000-4000-8000-000000000001/run-now", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/reports/schedules/00000000-0000-4000-8000-000000000001/run-now", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 202 ```json { "data": { "content_hash": "3c8dc35de8b20338c83d488ff7e387f6ff1542a8747f8aa45665c4bacefe149a", "created_at": "2026-10-27T09:00:00Z", "delivery": { "attempts": 1, "next_attempt_at": "2026-10-27T09:00:00Z", "sent": 0, "total": 1 }, "error": "1 recipient(s) could not be reached: anna@example.com: email delivery is not configured", "format": "pdf", "id": "00000000-0000-4000-8000-000000000002", "organization_id": "00000000-0000-4000-8000-000000000003", "output_bytes": 18432, "output_sha256": "721378bc96ef2ddece4a66b447ba60d6a1266860d9cb23f4e0e6c152f30279cb", "params": { "from": "2026-10-20T09:00:00Z", "lang": "en", "preset": "7d", "scope": { "HideCompanyLevel": false, "ID": "00000000-0000-4000-8000-000000000004", "Mode": 1, "TeamVisibleClientIDs": null }, "timezone": "Europe/Berlin", "to": "2026-10-27T09:00:00Z" }, "range_from": "2026-10-20T09:00:00Z", "range_to": "2026-10-27T09:00:00Z", "report_type": "summary", "requested_by": "00000000-0000-4000-8000-000000000005", "schedule_id": "00000000-0000-4000-8000-000000000001", "status": "retrying", "storage_key": "example/reports/v1/00000000-0000-4000-8000-000000000003/00000000-0000-4000-8000-000000000002.pdf" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "billing_action_unavailable", "message": "The Company's billing state does not allow this action.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` ### 429 ```json { "error": { "code": "quota_limit_reached", "message": "A usage or inventory limit of the Company was reached.", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "quota_gate_unavailable", "message": "The limit check itself failed, so the action was not carried out.", "request_id": "req_…" } } ``` --- # List report runs — API reference > Returns report run status, output details, ranges, formats and pagination. Source: https://upfour.io/docs/api/reports/list-report-runs [Developers](https://upfour.io/docs/api.md)/[Reports](https://upfour.io/docs/api/reports.md) Returns report run status, output details, ranges, formats and pagination. GET https://app.upfour.io/v1/reports/runs scope report.read stable returns 200 ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ReportRun[] · nullable | | | Each item · 23 fields FieldTypeDescription `created_at` string (date-time) `format` string one of`csv` `json` `pdf` `xlsx` `id` uuid `organization_id` uuid `params` object · nullable free-form keys The parameters the report was rendered with. `range_from` string (date-time) `range_to` string (date-time) `report_type` string one of`executive_summary` `summary` `monitor_report` `monitors` `monitor_sla` `sla` `incident_analytics` `incidents` `response_time` `response-time` `downtime_ledger` `downtime-ledger` `trends` `status` string one of`pending` `running` `retrying` `succeeded` `failed` `client_id` uuid `completed_at` string (date-time) `content_hash` string `delivery` ReportDeliveryStatus Mail delivery of a scheduled run; absent for downloads. ReportDeliveryStatus · 4 fields FieldTypeDescription `attempts` integer `sent` integer `total` integer `next_attempt_at` string (date-time) `error` string Why a run failed or was skipped. `output_bytes` integer `output_sha256` string `requested_by` uuid `schedule_id` uuid `storage_key` string | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousRun a report schedule now](https://upfour.io/docs/api/reports/run-a-report-schedule-now.md) [NextList report share links](https://upfour.io/docs/api/reports/list-report-share-links.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/reports/runs" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/reports/runs", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/reports/runs", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/reports/runs", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "content_hash": "721378bc96ef2ddece4a66b447ba60d6a1266860d9cb23f4e0e6c152f30279cb", "created_at": "2026-10-27T09:00:00Z", "delivery": { "attempts": 1, "next_attempt_at": "2026-10-27T09:00:00Z", "sent": 0, "total": 1 }, "error": "1 recipient(s) could not be reached: anna@example.com: email delivery is not configured", "format": "pdf", "id": "00000000-0000-4000-8000-000000000001", "organization_id": "00000000-0000-4000-8000-000000000002", "output_bytes": 18432, "output_sha256": "3c8dc35de8b20338c83d488ff7e387f6ff1542a8747f8aa45665c4bacefe149a", "params": { "from": "2026-10-20T09:00:00Z", "lang": "en", "preset": "7d", "scope": { "HideCompanyLevel": false, "ID": "00000000-0000-4000-8000-000000000003", "Mode": 1, "TeamVisibleClientIDs": null }, "timezone": "Europe/Berlin", "to": "2026-10-27T09:00:00Z" }, "range_from": "2026-10-20T09:00:00Z", "range_to": "2026-10-27T09:00:00Z", "report_type": "summary", "requested_by": "00000000-0000-4000-8000-000000000004", "schedule_id": "00000000-0000-4000-8000-000000000005", "status": "retrying", "storage_key": "example/reports/v1/00000000-0000-4000-8000-000000000002/00000000-0000-4000-8000-000000000001.pdf" }, { "completed_at": "2026-10-27T09:00:00Z", "content_hash": "67720e645b28c194f156531039886954216b868a49b6b75701842fc7ca1e80ee", "created_at": "2026-10-27T09:00:00Z", "format": "json", "id": "00000000-0000-4000-8000-000000000006", "organization_id": "00000000-0000-4000-8000-000000000002", "params": { "from": "2026-09-27T09:00:00Z", "preset": "30d", "scope": { "HideCompanyLevel": false, "ID": "00000000-0000-4000-8000-000000000003", "Mode": 1, "TeamVisibleClientIDs": null }, "timezone": "UTC", "to": "2026-10-27T09:00:00Z" }, "range_from": "2026-09-27T09:00:00Z", "range_to": "2026-10-27T09:00:00Z", "report_type": "monitor_report", "requested_by": "00000000-0000-4000-8000-000000000004", "status": "succeeded" }, { "completed_at": "2026-10-27T09:00:00Z", "content_hash": "1a6f66d8015109dd9fad363cb57981130e563515228285185bcdf1bff74ebb8d", "created_at": "2026-10-27T09:00:00Z", "format": "csv", "id": "00000000-0000-4000-8000-000000000007", "organization_id": "00000000-0000-4000-8000-000000000002", "params": { "from": "2026-09-27T09:00:00Z", "preset": "30d", "scope": { "HideCompanyLevel": false, "ID": "00000000-0000-4000-8000-000000000003", "Mode": 1, "TeamVisibleClientIDs": null }, "timezone": "UTC", "to": "2026-10-27T09:00:00Z" }, "range_from": "2026-09-27T09:00:00Z", "range_to": "2026-10-27T09:00:00Z", "report_type": "monitor_report", "requested_by": "00000000-0000-4000-8000-000000000004", "status": "succeeded" }, { "completed_at": "2026-10-27T09:00:00Z", "content_hash": "ed686270eb166d01c13673f8f7e9cccb4c08251cea4f1a25809307f592b32a44", "created_at": "2026-10-27T09:00:00Z", "format": "json", "id": "00000000-0000-4000-8000-000000000008", "organization_id": "00000000-0000-4000-8000-000000000002", "params": { "from": "2026-09-27T09:00:00Z", "preset": "30d", "scope": { "HideCompanyLevel": false, "ID": "00000000-0000-4000-8000-000000000003", "Mode": 1, "TeamVisibleClientIDs": null }, "timezone": "UTC", "to": "2026-10-27T09:00:00Z" }, "range_from": "2026-09-27T09:00:00Z", "range_to": "2026-10-27T09:00:00Z", "report_type": "downtime_ledger", "requested_by": "00000000-0000-4000-8000-000000000004", "status": "succeeded" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # List report share links — API reference > Returns report share links with labels, expiration, ranges, formats, view counts and pagination. Source: https://upfour.io/docs/api/reports/list-report-share-links [Developers](https://upfour.io/docs/api.md)/[Reports](https://upfour.io/docs/api/reports.md) Returns report share links with labels, expiration, ranges, formats, view counts and pagination. GET https://app.upfour.io/v1/reports/share-links scope report.read stable returns 200 ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ReportShareLink[] · nullable | | | Each item · 14 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `label` string `organization_id` uuid `report_run_id` uuid `view_count` integer `client_id` uuid `created_by` uuid `expires_at` string (date-time) Absent for a link that never expires. `format` string one of`csv` `json` `pdf` `xlsx` `range_from` string (date-time) `range_to` string (date-time) `report_type` string one of`executive_summary` `summary` `monitor_report` `monitors` `monitor_sla` `sla` `incident_analytics` `incidents` `response_time` `response-time` `downtime_ledger` `downtime-ledger` `trends` `revoked_at` string (date-time) | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList report runs](https://upfour.io/docs/api/reports/list-report-runs.md) [NextCreate a report share link](https://upfour.io/docs/api/reports/create-a-report-share-link.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/reports/share-links" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/reports/share-links", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/reports/share-links", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/reports/share-links", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "created_at": "2026-10-27T09:00:00Z", "created_by": "00000000-0000-4000-8000-000000000001", "expires_at": "2027-01-25T09:00:00Z", "format": "pdf", "id": "00000000-0000-4000-8000-000000000002", "label": "Review with the customer", "organization_id": "00000000-0000-4000-8000-000000000003", "range_from": "2026-10-20T09:00:00Z", "range_to": "2026-10-27T09:00:00Z", "report_run_id": "00000000-0000-4000-8000-000000000004", "report_type": "summary", "view_count": 0 }, { "created_at": "2026-10-27T09:00:00Z", "created_by": "00000000-0000-4000-8000-000000000001", "expires_at": "2027-01-25T09:00:00Z", "format": "pdf", "id": "00000000-0000-4000-8000-000000000005", "label": "Weekly summary", "organization_id": "00000000-0000-4000-8000-000000000003", "range_from": "2026-10-20T09:00:00Z", "range_to": "2026-10-27T09:00:00Z", "report_run_id": "00000000-0000-4000-8000-000000000004", "report_type": "summary", "view_count": 0 } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Create a report share link — API reference > Creates a share link for a report run with an optional label, expiry time or never-expire setting. Source: https://upfour.io/docs/api/reports/create-a-report-share-link [Developers](https://upfour.io/docs/api.md)/[Reports](https://upfour.io/docs/api/reports.md) Creates a share link for a report run with an optional label, expiry time or never-expire setting. POST https://app.upfour.io/v1/reports/{runID}/share scope report.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Parameters | Field | Type | Description | | --- | --- | --- | | `runID` required | uuid | path parameter | ## Body `application/json` optional 3 fields All other fields3 fields expires_atstring (date-time) · nullable When the link stops working. Without it and without never_expires the link works for 90 days. labelstring A note for yourself; shown in the list of links. never_expiresboolean A link without an end. Cannot be combined with expires_at. ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ReportShareLinkCreated | | | ReportShareLinkCreated · 17 fields FieldTypeDescription `link` ReportShareLink ReportShareLink · 14 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `label` string `organization_id` uuid `report_run_id` uuid `view_count` integer `client_id` uuid `created_by` uuid `expires_at` string (date-time) Absent for a link that never expires. `format` string one of`csv` `json` `pdf` `xlsx` `range_from` string (date-time) `range_to` string (date-time) `report_type` string one of`executive_summary` `summary` `monitor_report` `monitors` `monitor_sla` `sla` `incident_analytics` `incidents` `response_time` `response-time` `downtime_ledger` `downtime-ledger` `trends` `revoked_at` string (date-time) `token` string The secret part of the link; shown only in this answer. `url` string The public link of the snapshot, on the Company's report domain when one is set. | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 402 | [billing_action_unavailable](https://upfour.io/docs/api/errors#code-billing_action_unavailable) | The Company's billing state does not allow this action. **When:** A metered or limited action while the billing state, such as paused or without credit, blocks it; details holds the action and billing_state. **Fix:** Top up the credit or change the plan, then retry. | | 409 | [conflict](https://upfour.io/docs/api/errors#code-conflict) | The request conflicts with the current state. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 503 | [quota_gate_unavailable](https://upfour.io/docs/api/errors#code-quota_gate_unavailable) | The limit check itself failed, so the action was not carried out. **When:** The quota service could not decide while the request ran. **Fix:** Retry in a moment; nothing was changed. | | 403 / 429 | [quota_limit_reached](https://upfour.io/docs/api/errors#code-quota_limit_reached) | A usage or inventory limit of the Company was reached. **When:** Counters and concurrency limits answer 429 with Retry-After, inventory and capacity limits answer 403; details holds purpose, limit and window. **Fix:** On 429 wait Retry-After seconds; on 403 free something up, request a higher limit where details.raise_request is true, or buy an extension. | | 503 | [service_unavailable](https://upfour.io/docs/api/errors#code-service_unavailable) | The service or a required dependency is temporarily unavailable. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList report share links](https://upfour.io/docs/api/reports/list-report-share-links.md) [NextDelete a report share link](https://upfour.io/docs/api/reports/delete-a-report-share-link.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/reports/00000000-0000-4000-8000-000000000001/share" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "label": "Review with the customer", "never_expires": false }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/reports/00000000-0000-4000-8000-000000000001/share", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ label: "Review with the customer", never_expires: false, }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/reports/00000000-0000-4000-8000-000000000001/share", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "label": "Review with the customer", "never_expires": False, }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "label": "Review with the customer", "never_expires": false }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/reports/00000000-0000-4000-8000-000000000001/share", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "link": { "created_at": "2026-10-27T09:00:00Z", "created_by": "00000000-0000-4000-8000-000000000002", "expires_at": "2027-01-25T09:00:00Z", "format": "pdf", "id": "00000000-0000-4000-8000-000000000003", "label": "Review with the customer", "organization_id": "00000000-0000-4000-8000-000000000004", "range_from": "2026-10-20T09:00:00Z", "range_to": "2026-10-27T09:00:00Z", "report_run_id": "00000000-0000-4000-8000-000000000001", "report_type": "summary", "view_count": 0 }, "token": "••••", "url": "http://example.com:3001/share/reports/••••" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "billing_action_unavailable", "message": "The Company's billing state does not allow this action.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` ### 409 ```json { "error": { "code": "conflict", "message": "The request conflicts with the current state.", "request_id": "req_…" } } ``` ### 429 ```json { "error": { "code": "quota_limit_reached", "message": "A usage or inventory limit of the Company was reached.", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "quota_gate_unavailable", "message": "The limit check itself failed, so the action was not carried out.", "request_id": "req_…" } } ``` --- # Delete a report share link — API reference > Deletes one report share link and returns status 204 without a response body. Source: https://upfour.io/docs/api/reports/delete-a-report-share-link [Developers](https://upfour.io/docs/api.md)/[Reports](https://upfour.io/docs/api/reports.md) Deletes one report share link and returns status 204 without a response body. DELETE https://app.upfour.io/v1/reports/share-links/{linkID} scope report.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `linkID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousCreate a report share link](https://upfour.io/docs/api/reports/create-a-report-share-link.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/reports/share-links/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/reports/share-links/00000000-0000-4000-8000-000000000001", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/reports/share-links/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/reports/share-links/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # SLA policies — API reference > Targets and business-hours masks that reports measure against. REST endpoints with request and response fields, examples in curl, JavaScript, Python, Go and PHP, and an in-browser Try it console. Source: https://upfour.io/docs/api/sla-policies [Developers](https://upfour.io/docs/api.md)/Pages & reporting Targets and business-hours masks that reports measure against. Base URL `https://app.upfour.io/v1`. Send your key as `Authorization: Bearer `; every key acts on your real Company, so [test without paging anyone](https://upfour.io/docs/api/testing.md). New here? Start with the [quickstart](https://upfour.io/docs/api/quickstart.md). The examples read the key from `UP4_API_KEY`. ## Everyday [GET **List SLA policies** /sla-policies](https://upfour.io/docs/api/sla-policies/list-sla-policies.md)[POST **Create an SLA policy** /sla-policies](https://upfour.io/docs/api/sla-policies/create-an-sla-policy.md)[GET **Get an SLA policy** /sla-policies/{policyID}](https://upfour.io/docs/api/sla-policies/get-an-sla-policy.md)[PUT **Update an SLA policy** /sla-policies/{policyID}](https://upfour.io/docs/api/sla-policies/update-an-sla-policy.md) ## Control [DEL **Delete an SLA policy** /sla-policies/{policyID}](https://upfour.io/docs/api/sla-policies/delete-an-sla-policy.md) --- # List SLA policies — API reference > Returns service-level targets with monitor scope, rolling windows and pagination. Source: https://upfour.io/docs/api/sla-policies/list-sla-policies [Developers](https://upfour.io/docs/api.md)/[SLA policies](https://upfour.io/docs/api/sla-policies.md) Returns service-level targets with monitor scope, rolling windows and pagination. GET https://app.upfour.io/v1/sla-policies scope report.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `limit` optional | integer 1–250 default `50` | query parameter | | `cursor` optional | string | query parameter | | `client_id` optional | uuid | query parameter | | `all_clients` optional | boolean | query parameter | | `q` optional | string | query parameter | | `sort` optional | string | query parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | SLAPolicy[] · nullable | | | Each item · 14 fields FieldTypeDescription `created_at` string (date-time) `degraded_counts_as_violation` boolean `id` uuid `name` string `organization_id` uuid `target_pct` number The uptime target in percent. `updated_at` string (date-time) `window_days` integer `business_hours` BusinessHoursResponse BusinessHoursResponse · 2 fields FieldTypeDescription `days` map of string[][] · nullable The weekdays measured, keyed by ISO weekday from 1 (Monday) to 7 (Sunday); each lists its \[start, end) windows as HH:MM pairs in tz. A day with no windows is excluded; at least one window is required, and windows of a day may not overlap. `tz` string IANA time zone name; UTC when empty. `client_id` uuid `created_by` uuid `monitor_id` uuid | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [NextCreate an SLA policy](https://upfour.io/docs/api/sla-policies/create-an-sla-policy.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/sla-policies" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/sla-policies", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/sla-policies", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/sla-policies", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "created_at": "2026-10-27T09:00:00Z", "created_by": "00000000-0000-4000-8000-000000000001", "degraded_counts_as_violation": false, "id": "00000000-0000-4000-8000-000000000002", "monitor_id": "00000000-0000-4000-8000-000000000003", "name": "Checkout 99.9", "organization_id": "00000000-0000-4000-8000-000000000004", "target_pct": 99.9, "updated_at": "2026-10-27T09:00:00Z", "window_days": 30 } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Create an SLA policy — API reference > Creates an uptime target with an optional monitor or Client scope, rolling window and business hours. Source: https://upfour.io/docs/api/sla-policies/create-an-sla-policy [Developers](https://upfour.io/docs/api.md)/[SLA policies](https://upfour.io/docs/api/sla-policies.md) Creates an uptime target with an optional monitor or Client scope, rolling window and business hours. POST https://app.upfour.io/v1/sla-policies scope report.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Body `application/json` required 7 fields namestring (required) target_pctnumber (required) The uptime target in percent, above 0 and at most 100, e.g. 99.9. All other fields7 fields business_hoursBusinessHours · nullable Restricts the measurement to these hours; without it the policy measures around the clock. Nested fields 2 fields | Field | Type | Description | | --- | --- | --- | | `days` optional | map of string[][] | The weekdays measured, keyed by ISO weekday from 1 (Monday) to 7 (Sunday); each lists its \[start, end) windows as HH:MM pairs in tz. A day with no windows is excluded; at least one window is required, and windows of a day may not overlap. | | `tz` optional | string | IANA time zone name; UTC when empty. | client_iduuid · nullable degraded_counts_as_violationboolean monitor_iduuid · nullable Scopes the policy to one monitor; without it the policy is the default of its client or Company. window_daysinteger The rolling window, 1 to 730 days; 30 when omitted or 0. ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | SLAPolicy | | | SLAPolicy · 14 fields FieldTypeDescription `created_at` string (date-time) `degraded_counts_as_violation` boolean `id` uuid `name` string `organization_id` uuid `target_pct` number The uptime target in percent. `updated_at` string (date-time) `window_days` integer `business_hours` BusinessHoursResponse BusinessHoursResponse · 2 fields FieldTypeDescription `days` map of string[][] · nullable The weekdays measured, keyed by ISO weekday from 1 (Monday) to 7 (Sunday); each lists its \[start, end) windows as HH:MM pairs in tz. A day with no windows is excluded; at least one window is required, and windows of a day may not overlap. `tz` string IANA time zone name; UTC when empty. `client_id` uuid `created_by` uuid `monitor_id` uuid | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList SLA policies](https://upfour.io/docs/api/sla-policies/list-sla-policies.md) [NextGet an SLA policy](https://upfour.io/docs/api/sla-policies/get-an-sla-policy.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/sla-policies" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "degraded_counts_as_violation": false, "monitor_id": "00000000-0000-4000-8000-000000000001", "name": "Checkout 99.9", "target_pct": 99.9, "window_days": 30 }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/sla-policies", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ degraded_counts_as_violation: false, monitor_id: "00000000-0000-4000-8000-000000000001", name: "Checkout 99.9", target_pct: 99.9, window_days: 30, }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/sla-policies", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "degraded_counts_as_violation": False, "monitor_id": "00000000-0000-4000-8000-000000000001", "name": "Checkout 99.9", "target_pct": 99.9, "window_days": 30, }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "degraded_counts_as_violation": false, "monitor_id": "00000000-0000-4000-8000-000000000001", "name": "Checkout 99.9", "target_pct": 99.9, "window_days": 30 }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/sla-policies", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "created_by": "00000000-0000-4000-8000-000000000002", "degraded_counts_as_violation": false, "id": "00000000-0000-4000-8000-000000000003", "monitor_id": "00000000-0000-4000-8000-000000000001", "name": "Checkout 99.9", "organization_id": "00000000-0000-4000-8000-000000000004", "target_pct": 99.9, "updated_at": "2026-10-27T09:00:00Z", "window_days": 30 } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Get an SLA policy — API reference > Returns one SLA policy with its uptime target, scope, rolling window and business hours. Source: https://upfour.io/docs/api/sla-policies/get-an-sla-policy [Developers](https://upfour.io/docs/api.md)/[SLA policies](https://upfour.io/docs/api/sla-policies.md) Returns one SLA policy with its uptime target, scope, rolling window and business hours. GET https://app.upfour.io/v1/sla-policies/{policyID} scope report.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `policyID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | SLAPolicy | | | SLAPolicy · 14 fields FieldTypeDescription `created_at` string (date-time) `degraded_counts_as_violation` boolean `id` uuid `name` string `organization_id` uuid `target_pct` number The uptime target in percent. `updated_at` string (date-time) `window_days` integer `business_hours` BusinessHoursResponse BusinessHoursResponse · 2 fields FieldTypeDescription `days` map of string[][] · nullable The weekdays measured, keyed by ISO weekday from 1 (Monday) to 7 (Sunday); each lists its \[start, end) windows as HH:MM pairs in tz. A day with no windows is excluded; at least one window is required, and windows of a day may not overlap. `tz` string IANA time zone name; UTC when empty. `client_id` uuid `created_by` uuid `monitor_id` uuid | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousCreate an SLA policy](https://upfour.io/docs/api/sla-policies/create-an-sla-policy.md) [NextUpdate an SLA policy](https://upfour.io/docs/api/sla-policies/update-an-sla-policy.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/sla-policies/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/sla-policies/00000000-0000-4000-8000-000000000001", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/sla-policies/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/sla-policies/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "created_by": "00000000-0000-4000-8000-000000000002", "degraded_counts_as_violation": false, "id": "00000000-0000-4000-8000-000000000001", "monitor_id": "00000000-0000-4000-8000-000000000003", "name": "Checkout 99.9", "organization_id": "00000000-0000-4000-8000-000000000004", "target_pct": 99.9, "updated_at": "2026-10-27T09:00:00Z", "window_days": 30 } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Update an SLA policy — API reference > Replaces an SLA policy's uptime target, scope, rolling window and business hours. Source: https://upfour.io/docs/api/sla-policies/update-an-sla-policy [Developers](https://upfour.io/docs/api.md)/[SLA policies](https://upfour.io/docs/api/sla-policies.md) Replaces an SLA policy's uptime target, scope, rolling window and business hours. PUT https://app.upfour.io/v1/sla-policies/{policyID} scope report.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `policyID` required | uuid | path parameter | ## Body `application/json` required 7 fields namestring (required) target_pctnumber (required) The uptime target in percent, above 0 and at most 100, e.g. 99.9. All other fields7 fields business_hoursBusinessHours · nullable Restricts the measurement to these hours; without it the policy measures around the clock. Nested fields 2 fields | Field | Type | Description | | --- | --- | --- | | `days` optional | map of string[][] | The weekdays measured, keyed by ISO weekday from 1 (Monday) to 7 (Sunday); each lists its \[start, end) windows as HH:MM pairs in tz. A day with no windows is excluded; at least one window is required, and windows of a day may not overlap. | | `tz` optional | string | IANA time zone name; UTC when empty. | client_iduuid · nullable degraded_counts_as_violationboolean monitor_iduuid · nullable Scopes the policy to one monitor; without it the policy is the default of its client or Company. window_daysinteger The rolling window, 1 to 730 days; 30 when omitted or 0. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | SLAPolicy | | | SLAPolicy · 14 fields FieldTypeDescription `created_at` string (date-time) `degraded_counts_as_violation` boolean `id` uuid `name` string `organization_id` uuid `target_pct` number The uptime target in percent. `updated_at` string (date-time) `window_days` integer `business_hours` BusinessHoursResponse BusinessHoursResponse · 2 fields FieldTypeDescription `days` map of string[][] · nullable The weekdays measured, keyed by ISO weekday from 1 (Monday) to 7 (Sunday); each lists its \[start, end) windows as HH:MM pairs in tz. A day with no windows is excluded; at least one window is required, and windows of a day may not overlap. `tz` string IANA time zone name; UTC when empty. `client_id` uuid `created_by` uuid `monitor_id` uuid | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet an SLA policy](https://upfour.io/docs/api/sla-policies/get-an-sla-policy.md) [NextDelete an SLA policy](https://upfour.io/docs/api/sla-policies/delete-an-sla-policy.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/sla-policies/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "degraded_counts_as_violation": true, "monitor_id": "00000000-0000-4000-8000-000000000002", "name": "Checkout 99.95", "target_pct": 99.95, "window_days": 30 }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/sla-policies/00000000-0000-4000-8000-000000000001", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ degraded_counts_as_violation: true, monitor_id: "00000000-0000-4000-8000-000000000002", name: "Checkout 99.95", target_pct: 99.95, window_days: 30, }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/sla-policies/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "degraded_counts_as_violation": True, "monitor_id": "00000000-0000-4000-8000-000000000002", "name": "Checkout 99.95", "target_pct": 99.95, "window_days": 30, }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "degraded_counts_as_violation": true, "monitor_id": "00000000-0000-4000-8000-000000000002", "name": "Checkout 99.95", "target_pct": 99.95, "window_days": 30 }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/sla-policies/00000000-0000-4000-8000-000000000001", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "created_by": "00000000-0000-4000-8000-000000000003", "degraded_counts_as_violation": true, "id": "00000000-0000-4000-8000-000000000001", "monitor_id": "00000000-0000-4000-8000-000000000002", "name": "Checkout 99.95", "organization_id": "00000000-0000-4000-8000-000000000004", "target_pct": 99.95, "updated_at": "2026-10-27T09:00:00Z", "window_days": 30 } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Delete an SLA policy — API reference > Deletes one SLA policy and returns status 204 without a response body. Source: https://upfour.io/docs/api/sla-policies/delete-an-sla-policy [Developers](https://upfour.io/docs/api.md)/[SLA policies](https://upfour.io/docs/api/sla-policies.md) Deletes one SLA policy and returns status 204 without a response body. DELETE https://app.upfour.io/v1/sla-policies/{policyID} scope report.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `policyID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpdate an SLA policy](https://upfour.io/docs/api/sla-policies/update-an-sla-policy.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/sla-policies/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/sla-policies/00000000-0000-4000-8000-000000000001", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/sla-policies/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/sla-policies/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Organization — API reference > Company-level settings. REST endpoints with request and response fields, examples in curl, JavaScript, Python, Go and PHP, and an in-browser Try it console. Source: https://upfour.io/docs/api/organization [Developers](https://upfour.io/docs/api.md)/Organization Company-level settings. Base URL `https://app.upfour.io/v1`. Send your key as `Authorization: Bearer `; every key acts on your real Company, so [test without paging anyone](https://upfour.io/docs/api/testing.md). New here? Start with the [quickstart](https://upfour.io/docs/api/quickstart.md). The examples read the key from `UP4_API_KEY`. ## Everyday [GET **Get company details** /organization](https://upfour.io/docs/api/organization/get-company-details.md)[POST **Create a company** /organization](https://upfour.io/docs/api/organization/create-a-company.md)[PUT **Update a company** /organization](https://upfour.io/docs/api/organization/update-a-company.md)[DEL **Delete a company** /organization](https://upfour.io/docs/api/organization/delete-a-company.md) ## Members and access [GET **List company members** /organization/members](https://upfour.io/docs/api/organization/list-company-members.md)[GET **Get a company member by email** /organization/members/email/{email}](https://upfour.io/docs/api/organization/get-a-company-member-by-email.md)[PUT **Update a member role** /organization/members/{memberID}/role](https://upfour.io/docs/api/organization/update-a-member-role.md)[DEL **Delete a member by email** /organization/members/email/{email}](https://upfour.io/docs/api/organization/delete-a-member-by-email.md)[DEL **Delete a member** /organization/members/{memberID}](https://upfour.io/docs/api/organization/delete-a-member.md) ## Settings [GET **Get report branding** /organization/report-branding](https://upfour.io/docs/api/organization/get-report-branding.md)[PUT **Update report branding** /organization/report-branding](https://upfour.io/docs/api/organization/update-report-branding.md)[GET **Get company security settings** /organization/security](https://upfour.io/docs/api/organization/get-company-security-settings.md)[PUT **Update company security settings** /organization/security](https://upfour.io/docs/api/organization/update-company-security-settings.md)[GET **List SSO providers** /organization/sso](https://upfour.io/docs/api/organization/list-sso-providers.md)[PUT **Update an SSO provider** /organization/sso/{provider}](https://upfour.io/docs/api/organization/update-an-sso-provider.md) ## Usage and history [GET **Get AI usage** /organization/ai-usage](https://upfour.io/docs/api/organization/get-ai-usage.md)[PUT **Update AI quota** /organization/ai-quota](https://upfour.io/docs/api/organization/update-ai-quota.md)[GET **List audit logs** /organization/audit-logs](https://upfour.io/docs/api/organization/list-audit-logs.md) --- # Get company details — API reference > Returns the Company name, slug, owner ID, settings and creation and update timestamps. Source: https://upfour.io/docs/api/organization/get-company-details [Developers](https://upfour.io/docs/api.md)/[Organization](https://upfour.io/docs/api/organization.md) Returns the Company name, slug, owner ID, settings and creation and update timestamps. GET https://app.upfour.io/v1/organization scope organization.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `limit` optional | integer 1–250 default `50` | query parameter | | `cursor` optional | string | query parameter | | `client_id` optional | uuid | query parameter | | `all_clients` optional | boolean | query parameter | | `q` optional | string | query parameter | | `sort` optional | string | query parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | Organization | | | Organization · 8 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `name` string `owner_id` uuid `settings` object · nullable free-form keys Company settings, for example timezone, date_format (day_month_year, month_day_year or iso), default_language (en or de) and incident_screenshots_default. The audit drain secret is never returned. `slug` string `updated_at` string (date-time) `deleted_at` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [NextCreate a company](https://upfour.io/docs/api/organization/create-a-company.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/organization" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/organization", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/organization", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/organization", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000001", "name": "Acme GmbH", "owner_id": "00000000-0000-4000-8000-000000000002", "settings": { "allow_ai_features": true }, "slug": "acme-gmbh", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Create a company — API reference > Creates a Company with the supplied name. Source: https://upfour.io/docs/api/organization/create-a-company [Developers](https://upfour.io/docs/api.md)/[Organization](https://upfour.io/docs/api/organization.md) Creates a Company with the supplied name. POST https://app.upfour.io/v1/organization scope organization.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Body `application/json` required 1 field namestring (required) ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | Organization | | | Organization · 8 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `name` string `owner_id` uuid `settings` object · nullable free-form keys Company settings, for example timezone, date_format (day_month_year, month_day_year or iso), default_language (en or de) and incident_screenshots_default. The audit drain secret is never returned. `slug` string `updated_at` string (date-time) `deleted_at` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 409 | [conflict](https://upfour.io/docs/api/errors#code-conflict) | The request conflicts with the current state. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet company details](https://upfour.io/docs/api/organization/get-company-details.md) [NextUpdate a company](https://upfour.io/docs/api/organization/update-a-company.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/organization" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Example" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/organization", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "Example", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/organization", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "name": "Example", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "name": "Example" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/organization", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "created_at": "2026-01-15T09:30:00Z", "id": "3f6c1a52-8b1e-4f0d-9a43-2c5d7e8f1a90", "name": "Example", "owner_id": "3f6c1a52-8b1e-4f0d-9a43-2c5d7e8f1a90", "settings": {}, "slug": "example", "updated_at": "2026-01-15T09:30:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 409 ```json { "error": { "code": "conflict", "message": "The request conflicts with the current state.", "request_id": "req_…" } } ``` --- # Update a company — API reference > Updates the Company name, settings or slug, with optional slug regeneration from the new name. Source: https://upfour.io/docs/api/organization/update-a-company [Developers](https://upfour.io/docs/api.md)/[Organization](https://upfour.io/docs/api/organization.md) Updates the Company name, settings or slug, with optional slug regeneration from the new name. PUT https://app.upfour.io/v1/organization scope organization.write stable returns 200 ## Body `application/json` required 4 fields All other fields4 fields namestring Empty keeps the current name. settingsobjectfree-form keys Replaces the whole settings object; omitted leaves it unchanged. Checked keys: timezone (IANA name), date_format (day_month_year, month_day_year or iso), default_language (en or de) and incident_screenshots_default (boolean). default_language, incident_screenshots_default and the audit drain keep their stored value when not sent; the report identity is only changed through /organization/report-branding. slugstring Empty keeps the current slug. A new slug has to derive from the Company name or from a verified domain of the Company. slug_from_nameboolean Lets a slug that was generated from the old name follow the new name. Ignored when slug is sent. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | Organization | | | Organization · 8 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `name` string `owner_id` uuid `settings` object · nullable free-form keys Company settings, for example timezone, date_format (day_month_year, month_day_year or iso), default_language (en or de) and incident_screenshots_default. The audit drain secret is never returned. `slug` string `updated_at` string (date-time) `deleted_at` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousCreate a company](https://upfour.io/docs/api/organization/create-a-company.md) [NextDelete a company](https://upfour.io/docs/api/organization/delete-a-company.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/organization" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Acme GmbH" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/organization", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "Acme GmbH", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/organization", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "name": "Acme GmbH", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "name": "Acme GmbH" }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/organization", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000001", "name": "Acme GmbH", "owner_id": "00000000-0000-4000-8000-000000000002", "settings": { "allow_ai_features": true }, "slug": "acme-gmbh", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Delete a company — API reference > Deletes the Company after confirming its exact name and the owner's password. Source: https://upfour.io/docs/api/organization/delete-a-company [Developers](https://upfour.io/docs/api.md)/[Organization](https://upfour.io/docs/api/organization.md) Deletes the Company after confirming its exact name and the owner's password. DELETE https://app.upfour.io/v1/organization scope organization.write stable returns 204 ## Body `application/json` required 2 fields organization_namestring (required) The exact Company name, as confirmation. passwordstring (required) The owner's password. ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpdate a company](https://upfour.io/docs/api/organization/update-a-company.md) [NextList company members](https://upfour.io/docs/api/organization/list-company-members.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/organization" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "organization_name": "Acme GmbH", "password": "••••" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/organization", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ organization_name: "Acme GmbH", password: "••••", }), }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/organization", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "organization_name": "Acme GmbH", "password": "••••", }, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "organization_name": "Acme GmbH", "password": "••••" }`) req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/organization", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # List company members — API reference > Returns members with email, role, user ID, membership ID, join time and two-factor authentication status. Source: https://upfour.io/docs/api/organization/list-company-members [Developers](https://upfour.io/docs/api.md)/[Organization](https://upfour.io/docs/api/organization.md) Returns members with email, role, user ID, membership ID, join time and two-factor authentication status. GET https://app.upfour.io/v1/organization/members scope member.read stable returns 200 ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | OrganizationMember[] · nullable | | | Each item · 9 fields FieldTypeDescription `has_2fa` boolean Only filled on GET /organization/members; the lookup by email always answers false. `id` uuid The membership id. The member endpoints take user_id in their path, not this id. `joined_at` string (date-time) `organization_id` uuid `role` string one of`owner` `admin` `member` `viewer` `user_id` uuid `email` string `first_name` string `last_name` string | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousDelete a company](https://upfour.io/docs/api/organization/delete-a-company.md) [NextGet a company member by email](https://upfour.io/docs/api/organization/get-a-company-member-by-email.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/organization/members" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/organization/members", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/organization/members", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/organization/members", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "email": "anna@example.com", "has_2fa": false, "id": "00000000-0000-4000-8000-000000000001", "joined_at": "2026-10-27T09:00:00Z", "organization_id": "00000000-0000-4000-8000-000000000002", "role": "owner", "user_id": "00000000-0000-4000-8000-000000000003" }, { "email": "carla@example.com", "has_2fa": false, "id": "00000000-0000-4000-8000-000000000004", "joined_at": "2026-10-27T09:00:00Z", "organization_id": "00000000-0000-4000-8000-000000000002", "role": "member", "user_id": "00000000-0000-4000-8000-000000000005" }, { "email": "ben@example.com", "has_2fa": false, "id": "00000000-0000-4000-8000-000000000006", "joined_at": "2026-10-27T09:00:00Z", "organization_id": "00000000-0000-4000-8000-000000000002", "role": "member", "user_id": "00000000-0000-4000-8000-000000000007" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` --- # Get a company member by email — API reference > Returns the member matching the URL-encoded email address, with role, user ID and two-factor authentication status. Source: https://upfour.io/docs/api/organization/get-a-company-member-by-email [Developers](https://upfour.io/docs/api.md)/[Organization](https://upfour.io/docs/api/organization.md) Returns the member matching the URL-encoded email address, with role, user ID and two-factor authentication status. GET https://app.upfour.io/v1/organization/members/email/{email} scope member.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `email` required | string (email) | path parameter · The member's email address, URL-encoded. | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | OrganizationMember | | | OrganizationMember · 9 fields FieldTypeDescription `has_2fa` boolean Only filled on GET /organization/members; the lookup by email always answers false. `id` uuid The membership id. The member endpoints take user_id in their path, not this id. `joined_at` string (date-time) `organization_id` uuid `role` string one of`owner` `admin` `member` `viewer` `user_id` uuid `email` string `first_name` string `last_name` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList company members](https://upfour.io/docs/api/organization/list-company-members.md) [NextUpdate a member role](https://upfour.io/docs/api/organization/update-a-member-role.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/organization/members/email/ben@example.com" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/organization/members/email/ben@example.com", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/organization/members/email/ben@example.com", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/organization/members/email/ben@example.com", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "email": "ben@example.com", "has_2fa": false, "id": "00000000-0000-4000-8000-000000000001", "joined_at": "2026-10-27T09:00:00Z", "organization_id": "00000000-0000-4000-8000-000000000002", "role": "member", "user_id": "00000000-0000-4000-8000-000000000003" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Update a member role — API reference > Changes a member's role to owner, admin, member or viewer. Source: https://upfour.io/docs/api/organization/update-a-member-role [Developers](https://upfour.io/docs/api.md)/[Organization](https://upfour.io/docs/api/organization.md) Changes a member's role to owner, admin, member or viewer. PUT https://app.upfour.io/v1/organization/members/{memberID}/role scope member.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `memberID` required | uuid | path parameter · The member's `user_id` (not the membership `id`) from the member list. | ## Body `application/json` required 1 field rolestring (required) owneradminmemberviewer In the app: **Role** ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet a company member by email](https://upfour.io/docs/api/organization/get-a-company-member-by-email.md) [NextDelete a member by email](https://upfour.io/docs/api/organization/delete-a-member-by-email.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/organization/members/00000000-0000-4000-8000-000000000001/role" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "role": "admin" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/organization/members/00000000-0000-4000-8000-000000000001/role", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ role: "admin", }), }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/organization/members/00000000-0000-4000-8000-000000000001/role", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "role": "admin", }, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "role": "admin" }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/organization/members/00000000-0000-4000-8000-000000000001/role", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Done. The response has no body. ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Delete a member by email — API reference > Removes the member identified by the URL-encoded email address. Source: https://upfour.io/docs/api/organization/delete-a-member-by-email [Developers](https://upfour.io/docs/api.md)/[Organization](https://upfour.io/docs/api/organization.md) Removes the member identified by the URL-encoded email address. DELETE https://app.upfour.io/v1/organization/members/email/{email} scope member.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `email` required | string (email) | path parameter · The member's email address, URL-encoded. | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpdate a member role](https://upfour.io/docs/api/organization/update-a-member-role.md) [NextDelete a member](https://upfour.io/docs/api/organization/delete-a-member.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/organization/members/email/carla@example.com" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/organization/members/email/carla@example.com", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/organization/members/email/carla@example.com", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/organization/members/email/carla@example.com", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Delete a member — API reference > Removes the member identified by user ID from the member list. Source: https://upfour.io/docs/api/organization/delete-a-member [Developers](https://upfour.io/docs/api.md)/[Organization](https://upfour.io/docs/api/organization.md) Removes the member identified by user ID from the member list. DELETE https://app.upfour.io/v1/organization/members/{memberID} scope member.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `memberID` required | uuid | path parameter · The member's `user_id` (not the membership `id`) from the member list. | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousDelete a member by email](https://upfour.io/docs/api/organization/delete-a-member-by-email.md) [NextGet report branding](https://upfour.io/docs/api/organization/get-report-branding.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/organization/members/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/organization/members/00000000-0000-4000-8000-000000000001", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/organization/members/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/organization/members/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Get report branding — API reference > Returns report name, footer, website, primary color, logo data and custom-domain settings. Source: https://upfour.io/docs/api/organization/get-report-branding [Developers](https://upfour.io/docs/api.md)/[Organization](https://upfour.io/docs/api/organization.md) Returns report name, footer, website, primary color, logo data and custom-domain settings. GET https://app.upfour.io/v1/organization/report-branding scope organization.read stable returns 200 ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ReportBrandingSettingsResponse | | | ReportBrandingSettingsResponse · 9 fields FieldTypeDescription `domain_page_id` uuid · nullable A status page of the Company with a custom domain; reports are then shared on that domain. null uses up4's own address. `footer_text` string Up to 240 characters. `logo_data_url` string The logo as a data:image/png;base64 or data:image/jpeg;base64 URL, at most 1 MB. Empty together with logo_url removes the logo. `logo_url` string An http(s) address of a PNG or JPEG. up4 fetches it and stores a copy; when it is set, logo_data_url is derived from it. `name` string The agency name on reports. Empty uses the Company name. `primary_color` string #RRGGBB; defaults to #294E9E. `website_url` string An http(s) address without credentials. `domain` string Read-only: the custom domain of domain_page_id. `domain_status` string one of`none` `pending_dns` `queued` `provisioning` `active` `failed` `disabled` `conflict_requires_review` `unavailable` Read-only: the state of that domain; shared links only work on it while it is active. | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 400 | [file_empty](https://upfour.io/docs/api/errors#code-file_empty) | The uploaded file is empty. **When:** Uploading a logo or image with no content. **Fix:** Upload a file that has content. | | 413 | [file_too_large](https://upfour.io/docs/api/errors#code-file_too_large) | The uploaded file is over the size limit. **When:** Uploading a logo or image larger than details.max_bytes. **Fix:** Reduce the file size and upload again. | | 400 | [file_type_unsupported](https://upfour.io/docs/api/errors#code-file_type_unsupported) | The file's type is not accepted for this upload. **When:** Uploading a file whose type is not one of the formats the operation accepts; details.formats lists them. **Fix:** Upload a file in one of details.formats. | | 400 | [image_dimensions_too_large](https://upfour.io/docs/api/errors#code-image_dimensions_too_large) | The image has more pixels than allowed. **When:** Uploading an image with more pixels than details.max_pixels. **Fix:** Scale the image down and upload it again. | | 400 | [image_invalid](https://upfour.io/docs/api/errors#code-image_invalid) | The file is not a readable image. **When:** Uploading a file with an image type that cannot be decoded. **Fix:** Export the image again and upload the new file. | | 400 | [logo_fetch_failed](https://upfour.io/docs/api/errors#code-logo_fetch_failed) | The logo could not be fetched from the address given. **When:** Importing a client logo from a URL that cannot be reached or that does not return an acceptable image. **Fix:** Check the URL, or upload the logo file instead. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 400 | [svg_unsafe](https://upfour.io/docs/api/errors#code-svg_unsafe) | The SVG contains content that is not allowed. **When:** Uploading an SVG that contains a script, a foreignObject, a javascript: URL, an event-handler attribute or an external href. **Fix:** Remove those parts from the SVG, or upload a PNG or JPEG. | | 400 | [upload_invalid](https://upfour.io/docs/api/errors#code-upload_invalid) | The upload could not be read as a multipart form. **When:** A body that is not a valid multipart form, or whose file cannot be parsed. **Fix:** Send a well-formed multipart/form-data request. | | 400 | [upload_no_file](https://upfour.io/docs/api/errors#code-upload_no_file) | The multipart request has no file part. **When:** An upload request without a file field. **Fix:** Send the file as multipart/form-data in the field the operation names. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousDelete a member](https://upfour.io/docs/api/organization/delete-a-member.md) [NextUpdate report branding](https://upfour.io/docs/api/organization/update-report-branding.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/organization/report-branding" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/organization/report-branding", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/organization/report-branding", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/organization/report-branding", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "domain_page_id": null, "footer_text": "", "logo_data_url": "", "logo_url": "", "name": "Acme GmbH", "primary_color": "#294E9E", "website_url": "" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 413 ```json { "error": { "code": "file_too_large", "message": "The uploaded file is over the size limit.", "request_id": "req_…" } } ``` --- # Update report branding — API reference > Replaces report identity, footer, website, primary color, logo data and custom-domain settings. Source: https://upfour.io/docs/api/organization/update-report-branding [Developers](https://upfour.io/docs/api.md)/[Organization](https://upfour.io/docs/api/organization.md) Replaces report identity, footer, website, primary color, logo data and custom-domain settings. PUT https://app.upfour.io/v1/organization/report-branding scope organization.write stable returns 200 ## Body The 4 fields most calls use come first; the rest are grouped below. `application/json` required 9 fields namestring The agency name on reports. Empty uses the Company name. footer_textstring Up to 240 characters. website_urlstring An http(s) address without credentials. primary_colorstring #RRGGBB; defaults to #294E9E. Logo2 fields logo_data_urlstring The logo as a data:image/png;base64 or data:image/jpeg;base64 URL, at most 1 MB. Empty together with logo_url removes the logo. logo_urlstring An http(s) address of a PNG or JPEG. up4 fetches it and stores a copy; when it is set, logo_data_url is derived from it. Custom domain3 fields domainstring Read-only: the custom domain of domain_page_id. domain_page_iduuid · nullable A status page of the Company with a custom domain; reports are then shared on that domain. null uses up4's own address. domain_statusstring Read-only: the state of that domain; shared links only work on it while it is active. nonepending_dnsqueuedprovisioningactivefaileddisabledconflict_requires_reviewunavailable ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ReportBrandingSettingsResponse | | | ReportBrandingSettingsResponse · 9 fields FieldTypeDescription `domain_page_id` uuid · nullable A status page of the Company with a custom domain; reports are then shared on that domain. null uses up4's own address. `footer_text` string Up to 240 characters. `logo_data_url` string The logo as a data:image/png;base64 or data:image/jpeg;base64 URL, at most 1 MB. Empty together with logo_url removes the logo. `logo_url` string An http(s) address of a PNG or JPEG. up4 fetches it and stores a copy; when it is set, logo_data_url is derived from it. `name` string The agency name on reports. Empty uses the Company name. `primary_color` string #RRGGBB; defaults to #294E9E. `website_url` string An http(s) address without credentials. `domain` string Read-only: the custom domain of domain_page_id. `domain_status` string one of`none` `pending_dns` `queued` `provisioning` `active` `failed` `disabled` `conflict_requires_review` `unavailable` Read-only: the state of that domain; shared links only work on it while it is active. | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 400 | [file_empty](https://upfour.io/docs/api/errors#code-file_empty) | The uploaded file is empty. **When:** Uploading a logo or image with no content. **Fix:** Upload a file that has content. | | 413 | [file_too_large](https://upfour.io/docs/api/errors#code-file_too_large) | The uploaded file is over the size limit. **When:** Uploading a logo or image larger than details.max_bytes. **Fix:** Reduce the file size and upload again. | | 400 | [file_type_unsupported](https://upfour.io/docs/api/errors#code-file_type_unsupported) | The file's type is not accepted for this upload. **When:** Uploading a file whose type is not one of the formats the operation accepts; details.formats lists them. **Fix:** Upload a file in one of details.formats. | | 400 | [image_dimensions_too_large](https://upfour.io/docs/api/errors#code-image_dimensions_too_large) | The image has more pixels than allowed. **When:** Uploading an image with more pixels than details.max_pixels. **Fix:** Scale the image down and upload it again. | | 400 | [image_invalid](https://upfour.io/docs/api/errors#code-image_invalid) | The file is not a readable image. **When:** Uploading a file with an image type that cannot be decoded. **Fix:** Export the image again and upload the new file. | | 400 | [logo_fetch_failed](https://upfour.io/docs/api/errors#code-logo_fetch_failed) | The logo could not be fetched from the address given. **When:** Importing a client logo from a URL that cannot be reached or that does not return an acceptable image. **Fix:** Check the URL, or upload the logo file instead. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 400 | [svg_unsafe](https://upfour.io/docs/api/errors#code-svg_unsafe) | The SVG contains content that is not allowed. **When:** Uploading an SVG that contains a script, a foreignObject, a javascript: URL, an event-handler attribute or an external href. **Fix:** Remove those parts from the SVG, or upload a PNG or JPEG. | | 400 | [upload_invalid](https://upfour.io/docs/api/errors#code-upload_invalid) | The upload could not be read as a multipart form. **When:** A body that is not a valid multipart form, or whose file cannot be parsed. **Fix:** Send a well-formed multipart/form-data request. | | 400 | [upload_no_file](https://upfour.io/docs/api/errors#code-upload_no_file) | The multipart request has no file part. **When:** An upload request without a file field. **Fix:** Send the file as multipart/form-data in the field the operation names. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet report branding](https://upfour.io/docs/api/organization/get-report-branding.md) [NextGet company security settings](https://upfour.io/docs/api/organization/get-company-security-settings.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/organization/report-branding" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "footer_text": "Prepared by Acme GmbH", "name": "Acme GmbH", "primary_color": "#2547b0", "website_url": "https://example.com" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/organization/report-branding", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ footer_text: "Prepared by Acme GmbH", name: "Acme GmbH", primary_color: "#2547b0", website_url: "https://example.com", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/organization/report-branding", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "footer_text": "Prepared by Acme GmbH", "name": "Acme GmbH", "primary_color": "#2547b0", "website_url": "https://example.com", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "footer_text": "Prepared by Acme GmbH", "name": "Acme GmbH", "primary_color": "#2547b0", "website_url": "https://example.com" }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/organization/report-branding", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "domain_page_id": null, "footer_text": "Prepared by Acme GmbH", "logo_data_url": "", "logo_url": "", "name": "Acme GmbH", "primary_color": "#2547b0", "website_url": "https://example.com" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 413 ```json { "error": { "code": "file_too_large", "message": "The uploaded file is over the size limit.", "request_id": "req_…" } } ``` --- # Get company security settings — API reference > Returns AI feature access, two-factor policy, audit-drain status and members who lack two-factor authentication. Source: https://upfour.io/docs/api/organization/get-company-security-settings [Developers](https://upfour.io/docs/api.md)/[Organization](https://upfour.io/docs/api/organization.md) Returns AI feature access, two-factor policy, audit-drain status and members who lack two-factor authentication. GET https://app.upfour.io/v1/organization/security scope organization.read stable returns 200 ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | OrganizationSecuritySettings | | | OrganizationSecuritySettings · 12 fields FieldTypeDescription `allow_ai_features` boolean `audit_drain` AuditDrainConfig AuditDrainConfig · 2 fields FieldTypeDescription `secret` string Always empty: the secret is never returned. audit_drain_configured says whether a drain is set up. `url` string `audit_drain_configured` boolean `members_without_2fa` MemberWithout2FA[] · nullable Each item · 5 fields FieldTypeDescription `email` string `name` string `password_set` boolean false for an account that only signs in by email link; it has to add a password before it can set up an authenticator app. `role` string one of`owner` `admin` `member` `viewer` `user_id` uuid `require_2fa` boolean | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpdate report branding](https://upfour.io/docs/api/organization/update-report-branding.md) [NextUpdate company security settings](https://upfour.io/docs/api/organization/update-company-security-settings.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/organization/security" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/organization/security", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/organization/security", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/organization/security", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "allow_ai_features": true, "audit_drain": { "secret": "", "url": "" }, "audit_drain_configured": false, "members_without_2fa": [ { "email": "anna@example.com", "name": "", "password_set": true, "role": "owner", "user_id": "00000000-0000-4000-8000-000000000001" } ], "require_2fa": false } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Update company security settings — API reference > Replaces AI access, two-factor policy and audit-drain URL or secret settings. Source: https://upfour.io/docs/api/organization/update-company-security-settings [Developers](https://upfour.io/docs/api.md)/[Organization](https://upfour.io/docs/api/organization.md) Replaces AI access, two-factor policy and audit-drain URL or secret settings. PUT https://app.upfour.io/v1/organization/security scope organization.write stable returns 200 ## Body `application/json` required 4 fields All other fields4 fields allow_ai_featuresboolean Replaced on every call: omitted means false. audit_drain_secretstring Signing secret of the drain. Write-only; empty keeps the stored secret. audit_drain_urlstring HTTPS endpoint that receives each audit-log entry. Empty switches the drain off. require_2faboolean Replaced on every call: omitted means false. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | OrganizationSecuritySettings | | | OrganizationSecuritySettings · 12 fields FieldTypeDescription `allow_ai_features` boolean `audit_drain` AuditDrainConfig AuditDrainConfig · 2 fields FieldTypeDescription `secret` string Always empty: the secret is never returned. audit_drain_configured says whether a drain is set up. `url` string `audit_drain_configured` boolean `members_without_2fa` MemberWithout2FA[] · nullable Each item · 5 fields FieldTypeDescription `email` string `name` string `password_set` boolean false for an account that only signs in by email link; it has to add a password before it can set up an authenticator app. `role` string one of`owner` `admin` `member` `viewer` `user_id` uuid `require_2fa` boolean | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet company security settings](https://upfour.io/docs/api/organization/get-company-security-settings.md) [NextList SSO providers](https://upfour.io/docs/api/organization/list-sso-providers.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/organization/security" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "allow_ai_features": true, "require_2fa": false }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/organization/security", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ allow_ai_features: true, require_2fa: false, }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/organization/security", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "allow_ai_features": True, "require_2fa": False, }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "allow_ai_features": true, "require_2fa": false }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/organization/security", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "allow_ai_features": true, "audit_drain": { "secret": "", "url": "" }, "audit_drain_configured": false, "members_without_2fa": [ { "email": "anna@example.com", "name": "", "password_set": true, "role": "owner", "user_id": "00000000-0000-4000-8000-000000000001" } ], "require_2fa": false } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # List SSO providers — API reference > Returns configured Google and GitHub SSO providers with pagination. Source: https://upfour.io/docs/api/organization/list-sso-providers [Developers](https://upfour.io/docs/api.md)/[Organization](https://upfour.io/docs/api/organization.md) Returns configured Google and GitHub SSO providers with pagination. GET https://app.upfour.io/v1/organization/sso scope organization.read stable returns 200 ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | OrganizationSSOProvider[] · nullable | | | Each item · 7 fields FieldTypeDescription `client_id` string `created_at` string (date-time) `enabled` boolean `id` uuid `organization_id` uuid `provider` string one of`google` `github` `updated_at` string (date-time) | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpdate company security settings](https://upfour.io/docs/api/organization/update-company-security-settings.md) [NextUpdate an SSO provider](https://upfour.io/docs/api/organization/update-an-sso-provider.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/organization/sso" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/organization/sso", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/organization/sso", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/organization/sso", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Update an SSO provider — API reference > Updates a provider's client ID, secret and enabled state; the provider comes from the path. Source: https://upfour.io/docs/api/organization/update-an-sso-provider [Developers](https://upfour.io/docs/api.md)/[Organization](https://upfour.io/docs/api/organization.md) Updates a provider's client ID, secret and enabled state; the provider comes from the path. PUT https://app.upfour.io/v1/organization/sso/{provider} scope organization.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `provider` required | string one of`google` `github` | path parameter | ## Body `application/json` required 4 fields client_idstring (required) All other fields3 fields client_secretstring Write-only. Required the first time a provider is set up; empty keeps the stored secret afterwards. enabledboolean false when omitted. providerstring Ignored: the provider is taken from the path. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | OrganizationSSOProvider | | | OrganizationSSOProvider · 7 fields FieldTypeDescription `client_id` string `created_at` string (date-time) `enabled` boolean `id` uuid `organization_id` uuid `provider` string one of`google` `github` `updated_at` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList SSO providers](https://upfour.io/docs/api/organization/list-sso-providers.md) [NextGet AI usage](https://upfour.io/docs/api/organization/get-ai-usage.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/organization/sso/google" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "client_id": "1234567890-acme.apps.googleusercontent.com", "client_secret": "••••", "enabled": false }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/organization/sso/google", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ client_id: "1234567890-acme.apps.googleusercontent.com", client_secret: "••••", enabled: false, }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/organization/sso/google", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "client_id": "1234567890-acme.apps.googleusercontent.com", "client_secret": "••••", "enabled": False, }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "client_id": "1234567890-acme.apps.googleusercontent.com", "client_secret": "••••", "enabled": false }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/organization/sso/google", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "client_id": "1234567890-acme.apps.googleusercontent.com", "created_at": "2026-10-27T09:00:00Z", "enabled": false, "id": "00000000-0000-4000-8000-000000000001", "organization_id": "00000000-0000-4000-8000-000000000002", "provider": "google", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Get AI usage — API reference > Returns monthly AI calls, active limits, platform limits, period timestamps and usage cost in cents. Source: https://upfour.io/docs/api/organization/get-ai-usage [Developers](https://upfour.io/docs/api.md)/[Organization](https://upfour.io/docs/api/organization.md) Returns monthly AI calls, active limits, platform limits, period timestamps and usage cost in cents. GET https://app.upfour.io/v1/organization/ai-usage scope organization.read stable returns 200 ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | AIUsageSummary | | | AIUsageSummary · 8 fields FieldTypeDescription `call_cap` integer The monthly call limit in force: the Company's own value, never above platform_call_cap. `calls` integer `cap_cents` integer The monthly budget in force: the Company's own value, never above platform_cap_cents. `period_resets_at` string (date-time) `period_started_at` string (date-time) `platform_call_cap` integer `platform_cap_cents` integer `used_cents` integer This month's AI usage in cents, at the price the Company pays. | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 402 | [ai_billing_blocked](https://upfour.io/docs/api/errors#code-ai_billing_blocked) | AI features are switched off because the Company has no active credit. **When:** An AI summary or postmortem request while the Company has no active credit. **Fix:** Top up the credit, then retry. | | 422 | [ai_budget_above_platform_limit](https://upfour.io/docs/api/errors#code-ai_budget_above_platform_limit) | The monthly AI budget is above the platform limit. **When:** Saving an AI budget larger than the platform allows; details.limit gives the maximum. **Fix:** Set a budget at or below details.limit. | | 422 | [ai_call_cap_above_platform_limit](https://upfour.io/docs/api/errors#code-ai_call_cap_above_platform_limit) | The monthly AI call cap is above the platform limit. **When:** Saving an AI call cap larger than the platform allows; details.limit gives the maximum. **Fix:** Set a cap at or below details.limit. | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 409 | [conflict](https://upfour.io/docs/api/errors#code-conflict) | The request conflicts with the current state. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 503 | [service_unavailable](https://upfour.io/docs/api/errors#code-service_unavailable) | The service or a required dependency is temporarily unavailable. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpdate an SSO provider](https://upfour.io/docs/api/organization/update-an-sso-provider.md) [NextUpdate AI quota](https://upfour.io/docs/api/organization/update-ai-quota.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/organization/ai-usage" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/organization/ai-usage", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/organization/ai-usage", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/organization/ai-usage", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "call_cap": 100, "calls": 2, "cap_cents": 500, "period_resets_at": "2026-10-31T16:55:00Z", "period_started_at": "2026-09-30T16:55:00Z", "platform_call_cap": 1000, "platform_cap_cents": 4000, "used_cents": 2 } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "ai_billing_blocked", "message": "AI features are switched off because the Company has no active credit.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` ### 409 ```json { "error": { "code": "conflict", "message": "The request conflicts with the current state.", "request_id": "req_…" } } ``` ### 422 ```json { "error": { "code": "ai_budget_above_platform_limit", "message": "The monthly AI budget is above the platform limit.", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "service_unavailable", "message": "The service or a required dependency is temporarily unavailable.", "request_id": "req_…" } } ``` --- # Update AI quota — API reference > Sets the Company's monthly AI call limit and budget, or removes either limit when its value is null. Source: https://upfour.io/docs/api/organization/update-ai-quota [Developers](https://upfour.io/docs/api.md)/[Organization](https://upfour.io/docs/api/organization.md) Sets the Company's monthly AI call limit and budget, or removes either limit when its value is null. PUT https://app.upfour.io/v1/organization/ai-quota scope organization.write stable returns 200 ## Body `application/json` required 2 fields All other fields2 fields monthly_call_capinteger · nullable Monthly AI call limit, at most platform_call_cap. null removes the Company's own limit so the default applies. monthly_cap_centsinteger · nullable Monthly AI budget in cents, at most platform_cap_cents of GET /organization/ai-usage. null removes the Company's own budget so the default applies. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | AIUsageSummary | | | AIUsageSummary · 8 fields FieldTypeDescription `call_cap` integer The monthly call limit in force: the Company's own value, never above platform_call_cap. `calls` integer `cap_cents` integer The monthly budget in force: the Company's own value, never above platform_cap_cents. `period_resets_at` string (date-time) `period_started_at` string (date-time) `platform_call_cap` integer `platform_cap_cents` integer `used_cents` integer This month's AI usage in cents, at the price the Company pays. | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 402 | [ai_billing_blocked](https://upfour.io/docs/api/errors#code-ai_billing_blocked) | AI features are switched off because the Company has no active credit. **When:** An AI summary or postmortem request while the Company has no active credit. **Fix:** Top up the credit, then retry. | | 422 | [ai_budget_above_platform_limit](https://upfour.io/docs/api/errors#code-ai_budget_above_platform_limit) | The monthly AI budget exceeds the platform limit (`error.details.limit`). **When:** Saving an AI budget larger than the platform allows; details.limit gives the maximum. **Fix:** Set a budget at or below details.limit. `ai_budget_above_platform_limit` or `ai_call_cap_above_platform_limit`: the monthly AI budget or call cap is above the platform limit (`error.details.limit`). | | 422 | [ai_call_cap_above_platform_limit](https://upfour.io/docs/api/errors#code-ai_call_cap_above_platform_limit) | The monthly AI call cap exceeds the platform limit (`error.details.limit`). **When:** Saving an AI call cap larger than the platform allows; details.limit gives the maximum. **Fix:** Set a cap at or below details.limit. | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 409 | [conflict](https://upfour.io/docs/api/errors#code-conflict) | The request conflicts with the current state. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 503 | [service_unavailable](https://upfour.io/docs/api/errors#code-service_unavailable) | The service or a required dependency is temporarily unavailable. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet AI usage](https://upfour.io/docs/api/organization/get-ai-usage.md) [NextList audit logs](https://upfour.io/docs/api/organization/list-audit-logs.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/organization/ai-quota" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "monthly_call_cap": 200, "monthly_cap_cents": 1000 }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/organization/ai-quota", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ monthly_call_cap: 200, monthly_cap_cents: 1000, }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/organization/ai-quota", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "monthly_call_cap": 200, "monthly_cap_cents": 1000, }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "monthly_call_cap": 200, "monthly_cap_cents": 1000 }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/organization/ai-quota", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "call_cap": 200, "calls": 2, "cap_cents": 1000, "period_resets_at": "2026-10-31T16:55:00Z", "period_started_at": "2026-09-30T16:55:00Z", "platform_call_cap": 1000, "platform_cap_cents": 4000, "used_cents": 2 } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "ai_billing_blocked", "message": "AI features are switched off because the Company has no active credit.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` ### 409 ```json { "error": { "code": "conflict", "message": "The request conflicts with the current state.", "request_id": "req_…" } } ``` ### 422 ```json { "error": { "code": "ai_budget_above_platform_limit", "message": "The monthly AI budget exceeds the platform limit (error.details.limit).", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "service_unavailable", "message": "The service or a required dependency is temporarily unavailable.", "request_id": "req_…" } } ``` --- # List audit logs — API reference > Returns actions with actors, resource identifiers, metadata, timestamps and pagination. Source: https://upfour.io/docs/api/organization/list-audit-logs [Developers](https://upfour.io/docs/api.md)/[Organization](https://upfour.io/docs/api/organization.md) Returns actions with actors, resource identifiers, metadata, timestamps and pagination. GET https://app.upfour.io/v1/organization/audit-logs scope audit_log.read stable returns 200 ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | AuditLog[] · nullable | | | Each item · 9 fields FieldTypeDescription `action` string `actor_email` string `created_at` string (date-time) `id` uuid `metadata` object · nullable free-form keys Details of the action; the keys depend on the action. `organization_id` uuid `resource_id` string The id of the changed object; not always a UUID (an SSO entry carries its provider name). `resource_type` string `actor_user_id` uuid | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpdate AI quota](https://upfour.io/docs/api/organization/update-ai-quota.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/organization/audit-logs" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/organization/audit-logs", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/organization/audit-logs", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/organization/audit-logs", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "action": "organization.ai_quota_updated", "actor_email": "anna@example.com", "actor_user_id": "00000000-0000-4000-8000-000000000001", "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000002", "metadata": { "monthly_call_cap": 200, "monthly_cap_cents": 1000 }, "organization_id": "00000000-0000-4000-8000-000000000003", "resource_id": "00000000-0000-4000-8000-000000000003", "resource_type": "organization" }, { "action": "sso.provider_updated", "actor_email": "anna@example.com", "actor_user_id": "00000000-0000-4000-8000-000000000001", "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000004", "metadata": { "enabled": false, "provider": "google" }, "organization_id": "00000000-0000-4000-8000-000000000003", "resource_id": "google", "resource_type": "organization_sso_provider" }, { "action": "organization.security_updated", "actor_email": "anna@example.com", "actor_user_id": "00000000-0000-4000-8000-000000000001", "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000005", "metadata": { "allow_ai_features": true, "audit_drain_url": "", "require_2fa": false }, "organization_id": "00000000-0000-4000-8000-000000000003", "resource_id": "00000000-0000-4000-8000-000000000003", "resource_type": "organization" }, { "action": "advisor.fix_applied", "actor_email": "anna@example.com", "actor_user_id": "00000000-0000-4000-8000-000000000001", "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000006", "metadata": { "applied": "channel_attached", "rule_id": "monitor_without_recipient" }, "organization_id": "00000000-0000-4000-8000-000000000003", "resource_id": "00000000-0000-4000-8000-000000000007", "resource_type": "monitor" }, { "action": "status_page.access_token_revoked", "actor_email": "anna@example.com", "actor_user_id": "00000000-0000-4000-8000-000000000001", "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000008", "metadata": {}, "organization_id": "00000000-0000-4000-8000-000000000003", "resource_id": "00000000-0000-4000-8000-000000000009", "resource_type": "status_page" }, { "action": "status_page.access_token_rotated", "actor_email": "anna@example.com", "actor_user_id": "00000000-0000-4000-8000-000000000001", "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000010", "metadata": {}, "organization_id": "00000000-0000-4000-8000-000000000003", "resource_id": "00000000-0000-4000-8000-000000000009", "resource_type": "status_page" }, { "action": "status_page.access_policy_updated", "actor_email": "anna@example.com", "actor_user_id": "00000000-0000-4000-8000-000000000001", "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000011", "metadata": { "access_email_codes": false, "ip_allowlist": [ "203.0.113.0/24" ] }, "organization_id": "00000000-0000-4000-8000-000000000003", "resource_id": "00000000-0000-4000-8000-000000000009", "resource_type": "status_page" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Teams — API reference > Teams and membership. REST endpoints with request and response fields, examples in curl, JavaScript, Python, Go and PHP, and an in-browser Try it console. Source: https://upfour.io/docs/api/teams [Developers](https://upfour.io/docs/api.md)/Organization Teams and membership. Base URL `https://app.upfour.io/v1`. Send your key as `Authorization: Bearer `; every key acts on your real Company, so [test without paging anyone](https://upfour.io/docs/api/testing.md). New here? Start with the [quickstart](https://upfour.io/docs/api/quickstart.md). The examples read the key from `UP4_API_KEY`. ## Everyday [GET **List teams** /teams](https://upfour.io/docs/api/teams/list-teams.md)[POST **Create a team** /teams](https://upfour.io/docs/api/teams/create-a-team.md)[GET **Get a team** /teams/{teamID}](https://upfour.io/docs/api/teams/get-a-team.md)[PUT **Update a team** /teams/{teamID}](https://upfour.io/docs/api/teams/update-a-team.md) ## Members [GET **List team members** /teams/{teamID}/members](https://upfour.io/docs/api/teams/list-team-members.md)[POST **Add a member to a team** /teams/{teamID}/members](https://upfour.io/docs/api/teams/add-a-member-to-a-team.md)[GET **Get a team member by email** /teams/{teamID}/members/email/{email}](https://upfour.io/docs/api/teams/get-a-team-member-by-email.md)[PUT **Update a team member role** /teams/{teamID}/members/{memberID}/role](https://upfour.io/docs/api/teams/update-a-team-member-role.md)[DEL **Remove a team member by email** /teams/{teamID}/members/email/{email}](https://upfour.io/docs/api/teams/remove-a-team-member-by-email.md)[DEL **Remove a team member** /teams/{teamID}/members/{memberID}](https://upfour.io/docs/api/teams/remove-a-team-member.md) ## Clients [GET **List team clients** /teams/{teamID}/clients](https://upfour.io/docs/api/teams/list-team-clients.md)[POST **Assign a Client to a team** /teams/{teamID}/clients](https://upfour.io/docs/api/teams/assign-a-client-to-a-team.md)[DEL **Remove a Client from a team** /teams/{teamID}/clients/{clientID}](https://upfour.io/docs/api/teams/remove-a-client-from-a-team.md) ## Control [DEL **Delete a team** /teams/{teamID}](https://upfour.io/docs/api/teams/delete-a-team.md) --- # List teams — API reference > Lists teams with pagination. Source: https://upfour.io/docs/api/teams/list-teams [Developers](https://upfour.io/docs/api.md)/[Teams](https://upfour.io/docs/api/teams.md) Lists teams with pagination. GET https://app.upfour.io/v1/teams scope team.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `limit` optional | integer 1–250 default `50` | query parameter | | `cursor` optional | string | query parameter | | `client_id` optional | uuid | query parameter | | `all_clients` optional | boolean | query parameter | | `q` optional | string | query parameter | | `sort` optional | string | query parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | Team[] · nullable | | | Each item · 7 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `is_default` boolean `member_count` integer `name` string `organization_id` uuid `updated_at` string (date-time) | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [NextCreate a team](https://upfour.io/docs/api/teams/create-a-team.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/teams" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/teams", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/teams", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/teams", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000001", "is_default": true, "member_count": 0, "name": "Operations", "organization_id": "00000000-0000-4000-8000-000000000002", "updated_at": "2026-10-27T09:00:00Z" }, { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000003", "is_default": false, "member_count": 0, "name": "Platform", "organization_id": "00000000-0000-4000-8000-000000000002", "updated_at": "2026-10-27T09:00:00Z" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` --- # Create a team — API reference > Creates a team with the supplied name. Source: https://upfour.io/docs/api/teams/create-a-team [Developers](https://upfour.io/docs/api.md)/[Teams](https://upfour.io/docs/api/teams.md) Creates a team with the supplied name. POST https://app.upfour.io/v1/teams scope team.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Body `application/json` required 1 field namestring (required) In the app: **Team name** ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | Team | | | Team · 7 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `is_default` boolean `member_count` integer `name` string `organization_id` uuid `updated_at` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList teams](https://upfour.io/docs/api/teams/list-teams.md) [NextGet a team](https://upfour.io/docs/api/teams/get-a-team.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/teams" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Platform" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/teams", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "Platform", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/teams", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "name": "Platform", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "name": "Platform" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/teams", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000001", "is_default": false, "member_count": 0, "name": "Platform", "organization_id": "00000000-0000-4000-8000-000000000002", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Get a team — API reference > Returns a team with its member count and default status. Source: https://upfour.io/docs/api/teams/get-a-team [Developers](https://upfour.io/docs/api.md)/[Teams](https://upfour.io/docs/api/teams.md) Returns a team with its member count and default status. GET https://app.upfour.io/v1/teams/{teamID} scope team.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `teamID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | Team | | | Team · 7 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `is_default` boolean `member_count` integer `name` string `organization_id` uuid `updated_at` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousCreate a team](https://upfour.io/docs/api/teams/create-a-team.md) [NextUpdate a team](https://upfour.io/docs/api/teams/update-a-team.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000001", "is_default": false, "member_count": 0, "name": "Platform", "organization_id": "00000000-0000-4000-8000-000000000002", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Update a team — API reference > Replaces a team's name. Source: https://upfour.io/docs/api/teams/update-a-team [Developers](https://upfour.io/docs/api.md)/[Teams](https://upfour.io/docs/api/teams.md) Replaces a team's name. PUT https://app.upfour.io/v1/teams/{teamID} scope team.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `teamID` required | uuid | path parameter | ## Body `application/json` required 1 field namestring (required) ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | Team | | | Team · 7 fields FieldTypeDescription `created_at` string (date-time) `id` uuid `is_default` boolean `member_count` integer `name` string `organization_id` uuid `updated_at` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet a team](https://upfour.io/docs/api/teams/get-a-team.md) [NextList team members](https://upfour.io/docs/api/teams/list-team-members.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Platform engineering" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "Platform engineering", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "name": "Platform engineering", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "name": "Platform engineering" }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000001", "is_default": false, "member_count": 0, "name": "Platform engineering", "organization_id": "00000000-0000-4000-8000-000000000002", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # List team members — API reference > Lists a team's members with pagination. Source: https://upfour.io/docs/api/teams/list-team-members [Developers](https://upfour.io/docs/api.md)/[Teams](https://upfour.io/docs/api/teams.md) Lists a team's members with pagination. GET https://app.upfour.io/v1/teams/{teamID}/members scope member.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `teamID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | TeamMember[] · nullable | | | Each item · 8 fields FieldTypeDescription `id` uuid The membership id. Routes with {memberID} accept the user_id as well. `joined_at` string (date-time) `role` string one of`admin` `member` `viewer` `team_id` uuid `user_id` uuid `email` string `first_name` string `last_name` string | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpdate a team](https://upfour.io/docs/api/teams/update-a-team.md) [NextAdd a member to a team](https://upfour.io/docs/api/teams/add-a-member-to-a-team.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/members" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/members", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/members", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/members", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "email": "ben@example.com", "id": "00000000-0000-4000-8000-000000000002", "joined_at": "2026-10-27T09:00:00Z", "role": "member", "team_id": "00000000-0000-4000-8000-000000000001", "user_id": "00000000-0000-4000-8000-000000000003" }, { "email": "carla@example.com", "id": "00000000-0000-4000-8000-000000000004", "joined_at": "2026-10-27T09:00:00Z", "role": "viewer", "team_id": "00000000-0000-4000-8000-000000000001", "user_id": "00000000-0000-4000-8000-000000000005" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Add a member to a team — API reference > Adds a Company member to a team and returns no response body. Source: https://upfour.io/docs/api/teams/add-a-member-to-a-team [Developers](https://upfour.io/docs/api.md)/[Teams](https://upfour.io/docs/api/teams.md) Adds a Company member to a team and returns no response body. POST https://app.upfour.io/v1/teams/{teamID}/members scope member.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Parameters | Field | Type | Description | | --- | --- | --- | | `teamID` required | uuid | path parameter | ## Body `application/json` required 2 fields user_iduuid (required) A user that is a member of the company. All other fields1 fields rolestring Omitted or any other value adds the user as member. Adding someone who is already in the team changes nothing. adminmemberviewer ## Returns A successful request returns `201` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList team members](https://upfour.io/docs/api/teams/list-team-members.md) [NextGet a team member by email](https://upfour.io/docs/api/teams/get-a-team-member-by-email.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/members" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "role": "member", "user_id": "00000000-0000-4000-8000-000000000002" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/members", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ role: "member", user_id: "00000000-0000-4000-8000-000000000002", }), }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/members", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "role": "member", "user_id": "00000000-0000-4000-8000-000000000002", }, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "role": "member", "user_id": "00000000-0000-4000-8000-000000000002" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/members", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 201 ``` Done. The response has no body. ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Get a team member by email — API reference > Returns the membership ID, role, team ID, user ID and joined-at timestamp for the matching address. Source: https://upfour.io/docs/api/teams/get-a-team-member-by-email [Developers](https://upfour.io/docs/api.md)/[Teams](https://upfour.io/docs/api/teams.md) Returns the membership ID, role, team ID, user ID and joined-at timestamp for the matching address. GET https://app.upfour.io/v1/teams/{teamID}/members/email/{email} scope member.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `teamID` required | uuid | path parameter | | `email` required | string (email) | path parameter · The member's email address, URL-encoded. | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | TeamMember | | | TeamMember · 8 fields FieldTypeDescription `id` uuid The membership id. Routes with {memberID} accept the user_id as well. `joined_at` string (date-time) `role` string one of`admin` `member` `viewer` `team_id` uuid `user_id` uuid `email` string `first_name` string `last_name` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousAdd a member to a team](https://upfour.io/docs/api/teams/add-a-member-to-a-team.md) [NextUpdate a team member role](https://upfour.io/docs/api/teams/update-a-team-member-role.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/members/email/ben@example.com" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/members/email/ben@example.com", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/members/email/ben@example.com", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/members/email/ben@example.com", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "email": "ben@example.com", "id": "00000000-0000-4000-8000-000000000002", "joined_at": "2026-10-27T09:00:00Z", "role": "member", "team_id": "00000000-0000-4000-8000-000000000001", "user_id": "00000000-0000-4000-8000-000000000003" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Update a team member role — API reference > Sets a team member's role to admin, member or viewer. Source: https://upfour.io/docs/api/teams/update-a-team-member-role [Developers](https://upfour.io/docs/api.md)/[Teams](https://upfour.io/docs/api/teams.md) Sets a team member's role to admin, member or viewer. PUT https://app.upfour.io/v1/teams/{teamID}/members/{memberID}/role scope member.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `teamID` required | uuid | path parameter | | `memberID` required | uuid | path parameter | ## Body `application/json` required 1 field rolestring (required) adminmemberviewer ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet a team member by email](https://upfour.io/docs/api/teams/get-a-team-member-by-email.md) [NextRemove a team member by email](https://upfour.io/docs/api/teams/remove-a-team-member-by-email.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/members/00000000-0000-4000-8000-000000000002/role" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "role": "admin" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/members/00000000-0000-4000-8000-000000000002/role", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ role: "admin", }), }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/members/00000000-0000-4000-8000-000000000002/role", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "role": "admin", }, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "role": "admin" }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/members/00000000-0000-4000-8000-000000000002/role", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Done. The response has no body. ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Remove a team member by email — API reference > Deletes the member identified by the URL-encoded email address and returns status 204. Source: https://upfour.io/docs/api/teams/remove-a-team-member-by-email [Developers](https://upfour.io/docs/api.md)/[Teams](https://upfour.io/docs/api/teams.md) Deletes the member identified by the URL-encoded email address and returns status 204. DELETE https://app.upfour.io/v1/teams/{teamID}/members/email/{email} scope member.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `teamID` required | uuid | path parameter | | `email` required | string (email) | path parameter · The member's email address, URL-encoded. | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpdate a team member role](https://upfour.io/docs/api/teams/update-a-team-member-role.md) [NextRemove a team member](https://upfour.io/docs/api/teams/remove-a-team-member.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/members/email/carla@example.com" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/members/email/carla@example.com", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/members/email/carla@example.com", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/members/email/carla@example.com", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Remove a team member — API reference > Removes a team membership and returns no response body. Source: https://upfour.io/docs/api/teams/remove-a-team-member [Developers](https://upfour.io/docs/api.md)/[Teams](https://upfour.io/docs/api/teams.md) Removes a team membership and returns no response body. DELETE https://app.upfour.io/v1/teams/{teamID}/members/{memberID} scope member.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `teamID` required | uuid | path parameter | | `memberID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousRemove a team member by email](https://upfour.io/docs/api/teams/remove-a-team-member-by-email.md) [NextList team clients](https://upfour.io/docs/api/teams/list-team-clients.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/members/00000000-0000-4000-8000-000000000002" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/members/00000000-0000-4000-8000-000000000002", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/members/00000000-0000-4000-8000-000000000002", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/members/00000000-0000-4000-8000-000000000002", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # List team clients — API reference > Lists Clients assigned to a team with pagination. Source: https://upfour.io/docs/api/teams/list-team-clients [Developers](https://upfour.io/docs/api.md)/[Teams](https://upfour.io/docs/api/teams.md) Lists Clients assigned to a team with pagination. GET https://app.upfour.io/v1/teams/{teamID}/clients scope team.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `teamID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | TeamClientRow[] · nullable | | | Each item · 10 fields FieldTypeDescription `assigned_at` string (date-time) `id` uuid `logo` StoredClientLogo StoredClientLogo · 4 fields FieldTypeDescription `background` string one of`light` `dark` `transparent` The background the logo is made for. `dark` string Same as standard, for dark backgrounds. Needs a standard logo. `icon` string Same as standard, for compact places. Needs a standard logo. `standard` string Request: a PNG as data:image/png;base64,... of at most 192 KiB, or the value the client already has to keep that image; empty removes it. Response: the path of the stored image. `logo_url` string `name` string `slug` string | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousRemove a team member](https://upfour.io/docs/api/teams/remove-a-team-member.md) [NextAssign a Client to a team](https://upfour.io/docs/api/teams/assign-a-client-to-a-team.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/clients" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/clients", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/clients", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/clients", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "assigned_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000002", "logo": { "background": "light", "dark": "", "icon": "", "standard": "" }, "logo_url": "", "name": "Globex Corporation", "slug": "globex" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Assign a Client to a team — API reference > Assigns the supplied Client to a team and returns no response body. Source: https://upfour.io/docs/api/teams/assign-a-client-to-a-team [Developers](https://upfour.io/docs/api.md)/[Teams](https://upfour.io/docs/api/teams.md) Assigns the supplied Client to a team and returns no response body. POST https://app.upfour.io/v1/teams/{teamID}/clients scope team.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `teamID` required | uuid | path parameter | ## Body `application/json` required 1 field client_iduuid (required) ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList team clients](https://upfour.io/docs/api/teams/list-team-clients.md) [NextRemove a Client from a team](https://upfour.io/docs/api/teams/remove-a-client-from-a-team.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/clients" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "client_id": "00000000-0000-4000-8000-000000000002" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/clients", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ client_id: "00000000-0000-4000-8000-000000000002", }), }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/clients", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "client_id": "00000000-0000-4000-8000-000000000002", }, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "client_id": "00000000-0000-4000-8000-000000000002" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/clients", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Done. The response has no body. ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Remove a Client from a team — API reference > Removes a Client from a team and returns no response body. Source: https://upfour.io/docs/api/teams/remove-a-client-from-a-team [Developers](https://upfour.io/docs/api.md)/[Teams](https://upfour.io/docs/api/teams.md) Removes a Client from a team and returns no response body. DELETE https://app.upfour.io/v1/teams/{teamID}/clients/{clientID} scope team.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `teamID` required | uuid | path parameter | | `clientID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousAssign a Client to a team](https://upfour.io/docs/api/teams/assign-a-client-to-a-team.md) [NextDelete a team](https://upfour.io/docs/api/teams/delete-a-team.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/clients/00000000-0000-4000-8000-000000000002" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/clients/00000000-0000-4000-8000-000000000002", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/clients/00000000-0000-4000-8000-000000000002", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001/clients/00000000-0000-4000-8000-000000000002", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Delete a team — API reference > Returns an empty response after deleting the team. Source: https://upfour.io/docs/api/teams/delete-a-team [Developers](https://upfour.io/docs/api.md)/[Teams](https://upfour.io/docs/api/teams.md) Returns an empty response after deleting the team. DELETE https://app.upfour.io/v1/teams/{teamID} scope team.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `teamID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousRemove a Client from a team](https://upfour.io/docs/api/teams/remove-a-client-from-a-team.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/teams/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Invites — API reference > Invite colleagues — seats are unlimited everywhere. REST endpoints with request and response fields, examples in curl, JavaScript, Python, Go and PHP, and an in-browser Try it console. Source: https://upfour.io/docs/api/invites [Developers](https://upfour.io/docs/api.md)/Organization Invite colleagues — seats are unlimited everywhere. Base URL `https://app.upfour.io/v1`. Send your key as `Authorization: Bearer `; every key acts on your real Company, so [test without paging anyone](https://upfour.io/docs/api/testing.md). New here? Start with the [quickstart](https://upfour.io/docs/api/quickstart.md). The examples read the key from `UP4_API_KEY`. ## Everyday [GET **List invitations** /invites](https://upfour.io/docs/api/invites/list-invitations.md)[POST **Create an invitation** /invites](https://upfour.io/docs/api/invites/create-an-invitation.md) ## Control [POST **Resend an invitation** /invites/{inviteID}/resend](https://upfour.io/docs/api/invites/resend-an-invitation.md)[DEL **Delete an invitation** /invites/{inviteID}](https://upfour.io/docs/api/invites/delete-an-invitation.md) --- # List invitations — API reference > Lists invitations with pagination and expiry details. Source: https://upfour.io/docs/api/invites/list-invitations [Developers](https://upfour.io/docs/api.md)/[Invites](https://upfour.io/docs/api/invites.md) Lists invitations with pagination and expiry details. GET https://app.upfour.io/v1/invites scope member.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `limit` optional | integer 1–250 default `50` | query parameter | | `cursor` optional | string | query parameter | | `client_id` optional | uuid | query parameter | | `all_clients` optional | boolean | query parameter | | `q` optional | string | query parameter | | `sort` optional | string | query parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | Invite[] · nullable | | | Each item · 16 fields FieldTypeDescription `created_at` string (date-time) `email` string `expired` boolean Always false in these answers; compare expires_at with the current time. `expires_at` string (date-time) `id` uuid `invited_by` uuid `organization_id` uuid `role` string one of`owner` `admin` `member` `viewer` `editor` The company role; for a client portal invite (client_id set) the client member role viewer, editor or admin. `accepted_at` string (date-time) `client_id` uuid Set for a client portal invite, whose person only sees that client. `client_name` string `inviter_email` string `language` string one of`en` `de` `org_name` string `team_id` uuid `team_name` string | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [NextCreate an invitation](https://upfour.io/docs/api/invites/create-an-invitation.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/invites" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/invites", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/invites", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/invites", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "created_at": "2026-10-27T09:00:00Z", "email": "dan@example.com", "expired": false, "expires_at": "2026-11-03T09:00:00Z", "id": "00000000-0000-4000-8000-000000000001", "invited_by": "00000000-0000-4000-8000-000000000002", "inviter_email": "anna@example.com", "language": "en", "org_name": "Acme GmbH", "organization_id": "00000000-0000-4000-8000-000000000003", "role": "member" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` --- # Create an invitation — API reference > Creates an invitation for an email address with optional language, role and extra team. Source: https://upfour.io/docs/api/invites/create-an-invitation [Developers](https://upfour.io/docs/api.md)/[Invites](https://upfour.io/docs/api/invites.md) Creates an invitation for an email address with optional language, role and extra team. POST https://app.upfour.io/v1/invites scope member.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Body `application/json` required 4 fields emailstring (email) (required) In the app: **Email address** All other fields3 fields languagestring · nullable Language of the invitation mail; omitted lets up4 pick from the invited person, the company and the inviter. ende rolestring The company role the person gets on accepting. Omitted or any other value invites as member. owneradminmemberviewer In the app: **Role** team_iduuid · nullable An extra team to add the person to besides the default team. In the app: **Team** ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | Invite | | | Invite · 16 fields FieldTypeDescription `created_at` string (date-time) `email` string `expired` boolean Always false in these answers; compare expires_at with the current time. `expires_at` string (date-time) `id` uuid `invited_by` uuid `organization_id` uuid `role` string one of`owner` `admin` `member` `viewer` `editor` The company role; for a client portal invite (client_id set) the client member role viewer, editor or admin. `accepted_at` string (date-time) `client_id` uuid Set for a client portal invite, whose person only sees that client. `client_name` string `inviter_email` string `language` string one of`en` `de` `org_name` string `team_id` uuid `team_name` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 402 | [billing_action_unavailable](https://upfour.io/docs/api/errors#code-billing_action_unavailable) | The Company's billing state does not allow this action. **When:** A metered or limited action while the billing state, such as paused or without credit, blocks it; details holds the action and billing_state. **Fix:** Top up the credit or change the plan, then retry. | | 409 | [conflict](https://upfour.io/docs/api/errors#code-conflict) | The request conflicts with the current state. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 503 | [quota_gate_unavailable](https://upfour.io/docs/api/errors#code-quota_gate_unavailable) | The limit check itself failed, so the action was not carried out. **When:** The quota service could not decide while the request ran. **Fix:** Retry in a moment; nothing was changed. | | 403 / 429 | [quota_limit_reached](https://upfour.io/docs/api/errors#code-quota_limit_reached) | The protection limit for this action is used up; retry after the response's `Retry-After` delay. **When:** Counters and concurrency limits answer 429 with Retry-After, inventory and capacity limits answer 403; details holds purpose, limit and window. **Fix:** On 429 wait Retry-After seconds; on 403 free something up, request a higher limit where details.raise_request is true, or buy an extension. `quota_limit_reached`: the protection limit of this action is used up for now. `error.details` carries `purpose`, `limit`, `window` and `retry_after_seconds`; the `Retry-After` header says when the same request can succeed. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList invitations](https://upfour.io/docs/api/invites/list-invitations.md) [NextResend an invitation](https://upfour.io/docs/api/invites/resend-an-invitation.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/invites" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "email": "dan@example.com", "language": "en", "role": "member" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/invites", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ email: "dan@example.com", language: "en", role: "member", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/invites", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "email": "dan@example.com", "language": "en", "role": "member", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "email": "dan@example.com", "language": "en", "role": "member" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/invites", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "email": "dan@example.com", "expired": false, "expires_at": "2026-11-03T09:00:00Z", "id": "00000000-0000-4000-8000-000000000001", "invited_by": "00000000-0000-4000-8000-000000000002", "language": "en", "organization_id": "00000000-0000-4000-8000-000000000003", "role": "member" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "billing_action_unavailable", "message": "The Company's billing state does not allow this action.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 409 ```json { "error": { "code": "conflict", "message": "The request conflicts with the current state.", "request_id": "req_…" } } ``` ### 429 ```json { "error": { "code": "quota_limit_reached", "message": "The protection limit for this action is used up; retry after the response's Retry-After delay.", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "quota_gate_unavailable", "message": "The limit check itself failed, so the action was not carried out.", "request_id": "req_…" } } ``` --- # Resend an invitation — API reference > Sends the invitation email again and returns no response body. Source: https://upfour.io/docs/api/invites/resend-an-invitation [Developers](https://upfour.io/docs/api.md)/[Invites](https://upfour.io/docs/api/invites.md) Sends the invitation email again and returns no response body. POST https://app.upfour.io/v1/invites/{inviteID}/resend scope member.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `inviteID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 402 | [billing_action_unavailable](https://upfour.io/docs/api/errors#code-billing_action_unavailable) | The Company's billing state does not allow this action. **When:** A metered or limited action while the billing state, such as paused or without credit, blocks it; details holds the action and billing_state. **Fix:** Top up the credit or change the plan, then retry. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 503 | [quota_gate_unavailable](https://upfour.io/docs/api/errors#code-quota_gate_unavailable) | The limit check itself failed, so the action was not carried out. **When:** The quota service could not decide while the request ran. **Fix:** Retry in a moment; nothing was changed. | | 403 / 429 | [quota_limit_reached](https://upfour.io/docs/api/errors#code-quota_limit_reached) | The protection limit for this action is used up; retry after the response's `Retry-After` delay. **When:** Counters and concurrency limits answer 429 with Retry-After, inventory and capacity limits answer 403; details holds purpose, limit and window. **Fix:** On 429 wait Retry-After seconds; on 403 free something up, request a higher limit where details.raise_request is true, or buy an extension. `quota_limit_reached`: the protection limit of this action is used up for now. `error.details` carries `purpose`, `limit`, `window` and `retry_after_seconds`; the `Retry-After` header says when the same request can succeed. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousCreate an invitation](https://upfour.io/docs/api/invites/create-an-invitation.md) [NextDelete an invitation](https://upfour.io/docs/api/invites/delete-an-invitation.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/invites/00000000-0000-4000-8000-000000000001/resend" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/invites/00000000-0000-4000-8000-000000000001/resend", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/invites/00000000-0000-4000-8000-000000000001/resend", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/invites/00000000-0000-4000-8000-000000000001/resend", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Done. The response has no body. ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "billing_action_unavailable", "message": "The Company's billing state does not allow this action.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 429 ```json { "error": { "code": "quota_limit_reached", "message": "The protection limit for this action is used up; retry after the response's Retry-After delay.", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "quota_gate_unavailable", "message": "The limit check itself failed, so the action was not carried out.", "request_id": "req_…" } } ``` --- # Delete an invitation — API reference > Returns an empty response after deleting the invitation. Source: https://upfour.io/docs/api/invites/delete-an-invitation [Developers](https://upfour.io/docs/api.md)/[Invites](https://upfour.io/docs/api/invites.md) Returns an empty response after deleting the invitation. DELETE https://app.upfour.io/v1/invites/{inviteID} scope member.write stable returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `inviteID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousResend an invitation](https://upfour.io/docs/api/invites/resend-an-invitation.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/invites/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/invites/00000000-0000-4000-8000-000000000001", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/invites/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/invites/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Clients (agency mode) — API reference > The agency partition: clients, client teams, share links, archive/unarchive. REST endpoints with request and response fields, examples in curl, JavaScript, Python, Go and PHP, and an in-browser Try it console. Source: https://upfour.io/docs/api/clients [Developers](https://upfour.io/docs/api.md)/Organization The agency partition: clients, client teams, share links, archive/unarchive. Base URL `https://app.upfour.io/v1`. Send your key as `Authorization: Bearer `; every key acts on your real Company, so [test without paging anyone](https://upfour.io/docs/api/testing.md). New here? Start with the [quickstart](https://upfour.io/docs/api/quickstart.md). The examples read the key from `UP4_API_KEY`. ## Everyday [GET **List clients** /clients](https://upfour.io/docs/api/clients/list-clients.md)[POST **Create a client** /clients](https://upfour.io/docs/api/clients/create-a-client.md)[GET **Get a client** /clients/{clientID}](https://upfour.io/docs/api/clients/get-a-client.md)[PUT **Update a client** /clients/{clientID}](https://upfour.io/docs/api/clients/update-a-client.md) ## Client setup [POST **Import a client logo** /clients/logo-import](https://upfour.io/docs/api/clients/import-a-client-logo.md) ## Client access [GET **Get client portal settings** /clients/{clientID}/portal-settings](https://upfour.io/docs/api/clients/get-client-portal-settings.md)[PUT **Update client portal settings** /clients/{clientID}/portal-settings](https://upfour.io/docs/api/clients/update-client-portal-settings.md)[GET **List client teams** /clients/{clientID}/teams](https://upfour.io/docs/api/clients/list-client-teams.md) ## Sharing [GET **List client share links** /clients/{clientID}/share-links](https://upfour.io/docs/api/clients/list-client-share-links.md)[POST **Create a client share link** /clients/{clientID}/share-links](https://upfour.io/docs/api/clients/create-a-client-share-link.md)[DEL **Delete a client share link** /clients/{clientID}/share-links/{linkID}](https://upfour.io/docs/api/clients/delete-a-client-share-link.md) ## Control [POST **Archive a client** /clients/{clientID}/archive](https://upfour.io/docs/api/clients/archive-a-client.md)[POST **Unarchive a client** /clients/{clientID}/unarchive](https://upfour.io/docs/api/clients/unarchive-a-client.md) --- # List clients — API reference > Returns clients with optional filters, sorting, cursor and page-size parameters. Source: https://upfour.io/docs/api/clients/list-clients [Developers](https://upfour.io/docs/api.md)/[Clients (agency mode)](https://upfour.io/docs/api/clients.md) Returns clients with optional filters, sorting, cursor and page-size parameters. GET https://app.upfour.io/v1/clients scope client.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `limit` optional | integer 1–250 default `50` | query parameter | | `cursor` optional | string | query parameter | | `client_id` optional | uuid | query parameter | | `all_clients` optional | boolean | query parameter | | `q` optional | string | query parameter | | `sort` optional | string | query parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | Client[] · nullable | | | Each item · 20 fields FieldTypeDescription `allow_login` boolean `contact_email` string `created_at` string (date-time) `id` uuid `language` string · nullable one of`en` `de` `logo` StoredClientLogo StoredClientLogo · 4 fields FieldTypeDescription `background` string one of`light` `dark` `transparent` The background the logo is made for. `dark` string Same as standard, for dark backgrounds. Needs a standard logo. `icon` string Same as standard, for compact places. Needs a standard logo. `standard` string Request: a PNG as data:image/png;base64,... of at most 192 KiB, or the value the client already has to keep that image; empty removes it. Response: the path of the stored image. `logo_url` string A hosted logo; empty once a standard logo is stored. `name` string `organization_id` uuid `slug` string `timezone` string `updated_at` string (date-time) `archived_at` string (date-time) Set while the client is archived. `effective_language` string one of`en` `de` Only in the answer for one client: the language the client actually gets, its own or the company default. `monitor_count` integer Only in lists, omitted when zero. `open_incidents` integer Only in lists, omitted when zero. | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [NextCreate a client](https://upfour.io/docs/api/clients/create-a-client.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/clients" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/clients", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/clients", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/clients", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "allow_login": false, "contact_email": "anna@example.com", "created_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000001", "language": "en", "logo": { "background": "light", "dark": "", "icon": "", "standard": "" }, "logo_url": "", "name": "Globex Corp", "organization_id": "00000000-0000-4000-8000-000000000002", "slug": "globex", "timezone": "Europe/Berlin", "updated_at": "2026-10-27T09:00:00Z" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Create a client — API reference > Creates a Client with optional portal access, localization, branding, and a template that can create monitors or status pages. Source: https://upfour.io/docs/api/clients/create-a-client [Developers](https://upfour.io/docs/api.md)/[Clients (agency mode)](https://upfour.io/docs/api/clients.md) Creates a Client with optional portal access, localization, branding, and a template that can create monitors or status pages. POST https://app.upfour.io/v1/clients scope client.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Body The 5 fields most calls use come first; the rest are grouped below. `application/json` required 12 fields namestring (required) In the app: **Client name** slugstring Lowercase letters, digits and hyphens; derived from the name when empty. contact_emailstring languagestring · nullable The language of mails and documents for this client; null or empty inherits the company default. ende In the app: **Communication language** timezonestring IANA timezone name; empty takes the company's reporting timezone. Portal access1 fields allow_loginboolean Lets the client's own staff sign in to the client portal. Without it the client only has share links. In the app: **Let this client sign in** Template application3 fields template_idstring A client template to apply to the new client. In the app: **Starter template** domainstring The client's domain for the template's {{domain}} placeholder. Required when the template uses it. In the app: **Primary domain** design_template_revisionsmap of integer Maps the id of each status page design the template uses to the revision you saw; the create stops with a conflict when a design changed since. Logo6 fields logoClientLogo · nullable Logo images; ignored when creation_key matches a client that exists. Nested fields 4 fields | Field | Type | Description | | --- | --- | --- | | `background` optional | string one of`light` `dark` `transparent` | The background the logo is made for. | | `dark` optional | string | Same as standard, for dark backgrounds. Needs a standard logo. | | `icon` optional | string | Same as standard, for compact places. Needs a standard logo. | | `standard` optional | string | Request: a PNG as data:image/png;base64,... of at most 192 KiB, or the value the client already has to keep that image; empty removes it. Response: the path of the stored image. | logo_urlstring A hosted logo, http:// or https://. Saving a standard logo clears it. Idempotency1 fields creation_keyuuid · nullable Idempotency key: creating again with the same key returns the client that exists instead of a second one. ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ClientCreateResponse | | | ClientCreateResponse · 42 fields FieldTypeDescription `allow_login` boolean `contact_email` string `created_at` string (date-time) `id` uuid `language` string · nullable one of`en` `de` `logo` StoredClientLogo StoredClientLogo · 4 fields FieldTypeDescription `background` string one of`light` `dark` `transparent` The background the logo is made for. `dark` string Same as standard, for dark backgrounds. Needs a standard logo. `icon` string Same as standard, for compact places. Needs a standard logo. `standard` string Request: a PNG as data:image/png;base64,... of at most 192 KiB, or the value the client already has to keep that image; empty removes it. Response: the path of the stored image. `logo_url` string A hosted logo; empty once a standard logo is stored. `name` string `organization_id` uuid `slug` string `timezone` string `updated_at` string (date-time) `archived_at` string (date-time) Set while the client is archived. `effective_language` string one of`en` `de` Only in the answer for one client: the language the client actually gets, its own or the company default. `materialization` MaterializeResult MaterializeResult · 21 fields FieldTypeDescription `monitors_created` integer `policies_created` integer `status_pages_created` integer `errors` MaterializeError[] Each item · 3 fields FieldTypeDescription `kind` string one of`template` `monitor` `monitor_alarm_route` `policy` `status_page` `message` string `name` string `monitors` MaterializedMonitor[] Each item · 8 fields FieldTypeDescription `alarm_route_status` string one of`configured` `without_alarm_route` `id` uuid `name` string `channels` MaterializedChannel[] Each item · 4 fields FieldTypeDescription `id` uuid `name` string `scope` string `type` string `status_pages` MaterializedStatusPage[] Each item · 4 fields FieldTypeDescription `id` uuid `is_published` boolean `name` string `subdomain` string `monitor_count` integer Only in lists, omitted when zero. `open_incidents` integer Only in lists, omitted when zero. | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [check_capacity_exceeded](https://upfour.io/docs/api/errors#code-check_capacity_exceeded) | For subscriptions, the change would raise projected 30-day monitor usage above the included check budget; `error.details` gives projected, included and change costs. **When:** Creating a monitor, directly or through a client template, or changing one, when the projected cost including the change exceeds the budget; running monitors are not affected, and details carries projected_cents, included_cents and change_cents. **Fix:** Use a longer interval, fewer regions or fewer monitors, or raise the included budget. `check_capacity_exceeded` (subscriptions only): the change would raise the projected 30-day list usage of all active monitors above the included check budget. `error.details` carries `projected_cents`, `included_cents` and `change_cents`; a longer interval, fewer regions or fewer monitors fit. Running monitors and changes that lower the load are never refused. | | 409 | [design_template_conflict](https://upfour.io/docs/api/errors#code-design_template_conflict) | The design template was changed after the caller read it. **When:** Updating or applying a design template that was changed after the caller read it. **Fix:** Reload the template and apply the change again. | | 404 | [design_template_not_found](https://upfour.io/docs/api/errors#code-design_template_not_found) | The design template does not exist or is not visible to the caller. **When:** A design template id in the path or body that is unknown or not visible to the caller. **Fix:** List design templates and use an id from the list. | | 400 | [file_empty](https://upfour.io/docs/api/errors#code-file_empty) | The uploaded logo file is empty. **When:** Uploading a logo or image with no content. **Fix:** Upload a file that has content. Stable codes next to the English message when a logo is refused: `file_empty`, `file_type_unsupported` (`error.details.formats` lists the accepted ones), `image_invalid` (damaged or unreadable), `image_dimensions_too_large` (`error.details.max_pixels`), `svg_unsafe`. | | 413 | [file_too_large](https://upfour.io/docs/api/errors#code-file_too_large) | The logo file exceeds its size limit; `error.details.max_bytes` gives the limit. **When:** Uploading a logo or image larger than details.max_bytes. **Fix:** Reduce the file size and upload again. `file_too_large`: the file is over its limit (`error.details.max_bytes`). A proxy in front of the API may answer 413 with a plain HTML body and no code. | | 400 | [file_type_unsupported](https://upfour.io/docs/api/errors#code-file_type_unsupported) | The uploaded logo file type is unsupported; `error.details.formats` lists accepted formats. **When:** Uploading a file whose type is not one of the formats the operation accepts; details.formats lists them. **Fix:** Upload a file in one of details.formats. | | 400 | [image_dimensions_too_large](https://upfour.io/docs/api/errors#code-image_dimensions_too_large) | The logo image exceeds the allowed dimensions; `error.details.max_pixels` gives the limit. **When:** Uploading an image with more pixels than details.max_pixels. **Fix:** Scale the image down and upload it again. | | 400 | [image_invalid](https://upfour.io/docs/api/errors#code-image_invalid) | The uploaded logo image is damaged or unreadable. **When:** Uploading a file with an image type that cannot be decoded. **Fix:** Export the image again and upload the new file. | | 400 | [logo_fetch_failed](https://upfour.io/docs/api/errors#code-logo_fetch_failed) | The logo could not be fetched from the address given. **When:** Importing a client logo from a URL that cannot be reached or that does not return an acceptable image. **Fix:** Check the URL, or upload the logo file instead. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 400 | [svg_unsafe](https://upfour.io/docs/api/errors#code-svg_unsafe) | The SVG logo contains unsafe content. **When:** Uploading an SVG that contains a script, a foreignObject, a javascript: URL, an event-handler attribute or an external href. **Fix:** Remove those parts from the SVG, or upload a PNG or JPEG. | | 400 | [upload_invalid](https://upfour.io/docs/api/errors#code-upload_invalid) | The upload could not be read as a multipart form. **When:** A body that is not a valid multipart form, or whose file cannot be parsed. **Fix:** Send a well-formed multipart/form-data request. | | 400 | [upload_no_file](https://upfour.io/docs/api/errors#code-upload_no_file) | The multipart request has no file part. **When:** An upload request without a file field. **Fix:** Send the file as multipart/form-data in the field the operation names. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList clients](https://upfour.io/docs/api/clients/list-clients.md) [NextGet a client](https://upfour.io/docs/api/clients/get-a-client.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/clients" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "contact_email": "anna@example.com", "language": "en", "name": "Globex Corp", "slug": "globex", "timezone": "Europe/Berlin" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/clients", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ contact_email: "anna@example.com", language: "en", name: "Globex Corp", slug: "globex", timezone: "Europe/Berlin", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/clients", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "contact_email": "anna@example.com", "language": "en", "name": "Globex Corp", "slug": "globex", "timezone": "Europe/Berlin", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "contact_email": "anna@example.com", "language": "en", "name": "Globex Corp", "slug": "globex", "timezone": "Europe/Berlin" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/clients", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "allow_login": false, "contact_email": "anna@example.com", "created_at": "2026-10-27T09:00:00Z", "effective_language": "en", "id": "00000000-0000-4000-8000-000000000001", "language": "en", "logo": { "background": "light", "dark": "", "icon": "", "standard": "" }, "logo_url": "", "name": "Globex Corp", "organization_id": "00000000-0000-4000-8000-000000000002", "slug": "globex", "timezone": "Europe/Berlin", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "check_capacity_exceeded", "message": "For subscriptions, the change would raise projected 30-day monitor usage above the included check budget; error.details gives projected, included and change costs.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "design_template_not_found", "message": "The design template does not exist or is not visible to the caller.", "request_id": "req_…" } } ``` ### 409 ```json { "error": { "code": "design_template_conflict", "message": "The design template was changed after the caller read it.", "request_id": "req_…" } } ``` ### 413 ```json { "error": { "code": "file_too_large", "message": "The logo file exceeds its size limit; error.details.max_bytes gives the limit.", "request_id": "req_…" } } ``` --- # Get a client — API reference > Returns a Client with its name, slug, language, timezone, logo and portal access setting. Source: https://upfour.io/docs/api/clients/get-a-client [Developers](https://upfour.io/docs/api.md)/[Clients (agency mode)](https://upfour.io/docs/api/clients.md) Returns a Client with its name, slug, language, timezone, logo and portal access setting. GET https://app.upfour.io/v1/clients/{clientID} scope client.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `clientID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | Client | | | Client · 20 fields FieldTypeDescription `allow_login` boolean `contact_email` string `created_at` string (date-time) `id` uuid `language` string · nullable one of`en` `de` `logo` StoredClientLogo StoredClientLogo · 4 fields FieldTypeDescription `background` string one of`light` `dark` `transparent` The background the logo is made for. `dark` string Same as standard, for dark backgrounds. Needs a standard logo. `icon` string Same as standard, for compact places. Needs a standard logo. `standard` string Request: a PNG as data:image/png;base64,... of at most 192 KiB, or the value the client already has to keep that image; empty removes it. Response: the path of the stored image. `logo_url` string A hosted logo; empty once a standard logo is stored. `name` string `organization_id` uuid `slug` string `timezone` string `updated_at` string (date-time) `archived_at` string (date-time) Set while the client is archived. `effective_language` string one of`en` `de` Only in the answer for one client: the language the client actually gets, its own or the company default. `monitor_count` integer Only in lists, omitted when zero. `open_incidents` integer Only in lists, omitted when zero. | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousCreate a client](https://upfour.io/docs/api/clients/create-a-client.md) [NextUpdate a client](https://upfour.io/docs/api/clients/update-a-client.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "allow_login": false, "contact_email": "anna@example.com", "created_at": "2026-10-27T09:00:00Z", "effective_language": "en", "id": "00000000-0000-4000-8000-000000000001", "language": "en", "logo": { "background": "light", "dark": "", "icon": "", "standard": "" }, "logo_url": "", "name": "Globex Corp", "organization_id": "00000000-0000-4000-8000-000000000002", "slug": "globex", "timezone": "Europe/Berlin", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Update a client — API reference > Updates a Client's name, portal access, branding, language, timezone, or slug. Source: https://upfour.io/docs/api/clients/update-a-client [Developers](https://upfour.io/docs/api.md)/[Clients (agency mode)](https://upfour.io/docs/api/clients.md) Updates a Client's name, portal access, branding, language, timezone, or slug. PUT https://app.upfour.io/v1/clients/{clientID} scope client.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `clientID` required | uuid | path parameter | ## Body `application/json` required 8 fields namestring (required) In the app: **Client name** All other fields11 fields allow_loginboolean Lets the client's own staff sign in to the client portal. The update replaces all fields: an omitted value turns it off. In the app: **Let this client sign in** contact_emailstring languagestring · nullable The language of mails and documents for this client; null or empty inherits the company default. The update replaces all fields: an omitted language resets it to inherit. ende In the app: **Communication language** logoClientLogo · nullable Logo images. Omitted keeps the stored logo. Nested fields 4 fields | Field | Type | Description | | --- | --- | --- | | `background` optional | string one of`light` `dark` `transparent` | The background the logo is made for. | | `dark` optional | string | Same as standard, for dark backgrounds. Needs a standard logo. | | `icon` optional | string | Same as standard, for compact places. Needs a standard logo. | | `standard` optional | string | Request: a PNG as data:image/png;base64,... of at most 192 KiB, or the value the client already has to keep that image; empty removes it. Response: the path of the stored image. | logo_urlstring A hosted logo, http:// or https://. Saving a standard logo clears it. slugstring Lowercase letters, digits and hyphens; derived from the name when empty. timezonestring IANA timezone name; empty resets it to UTC. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | Client | | | Client · 20 fields FieldTypeDescription `allow_login` boolean `contact_email` string `created_at` string (date-time) `id` uuid `language` string · nullable one of`en` `de` `logo` StoredClientLogo StoredClientLogo · 4 fields FieldTypeDescription `background` string one of`light` `dark` `transparent` The background the logo is made for. `dark` string Same as standard, for dark backgrounds. Needs a standard logo. `icon` string Same as standard, for compact places. Needs a standard logo. `standard` string Request: a PNG as data:image/png;base64,... of at most 192 KiB, or the value the client already has to keep that image; empty removes it. Response: the path of the stored image. `logo_url` string A hosted logo; empty once a standard logo is stored. `name` string `organization_id` uuid `slug` string `timezone` string `updated_at` string (date-time) `archived_at` string (date-time) Set while the client is archived. `effective_language` string one of`en` `de` Only in the answer for one client: the language the client actually gets, its own or the company default. `monitor_count` integer Only in lists, omitted when zero. `open_incidents` integer Only in lists, omitted when zero. | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 400 | [file_empty](https://upfour.io/docs/api/errors#code-file_empty) | The uploaded logo file is empty. **When:** Uploading a logo or image with no content. **Fix:** Upload a file that has content. Stable codes next to the English message when a logo is refused: `file_empty`, `file_type_unsupported` (`error.details.formats` lists the accepted ones), `image_invalid` (damaged or unreadable), `image_dimensions_too_large` (`error.details.max_pixels`), `svg_unsafe`. | | 413 | [file_too_large](https://upfour.io/docs/api/errors#code-file_too_large) | The logo file exceeds its size limit; `error.details.max_bytes` gives the limit. **When:** Uploading a logo or image larger than details.max_bytes. **Fix:** Reduce the file size and upload again. `file_too_large`: the file is over its limit (`error.details.max_bytes`). A proxy in front of the API may answer 413 with a plain HTML body and no code. | | 400 | [file_type_unsupported](https://upfour.io/docs/api/errors#code-file_type_unsupported) | The uploaded logo file type is unsupported; `error.details.formats` lists accepted formats. **When:** Uploading a file whose type is not one of the formats the operation accepts; details.formats lists them. **Fix:** Upload a file in one of details.formats. | | 400 | [image_dimensions_too_large](https://upfour.io/docs/api/errors#code-image_dimensions_too_large) | The logo image exceeds the allowed dimensions; `error.details.max_pixels` gives the limit. **When:** Uploading an image with more pixels than details.max_pixels. **Fix:** Scale the image down and upload it again. | | 400 | [image_invalid](https://upfour.io/docs/api/errors#code-image_invalid) | The uploaded logo image is damaged or unreadable. **When:** Uploading a file with an image type that cannot be decoded. **Fix:** Export the image again and upload the new file. | | 400 | [logo_fetch_failed](https://upfour.io/docs/api/errors#code-logo_fetch_failed) | The logo could not be fetched from the address given. **When:** Importing a client logo from a URL that cannot be reached or that does not return an acceptable image. **Fix:** Check the URL, or upload the logo file instead. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 400 | [svg_unsafe](https://upfour.io/docs/api/errors#code-svg_unsafe) | The SVG logo contains unsafe content. **When:** Uploading an SVG that contains a script, a foreignObject, a javascript: URL, an event-handler attribute or an external href. **Fix:** Remove those parts from the SVG, or upload a PNG or JPEG. | | 400 | [upload_invalid](https://upfour.io/docs/api/errors#code-upload_invalid) | The upload could not be read as a multipart form. **When:** A body that is not a valid multipart form, or whose file cannot be parsed. **Fix:** Send a well-formed multipart/form-data request. | | 400 | [upload_no_file](https://upfour.io/docs/api/errors#code-upload_no_file) | The multipart request has no file part. **When:** An upload request without a file field. **Fix:** Send the file as multipart/form-data in the field the operation names. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet a client](https://upfour.io/docs/api/clients/get-a-client.md) [NextImport a client logo](https://upfour.io/docs/api/clients/import-a-client-logo.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "contact_email": "anna@example.com", "language": "en", "name": "Globex Corporation", "slug": "globex", "timezone": "Europe/Berlin" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ contact_email: "anna@example.com", language: "en", name: "Globex Corporation", slug: "globex", timezone: "Europe/Berlin", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "contact_email": "anna@example.com", "language": "en", "name": "Globex Corporation", "slug": "globex", "timezone": "Europe/Berlin", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "contact_email": "anna@example.com", "language": "en", "name": "Globex Corporation", "slug": "globex", "timezone": "Europe/Berlin" }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "allow_login": false, "contact_email": "anna@example.com", "created_at": "2026-10-27T09:00:00Z", "effective_language": "en", "id": "00000000-0000-4000-8000-000000000001", "language": "en", "logo": { "background": "light", "dark": "", "icon": "", "standard": "" }, "logo_url": "", "name": "Globex Corporation", "organization_id": "00000000-0000-4000-8000-000000000002", "slug": "globex", "timezone": "Europe/Berlin", "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` ### 413 ```json { "error": { "code": "file_too_large", "message": "The logo file exceeds its size limit; error.details.max_bytes gives the limit.", "request_id": "req_…" } } ``` --- # Import a client logo — API reference > Fetches a public HTTP(S) image for use as a Client logo. Source: https://upfour.io/docs/api/clients/import-a-client-logo [Developers](https://upfour.io/docs/api.md)/[Clients (agency mode)](https://upfour.io/docs/api/clients.md) Fetches a public HTTP(S) image for use as a Client logo. POST https://app.upfour.io/v1/clients/logo-import scope client.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Body `application/json` required 1 field urlstring (required) The http:// or https:// address of the image to fetch. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ClientLogoImportResponse | | | ClientLogoImportResponse · 1 fields FieldTypeDescription `source` string The fetched image, base64 encoded; it is returned for the logo editor and not stored. | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 400 | [logo_fetch_failed](https://upfour.io/docs/api/errors#code-logo_fetch_failed) | The image is not a public HTTP(S) image of at most 1 MiB or could not be fetched. **When:** Importing a client logo from a URL that cannot be reached or that does not return an acceptable image. **Fix:** Check the URL, or upload the logo file instead. `logo_fetch_failed`: the address is not a public HTTP(S) image of at most 1 MiB, or could not be fetched. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpdate a client](https://upfour.io/docs/api/clients/update-a-client.md) [NextGet client portal settings](https://upfour.io/docs/api/clients/get-client-portal-settings.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/clients/logo-import" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/clients/logo-import", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ url: "https://example.com", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/clients/logo-import", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "url": "https://example.com", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "url": "https://example.com" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/clients/logo-import", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "source": "example" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Get client portal settings — API reference > Returns a Client's portal preset, incident visibility, report visibility and export setting. Source: https://upfour.io/docs/api/clients/get-client-portal-settings [Developers](https://upfour.io/docs/api.md)/[Clients (agency mode)](https://upfour.io/docs/api/clients.md) Returns a Client's portal preset, incident visibility, report visibility and export setting. GET https://app.upfour.io/v1/clients/{clientID}/portal-settings scope client.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `clientID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ClientPortalSettingsResponse | | | ClientPortalSettingsResponse · 10 fields FieldTypeDescription `allow_report_export` boolean `client_id` uuid Taken from the path on update. `preset` string one of`compact` `transparent` `custom` compact and transparent set all switches themselves and ignore the ones sent; custom takes them as sent. Empty means compact. `show_incidents` boolean `show_report_downtime` boolean `show_report_incidents` boolean `show_report_response_time` boolean `show_report_sla` boolean `show_reports` boolean The report views. With preset custom and this off, the report switches below are stored off. `updated_at` string (date-time) Ignored on update. | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousImport a client logo](https://upfour.io/docs/api/clients/import-a-client-logo.md) [NextUpdate client portal settings](https://upfour.io/docs/api/clients/update-client-portal-settings.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/portal-settings" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/portal-settings", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/portal-settings", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/portal-settings", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "allow_report_export": false, "client_id": "00000000-0000-4000-8000-000000000001", "preset": "compact", "show_incidents": false, "show_report_downtime": false, "show_report_incidents": false, "show_report_response_time": true, "show_report_sla": true, "show_reports": true, "updated_at": "1734-07-18T09:15:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Update client portal settings — API reference > Updates a Client's portal preset, incident visibility, report visibility and export setting. Source: https://upfour.io/docs/api/clients/update-client-portal-settings [Developers](https://upfour.io/docs/api.md)/[Clients (agency mode)](https://upfour.io/docs/api/clients.md) Updates a Client's portal preset, incident visibility, report visibility and export setting. PUT https://app.upfour.io/v1/clients/{clientID}/portal-settings scope client.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `clientID` required | uuid | path parameter | ## Body The 3 fields most calls use come first; the rest are grouped below. `application/json` required 10 fields presetstring compact and transparent set all switches themselves and ignore the ones sent; custom takes them as sent. Empty means compact. compacttransparentcustom In the app: **Portal view** show_reportsboolean The report views. With preset custom and this off, the report switches below are stored off. In the app: **Reports** allow_report_exportboolean In the app: **Report downloads** Incident visibility1 fields show_incidentsboolean In the app: **Incidents** Report visibility4 fields show_report_downtimeboolean In the app: **Downtime evidence** show_report_incidentsboolean In the app: **Incident analytics** show_report_response_timeboolean In the app: **Response time** show_report_slaboolean In the app: **Service levels** Request metadata2 fields client_iduuid Taken from the path on update. updated_atstring (date-time) Ignored on update. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ClientPortalSettingsResponse | | | ClientPortalSettingsResponse · 10 fields FieldTypeDescription `allow_report_export` boolean `client_id` uuid Taken from the path on update. `preset` string one of`compact` `transparent` `custom` compact and transparent set all switches themselves and ignore the ones sent; custom takes them as sent. Empty means compact. `show_incidents` boolean `show_report_downtime` boolean `show_report_incidents` boolean `show_report_response_time` boolean `show_report_sla` boolean `show_reports` boolean The report views. With preset custom and this off, the report switches below are stored off. `updated_at` string (date-time) Ignored on update. | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet client portal settings](https://upfour.io/docs/api/clients/get-client-portal-settings.md) [NextList client teams](https://upfour.io/docs/api/clients/list-client-teams.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/portal-settings" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "allow_report_export": false, "preset": "compact", "show_incidents": true, "show_report_sla": true, "show_reports": true }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/portal-settings", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ allow_report_export: false, preset: "compact", show_incidents: true, show_report_sla: true, show_reports: true, }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/portal-settings", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "allow_report_export": False, "preset": "compact", "show_incidents": True, "show_report_sla": True, "show_reports": True, }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "allow_report_export": false, "preset": "compact", "show_incidents": true, "show_report_sla": true, "show_reports": true }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/portal-settings", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "allow_report_export": false, "client_id": "00000000-0000-4000-8000-000000000001", "preset": "compact", "show_incidents": false, "show_report_downtime": false, "show_report_incidents": false, "show_report_response_time": true, "show_report_sla": true, "show_reports": true, "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # List client teams — API reference > Returns the teams assigned to a Client with member counts and default flags. Source: https://upfour.io/docs/api/clients/list-client-teams [Developers](https://upfour.io/docs/api.md)/[Clients (agency mode)](https://upfour.io/docs/api/clients.md) Returns the teams assigned to a Client with member counts and default flags. GET https://app.upfour.io/v1/clients/{clientID}/teams scope client.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `clientID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ClientTeamRow[] · nullable | | | Each item · 5 fields FieldTypeDescription `assigned_at` string (date-time) `id` uuid `is_default` boolean `member_count` integer `name` string | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpdate client portal settings](https://upfour.io/docs/api/clients/update-client-portal-settings.md) [NextList client share links](https://upfour.io/docs/api/clients/list-client-share-links.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/teams" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/teams", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/teams", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/teams", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "assigned_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000002", "is_default": true, "member_count": 0, "name": "Operations" }, { "assigned_at": "2026-10-27T09:00:00Z", "id": "00000000-0000-4000-8000-000000000003", "is_default": false, "member_count": 2, "name": "Platform engineering" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # List client share links — API reference > Returns a Client's share links with labels, expiration and usage timestamps. Source: https://upfour.io/docs/api/clients/list-client-share-links [Developers](https://upfour.io/docs/api.md)/[Clients (agency mode)](https://upfour.io/docs/api/clients.md) Returns a Client's share links with labels, expiration and usage timestamps. GET https://app.upfour.io/v1/clients/{clientID}/share-links scope client.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `clientID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ClientShareLink[] · nullable | | | Each item · 8 fields FieldTypeDescription `client_id` uuid `created_at` string (date-time) `id` uuid `label` string `created_by` uuid `expires_at` string (date-time) `last_used_at` string (date-time) When someone last opened the link. `revoked_at` string (date-time) Set once the link is revoked. | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList client teams](https://upfour.io/docs/api/clients/list-client-teams.md) [NextCreate a client share link](https://upfour.io/docs/api/clients/create-a-client-share-link.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/share-links" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/share-links", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/share-links", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/share-links", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "client_id": "00000000-0000-4000-8000-000000000001", "created_at": "2026-10-27T09:00:00Z", "created_by": "00000000-0000-4000-8000-000000000002", "expires_at": "2027-01-25T09:00:00Z", "id": "00000000-0000-4000-8000-000000000003", "label": "Quarterly review" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Create a client share link — API reference > Creates a share link for a Client with an optional label and expiry. Source: https://upfour.io/docs/api/clients/create-a-client-share-link [Developers](https://upfour.io/docs/api.md)/[Clients (agency mode)](https://upfour.io/docs/api/clients.md) Creates a share link for a Client with an optional label and expiry. POST https://app.upfour.io/v1/clients/{clientID}/share-links scope client.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Parameters | Field | Type | Description | | --- | --- | --- | | `clientID` required | uuid | path parameter | ## Body `application/json` required 2 fields All other fields2 fields expires_atstring (date-time) · nullable Defaults to 90 days from now. labelstring A name for the link, e.g. who it went to. ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | CreateShareLinkResult | | | CreateShareLinkResult · 9 fields FieldTypeDescription `client_id` uuid `created_at` string (date-time) `id` uuid `label` string `token` string The link token. Only this answer carries it; copy it now. `created_by` uuid `expires_at` string (date-time) `last_used_at` string (date-time) When someone last opened the link. `revoked_at` string (date-time) Set once the link is revoked. | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList client share links](https://upfour.io/docs/api/clients/list-client-share-links.md) [NextDelete a client share link](https://upfour.io/docs/api/clients/delete-a-client-share-link.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/share-links" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "label": "Quarterly review" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/share-links", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ label: "Quarterly review", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/share-links", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "label": "Quarterly review", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "label": "Quarterly review" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/share-links", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "client_id": "00000000-0000-4000-8000-000000000001", "created_at": "2026-10-27T09:00:00Z", "created_by": "00000000-0000-4000-8000-000000000002", "expires_at": "2027-01-25T09:00:00Z", "id": "00000000-0000-4000-8000-000000000003", "label": "Quarterly review", "token": "••••" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Delete a client share link — API reference > Returns a confirmation message after deleting a Client share link. Source: https://upfour.io/docs/api/clients/delete-a-client-share-link [Developers](https://upfour.io/docs/api.md)/[Clients (agency mode)](https://upfour.io/docs/api/clients.md) Returns a confirmation message after deleting a Client share link. DELETE https://app.upfour.io/v1/clients/{clientID}/share-links/{linkID} scope client.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `clientID` required | uuid | path parameter | | `linkID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ClientMessageResponse | | | ClientMessageResponse · 1 fields FieldTypeDescription `message` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousCreate a client share link](https://upfour.io/docs/api/clients/create-a-client-share-link.md) [NextArchive a client](https://upfour.io/docs/api/clients/archive-a-client.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/share-links/00000000-0000-4000-8000-000000000002" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/share-links/00000000-0000-4000-8000-000000000002", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/share-links/00000000-0000-4000-8000-000000000002", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/share-links/00000000-0000-4000-8000-000000000002", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "message": "Share link revoked" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Archive a client — API reference > Returns a confirmation message after archiving a Client. Source: https://upfour.io/docs/api/clients/archive-a-client [Developers](https://upfour.io/docs/api.md)/[Clients (agency mode)](https://upfour.io/docs/api/clients.md) Returns a confirmation message after archiving a Client. POST https://app.upfour.io/v1/clients/{clientID}/archive scope client.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `clientID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ClientMessageResponse | | | ClientMessageResponse · 1 fields FieldTypeDescription `message` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousDelete a client share link](https://upfour.io/docs/api/clients/delete-a-client-share-link.md) [NextUnarchive a client](https://upfour.io/docs/api/clients/unarchive-a-client.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/archive" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/archive", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/archive", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/archive", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "message": "Client archived" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Unarchive a client — API reference > Returns a confirmation message after unarchiving a Client. Source: https://upfour.io/docs/api/clients/unarchive-a-client [Developers](https://upfour.io/docs/api.md)/[Clients (agency mode)](https://upfour.io/docs/api/clients.md) Returns a confirmation message after unarchiving a Client. POST https://app.upfour.io/v1/clients/{clientID}/unarchive scope client.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `clientID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ClientMessageResponse | | | ClientMessageResponse · 1 fields FieldTypeDescription `message` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousArchive a client](https://upfour.io/docs/api/clients/archive-a-client.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/unarchive" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/unarchive", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/unarchive", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/clients/00000000-0000-4000-8000-000000000001/unarchive", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "message": "Client unarchived" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Client templates — API reference > Materialize monitors, pages and alert plans for a new client in one step. REST endpoints with request and response fields, examples in curl, JavaScript, Python, Go and PHP, and an in-browser Try it console. Source: https://upfour.io/docs/api/client-templates [Developers](https://upfour.io/docs/api.md)/Organization Materialize monitors, pages and alert plans for a new client in one step. Base URL `https://app.upfour.io/v1`. Send your key as `Authorization: Bearer `; every key acts on your real Company, so [test without paging anyone](https://upfour.io/docs/api/testing.md). New here? Start with the [quickstart](https://upfour.io/docs/api/quickstart.md). The examples read the key from `UP4_API_KEY`. ## Everyday [GET **List client templates** /client-templates](https://upfour.io/docs/api/client-templates/list-client-templates.md)[POST **Create a client template** /client-templates](https://upfour.io/docs/api/client-templates/create-a-client-template.md)[GET **Get a client template** /client-templates/{templateID}](https://upfour.io/docs/api/client-templates/get-a-client-template.md)[PUT **Update a client template** /client-templates/{templateID}](https://upfour.io/docs/api/client-templates/update-a-client-template.md) ## Control [DEL **Delete a client template** /client-templates/{templateID}](https://upfour.io/docs/api/client-templates/delete-a-client-template.md)[POST **Preview a client template** /client-templates/{templateID}/dry-run](https://upfour.io/docs/api/client-templates/preview-a-client-template.md) --- # List client templates — API reference > Returns Client templates with pagination and optional scope, search and sorting filters. Source: https://upfour.io/docs/api/client-templates/list-client-templates [Developers](https://upfour.io/docs/api.md)/[Client templates](https://upfour.io/docs/api/client-templates.md) Returns Client templates with pagination and optional scope, search and sorting filters. GET https://app.upfour.io/v1/client-templates scope client.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `limit` optional | integer 1–250 default `50` | query parameter | | `cursor` optional | string | query parameter | | `client_id` optional | uuid | query parameter | | `all_clients` optional | boolean | query parameter | | `q` optional | string | query parameter | | `sort` optional | string | query parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ClientTemplateView[] · nullable | | | Each item · 15 fields FieldTypeDescription `created_at` string (date-time) `description` string `design_templates` object[] · nullable The status page designs the spec refers to. Each item · 7 fields FieldTypeDescription `id` uuid `asset_kinds` string[] `includes_css` boolean `layout` string `missing` boolean True, with only id present, when the design no longer exists. `name` string `revision` integer `id` uuid `name` string `organization_id` uuid `spec` any The spec as stored: the monitors, escalation_policies and status_pages lists described in the request, each optional. `updated_at` string (date-time) | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [NextCreate a client template](https://upfour.io/docs/api/client-templates/create-a-client-template.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/client-templates" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/client-templates", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/client-templates", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/client-templates", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "created_at": "2026-10-27T09:00:00Z", "description": "Starter template — one HTTP monitor for the client's primary domain plus a public status page.", "design_templates": [], "id": "00000000-0000-4000-8000-000000000001", "name": "Default", "organization_id": "00000000-0000-4000-8000-000000000002", "spec": { "monitors": [ { "alert_type": "status", "http_method": "GET", "interval": 180, "kind": "http", "name_from": "{{client_name}} — website", "url": "https://{{domain}}" } ], "status_pages": [ { "include_all_monitors": true, "name": "{{client_name}} status", "subdomain_from": "{{client_slug}}" } ] }, "updated_at": "2026-10-27T09:00:00Z" }, { "created_at": "2026-10-27T09:00:00Z", "description": "One website monitor per client.", "design_templates": [], "id": "00000000-0000-4000-8000-000000000003", "name": "Website starter", "organization_id": "00000000-0000-4000-8000-000000000002", "spec": { "monitors": [ { "interval": 180, "kind": "http", "name_from": "{{domain}}", "url": "https://{{domain}}" } ] }, "updated_at": "2026-10-27T09:00:00Z" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Create a client template — API reference > Creates a Client template containing optional monitors, Alert plans or status pages. Source: https://upfour.io/docs/api/client-templates/create-a-client-template [Developers](https://upfour.io/docs/api.md)/[Client templates](https://upfour.io/docs/api/client-templates.md) Creates a Client template containing optional monitors, Alert plans or status pages. POST https://app.upfour.io/v1/client-templates scope client.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Body `application/json` required 3 fields namestring (required) All other fields31 fields descriptionstring specTemplateSpec What a new client gets: monitors, alert plans and status pages. Omitted stores an empty template. Nested fields 29 fields | Field | Type | Description | | --- | --- | --- | | `escalation_policies` optional | TemplateEscalationPolicy[] | | | Each item · 7 fields FieldTypeDescription `description` optional string `name` optional string `steps` optional TemplateEscalationStep[] Each item · 4 fields FieldTypeDescription `after_seconds` optional integer Seconds after the incident starts, not negative. `channel` optional string one of`""` `email` email notifies through the company-wide default email channel; set either channel or targets, not both. `targets` optional uuid[] Ids of alert channels that belong to the whole company, not to one client. `urgency` optional string one of`""` `sms` `voice` `high` `low` | | | | `monitors` optional | TemplateMonitor[] | | | Each item · 12 fields FieldTypeDescription `alert_type` optional string one of`status` `keyword` `keyword_absence` `expected_status_code` Omitted means status. `dns_query` optional string `dns_type` optional string one of`A` `AAAA` `MX` `CNAME` `TXT` `NS` Omitted means A. `host_from` optional string Host of a tcp or ping monitor, used when tcp_host is empty; takes the same placeholders. `http_method` optional string Omitted means HEAD. `interval` optional integer Check interval in seconds; 0 or omitted means 180. `kind` optional string one of`http` `tcp` `ping` `dns` `name` optional string `name_from` optional string Monitor name, used when name is empty. Placeholders {{domain}}, {{client_name}}, {{contact_email}} and {{client_slug}} are filled when the template is applied. `tcp_host` optional string `tcp_port` optional integer `url` optional string Address to check; takes the same placeholders as name_from. | | | | `status_pages` optional | TemplateStatusPage[] | | | Each item · 7 fields FieldTypeDescription `apply_design_assets` optional boolean Also copies the design's brand images; needs design_template_id. `apply_design_css` optional boolean Also copies the design's custom CSS; needs design_template_id. `design_template_id` optional uuid · nullable A status page design of the company the page starts from. `include_all_monitors` optional boolean `name` optional string Page name with placeholders; empty becomes the client name followed by status. `subdomain` optional string Address of the page; a taken one gets a numeric suffix. Empty uses subdomain_from, then the client slug. `subdomain_from` optional string Same as subdomain, with the placeholders of name_from. | | | ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ClientTemplateView | | | ClientTemplateView · 15 fields FieldTypeDescription `created_at` string (date-time) `description` string `design_templates` object[] · nullable The status page designs the spec refers to. Each item · 7 fields FieldTypeDescription `id` uuid `asset_kinds` string[] `includes_css` boolean `layout` string `missing` boolean True, with only id present, when the design no longer exists. `name` string `revision` integer `id` uuid `name` string `organization_id` uuid `spec` any The spec as stored: the monitors, escalation_policies and status_pages lists described in the request, each optional. `updated_at` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [design_template_not_found](https://upfour.io/docs/api/errors#code-design_template_not_found) | The design template does not exist or is not visible to the caller. **When:** A design template id in the path or body that is unknown or not visible to the caller. **Fix:** List design templates and use an id from the list. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList client templates](https://upfour.io/docs/api/client-templates/list-client-templates.md) [NextGet a client template](https://upfour.io/docs/api/client-templates/get-a-client-template.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/client-templates" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "description": "One website monitor per client.", "name": "Website starter", "spec": { "monitors": [ { "interval": 180, "kind": "http", "name_from": "{{domain}}", "url": "https://{{domain}}" } ] } }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/client-templates", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ description: "One website monitor per client.", name: "Website starter", spec: { monitors: [ { interval: 180, kind: "http", name_from: "{{domain}}", url: "https://{{domain}}", }, ], }, }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/client-templates", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "description": "One website monitor per client.", "name": "Website starter", "spec": { "monitors": [ { "interval": 180, "kind": "http", "name_from": "{{domain}}", "url": "https://{{domain}}", }, ], }, }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "description": "One website monitor per client.", "name": "Website starter", "spec": { "monitors": [ { "interval": 180, "kind": "http", "name_from": "{{domain}}", "url": "https://{{domain}}" } ] } }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/client-templates", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "description": "One website monitor per client.", "design_templates": [], "id": "00000000-0000-4000-8000-000000000001", "name": "Website starter", "organization_id": "00000000-0000-4000-8000-000000000002", "spec": { "monitors": [ { "interval": 180, "kind": "http", "name_from": "{{domain}}", "url": "https://{{domain}}" } ] }, "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "design_template_not_found", "message": "The design template does not exist or is not visible to the caller.", "request_id": "req_…" } } ``` --- # Get a client template — API reference > Returns one Client template with its stored spec and referenced status page designs. Source: https://upfour.io/docs/api/client-templates/get-a-client-template [Developers](https://upfour.io/docs/api.md)/[Client templates](https://upfour.io/docs/api/client-templates.md) Returns one Client template with its stored spec and referenced status page designs. GET https://app.upfour.io/v1/client-templates/{templateID} scope client.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `templateID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ClientTemplateView | | | ClientTemplateView · 15 fields FieldTypeDescription `created_at` string (date-time) `description` string `design_templates` object[] · nullable The status page designs the spec refers to. Each item · 7 fields FieldTypeDescription `id` uuid `asset_kinds` string[] `includes_css` boolean `layout` string `missing` boolean True, with only id present, when the design no longer exists. `name` string `revision` integer `id` uuid `name` string `organization_id` uuid `spec` any The spec as stored: the monitors, escalation_policies and status_pages lists described in the request, each optional. `updated_at` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousCreate a client template](https://upfour.io/docs/api/client-templates/create-a-client-template.md) [NextUpdate a client template](https://upfour.io/docs/api/client-templates/update-a-client-template.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/client-templates/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/client-templates/00000000-0000-4000-8000-000000000001", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/client-templates/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/client-templates/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "description": "One website monitor per client.", "design_templates": [], "id": "00000000-0000-4000-8000-000000000001", "name": "Website starter", "organization_id": "00000000-0000-4000-8000-000000000002", "spec": { "monitors": [ { "interval": 180, "kind": "http", "name_from": "{{domain}}", "url": "https://{{domain}}" } ] }, "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Update a client template — API reference > Replaces a Client template's name, description or stored spec. Source: https://upfour.io/docs/api/client-templates/update-a-client-template [Developers](https://upfour.io/docs/api.md)/[Client templates](https://upfour.io/docs/api/client-templates.md) Replaces a Client template's name, description or stored spec. PUT https://app.upfour.io/v1/client-templates/{templateID} scope client.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `templateID` required | uuid | path parameter | ## Body `application/json` required 3 fields namestring (required) All other fields31 fields descriptionstring specTemplateSpec Replaces the stored spec. Omitted stores an empty template. Nested fields 29 fields | Field | Type | Description | | --- | --- | --- | | `escalation_policies` optional | TemplateEscalationPolicy[] | | | Each item · 7 fields FieldTypeDescription `description` optional string `name` optional string `steps` optional TemplateEscalationStep[] Each item · 4 fields FieldTypeDescription `after_seconds` optional integer Seconds after the incident starts, not negative. `channel` optional string one of`""` `email` email notifies through the company-wide default email channel; set either channel or targets, not both. `targets` optional uuid[] Ids of alert channels that belong to the whole company, not to one client. `urgency` optional string one of`""` `sms` `voice` `high` `low` | | | | `monitors` optional | TemplateMonitor[] | | | Each item · 12 fields FieldTypeDescription `alert_type` optional string one of`status` `keyword` `keyword_absence` `expected_status_code` Omitted means status. `dns_query` optional string `dns_type` optional string one of`A` `AAAA` `MX` `CNAME` `TXT` `NS` Omitted means A. `host_from` optional string Host of a tcp or ping monitor, used when tcp_host is empty; takes the same placeholders. `http_method` optional string Omitted means HEAD. `interval` optional integer Check interval in seconds; 0 or omitted means 180. `kind` optional string one of`http` `tcp` `ping` `dns` `name` optional string `name_from` optional string Monitor name, used when name is empty. Placeholders {{domain}}, {{client_name}}, {{contact_email}} and {{client_slug}} are filled when the template is applied. `tcp_host` optional string `tcp_port` optional integer `url` optional string Address to check; takes the same placeholders as name_from. | | | | `status_pages` optional | TemplateStatusPage[] | | | Each item · 7 fields FieldTypeDescription `apply_design_assets` optional boolean Also copies the design's brand images; needs design_template_id. `apply_design_css` optional boolean Also copies the design's custom CSS; needs design_template_id. `design_template_id` optional uuid · nullable A status page design of the company the page starts from. `include_all_monitors` optional boolean `name` optional string Page name with placeholders; empty becomes the client name followed by status. `subdomain` optional string Address of the page; a taken one gets a numeric suffix. Empty uses subdomain_from, then the client slug. `subdomain_from` optional string Same as subdomain, with the placeholders of name_from. | | | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ClientTemplateView | | | ClientTemplateView · 15 fields FieldTypeDescription `created_at` string (date-time) `description` string `design_templates` object[] · nullable The status page designs the spec refers to. Each item · 7 fields FieldTypeDescription `id` uuid `asset_kinds` string[] `includes_css` boolean `layout` string `missing` boolean True, with only id present, when the design no longer exists. `name` string `revision` integer `id` uuid `name` string `organization_id` uuid `spec` any The spec as stored: the monitors, escalation_policies and status_pages lists described in the request, each optional. `updated_at` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [design_template_not_found](https://upfour.io/docs/api/errors#code-design_template_not_found) | The design template does not exist or is not visible to the caller. **When:** A design template id in the path or body that is unknown or not visible to the caller. **Fix:** List design templates and use an id from the list. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousGet a client template](https://upfour.io/docs/api/client-templates/get-a-client-template.md) [NextDelete a client template](https://upfour.io/docs/api/client-templates/delete-a-client-template.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/client-templates/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "description": "One website monitor per client, checked every 3 minutes.", "name": "Website starter", "spec": { "monitors": [ { "interval": 180, "kind": "http", "name_from": "{{domain}}", "url": "https://{{domain}}" } ] } }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/client-templates/00000000-0000-4000-8000-000000000001", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ description: "One website monitor per client, checked every 3 minutes.", name: "Website starter", spec: { monitors: [ { interval: 180, kind: "http", name_from: "{{domain}}", url: "https://{{domain}}", }, ], }, }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/client-templates/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "description": "One website monitor per client, checked every 3 minutes.", "name": "Website starter", "spec": { "monitors": [ { "interval": 180, "kind": "http", "name_from": "{{domain}}", "url": "https://{{domain}}", }, ], }, }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "description": "One website monitor per client, checked every 3 minutes.", "name": "Website starter", "spec": { "monitors": [ { "interval": 180, "kind": "http", "name_from": "{{domain}}", "url": "https://{{domain}}" } ] } }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/client-templates/00000000-0000-4000-8000-000000000001", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "created_at": "2026-10-27T09:00:00Z", "description": "One website monitor per client, checked every 3 minutes.", "design_templates": [], "id": "00000000-0000-4000-8000-000000000001", "name": "Website starter", "organization_id": "00000000-0000-4000-8000-000000000002", "spec": { "monitors": [ { "interval": 180, "kind": "http", "name_from": "{{domain}}", "url": "https://{{domain}}" } ] }, "updated_at": "2026-10-27T09:00:00Z" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "design_template_not_found", "message": "The design template does not exist or is not visible to the caller.", "request_id": "req_…" } } ``` --- # Delete a client template — API reference > Returns a confirmation message after deleting a Client template. Source: https://upfour.io/docs/api/client-templates/delete-a-client-template [Developers](https://upfour.io/docs/api.md)/[Client templates](https://upfour.io/docs/api/client-templates.md) Returns a confirmation message after deleting a Client template. DELETE https://app.upfour.io/v1/client-templates/{templateID} scope client.write stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `templateID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | ClientMessageResponse | | | ClientMessageResponse · 1 fields FieldTypeDescription `message` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpdate a client template](https://upfour.io/docs/api/client-templates/update-a-client-template.md) [NextPreview a client template](https://upfour.io/docs/api/client-templates/preview-a-client-template.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/client-templates/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/client-templates/00000000-0000-4000-8000-000000000001", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/client-templates/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/client-templates/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "message": "Template deleted" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Preview a client template — API reference > Previews a Client template for a Client using supplied name, slug, contact email and domain values. Source: https://upfour.io/docs/api/client-templates/preview-a-client-template [Developers](https://upfour.io/docs/api.md)/[Client templates](https://upfour.io/docs/api/client-templates.md) Previews a Client template for a Client using supplied name, slug, contact email and domain values. POST https://app.upfour.io/v1/client-templates/{templateID}/dry-run scope client.write stable [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `templateID` required | uuid | path parameter | ## Body `application/json` required 5 fields client_iduuid (required) The client to preview the template for. All other fields4 fields client_namestring Fills {{client_name}}. client_slugstring Fills {{client_slug}}. contact_emailstring Fills {{contact_email}}. domainstring Fills {{domain}}. Required when the template uses it. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | TemplateDryRunResult | | | TemplateDryRunResult · 32 fields FieldTypeDescription `monitors` TemplateMonitorPreview[] · nullable Each item · 11 fields FieldTypeDescription `alarm_route_status` string one of`configured` `without_alarm_route` `name` string `channels` MaterializedChannel[] Each item · 4 fields FieldTypeDescription `id` uuid `name` string `scope` string `type` string `warnings` MaterializeError[] Each item · 3 fields FieldTypeDescription `kind` string one of`template` `monitor` `monitor_alarm_route` `policy` `status_page` `message` string `name` string `policies` TemplatePolicyPreview[] · nullable Each item · 7 fields FieldTypeDescription `channels` MaterializedChannel[] · nullable Each item · 4 fields FieldTypeDescription `id` uuid `name` string `scope` string `type` string `name` string `step_count` integer `status_pages` TemplateStatusPagePreview[] · nullable Each item · 11 fields FieldTypeDescription `design` TemplateStatusPageDesignPreview · nullable TemplateStatusPageDesignPreview · 8 fields FieldTypeDescription `apply_assets` boolean `apply_css` boolean `asset_kinds` string[] · nullable `id` uuid `includes_css` boolean `layout` string `name` string `revision` integer `name` string `subdomain` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 409 | [design_template_conflict](https://upfour.io/docs/api/errors#code-design_template_conflict) | The design template was changed after the caller read it. **When:** Updating or applying a design template that was changed after the caller read it. **Fix:** Reload the template and apply the change again. | | 404 | [design_template_not_found](https://upfour.io/docs/api/errors#code-design_template_not_found) | The design template does not exist or is not visible to the caller. **When:** A design template id in the path or body that is unknown or not visible to the caller. **Fix:** List design templates and use an id from the list. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousDelete a client template](https://upfour.io/docs/api/client-templates/delete-a-client-template.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/client-templates/00000000-0000-4000-8000-000000000001/dry-run" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "client_id": "00000000-0000-4000-8000-000000000002", "client_name": "Globex Corporation", "domain": "globex.example" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/client-templates/00000000-0000-4000-8000-000000000001/dry-run", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ client_id: "00000000-0000-4000-8000-000000000002", client_name: "Globex Corporation", domain: "globex.example", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/client-templates/00000000-0000-4000-8000-000000000001/dry-run", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "client_id": "00000000-0000-4000-8000-000000000002", "client_name": "Globex Corporation", "domain": "globex.example", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "client_id": "00000000-0000-4000-8000-000000000002", "client_name": "Globex Corporation", "domain": "globex.example" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/client-templates/00000000-0000-4000-8000-000000000001/dry-run", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "monitors": [ { "alarm_route_status": "configured", "channels": [ { "id": "00000000-0000-4000-8000-000000000003", "name": "Email · anna@example.com", "scope": "organization", "type": "email" } ], "name": "globex.example" } ], "policies": [], "status_pages": [] } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "design_template_not_found", "message": "The design template does not exist or is not visible to the caller.", "request_id": "req_…" } } ``` ### 409 ```json { "error": { "code": "design_template_conflict", "message": "The design template was changed after the caller read it.", "request_id": "req_…" } } ``` --- # Token introspection — API reference > Who am I? What can this key do? The first call to make. REST endpoints with request and response fields, examples in curl, JavaScript, Python, Go and PHP, and an in-browser Try it console. Source: https://upfour.io/docs/api/me [Developers](https://upfour.io/docs/api.md)/Platform Who am I? What can this key do? The first call to make. Base URL `https://app.upfour.io/v1`. Send your key as `Authorization: Bearer `; every key acts on your real Company, so [test without paging anyone](https://upfour.io/docs/api/testing.md). New here? Start with the [quickstart](https://upfour.io/docs/api/quickstart.md). The examples read the key from `UP4_API_KEY`. ## Everyday [GET **Inspect the current token** /me](https://upfour.io/docs/api/me/inspect-the-current-token.md) --- # Inspect the current token — API reference > Shows the API version, authentication method, scopes, Company role, user, team and client context for the authenticated key. Source: https://upfour.io/docs/api/me/inspect-the-current-token [Developers](https://upfour.io/docs/api.md)/[Token introspection](https://upfour.io/docs/api/me.md) Shows the API version, authentication method, scopes, Company role, user, team and client context for the authenticated key. GET https://app.upfour.io/v1/me scope self.read stable returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `limit` optional | integer 1–250 default `50` | query parameter | | `cursor` optional | string | query parameter | | `client_id` optional | uuid | query parameter | | `all_clients` optional | boolean | query parameter | | `q` optional | string | query parameter | | `sort` optional | string | query parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | TokenIntrospectionResponse | | | TokenIntrospectionResponse · 12 fields FieldTypeDescription `auth_method` any How the request authenticated: api_key on the public API. `client_set` ClientSet ClientSet · 3 fields FieldTypeDescription `All` boolean `CompanyLevel` boolean `IDs` uuid[] · nullable `org_id` uuid `org_role` string one of`owner` `admin` `member` `viewer` `api_key_id` string `api_version` string `scopes` string[] The permissions of the API key. `team_id` string `user_id` string | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). The specification does not list additional operation-specific errors. Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/me" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/me", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/me", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/me", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "api_key_id": "00000000-0000-4000-8000-000000000001", "api_version": "2026-05-07", "auth_method": "api_key", "client_set": { "All": true, "CompanyLevel": true, "IDs": null }, "org_id": "00000000-0000-4000-8000-000000000002", "org_role": "owner", "scopes": [ "api_key.read", "api_key.write", "audit_log.read", "billing.read", "billing.write", "client.read", "client.write", "escalation_policy.read", "escalation_policy.write", "evidence.read", "evidence.write", "feature_flag.read", "feature_flag.write", "heartbeat.read", "heartbeat.write", "incident.read", "incident.write", "member.read", "member.write", "metadata.read", "metadata.write", "monitor.delete", "monitor.read", "monitor.write", "not.a.permission", "notification_channel.read", "notification_channel.write", "oncall.read", "oncall.write", "organization.read", "organization.write", "report.read", "report.write", "runbook.read", "runbook.write", "self.read", "status_page.read", "status_page.subscriber.write", "status_page.write", "team.read", "team.write", "webhook.read", "webhook.write" ], "user_id": "00000000-0000-4000-8000-000000000003" } } ``` --- # Webhook subscriptions — API reference > Outbound, signed webhooks — subscribe, test, inspect deliveries, redeliver. REST endpoints with request and response fields, examples in curl, JavaScript, Python, Go and PHP, and an in-browser Try it console. Source: https://upfour.io/docs/api/webhook-subscriptions [Developers](https://upfour.io/docs/api.md)/Platform Outbound, signed webhooks — subscribe, test, inspect deliveries, redeliver. Base URL `https://app.upfour.io/v1`. Send your key as `Authorization: Bearer `; every key acts on your real Company, so [test without paging anyone](https://upfour.io/docs/api/testing.md). New here? Start with the [quickstart](https://upfour.io/docs/api/quickstart.md). The examples read the key from `UP4_API_KEY`. ## Everyday [GET **List webhook subscriptions** /webhook-subscriptions](https://upfour.io/docs/api/webhook-subscriptions/list-webhook-subscriptions.md)[POST **Create a webhook subscription** /webhook-subscriptions](https://upfour.io/docs/api/webhook-subscriptions/create-a-webhook-subscription.md)[PUT **Update a webhook subscription** /webhook-subscriptions/{subscriptionID}](https://upfour.io/docs/api/webhook-subscriptions/update-a-webhook-subscription.md)[DEL **Delete a webhook subscription** /webhook-subscriptions/{subscriptionID}](https://upfour.io/docs/api/webhook-subscriptions/delete-a-webhook-subscription.md) ## Deliveries [GET **List webhook deliveries** /webhook-subscriptions/{subscriptionID}/deliveries](https://upfour.io/docs/api/webhook-subscriptions/list-webhook-deliveries.md)[POST **Redeliver a webhook delivery** /webhook-subscriptions/{subscriptionID}/redeliver/{deliveryID}](https://upfour.io/docs/api/webhook-subscriptions/redeliver-a-webhook-delivery.md) ## Control [POST **Rotate a webhook subscription secret** /webhook-subscriptions/{subscriptionID}/rotate-secret](https://upfour.io/docs/api/webhook-subscriptions/rotate-a-webhook-subscription-secret.md)[POST **Send a webhook test** /webhook-subscriptions/{subscriptionID}/test](https://upfour.io/docs/api/webhook-subscriptions/send-a-webhook-test.md) --- # List webhook subscriptions — API reference > Lists webhook subscriptions with URLs, events, active state, failure counts and delivery timestamps. Source: https://upfour.io/docs/api/webhook-subscriptions/list-webhook-subscriptions [Developers](https://upfour.io/docs/api.md)/[Webhook subscriptions](https://upfour.io/docs/api/webhook-subscriptions.md) Lists webhook subscriptions with URLs, events, active state, failure counts and delivery timestamps. GET https://app.upfour.io/v1/webhook-subscriptions scope webhook.read beta returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `limit` optional | integer 1–250 default `50` | query parameter | | `cursor` optional | string | query parameter | | `client_id` optional | uuid | query parameter | | `all_clients` optional | boolean | query parameter | | `q` optional | string | query parameter | | `sort` optional | string | query parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | WebhookSubscription[] · nullable | | | Each item · 10 fields FieldTypeDescription `active` boolean `created_at` string (date-time) `events` string[] · nullable `failure_count` integer `id` uuid `org_id` uuid `updated_at` string (date-time) `url` string `disabled_at` string (date-time) Set while the subscription is switched off, by active: false or by the delivery worker after repeated failures. `last_delivery_at` string (date-time) | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [NextCreate a webhook subscription](https://upfour.io/docs/api/webhook-subscriptions/create-a-webhook-subscription.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/webhook-subscriptions" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/webhook-subscriptions", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/webhook-subscriptions", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/webhook-subscriptions", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "active": true, "created_at": "2026-10-27T09:00:00Z", "events": [ "incident.opened", "incident.resolved", "monitor.status_changed" ], "failure_count": 0, "id": "00000000-0000-4000-8000-000000000001", "org_id": "00000000-0000-4000-8000-000000000002", "updated_at": "2026-10-27T09:00:00Z", "url": "https://example.com/hooks/up4" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` --- # Create a webhook subscription — API reference > Creates a webhook subscription with event types and receiver URL, returning its signing secret once. Source: https://upfour.io/docs/api/webhook-subscriptions/create-a-webhook-subscription [Developers](https://upfour.io/docs/api.md)/[Webhook subscriptions](https://upfour.io/docs/api/webhook-subscriptions.md) Creates a webhook subscription with event types and receiver URL, returning its signing secret once. POST https://app.upfour.io/v1/webhook-subscriptions scope webhook.write beta [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 201 ## Body `application/json` required 3 fields eventsstring[] (required) Event types to deliver, at least one; blanks and duplicates are dropped. The service does not check the names, an event that is never published simply never arrives. Published: monitor.created, monitor.updated, monitor.deleted, monitor.status_changed, monitor.degraded_changed, monitor.unstable, monitors.bulk_updated, incident.opened, incident.acknowledged, incident.resolved, incident.reopened, incident.escalated, incident.propagated, incident.partially_recovered, heartbeat.missed, heartbeat.recovered, status_page.update_published, status_page.report_published, webhook.test. urlstring (uri) (required) http or https URL of the receiver. A host that resolves to a private or otherwise blocked address range is refused. All other fields1 fields activeboolean · nullable Defaults to true, also on update: a PUT without active switches a disabled subscription back on. ## Returns A successful request returns `201` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | WebhookSubscriptionCreated | | | WebhookSubscriptionCreated · 11 fields FieldTypeDescription `active` boolean `created_at` string (date-time) `events` string[] · nullable `failure_count` integer `id` uuid `org_id` uuid `secret` string Signing secret of the subscription, `whsec_` plus 64 hex characters. Returned by this call only; list, update and read calls never include it. Store it now: a lost secret cannot be read back, only replaced with POST /webhook-subscriptions/{subscriptionID}/rotate-secret. The old secret stops signing at once. `updated_at` string (date-time) `url` string `disabled_at` string (date-time) Set while the subscription is switched off, by active: false or by the delivery worker after repeated failures. `last_delivery_at` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 402 | [billing_action_unavailable](https://upfour.io/docs/api/errors#code-billing_action_unavailable) | The Company's billing state does not allow this action. **When:** A metered or limited action while the billing state, such as paused or without credit, blocks it; details holds the action and billing_state. **Fix:** Top up the credit or change the plan, then retry. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 503 | [quota_gate_unavailable](https://upfour.io/docs/api/errors#code-quota_gate_unavailable) | The limit check itself failed, so the action was not carried out. **When:** The quota service could not decide while the request ran. **Fix:** Retry in a moment; nothing was changed. | | 403 / 429 | [quota_limit_reached](https://upfour.io/docs/api/errors#code-quota_limit_reached) | The Company already holds the most webhook subscriptions it may have; `error.details` carries `purpose` and `limit`. **When:** Counters and concurrency limits answer 429 with Retry-After, inventory and capacity limits answer 403; details holds purpose, limit and window. **Fix:** On 429 wait Retry-After seconds; on 403 free something up, request a higher limit where details.raise_request is true, or buy an extension. `quota_limit_reached` (window `total`): the Company already holds the most of these it may have. `error.details` carries `purpose` and `limit`; remove one or ask support for a higher limit. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList webhook subscriptions](https://upfour.io/docs/api/webhook-subscriptions/list-webhook-subscriptions.md) [NextUpdate a webhook subscription](https://upfour.io/docs/api/webhook-subscriptions/update-a-webhook-subscription.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/webhook-subscriptions" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "active": true, "events": [ "incident.opened", "incident.resolved", "monitor.status_changed" ], "url": "https://example.com/hooks/up4" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/webhook-subscriptions", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ active: true, events: [ "incident.opened", "incident.resolved", "monitor.status_changed", ], url: "https://example.com/hooks/up4", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/webhook-subscriptions", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "active": True, "events": [ "incident.opened", "incident.resolved", "monitor.status_changed", ], "url": "https://example.com/hooks/up4", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "active": true, "events": [ "incident.opened", "incident.resolved", "monitor.status_changed" ], "url": "https://example.com/hooks/up4" }`) req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/webhook-subscriptions", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 201 ```json { "data": { "active": true, "created_at": "2026-10-27T09:00:00Z", "events": [ "incident.opened", "incident.resolved", "monitor.status_changed" ], "failure_count": 0, "id": "00000000-0000-4000-8000-000000000001", "org_id": "00000000-0000-4000-8000-000000000002", "secret": "whsec_••••", "updated_at": "2026-10-27T09:00:00Z", "url": "https://example.com/hooks/up4" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "billing_action_unavailable", "message": "The Company's billing state does not allow this action.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 429 ```json { "error": { "code": "quota_limit_reached", "message": "The Company already holds the most webhook subscriptions it may have; error.details carries purpose and limit.", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "quota_gate_unavailable", "message": "The limit check itself failed, so the action was not carried out.", "request_id": "req_…" } } ``` --- # Update a webhook subscription — API reference > Replaces a webhook subscription's receiver URL, event types and active state. Source: https://upfour.io/docs/api/webhook-subscriptions/update-a-webhook-subscription [Developers](https://upfour.io/docs/api.md)/[Webhook subscriptions](https://upfour.io/docs/api/webhook-subscriptions.md) Replaces a webhook subscription's receiver URL, event types and active state. PUT https://app.upfour.io/v1/webhook-subscriptions/{subscriptionID} scope webhook.write beta returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `subscriptionID` required | uuid | path parameter | ## Body `application/json` required 3 fields eventsstring[] (required) Event types to deliver, at least one; blanks and duplicates are dropped. The service does not check the names, an event that is never published simply never arrives. Published: monitor.created, monitor.updated, monitor.deleted, monitor.status_changed, monitor.degraded_changed, monitor.unstable, monitors.bulk_updated, incident.opened, incident.acknowledged, incident.resolved, incident.reopened, incident.escalated, incident.propagated, incident.partially_recovered, heartbeat.missed, heartbeat.recovered, status_page.update_published, status_page.report_published, webhook.test. urlstring (uri) (required) http or https URL of the receiver. A host that resolves to a private or otherwise blocked address range is refused. All other fields1 fields activeboolean · nullable Defaults to true, also on update: a PUT without active switches a disabled subscription back on. ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | WebhookSubscription | | | WebhookSubscription · 10 fields FieldTypeDescription `active` boolean `created_at` string (date-time) `events` string[] · nullable `failure_count` integer `id` uuid `org_id` uuid `updated_at` string (date-time) `url` string `disabled_at` string (date-time) Set while the subscription is switched off, by active: false or by the delivery worker after repeated failures. `last_delivery_at` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousCreate a webhook subscription](https://upfour.io/docs/api/webhook-subscriptions/create-a-webhook-subscription.md) [NextDelete a webhook subscription](https://upfour.io/docs/api/webhook-subscriptions/delete-a-webhook-subscription.md) ## Request ### cURL ```bash curl -s -X PUT "https://app.upfour.io/v1/webhook-subscriptions/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "active": true, "events": [ "incident.opened", "incident.resolved" ], "url": "https://example.com/hooks/up4" }' ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/webhook-subscriptions/00000000-0000-4000-8000-000000000001", { method: "PUT", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ active: true, events: ["incident.opened", "incident.resolved"], url: "https://example.com/hooks/up4", }), }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.put( "https://app.upfour.io/v1/webhook-subscriptions/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, json={ "active": True, "events": ["incident.opened", "incident.resolved"], "url": "https://example.com/hooks/up4", }, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { payload := strings.NewReader(`{ "active": true, "events": [ "incident.opened", "incident.resolved" ], "url": "https://example.com/hooks/up4" }`) req, _ := http.NewRequest("PUT", "https://app.upfour.io/v1/webhook-subscriptions/00000000-0000-4000-8000-000000000001", payload) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "active": true, "created_at": "2026-10-27T09:00:00Z", "events": [ "incident.opened", "incident.resolved" ], "failure_count": 0, "id": "00000000-0000-4000-8000-000000000001", "org_id": "00000000-0000-4000-8000-000000000002", "updated_at": "2026-10-27T09:00:00Z", "url": "https://example.com/hooks/up4" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` --- # Delete a webhook subscription — API reference > Confirms the webhook subscription removal with a 204 response and no response body. Source: https://upfour.io/docs/api/webhook-subscriptions/delete-a-webhook-subscription [Developers](https://upfour.io/docs/api.md)/[Webhook subscriptions](https://upfour.io/docs/api/webhook-subscriptions.md) Confirms the webhook subscription removal with a 204 response and no response body. DELETE https://app.upfour.io/v1/webhook-subscriptions/{subscriptionID} scope webhook.write beta returns 204 ## Parameters | Field | Type | Description | | --- | --- | --- | | `subscriptionID` required | uuid | path parameter | ## Returns A successful request returns `204` with no response body. ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousUpdate a webhook subscription](https://upfour.io/docs/api/webhook-subscriptions/update-a-webhook-subscription.md) [NextList webhook deliveries](https://upfour.io/docs/api/webhook-subscriptions/list-webhook-deliveries.md) ## Request ### cURL ```bash curl -s -X DELETE "https://app.upfour.io/v1/webhook-subscriptions/00000000-0000-4000-8000-000000000001" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/webhook-subscriptions/00000000-0000-4000-8000-000000000001", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status); ``` ### Python ```python import os import requests res = requests.delete( "https://app.upfour.io/v1/webhook-subscriptions/00000000-0000-4000-8000-000000000001", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code) ``` ### Go ```go package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://app.upfour.io/v1/webhook-subscriptions/00000000-0000-4000-8000-000000000001", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.StatusCode) } ``` ### PHP ```php "DELETE", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), "\n"; curl_close($ch); ``` ## Response ### 204 ``` Deleted ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # List webhook deliveries — API reference > Lists webhook deliveries with event, status, attempt number, payload, response summary and retry timestamps. Source: https://upfour.io/docs/api/webhook-subscriptions/list-webhook-deliveries [Developers](https://upfour.io/docs/api.md)/[Webhook subscriptions](https://upfour.io/docs/api/webhook-subscriptions.md) Lists webhook deliveries with event, status, attempt number, payload, response summary and retry timestamps. GET https://app.upfour.io/v1/webhook-subscriptions/{subscriptionID}/deliveries scope webhook.read beta returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `subscriptionID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 2 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | WebhookDelivery[] · nullable | | | Each item · 12 fields FieldTypeDescription `attempt` integer Number of the current attempt, at most 7. `created_at` string (date-time) `event` string `id` uuid `payload_json` any The event as sent to the receiver. `scheduled_at` string (date-time) `status` string one of`pending` `retrying` `delivered` `failed` `subscription_id` uuid `delivered_at` string (date-time) `next_retry_at` string (date-time) `response_body` string Never the receiver's answer, only a summary of the attempt: HTTP followed by the status code when the receiver answered (HTTP 200, HTTP 502), otherwise Delivery timed out, Delivery failed (for example a refused connection) or Delivery configuration or destination rejected. `response_code` integer | | | | `pagination` | Pagination | | | Pagination · 5 fields FieldTypeDescription `cursor` string · nullable `has_more` boolean `limit` integer 1–250 `next_cursor` string · nullable `prev_cursor` string · nullable | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousDelete a webhook subscription](https://upfour.io/docs/api/webhook-subscriptions/delete-a-webhook-subscription.md) [NextRedeliver a webhook delivery](https://upfour.io/docs/api/webhook-subscriptions/redeliver-a-webhook-delivery.md) ## Request ### cURL ```bash curl -s "https://app.upfour.io/v1/webhook-subscriptions/00000000-0000-4000-8000-000000000001/deliveries" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/webhook-subscriptions/00000000-0000-4000-8000-000000000001/deliveries", { headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.get( "https://app.upfour.io/v1/webhook-subscriptions/00000000-0000-4000-8000-000000000001/deliveries", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://app.upfour.io/v1/webhook-subscriptions/00000000-0000-4000-8000-000000000001/deliveries", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": [ { "attempt": 1, "created_at": "2026-10-27T09:00:00Z", "delivered_at": "2026-10-27T09:00:00Z", "event": "webhook.test", "id": "00000000-0000-4000-8000-000000000002", "payload_json": { "app_url": "http://example.com", "created_at": "2026-10-27T09:00:00Z", "data": { "message": "Webhook test" }, "id": "00000000-0000-4000-8000-000000000003", "payload_version": 2, "type": "webhook.test" }, "response_body": "HTTP 200", "response_code": 200, "scheduled_at": "2026-10-27T09:00:00Z", "status": "delivered", "subscription_id": "00000000-0000-4000-8000-000000000001" } ], "pagination": { "cursor": null, "has_more": false, "limit": 50, "next_cursor": null, "prev_cursor": null } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Redeliver a webhook delivery — API reference > Accepts a redelivery request and returns the delivery record with its current status. Source: https://upfour.io/docs/api/webhook-subscriptions/redeliver-a-webhook-delivery [Developers](https://upfour.io/docs/api.md)/[Webhook subscriptions](https://upfour.io/docs/api/webhook-subscriptions.md) Accepts a redelivery request and returns the delivery record with its current status. POST https://app.upfour.io/v1/webhook-subscriptions/{subscriptionID}/redeliver/{deliveryID} scope webhook.write beta [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 202 ## Parameters | Field | Type | Description | | --- | --- | --- | | `subscriptionID` required | uuid | path parameter | | `deliveryID` required | uuid | path parameter | ## Returns A successful request returns `202` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | WebhookDelivery | | | WebhookDelivery · 12 fields FieldTypeDescription `attempt` integer Number of the current attempt, at most 7. `created_at` string (date-time) `event` string `id` uuid `payload_json` any The event as sent to the receiver. `scheduled_at` string (date-time) `status` string one of`pending` `retrying` `delivered` `failed` `subscription_id` uuid `delivered_at` string (date-time) `next_retry_at` string (date-time) `response_body` string Never the receiver's answer, only a summary of the attempt: HTTP followed by the status code when the receiver answered (HTTP 200, HTTP 502), otherwise Delivery timed out, Delivery failed (for example a refused connection) or Delivery configuration or destination rejected. `response_code` integer | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 402 | [billing_action_unavailable](https://upfour.io/docs/api/errors#code-billing_action_unavailable) | This action is unavailable in the Company's billing state (`error.details.billing_state`), for example while credit is used up. **When:** A metered or limited action while the billing state, such as paused or without credit, blocks it; details holds the action and billing_state. **Fix:** Top up the credit or change the plan, then retry. `billing_action_unavailable`: this action is not available in the Company's billing state (`error.details.billing_state`), for example while the credit is used up. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 503 | [quota_gate_unavailable](https://upfour.io/docs/api/errors#code-quota_gate_unavailable) | The limit check itself failed, so the action was not carried out. **When:** The quota service could not decide while the request ran. **Fix:** Retry in a moment; nothing was changed. | | 403 / 429 | [quota_limit_reached](https://upfour.io/docs/api/errors#code-quota_limit_reached) | The protection limit for this action is used up for now; `error.details` carries `purpose`, `limit`, `window` and `retry_after_seconds`, and the `Retry-After` header says when the same request can succeed. **When:** Counters and concurrency limits answer 429 with Retry-After, inventory and capacity limits answer 403; details holds purpose, limit and window. **Fix:** On 429 wait Retry-After seconds; on 403 free something up, request a higher limit where details.raise_request is true, or buy an extension. `quota_limit_reached`: the protection limit of this action is used up for now. `error.details` carries `purpose`, `limit`, `window` and `retry_after_seconds`; the `Retry-After` header says when the same request can succeed. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousList webhook deliveries](https://upfour.io/docs/api/webhook-subscriptions/list-webhook-deliveries.md) [NextRotate a webhook subscription secret](https://upfour.io/docs/api/webhook-subscriptions/rotate-a-webhook-subscription-secret.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/webhook-subscriptions/00000000-0000-4000-8000-000000000001/redeliver/00000000-0000-4000-8000-000000000002" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/webhook-subscriptions/00000000-0000-4000-8000-000000000001/redeliver/00000000-0000-4000-8000-000000000002", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/webhook-subscriptions/00000000-0000-4000-8000-000000000001/redeliver/00000000-0000-4000-8000-000000000002", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/webhook-subscriptions/00000000-0000-4000-8000-000000000001/redeliver/00000000-0000-4000-8000-000000000002", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 202 ```json { "data": { "attempt": 1, "created_at": "2026-10-27T09:00:00Z", "delivered_at": "2026-10-27T09:00:00Z", "event": "webhook.test", "id": "00000000-0000-4000-8000-000000000003", "payload_json": { "app_url": "http://example.com", "created_at": "2026-10-27T09:00:00Z", "data": { "message": "Webhook test" }, "id": "00000000-0000-4000-8000-000000000004", "payload_version": 2, "type": "webhook.test" }, "response_body": "HTTP 200", "response_code": 200, "scheduled_at": "2026-10-27T09:00:00Z", "status": "delivered", "subscription_id": "00000000-0000-4000-8000-000000000001" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "billing_action_unavailable", "message": "This action is unavailable in the Company's billing state (error.details.billing_state), for example while credit is used up.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 429 ```json { "error": { "code": "quota_limit_reached", "message": "The protection limit for this action is used up for now; error.details carries purpose, limit, window and retry_after_seconds, and the Retry-After header says when the same request can succeed.", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "quota_gate_unavailable", "message": "The limit check itself failed, so the action was not carried out.", "request_id": "req_…" } } ``` --- # Rotate a webhook subscription secret — API reference > Replaces the signing secret and exposes it once with the subscription record. Source: https://upfour.io/docs/api/webhook-subscriptions/rotate-a-webhook-subscription-secret [Developers](https://upfour.io/docs/api.md)/[Webhook subscriptions](https://upfour.io/docs/api/webhook-subscriptions.md) Replaces the signing secret and exposes it once with the subscription record. POST https://app.upfour.io/v1/webhook-subscriptions/{subscriptionID}/rotate-secret scope webhook.write beta [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 200 ## Parameters | Field | Type | Description | | --- | --- | --- | | `subscriptionID` required | uuid | path parameter | ## Returns A successful request returns `200` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | WebhookSubscriptionCreated | | | WebhookSubscriptionCreated · 11 fields FieldTypeDescription `active` boolean `created_at` string (date-time) `events` string[] · nullable `failure_count` integer `id` uuid `org_id` uuid `secret` string Signing secret of the subscription, `whsec_` plus 64 hex characters. Returned by this call only; list, update and read calls never include it. Store it now: a lost secret cannot be read back, only replaced with POST /webhook-subscriptions/{subscriptionID}/rotate-secret. The old secret stops signing at once. `updated_at` string (date-time) `url` string `disabled_at` string (date-time) Set while the subscription is switched off, by active: false or by the delivery worker after repeated failures. `last_delivery_at` string (date-time) | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 404 | [not_found](https://upfour.io/docs/api/errors#code-not_found) | The requested resource does not exist or is outside the caller's scope. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousRedeliver a webhook delivery](https://upfour.io/docs/api/webhook-subscriptions/redeliver-a-webhook-delivery.md) [NextSend a webhook test](https://upfour.io/docs/api/webhook-subscriptions/send-a-webhook-test.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/webhook-subscriptions/3f6c1a52-8b1e-4f0d-9a43-2c5d7e8f1a90/rotate-secret" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/webhook-subscriptions/3f6c1a52-8b1e-4f0d-9a43-2c5d7e8f1a90/rotate-secret", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/webhook-subscriptions/3f6c1a52-8b1e-4f0d-9a43-2c5d7e8f1a90/rotate-secret", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/webhook-subscriptions/3f6c1a52-8b1e-4f0d-9a43-2c5d7e8f1a90/rotate-secret", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 200 ```json { "data": { "active": false, "created_at": "2026-01-15T09:30:00Z", "events": [ "example" ], "failure_count": 1, "id": "3f6c1a52-8b1e-4f0d-9a43-2c5d7e8f1a90", "org_id": "3f6c1a52-8b1e-4f0d-9a43-2c5d7e8f1a90", "secret": "example", "updated_at": "2026-01-15T09:30:00Z", "url": "https://example.com" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 404 ```json { "error": { "code": "not_found", "message": "The requested resource does not exist or is outside the caller's scope.", "request_id": "req_…" } } ``` --- # Send a webhook test — API reference > Accepts a webhook test request and returns the delivery record with its current status. Source: https://upfour.io/docs/api/webhook-subscriptions/send-a-webhook-test [Developers](https://upfour.io/docs/api.md)/[Webhook subscriptions](https://upfour.io/docs/api/webhook-subscriptions.md) Accepts a webhook test request and returns the delivery record with its current status. POST https://app.upfour.io/v1/webhook-subscriptions/{subscriptionID}/test scope webhook.write beta [Idempotency-Key supported](https://upfour.io/docs/api/idempotency.md) returns 202 ## Parameters | Field | Type | Description | | --- | --- | --- | | `subscriptionID` required | uuid | path parameter | ## Returns A successful request returns `202` with application/json. Response fields 1 top-level fields | Field | Type | Description | | --- | --- | --- | | `data` | WebhookDelivery | | | WebhookDelivery · 12 fields FieldTypeDescription `attempt` integer Number of the current attempt, at most 7. `created_at` string (date-time) `event` string `id` uuid `payload_json` any The event as sent to the receiver. `scheduled_at` string (date-time) `status` string one of`pending` `retrying` `delivered` `failed` `subscription_id` uuid `delivered_at` string (date-time) `next_retry_at` string (date-time) `response_body` string Never the receiver's answer, only a summary of the attempt: HTTP followed by the status code when the receiver answered (HTTP 200, HTTP 502), otherwise Delivery timed out, Delivery failed (for example a refused connection) or Delivery configuration or destination rejected. `response_code` integer | | | ## Errors Only the errors this operation can return. Every error uses the [same envelope](https://upfour.io/docs/api/responses.md). | Status | Code | Meaning | | --- | --- | --- | | 400 | [bad_request](https://upfour.io/docs/api/errors#code-bad_request) | The request is malformed or fails validation. | | 402 | [billing_action_unavailable](https://upfour.io/docs/api/errors#code-billing_action_unavailable) | This action is unavailable in the Company's billing state (`error.details.billing_state`), for example while credit is used up. **When:** A metered or limited action while the billing state, such as paused or without credit, blocks it; details holds the action and billing_state. **Fix:** Top up the credit or change the plan, then retry. `billing_action_unavailable`: this action is not available in the Company's billing state (`error.details.billing_state`), for example while the credit is used up. | | 403 | [permission_denied](https://upfour.io/docs/api/errors#code-permission_denied) | The credentials do not permit this action. | | 503 | [quota_gate_unavailable](https://upfour.io/docs/api/errors#code-quota_gate_unavailable) | The limit check itself failed, so the action was not carried out. **When:** The quota service could not decide while the request ran. **Fix:** Retry in a moment; nothing was changed. | | 403 / 429 | [quota_limit_reached](https://upfour.io/docs/api/errors#code-quota_limit_reached) | The protection limit for this action is used up for now; `error.details` carries `purpose`, `limit`, `window` and `retry_after_seconds`, and the `Retry-After` header says when the same request can succeed. **When:** Counters and concurrency limits answer 429 with Retry-After, inventory and capacity limits answer 403; details holds purpose, limit and window. **Fix:** On 429 wait Retry-After seconds; on 403 free something up, request a higher limit where details.raise_request is true, or buy an extension. `quota_limit_reached`: the protection limit of this action is used up for now. `error.details` carries `purpose`, `limit`, `window` and `retry_after_seconds`; the `Retry-After` header says when the same request can succeed. | Every call can also return `unauthorized` (401), `rate_limit_exceeded` (429) or `internal_error` (500 / 502). See [Responses and errors](https://upfour.io/docs/api/responses.md). [PreviousRotate a webhook subscription secret](https://upfour.io/docs/api/webhook-subscriptions/rotate-a-webhook-subscription-secret.md) ## Request ### cURL ```bash curl -s -X POST "https://app.upfour.io/v1/webhook-subscriptions/00000000-0000-4000-8000-000000000001/test" \ -H "Authorization: Bearer $UP4_API_KEY" ``` ### JavaScript ```javascript const res = await fetch("https://app.upfour.io/v1/webhook-subscriptions/00000000-0000-4000-8000-000000000001/test", { method: "POST", headers: { Authorization: `Bearer ${process.env.UP4_API_KEY}`, }, }); console.log(res.status, await res.json()); ``` ### Python ```python import os import requests res = requests.post( "https://app.upfour.io/v1/webhook-subscriptions/00000000-0000-4000-8000-000000000001/test", headers={"Authorization": f"Bearer {os.environ['UP4_API_KEY']}"}, ) print(res.status_code, res.json()) ``` ### Go ```go package main import ( "fmt" "io" "net/http" "os" ) func main() { req, _ := http.NewRequest("POST", "https://app.upfour.io/v1/webhook-subscriptions/00000000-0000-4000-8000-000000000001/test", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("UP4_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() out, _ := io.ReadAll(res.Body) fmt.Println(res.StatusCode, string(out)) } ``` ### PHP ```php "POST", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("UP4_API_KEY"), ], ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " ", $response, "\n"; curl_close($ch); ``` ## Response ### 202 ```json { "data": { "attempt": 1, "created_at": "2026-10-27T09:00:00Z", "delivered_at": "2026-10-27T09:00:00Z", "event": "webhook.test", "id": "00000000-0000-4000-8000-000000000002", "payload_json": { "app_url": "http://example.com", "created_at": "2026-10-27T09:00:00Z", "data": { "message": "Webhook test" }, "id": "00000000-0000-4000-8000-000000000003", "payload_version": 2, "type": "webhook.test" }, "response_body": "HTTP 200", "response_code": 200, "scheduled_at": "2026-10-27T09:00:00Z", "status": "delivered", "subscription_id": "00000000-0000-4000-8000-000000000001" } } ``` ### 400 ```json { "error": { "code": "bad_request", "message": "The request is malformed or fails validation.", "request_id": "req_…" } } ``` ### 402 ```json { "error": { "code": "billing_action_unavailable", "message": "This action is unavailable in the Company's billing state (error.details.billing_state), for example while credit is used up.", "request_id": "req_…" } } ``` ### 403 ```json { "error": { "code": "permission_denied", "message": "The credentials do not permit this action.", "request_id": "req_…" } } ``` ### 429 ```json { "error": { "code": "quota_limit_reached", "message": "The protection limit for this action is used up for now; error.details carries purpose, limit, window and retry_after_seconds, and the Retry-After header says when the same request can succeed.", "request_id": "req_…" } } ``` ### 503 ```json { "error": { "code": "quota_gate_unavailable", "message": "The limit check itself failed, so the action was not carried out.", "request_id": "req_…" } } ```