# Uptimeify Documentation, Full Export
> Expanded, text-first export of the Uptimeify documentation with page content.
Compact index: https://docs.uptimeify.io/llms.txt
## MCP Server
Uptimeify runs a stateless Model Context Protocol server. 20 check tools work with no account and no token; 5 more read your own monitors with an API token.
- Endpoint: `POST https://uptimeify.io/mcp` (Streamable HTTP, stateless)
- Server card: https://uptimeify.io/.well-known/mcp/server-card.json
- Registry manifest: https://uptimeify.io/.well-known/mcp/server.json
- Overview: https://uptimeify.io/mcp-server
- Technical reference: https://docs.uptimeify.io/api/mcp
- Limits: 120 requests/minute per IP on the endpoint, plus 15-30/minute per anonymous tool and 60/minute per authenticated tool. Exceeding a limit returns HTTP 429.
- Anonymous tools: check_ssl, check_dns, dns_propagation, mx_lookup, spf_check, dkim_check, dmarc_check, dnsbl_check, whois, domain_expiry, http_headers, hsts_check, redirect_check, port_check, ping_test, website_status, response_time, ip_geolocation, asn_lookup, reverse_dns
- API-token tools (read-only): list_monitors, monitor_status, list_incidents, check_history, uptime_summary
## Overview
### Welcome to Uptimeify
URL: https://docs.uptimeify.io
Description: White-label uptime, SSL & synthetic monitoring for agencies, hosted in the EU.
Summary: Uptimeify is a **white-label monitoring platform for agencies and MSPs**. Watch your clients' websites and infrastructure, alert the right people the moment something breaks, and give every client a branded status page and automated report, all under your own brand, hosted in the EU. ## What you can monitor From a single dashboard you can run: - **Websites**: uptime, SSL certificate, response time, keyword presence, page size, and HTTPS-redirect checks. - **Servers & services**: DNS, ICMP (ping), SSH, FTP, SMTP, IMAP/POP, TCP Port, DNSBL blacklist and domain-expiry checks. - **Synthetic & scheduled**: multi-step browser flows with Playwright, and Heartbeat (cron) checks for background jobs. See [Monitoring](/monitoring) for every check type and how to tune intervals, thresholds and locations. ## Alerting that doesn't cry wolf Every failure is confirmed from **multiple EU locations** before an incident opens, so you alert on real outages, not network blips. Route alerts to **29 notification channels** (Slack, Microsoft Teams, PagerDuty, Opsgenie, SMS, email, webhooks and more) with multi-step, time-based escalation so an unacknowledged alert moves to the next responder. Learn how detection works in [Incidents](/incidents), and wire up channels in [Integrations](/integrations). ## Client-facing, under your brand - **Status pages**: branded, public or password-protected, on your own custom domain. See [Status Pages](/status-pages). - **Automated PDF reports**: with your logo, to justify recurring fees. - **Maintenance windows**: suppress alerts during planned work. See [Maintenance](/maintenance). ## Built for agencies Unlimited sub-accounts per client, margin control on care plans, and a full **REST API** to automate customers, monitors, status pages and notification channels. Start with the [API Documentation](/api). ## Pick a topic Set up website, uptime, SSL, and synthetic monitors and tune their checks. Publish branded, public or password-protected status pages for your clients. Understand how incidents are detected, confirmed, and resolved. Schedule maintenance windows to suppress alerts during planned work. Connect Slack, webhooks, and other notification channels. Automate everything with the REST API: customers, monitors, status pages.
## API Reference
### API Documentation
URL: https://docs.uptimeify.io/api
Description: Overview of the REST API. Select a resource from the left.
Summary: ## AI / LLM Text Exports - [LLMS index for AI agents](https://uptimeify.io/llms) - [LLMS full reference for AI ingestion](https://uptimeify.io/llms-full) - [German LLMS index for AI agents](https://uptimeify.io/de/llms) - [German LLMS full reference for AI ingestion](https://uptimeify.io/de/llms-full) ## Base URL All examples use a placeholder base URL: ```bash BASE_URL="https://uptimeify.io" ``` ## Authentication Most endpoints require authentication. ```bash TOKEN="wsm_" curl -H "Authorization: Bearer $TOKEN" "$BASE_URL/api/websites" ``` API tokens generated in the dashboard always start with `wsm_`. Make sure you include the full token (including the prefix), otherwise the API will return `401 Unauthorized`. ## UUID Path Parameters For migrated resources, path parameters use the resource `publicId` UUID in the docs and examples. - Use UUIDs for endpoint paths such as `/api/websites/:websitePublicId`, `/api/customers/:customerPublicId`, `/api/organizations/:organizationPublicId`, `/api/incidents/:incidentPublicId`, `/api/customer-ips/:customerIpPublicId`, `/api/customer-domains/:customerDomainPublicId`, and monitor-specific `:...PublicId` parameters. - Internal numeric `id` fields may still appear in response payloads for internal references. - Query/body fields like `customerId`, `websiteId`, or `organizationId` stay numeric unless the endpoint page explicitly says otherwise. ## Responses and Errors - `2xx` indicates success - `4xx` indicates a request/auth problem - `5xx` indicates a server error - [Error codes and known API pitfalls](./error-codes-and-known-pitfalls) Example error response: ```json { "statusCode": 401, "statusMessage": "Unauthorized" } ```
### Global Administration
URL: https://docs.uptimeify.io/api/admin
Description: This section covers the global administration endpoints, which are protected and only accessible to platform administrators.
Summary: *(Endpoints are still being documented)* ## Endpoints
### Agent authentication (agentic registration)
URL: https://docs.uptimeify.io/api/agent-auth
Description: Register an AI agent and receive a short-lived, read-only access token for the Uptimeify API.
Summary: Uptimeify runs a WorkOS-style **agentic-registration authorization server** at `https://uptimeify.io`. Agents register, exchange a service-signed identity assertion for a short-lived opaque access token, and call the **read-only** API. All endpoints below are served only on the canonical host `uptimeify.io` (a 404 is returned on custom domains). ## Discover ```bash curl https://uptimeify.io/.well-known/oauth-authorization-server curl https://uptimeify.io/.well-known/oauth-protected-resource ``` The authorization-server metadata follows [RFC 8414](https://www.rfc-editor.org/rfc/rfc8414) (`token_endpoint`, `revocation_endpoint`, `jwks_uri`, `grant_types_supported`, `scopes_supported`) and adds an `agent_auth` extension block with `identity_endpoint`, `claim_endpoint`, `identity_types_supported` (currently `["anonymous", "service_auth"]`), and a `skill` pointer to `/auth.md`. The protected-resource metadata follows [RFC 9728](https://www.rfc-editor.org/rfc/rfc9728). ## Register (anonymous) ```bash curl -X POST https://uptimeify.io/agent/identity \ -H 'Content-Type: application/json' \ -d '{"type":"anonymous"}' ``` ```json { "registration_id": "reg_…", "identity_assertion": "", "pre_claim_scopes": ["tools.public"], "post_claim_scopes": ["api.read"], "token_endpoint": "https://uptimeify.io/oauth2/token" } ``` The `identity_assertion` is a ~24h EdDSA JWT: your durable credential. Rate limit: 30 requests/minute per caller. `post_claim_scopes` describes the scope a claimed registration reaches (`api.read`). An anonymous registration is active immediately with `tools.public` and is **usable as-is**: claiming it is optional. The response also carries a single-use `claim_token` (`clm_…`) and a `claim_url`: if you want to upgrade this registration to `api.read`, see [Claim flow](/api/agent-auth/claim-flow) for how to redeem it. ## Register (service_auth) `service_auth` registration is claimable-only: it starts `pending` and mints no token until a signed-in user authorizes it. See [Claim flow](/api/agent-auth/claim-flow) for the full `POST /agent/identity {"type":"service_auth", "login_hint":"…"}` request, the human consent step, and how to poll `/oauth2/token` for the resulting `api.read` access token. ## Exchange for an access token ```bash curl -X POST https://uptimeify.io/oauth2/token \ -H 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer' \ --data-urlencode 'assertion=' ``` ```json { "access_token": "wsma_…", "token_type": "Bearer", "expires_in": 3600, "scope": "tools.public" } ``` The access token is opaque, expires after 1 hour (3600s), and must be re-minted from the identity assertion afterward. Rate limit: 60 requests/minute per caller. ## Use (read-only) ```bash curl https://uptimeify.io/api/tools/dns-lookup?domain=deinkunde.com \ -H 'Authorization: Bearer wsma_…' ``` Agent tokens are read-only. The anonymous `tools.public` scope reaches only the public check tools (`GET /api/tools/*`). A claimed, `api.read`-scoped token (see [Claim flow](/api/agent-auth/claim-flow)) additionally reaches `GET /api/websites*`, `GET /api/incidents*`, and `GET /api/health`, scoped to the authorizing user's organization (or single customer). Any write, or any `GET`/`HEAD` request outside the allowlist for the token's scope, returns `403` with `data.code = agentTokenReadOnly`. ## Revoke ```bash curl -X POST https://uptimeify.io/oauth2/revoke \ -H 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'token=wsma_…' \ --data-urlencode 'token_type_hint=access_token' ``` Idempotent `200`: always succeeds, even for an unknown or already-revoked token. ## Common errors | Status | `error` / `data.code` | Meaning | | --- | --- | --- | | 400 | `service_auth_not_enabled` | The requested identity type is not enabled yet. | | 400 | `issuer_not_enabled` | `identity_assertion` (ID-JAG) is not enabled yet. | | 400 | `invalid_request` | Unknown/missing i…
### Claim flow (service_auth & anonymous)
URL: https://docs.uptimeify.io/api/agent-auth/claim-flow
Description: How an agent turns a registration into a read-only, account-scoped access token by having a signed-in user authorize it.
Summary: # Claim flow A **claim** binds an agent's registration to a signed-in user's organization (or a single customer within it), granting a **read-only** (`api.read`) access token. Agents never gain write access. There are two ways to reach a claim: - **`service_auth`** registration starts a claim ceremony immediately and returns the `user_code` directly to the calling agent (a trusted, device-flow-style integration). - An **`anonymous`** registration (see [Agent authentication](/api/agent-auth)) is usable immediately and can *optionally* be upgraded later via `POST /agent/identity/claim`: the `user_code` is never returned to the agent on this path; only the signed-in user sees it. ## 1. Register with `service_auth` `POST https://uptimeify.io/agent/identity` | Field | Type | Required | Description | | --- | --- | --- | --- | | `type` | string | yes | `"service_auth"` | | `login_hint` | string | yes | Email of the user who will authorize the agent. Must contain `@`. | ```bash curl -X POST https://uptimeify.io/agent/identity \ -H 'Content-Type: application/json' \ -d '{"type":"service_auth","login_hint":"user@deinkunde.com"}' ``` ```json { "registration_id": "reg_…", "claim": { "user_code": "WDJB-MJHT", "verification_uri": "https://uptimeify.io/claim", "expires_in": 600, "interval": 5 }, "claim_token": "clm_…", "post_claim_scopes": ["api.read"] } ``` Display `verification_uri` and `user_code` to your user (an RFC 8628 device-flow-style prompt). The registration starts `status: pending` and mints no access token until the ceremony completes. Poll step 3 below with the `claim_token`. `user_code` and `claim_token` both expire after `expires_in` seconds (600s / 10 minutes); call `POST /agent/identity` again to start a fresh ceremony if it lapses. ## 2. Or: claim an existing `anonymous` registration `POST https://uptimeify.io/agent/identity/claim` | Field | Type | Required | Description | | --- | --- | --- | --- | | `claim_token` | string | yes | The `clm_…` returned from an earlier `POST /agent/identity {"type":"anonymous"}` call. | | `email` | string | yes | Email of the user who will authorize the claim. Must contain `@`. | ```bash curl -X POST https://uptimeify.io/agent/identity/claim \ -H 'Content-Type: application/json' \ -d '{"claim_token":"clm_…","email":"user@deinkunde.com"}' ``` ```json { "verification_uri": "https://uptimeify.io/claim?claim_attempt=cat_…", "claim_attempt_token": "cat_…", "expires_in": 600, "interval": 5 } ``` Note what is **absent**: `user_code`. On this path the agent never learns the code, only the signed-in, bound-email user sees it, on the `/claim` page. The registration itself stays `active` and keeps working with its `tools.public` scope throughout: claiming only adds `api.read`, it never revokes anonymous access while the ceremony is pending. Calling this endpoint again before it expires overwrites the prior code/attempt token and resets the 10-minute window. A registration can only be claimed once: a second `POST /agent/identity/claim` (or a `service_auth` claim, which is always pre-bound) after it's already been confirmed returns `409 claimed_or_in_flight`. ## 3. Human consent at `/claim` The bound user must be signed in to Uptimeify as the exact `login_hint` / `email` used above, then either type the `user_code` shown by the agent, or open the `verification_uri` link (which carries `?claim_attempt=…` on the anonymous path). The Uptimeify web app resolves that into the confirmation screen via two session-cookie-authenticated endpoints: these are not called by the agent directly, they exist for completeness of the flow: - `GET /api/agent-claim/context?claim_attempt=`: returns `{ registration_id, agent_type, scope, bound_email, user_code, expires_at }` for the signed-in bound user, so the UI can show what's being authorized (and pre-fill the code on the anonymous path). - `POST /api/agent-claim/confirm { user_code }`: the signed-in user submits the code to authorize. On success: `{ ok: true, regis…
### API Tokens
URL: https://docs.uptimeify.io/api/api-tokens
Description: API tokens allow programmatic access to the Uptimeify API. Tokens are prefixed with wsm_ and can be scoped to a specific customer or organization-wide.
Summary: There are two endpoint scopes: - **Organization tokens** (`/api/organization/tokens`): admin-only, full org access - **Customer tokens** (`/api/customer/tokens`): scoped to accessible customers ## Authentication All examples assume a session cookie (not API tokens, API tokens cannot manage other API tokens): ```bash BASE_URL="https://uptimeify.io" ``` ## Endpoints - [List Organization Tokens](./list-organization-tokens) - [Create Organization Token](./create-organization-token) - [Delete Organization Token](./delete-organization-token) - [List Customer Tokens](./list-customer-tokens) - [Create Customer Token](./create-customer-token) - [Delete Customer Token](./delete-customer-token)
### Create Customer Token
URL: https://docs.uptimeify.io/api/api-tokens/create-customer-token
Description: Creates a new API token scoped to a customer. Restricted users must specify customerId. The full token is returned only once. Store it securely.
Summary: `POST /api/customer/tokens` ## Request Body | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `name` | string | Yes | - | Token display name (1-255 chars) | | `expiresInDays` | number | No | null | Days until expiration (1-365). Null = no expiration. | | `customerId` | number | No* | null | Scope token to a specific customer. Required for restricted users. | ## Example (cURL) ```bash curl -X POST "$BASE_URL/api/customer/tokens" \ -H "Cookie: $SESSION_COOKIE" \ -H "Content-Type: application/json" \ -d '{ "name": "Acme Corp API Token", "customerId": 5, "expiresInDays": 180 }' ``` ## Response ```json { "id": 2, "name": "Acme Corp API Token", "token": "wsm_f7e6d5c4b3a2...", "lastUsedAt": null, "expiresAt": "2026-10-15T08:00:00.000Z", "createdAt": "2026-04-15T08:00:00.000Z" } ``` ## Common errors - `400 Invalid customer ID` when customerId doesn't belong to your organization - `400 customerId is required` when restricted user doesn't specify customerId - `403 Forbidden` when customer is outside user's scope
### Create Organization Token
URL: https://docs.uptimeify.io/api/api-tokens/create-organization-token
Description: Creates a new API token. The full token is returned only once. Store it securely. Requires admin role.
Summary: `POST /api/organization/tokens` ## Request Body | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `name` | string | Yes | - | Token display name (1-255 chars) | | `expiresInDays` | number | No | null | Days until expiration (1-365). Null = no expiration. | | `customerId` | number | No | null | Scope token to a specific customer (must belong to your org) | ## Example (cURL) ```bash curl -X POST "$BASE_URL/api/organization/tokens" \ -H "Cookie: $SESSION_COOKIE" \ -H "Content-Type: application/json" \ -d '{ "name": "Production API", "expiresInDays": 90 }' ``` ## Response ```json { "id": 1, "name": "Production API", "token": "wsm_a1b2c3d4e5f6...", "lastUsedAt": null, "expiresAt": "2026-07-15T10:00:00.000Z", "createdAt": "2026-04-15T10:00:00.000Z" } ``` ## Common errors - `400 Invalid customer ID` when customerId doesn't belong to your organization - `401 Unauthorized` when not authenticated - `403 Forbidden` when not an admin
### Delete Customer Token
URL: https://docs.uptimeify.io/api/api-tokens/delete-customer-token
Description: Permanently revokes a customer-scoped API token. Users can only delete tokens within their customer scope.
Summary: `DELETE /api/customer/tokens/:id` ## Example (cURL) ```bash curl -X DELETE "$BASE_URL/api/customer/tokens/2" \ -H "Cookie: $SESSION_COOKIE" ``` ## Response ```json { "success": true } ``` ## Common errors - `403 Forbidden` when the token is outside your customer scope, or when the token is org-scoped (no `customerId`) - `404 Token not found` when the token doesn't exist or belongs to another org
### Delete Organization Token
URL: https://docs.uptimeify.io/api/api-tokens/delete-organization-token
Description: Permanently revokes an API token. Requires admin role.
Summary: `DELETE /api/organization/tokens/:id` ## Example (cURL) ```bash curl -X DELETE "$BASE_URL/api/organization/tokens/1" \ -H "Cookie: $SESSION_COOKIE" ``` ## Response ```json { "success": true } ``` ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` when not an admin - `404 Token not found` when the token doesn't exist or belongs to another org
### List Customer Tokens
URL: https://docs.uptimeify.io/api/api-tokens/list-customer-tokens
Description: Returns API tokens visible to the current user. Restricted users see only their customer-scoped tokens; admins see all tokens. Tokens are masked: only the first 8 characters are shown.
Summary: `GET /api/customer/tokens` ## Example (cURL) ```bash curl -X GET "$BASE_URL/api/customer/tokens" \ -H "Cookie: $SESSION_COOKIE" \ -H "Accept: application/json" ``` ## Response ```json [ { "id": 2, "organizationId": 1, "name": "Customer Scoped Token", "customerId": 5, "customerName": "Acme Corp", "lastUsedAt": null, "expiresAt": null, "createdAt": "2026-02-01T08:00:00.000Z", "tokenHint": "wsm_f7e6d5..." } ] ``` ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` when user lacks read access
### List Organization Tokens
URL: https://docs.uptimeify.io/api/api-tokens/list-organization-tokens
Description: Returns all API tokens for the organization. Tokens are masked: only the first 8 characters are shown. Requires admin role.
Summary: `GET /api/organization/tokens` ## Example (cURL) ```bash curl -X GET "$BASE_URL/api/organization/tokens" \ -H "Cookie: $SESSION_COOKIE" \ -H "Accept: application/json" ``` ## Response ```json [ { "id": 1, "organizationId": 1, "name": "Production API", "customerId": null, "customerName": null, "lastUsedAt": "2026-04-01T12:00:00.000Z", "expiresAt": null, "createdAt": "2026-01-15T10:00:00.000Z", "tokenHint": "wsm_a1b2c..." } ] ``` ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` when not an admin
### Auth & Session
URL: https://docs.uptimeify.io/api/auth
Description: Manage your current user session and retrieve profile information.
Summary: ## Endpoints - [Get Current User](./get-current-user)
### Get Current User
URL: https://docs.uptimeify.io/api/auth/get-current-user
Description: Returns a reduced view of the currently authenticated user and their session.
Summary: `GET /api/auth/get-session` ## Request ```http GET /api/auth/get-session HTTP/1.1 ``` This endpoint returns a public-safe session payload. For integrations, use API tokens (`Authorization: Bearer wsm_...`) with the REST endpoints under `/api/**`. Sensitive internal app fields such as `email`, `role`, `language`, `isGlobalAdmin`, `isGlobalSupporter`, `emailVerified`, `image`, and platform-admin customer context are intentionally not included. ## Response ```json { "user": { "id": "user_12345", "name": "Max Mustermann", "firstName": "Max", "lastName": "Mustermann", "organizationId": 1, "organizationStatus": "active", "isActive": true, "createdAt": "2026-03-31T15:50:49.030Z", "updatedAt": "2026-03-31T15:50:49.030Z" }, "session": { "userId": "user_12345", "expiresAt": "2027-03-31T15:58:26.618Z", "token": "session-token-value" } } ```
### Change Requests
URL: https://docs.uptimeify.io/api/change-requests
Description: How customers request changes to managed monitors and how organizations resolve them.
Summary: Monitors with `managementType: managed` are read-only for customer-scoped users (see [Managed vs. Self-Service Monitors](/monitoring/managed-vs-self-service)). Instead of editing directly, a customer opens a **change request** against the monitor; the organization reviews it in its inbox and accepts or rejects it. A change request has a `kind`: | Kind | Meaning | Effect on accept | |---|---|---| | `change` | Free-text change wish for a managed monitor | None automatic: the org applies the change manually | | `request_managed` | The customer asks the org to take over responsibility for a monitor | The monitor is flipped to `managed` automatically | Endpoints: - [Create Change Request](/api/change-requests/create-change-request): `POST /api/monitors/:monitorType/:monitorId/change-requests` (customer or org) - [List Change Requests](/api/change-requests/list-change-requests): `GET /api/change-requests` (org admins) - [Resolve Change Request](/api/change-requests/resolve-change-request): `PATCH /api/change-requests/:id` (org admins) Open requests are limited to **10 per customer** across all monitor types; further creates return `429` (`tooManyOpenRequests`).
### Create Change Request
URL: https://docs.uptimeify.io/api/change-requests/create-change-request
Description: Opens a change request against any monitor (all monitor types).
Summary: `POST /api/monitors/:monitorType/:monitorId/change-requests` Read access to the monitor is sufficient. The typical caller is a customer portal user who *lacks* write access to a `managed` monitor. ## Path parameters | Parameter | Description | |---|---| | `monitorType` | One of `website`, `dns`, `icmp`, `smtp`, `ssh`, `ftp`, `imap_pop`, `domain`, `dnsbl` | | `monitorId` | Numeric ID of the monitor | ## Request Body | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `kind` | string | No | `change` | `change` (free-text wish) or `request_managed` (ask the org to take the monitor over) | | `message` | string | Yes | - | The request text (1-2000 chars) | ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X POST "$BASE_URL/api/monitors/website/103/change-requests" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "kind": "change", "message": "Please lower the check interval to 1 minute." }' ``` ## Response ```json { "id": 12, "status": "open" } ``` ## Common errors - `400 Invalid monitor type` (`invalidMonitorType`) when `monitorType` is not one of the supported types - `400 Invalid monitor identifier` when `monitorId` is not a positive integer - `401 Unauthorized` when you are not logged in - `403 Forbidden` / `404 Not found` when the monitor is outside your scope - `429 Too many open requests` (`tooManyOpenRequests`) when the customer already has 10 open requests
### List Change Requests
URL: https://docs.uptimeify.io/api/change-requests/list-change-requests
Description: Lists the organization's change-request inbox.
Summary: `GET /api/change-requests` Organization write access required (organization admin or global admin). Results are strictly scoped to the caller's organization. ## Query parameters | Parameter | Type | Required | Default | Description | |---|---|---|---|---| | `status` | string | No | `open` | `open`, `accepted`, or `rejected` | ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl "$BASE_URL/api/change-requests?status=open" \ -H "Authorization: Bearer $TOKEN" ``` ## Response Newest first. `monitorName` is resolved per monitor type; `websiteId` is a legacy field populated only for website monitors. ```json [ { "id": 12, "monitorType": "website", "monitorId": 103, "websiteId": 103, "customerId": 101, "customerName": "Customer A GmbH", "monitorName": "New Landing Page", "requestedBy": "usr_123", "kind": "change", "message": "Please lower the check interval to 1 minute.", "status": "open", "resolvedBy": null, "resolvedAt": null, "createdAt": "2026-07-09T10:00:00.000Z" } ] ``` ## Common errors - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you are not an organization admin
### Resolve Change Request
URL: https://docs.uptimeify.io/api/change-requests/resolve-change-request
Description: Accepts or rejects an open change request.
Summary: `PATCH /api/change-requests/:id` Organization write access required. The status transition is atomic: under concurrent resolves only one caller wins; the loser receives `409`. Accepting a request with `kind: request_managed` additionally flips the target monitor to `managementType: managed` (dispatched to the correct table for the monitor's type). ## Request Body | Field | Type | Required | Description | |-------|------|----------|-------------| | `status` | string | Yes | `accepted` or `rejected` | ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X PATCH "$BASE_URL/api/change-requests/12" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "status": "accepted" }' ``` ## Response ```json { "id": 12, "status": "accepted" } ``` ## Common errors - `400 Invalid change-request identifier` - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you are not an organization admin - `404 Change request not found` - `409 Change request already resolved` when the request is no longer `open`
### Custom Fields
URL: https://docs.uptimeify.io/api/custom-fields
Description: Define custom metadata fields that can be attached to customers and websites. Custom fields support text, select, and multi-select types.
Summary: ## Authentication All examples assume a bearer token: ```bash BASE_URL="https://uptimeify.io" TOKEN="" ``` ## Endpoints - [List Custom Fields](./list-custom-fields) - [Create Custom Field](./create-custom-field) - [Update Custom Field](./update-custom-field) - [Delete Custom Field](./delete-custom-field)
### Create Custom Field
URL: https://docs.uptimeify.io/api/custom-fields/create-custom-field
Description: Creates a new custom field definition.
Summary: `POST /api/custom-fields` ## Request Body | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `organizationId` | number | Yes | - | Organization ID | | `name` | string | Yes | - | Display name | | `fieldKey` | string | No | auto from name | Unique key (auto-normalized: lowercase, non-alphanumeric → `_`) | | `fieldType` | string | No | `text` | `text`, `select`, or `multiselect` | | `isRequired` | boolean | No | false | Whether the field is required | | `displayOrder` | number | No | 0 | Sort order | | `options` | array | No | `[]` | Options for select/multiselect types | | `placeholder` | string\|null | No | null | Placeholder text | | `helpText` | string\|null | No | null | Help text below the field | | `showInTable` | boolean | No | true | Show in table views | ## Example (cURL) ```bash curl -X POST "$BASE_URL/api/custom-fields" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "organizationId": 1, "name": "Environment", "fieldType": "select", "options": ["production", "staging", "development"], "isRequired": true, "showInTable": true }' ``` ## Common errors - `409 Conflict` when `fieldKey` already exists for the organization ## Response Returns the created custom field object. See [Error Codes](/api/error-codes-and-known-pitfalls) for error responses.
### Delete Custom Field
URL: https://docs.uptimeify.io/api/custom-fields/delete-custom-field
Description: Soft-deletes a custom field (sets isActive: false).
Summary: `DELETE /api/custom-fields/:id` ## Example (cURL) ```bash curl -X DELETE "$BASE_URL/api/custom-fields/1" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "success": true, "message": "Custom field deleted successfully" } ```
### List Custom Fields
URL: https://docs.uptimeify.io/api/custom-fields/list-custom-fields
Description: Returns all active custom field definitions for the organization.
Summary: `GET /api/custom-fields` ## Query Parameters | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `organizationId` | number | session org | Override organization scope | ## Example (cURL) ```bash curl -X GET "$BASE_URL/api/custom-fields" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Response ```json [ { "id": 1, "organizationId": 1, "name": "Data Center", "fieldKey": "data_center", "fieldType": "text", "isRequired": false, "displayOrder": 0, "options": [], "placeholder": "e.g. fsn1", "helpText": "Primary data center location", "showInTable": true, "isActive": true, "createdAt": "2026-01-01T00:00:00.000Z", "updatedAt": "2026-01-01T00:00:00.000Z" } ] ``` Only active fields (`isActive: true`) are returned, ordered by `displayOrder`.
### Update Custom Field
URL: https://docs.uptimeify.io/api/custom-fields/update-custom-field
Description: Updates a custom field definition. All fields are optional.
Summary: `PATCH /api/custom-fields/:id` ## Request Body (all optional) | Field | Type | Description | |-------|------|-------------| | `name` | string | Display name | | `fieldType` | string | `text`, `select`, or `multiselect` | | `isRequired` | boolean | Whether the field is required | | `displayOrder` | number | Sort order | | `options` | array | Options for select/multiselect | | `placeholder` | string\|null | Placeholder text | | `helpText` | string\|null | Help text | | `showInTable` | boolean | Show in table views | | `isActive` | boolean | Soft delete (set to false) | ## Example (cURL) ```bash curl -X PATCH "$BASE_URL/api/custom-fields/1" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Environment", "options": ["production", "staging", "development", "qa"] }' ``` ## Response Returns the updated custom field object. See [Error Codes](/api/error-codes-and-known-pitfalls) for error responses.
### Customer Management
URL: https://docs.uptimeify.io/api/customers
Description: Path-based customer endpoints use customerPublicId UUIDs.
Summary: ## Endpoints - [List Customers](./list-customers) - [Create Customer](./create-customer) - [Get Customer Details](./get-customer-details) - [Update Customer](./update-customer) - [Change Package](./change-package) - [SMS Usage](./sms-usage)
### Bulk Actions
URL: https://docs.uptimeify.io/api/customers/bulk-actions
Description: Applies one action to many customers at once, for multi-select workflows.
Summary: `POST /api/customers/bulk` Applies a single action to a list of customer ids. Each id is processed independently: one conflicting or missing customer does not abort the rest. The response is always **`200 OK`**, even when some ids failed, so check `failed` rather than relying on the HTTP status alone. ## Authentication ```bash BASE_URL="https://uptimeify.io" TOKEN="" ``` ## Request Body ```json { "ids": [101, 102, 103], "action": "reports", "payload": { "enabled": true } } ``` | Field | Type | Required | Description | |-------|------|----------|--------------| | `ids` | number[] | yes | Customer ids (the numeric `id`, not `publicId`). Non-empty, at most **200** entries, positive integers, no duplicates. | | `action` | string | yes | One of `reports`, `activate`, `deactivate`, `delete`, `package`. | | `payload` | object | depends on `action` | Required for `reports` and `package` (see below). Ignored for `activate`, `deactivate`, `delete`. | ### Actions | Action | Effect | `payload` | |--------|--------|-----------| | `reports` | Sets `monthlyReportsEnabled` on each customer. | `{ "enabled": boolean }` (required) | | `activate` | Sets the customer's status to `active` (a no-op if already active) and cascades monitor statuses accordingly. | none | | `deactivate` | Sets the customer's status to `inactive` (a no-op if already inactive) and cascades monitor statuses accordingly. | none | | `delete` | Hard-deletes the customer. Monitors and check history are removed with it via cascade. **Not reversible.** | none | | `package` | Reassigns the customer to a different package by id. | `{ "packageId": number }` (required) | `package` only accepts a numeric `packageId` that belongs to **the target customer's own organization**. For every caller except a global admin that is the same thing as your own organization, because you can only address customers inside it. A global admin addressing customers across organizations needs a `packageId` from each customer's own organization; one belonging to a different organization is rejected with `invalidPackageType`. Unlike [Update Customer](/api/customers/update-customer), it does not resolve a legacy `packageType` string or display name, since guessing which package was meant across up to 200 rows at once is not something the bulk endpoint does; look up the id from [List Package Configs](/api/organization/list-package-configs) first. `payload.enabled` and `payload.packageId` are validated **once, up front**, before any customer is touched. A malformed payload fails the whole request with `400 invalidRequestBody`; it never turns into 200 identical per-id failures. ## Example Request ```bash curl -X POST "$BASE_URL/api/customers/bulk" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "ids": [101, 102, 103], "action": "reports", "payload": { "enabled": true } }' ``` ## Example Response (partial result) ```json { "ok": [101, 103], "failed": [ { "id": 102, "reason": "customerNotFound" } ] } ``` `ok` lists the ids that were updated. `failed` lists the ids that were not, each with a `reason` string. Render the split (for example "2 of 3 updated"), never a blanket success from the fact that the request itself returned `200`. ## Common Errors These fail the **whole request** before any customer is touched: - `401 unauthorized` you are not logged in - `403 forbidden` your role is `readonly`, or you are a global-supporter (both are read-only for this endpoint) - `400 invalidRequestBody` `ids` is missing/empty/not an array, contains more than 200 entries, contains a non-integer, a value `<= 0`, or a duplicate; `action` is not one of the five supported actions; or `payload.enabled` / `payload.packageId` is missing or the wrong type for the chosen action These appear per id, inside `failed[].reason`, without failing the request: | Reason | Meaning | |--------|---------| | `customerNotFound` | The id does not exist, or belongs to another organization (global admi…
### Change Package
URL: https://docs.uptimeify.io/api/customers/change-package
Description: Changes the customer's package assignment.
Summary: `PATCH /api/customers/:customerPublicId` This uses the same endpoint as [Update Customer](./update-customer). You usually send `packageId`. Legacy `packageType` values are still accepted for backward compatibility, but they must resolve to a package the organization actually has: a package key or a package display name. An unknown value is rejected with `400` and `data.code` `invalidPackageType`. ## Authentication ```bash BASE_URL="https://uptimeify.io" TOKEN="" ``` ## Request Body ```json { "packageId": 12 } ``` ## Example Request ```bash curl -X PATCH "$BASE_URL/api/customers/6bfec6f6-245a-47ce-843b-157d97d56f88" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "packageId": 12 }' ``` ## Example Response ```json { "id": 101, "publicId": "6bfec6f6-245a-47ce-843b-157d97d56f88", "organizationId": 1, "name": "Customer A GmbH", "email": "contact@customer-a.de", "notificationPhoneNumber": null, "notificationEmail": null, "packageId": 12, "status": "active", "monthlyReportsEnabled": true, "customFields": {}, "notificationChannels": null, "notificationTargets": null, "updatedAt": "2026-02-26T19:37:00.000Z" } ``` ## Common Errors - `400` Invalid Customer identifier - `400` Invalid packageId / packageType for this organization (`data.code`: `invalidPackageType`) - `401` Unauthorized - `403` Forbidden / Organization ID not found - `404` Customer not found - `500` Failed to update customer
### Create Customer
URL: https://docs.uptimeify.io/api/customers/create-customer
Description: Creates a new customer and assigns an organization-defined package via packageType.
Summary: `POST /api/customers` ## Authentication This endpoint requires authentication. ```bash BASE_URL="https://uptimeify.io" TOKEN="wsm_" ``` ## Request Body ```json { "name": "Customer Name", "email": "customer@deinkunde.com", "packageType": "business", // use the configured packageType or displayName for the organization "organizationId": 1, // optional (required only for global admins) "status": "active", // optional, Default: active "monthlyReportsEnabled": true, // optional, defaults to the package's monthlyReportsDefault "customFields": { ... } // optional } ``` Notes: - `packageType` can contain the configured package key or the package display name of the organization. Known aliases such as `aquisition_test` are normalized automatically. - `monthlyReportsEnabled` is optional. Omit it and the new customer is seeded from the assigned package's `monthlyReportsDefault` (see [Upsert Package Config](/api/organization/upsert-package-config)); if the organization has no config row for that package, it falls back to `true`. An explicit value in the request always wins, the customer field alone decides whether a report is sent. - If no configured package matches, the request is rejected with `400 Bad Request`. - For non-global-admin users, `organizationId` is derived from your session/token and must not be overridden. - For global admins, `organizationId` must be provided explicitly. ## Ownership & permission overrides (optional) These fields control the customer's [managed vs. self-service](/monitoring/managed-vs-self-service) permissions and channel-type policy. They are **organization-write gated**: only organization admins (or global admins) can set them. A non-admin caller's values are ignored and the schema defaults apply. `null` (or omitting the field) means *inherit from the customer's package config*. | Field | Type | Default | Description | |-------|------|---------|-------------| | `allowSelfService` | boolean\|null | null (inherit) | Whether the customer may create and manage `self_service` monitors | | `maxSelfServiceUrls` | number\|null | null (inherit) | Cap on the customer's **total** `self_service` monitors across all monitor types | | `canEditManaged` | boolean | false | Exception: lets this customer edit `managed` monitors (class flips stay org-only). Grants are written to the audit log. | | `enableEmailAlerts` | boolean\|null | null (inherit) | Channel-type policy override: email alerts | | `enableSmsAlerts` | boolean\|null | null (inherit) | Channel-type policy override: SMS alerts | | `enableWebhookAlerts` | boolean\|null | null (inherit) | Channel-type policy override: webhooks | | `enableIntegrationAlerts` | boolean\|null | null (inherit) | Channel-type policy override: integrations | | `enablePostRequestEscalation` | boolean\|null | null (inherit) | Channel-type policy override: POST-request escalation | The legacy `notificationChannels` JSONB override is deprecated: channel-type policy lives in the `enable*` fields above. ## Response ```json { "success": true, "customer": { "id": 64, "publicId": "6d74c32b-a97c-49b9-be3e-2b5e24bed826", "organizationId": 2, "name": "Example Customer", "email": "example@deinkunde.com", "notificationPhoneNumber": null, "notificationEmail": null, "packageId": 7, "packageType": "essential", "status": "active", "allowedCheckCountryCodes": null, "cancellationDate": null, "cancelledAt": null, "customFields": { "region": "EU", "planOwner": "Operations" }, "monthlyReportsEnabled": true, "notificationChannels": null, "notificationTargets": null, "notificationRules": null, "smsUsageCurrentMonth": 0, "createdAt": "2026-04-04T12:27:13.415Z", "updatedAt": "2026-04-04T12:27:13.415Z" } } ```
### Get Customer Details
URL: https://docs.uptimeify.io/api/customers/get-customer-details
Description: Returns details of a specific customer.
Summary: `GET /api/customers/:customerPublicId` `packageDisplayName` is the configured display label of the assigned organization package. It is not a normalized global enum and may contain organization-specific names such as `aquisition_test`. ## Response ```json { "id": 101, "publicId": "6bfec6f6-245a-47ce-843b-157d97d56f88", "organizationId": 1, "name": "Customer A GmbH", "email": "contact@customer-a.de", "notificationPhoneNumber": "+491701234567", "notificationEmail": "alerts@customer-a.de", "packageId": 3, "packageDisplayName": "Growth", "status": "active", "customFields": { "internalReference": "KD-999" }, "monthlyReportsEnabled": true, "notificationChannels": null, "notificationTargets": null, "notificationRules": null, "smsUsageCurrentMonth": 0, "updatedAt": "2023-05-15T10:00:00Z", "createdAt": "2023-05-15T10:00:00Z" } ``` ## Common Errors - `400` Invalid Customer identifier - `401` Unauthorized - `403` Forbidden / Organization ID not found - `404` Customer not found
### List Customers
URL: https://docs.uptimeify.io/api/customers/list-customers
Description: Lists all customers of the organization.
Summary: `GET /api/customers` ## Query Parameters - `organizationId` (required): The ID of the organization. - `fields` (optional): Set to `minimal` to return a lightweight shape containing only `id`, `publicId`, `name`, `status`, and `customFields`. The monitor counts and package details are omitted, and the response is much smaller. Useful for populating pickers/dropdowns. ## Response With `fields=minimal`: ```json [ { "id": 101, "publicId": "cus_9f3a…", "name": "Customer A GmbH", "status": "active", "customFields": { "region": "EU" } } ] ``` Default response: ```json [ { "id": 101, "organizationId": 1, "name": "Customer A GmbH", "email": "contact@customer-a.de", "packageId": 4, "packageDisplayName": "Business", "status": "active", "cancellationDate": null, "notificationEmail": "alerts@customer-a.de", "createdAt": "2023-05-15T10:00:00Z" }, { "id": 102, "organizationId": 1, "name": "StartUp XY", "email": "info@startup-xy.com", "packageId": 1, "packageDisplayName": "Essential", "status": "marked_for_cancellation", "cancellationDate": "2024-12-31T23:59:59Z", "createdAt": "2023-06-20T14:30:00Z" } ] ``` Note: The list response intentionally no longer exposes the raw `packageType`. Use `packageDisplayName` for display and `packageId` for technical mapping. `packageDisplayName` comes from the assigned organization package config and may therefore be organization-specific.
### SMS Usage
URL: https://docs.uptimeify.io/api/customers/sms-usage
Description: Returns the SMS usage for the current calendar month across all customers of the organization, sorted by usage descending.
Summary: `GET /api/customers/sms-usage` ## Query Parameters - `organizationId` (required): The ID of the organization. ## Response ```json { "total": 47, "customers": [ { "publicId": "6bfec6f6-245a-47ce-843b-157d97d56f88", "name": "Customer A GmbH", "status": "active", "smsUsageCurrentMonth": 32 }, { "publicId": "9a1c3d2e-8f4b-42a1-b7c9-3e5f12d84a90", "name": "StartUp XY", "status": "active", "smsUsageCurrentMonth": 15 }, { "publicId": "2d7e5f1a-6c3b-4d9e-a2f8-1b4c7e9d3a56", "name": "Inactive Corp", "status": "inactive", "smsUsageCurrentMonth": 0 } ] } ``` ### Fields | Field | Type | Description | |---|---|---| | `total` | `number` | Sum of `smsUsageCurrentMonth` across all returned customers. | | `customers[].publicId` | `string` | Customer UUID. | | `customers[].name` | `string` | Customer display name. | | `customers[].status` | `string` | Customer status: `active`, `inactive`, `marked_for_cancellation`, or `cancelled`. | | `customers[].smsUsageCurrentMonth` | `number` | Number of SMS sent for this customer in the current month. Resets at the start of each calendar month. | ## Notes - The usage counter `smsUsageCurrentMonth` resets at the beginning of each calendar month. - Customers with zero usage are included in the response. - Results are ordered by `smsUsageCurrentMonth` descending (highest usage first). - For the complete SMS quota and overage details of the entire organization, see the billing overview. ## Common Errors - `400` Organization ID required - `401` Unauthorized - `403` Forbidden
### Update Customer
URL: https://docs.uptimeify.io/api/customers/update-customer
Description: Updates customer details.
Summary: `PATCH /api/customers/:customerPublicId` ## Authentication ```bash BASE_URL="https://uptimeify.io" TOKEN="" ``` ## Request Body All fields are optional. Omitted fields keep their current values. ```json { "name": "New Name", "email": "new@deinkunde.com", "status": "active", "packageType": "business", // configured packageType or displayName for the organization "notificationEmail": "alerts@customer-a.de", "notificationPhoneNumber": "+491701234567", "monthlyReportsEnabled": true, "customFields": { "internalReference": "KD-999" } } ``` Notes: - `packageType` can contain the configured package key or the package display name of the organization. Known aliases such as `aquisition_test` are normalized automatically. A value that resolves to no package of this organization is rejected with `400` and `data.code` `invalidPackageType`; it is no longer stored as-is. ## Ownership & permission overrides (optional) These fields control the customer's [managed vs. self-service](/monitoring/managed-vs-self-service) permissions and channel-type policy. They are **organization-write gated**: a customer-scoped caller's writes to these fields are silently ignored server-side. `null` means *inherit from the customer's package config*. | Field | Type | Default | Description | |-------|------|---------|-------------| | `allowSelfService` | boolean\|null | null (inherit) | Whether the customer may create and manage `self_service` monitors | | `maxSelfServiceUrls` | number\|null | null (inherit) | Cap on the customer's **total** `self_service` monitors across all monitor types | | `canEditManaged` | boolean | false | Exception: lets this customer edit `managed` monitors (class flips stay org-only). Changes are written to the audit log. | | `enableEmailAlerts` | boolean\|null | null (inherit) | Channel-type policy override: email alerts | | `enableSmsAlerts` | boolean\|null | null (inherit) | Channel-type policy override: SMS alerts | | `enableWebhookAlerts` | boolean\|null | null (inherit) | Channel-type policy override: webhooks | | `enableIntegrationAlerts` | boolean\|null | null (inherit) | Channel-type policy override: integrations | | `enablePostRequestEscalation` | boolean\|null | null (inherit) | Channel-type policy override: POST-request escalation | The legacy `notificationChannels` JSONB override is deprecated: channel-type policy lives in the `enable*` fields above. ## Example Request ```bash curl -X PATCH "$BASE_URL/api/customers/6bfec6f6-245a-47ce-843b-157d97d56f88" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Customer A GmbH (Updated)", "monthlyReportsEnabled": true }' ``` ## Example Response ```json { "id": 101, "publicId": "6bfec6f6-245a-47ce-843b-157d97d56f88", "organizationId": 1, "name": "Customer A GmbH (Updated)", "email": "contact@customer-a.de", "notificationPhoneNumber": "+491701234567", "notificationEmail": "alerts@customer-a.de", "packageId": 3, "status": "active", "monthlyReportsEnabled": true, "customFields": { "internalReference": "KD-999" }, "notificationChannels": null, "notificationTargets": null, "updatedAt": "2026-02-26T19:37:00.000Z" } ``` ## Common Errors - `400` Invalid Customer identifier - `400` Invalid packageId / packageType for this organization (`data.code`: `invalidPackageType`) - `401` Unauthorized - `403` Forbidden / Organization ID not found - `404` Customer not found - `500` Failed to update customer
### Error codes and known API pitfalls
URL: https://docs.uptimeify.io/api/error-codes-and-known-pitfalls
Description: This page documents real integration issues that have occurred in production or testing.
Summary: ## Schema | Error Code | Cause | Solution / Bug | | --- | --- | --- | | `404 Website not found` | Some website sub-endpoints previously accepted only the legacy numeric website ID, even though the docs showed `websitePublicId`. | Fixed. `GET /api/websites/:websitePublicId/check-history` and `GET /api/websites/:websitePublicId/uptime-stats` now accept public IDs and still keep legacy-ID compatibility. | | `403 Forbidden` on `GET /api/organizations/:organizationPublicId` | The route previously interpreted the path parameter directly as a number. A UUID therefore failed during permission checks. | Fixed. Organization detail and billing routes now resolve `publicId` correctly. | | `400 Bad Request` with `ZodError` on `customer-ips` or `customer-domains` | Query parameters like `organizationId=`, `page=`, or `perPage=` were sent as empty strings. Zod coerced them to `0`, which then violated min constraints. | Fixed for `organizationId`, `page`, and `perPage`. Optional still means: omit unused parameters instead of sending empty strings. | ## Monitor ownership error codes Since the [managed vs. self-service](/monitoring/managed-vs-self-service) model, monitor write endpoints of **all** types (website, DNS, ICMP, SMTP, SSH, FTP, IMAP/POP, domain, DNSBL) can return these `403` codes in `data.code`: | Code | Meaning | | --- | --- | | `managed_by_organization` | A customer-scoped caller tried to edit/delete a `managed` monitor without the `canEditManaged` exception. Open a [change request](/api/change-requests) instead. | | `selfServiceNotAllowed` | A customer-scoped caller tried to create a monitor but the customer's resolved `allowSelfService` is false. | | `selfServiceQuotaReached` | Creating (or flipping to) a `self_service` monitor would exceed the customer's `maxSelfServiceUrls`: the quota counts **all** monitor types together. | | `managementTypeOrgOnly` | A non-org-admin tried to change a monitor's `managementType`. Class flips are organization-admin-only. | | `tooManyOpenRequests` | (`429`) The customer already has 10 open change requests. | ## Customer package error codes `POST /api/customers` and `PATCH /api/customers/:customerPublicId` resolve the package a customer is on. Both reject a package the organization does not have: | Code | Meaning | | --- | --- | | `invalidPackageType` | (`400`) The `packageId` does not belong to this organization, or the legacy `packageType` matched neither a configured package key nor a package display name of this organization. | `PATCH` previously stored an unknown `packageType` verbatim and left `packageId` empty. Such a customer was attached to no package config at all, which left its data retention unresolvable. Send `packageId`, or a `packageType` that exists on the organization. ## Check mode error codes (SSH/SMTP/FTP/IMAP-POP monitors) The credentialed monitor kinds (SSH, SMTP, FTP, IMAP/POP) support a `checkMode` field (`protocol` | `tcp`, default `protocol`). In `tcp` mode the monitor performs a bare TCP port-reachability check and no credentials are required or stored. Create and update endpoints for these four kinds can return these `400` codes in `data.code`: | Code | Meaning | | --- | --- | | `portRequiredForTcp` | IMAP/POP create or update: `checkMode` is (or resolves to) `tcp`, but no `port` is set on the request or already stored on the monitor. Only IMAP/POP requires an explicit `port` for `tcp` mode. SSH, SMTP, and FTP fall back to their protocol default port. | | `invalidCheckMode` | Update (`PATCH`) endpoints only: `checkMode` was provided but is neither `protocol` nor `tcp`. | ## Incident error codes Only manual (status-page) incidents can be deleted. `DELETE /api/incidents/:id` returns this `data.code`: | Code | Meaning | | --- | --- | | `notManualIncident` | (`403`) The incident is monitor-generated. Only manual incidents created by an admin can be deleted; automatic incidents are worker-owned and are never deletable via the API. | ## Maintenance window error codes `POST…
### Escalation Config
URL: https://docs.uptimeify.io/api/escalation
Description: Manage organization-level webhook escalation settings and default notification channels.
Summary: The escalation config auto-creates with sensible defaults when first accessed. ## Authentication All endpoints accept either session cookies or API bearer tokens: ```bash BASE_URL="https://uptimeify.io" TOKEN="" ``` ## Default Webhook Body Template Variables | Variable | Description | |----------|-------------| | `{{websiteName}}` | Name of the affected website | | `{{websiteUrl}}` | URL of the affected website | | `{{status}}` | Current status (e.g. `down`, `up`) | | `{{startedAt}}` | Incident start timestamp | | `{{incident}}` | Incident details object | | `{{errorMessage}}` | Error message if available | ## Endpoints - [Get Escalation Config](./get-escalation-config) - [Update Escalation Config](./update-escalation-config) - [Test Escalation Config](./test-escalation-config)
### Get Escalation Config
URL: https://docs.uptimeify.io/api/escalation/get-escalation-config
Description: Returns the escalation config for an organization. The :id parameter is the organization ID. If no config exists, one is auto-created with defaults.
Summary: `GET /api/escalation-config/:id` Also returns the organization's default notification settings and all notification channels. ## Example (cURL) ```bash curl -X GET "$BASE_URL/api/escalation-config/1" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Response ```json { "id": 1, "organizationId": 1, "webhookUrl": "https://deinkunde.com/webhook", "webhookMethod": "POST", "webhookHeaders": { "Content-Type": "application/json" }, "webhookBodyTemplate": "{\"websiteName\":\"{{websiteName}}\",...}", "webhookTimeout": 30, "webhookRetryAttempts": 3, "webhookRetryDelay": 60, "expectedStatusCodes": "200,201,202,204", "isActive": false, "lastTestedAt": null, "lastTestStatus": null, "lastTestError": null, "organization": { "defaultEmail": "ops@deinkunde.com", "defaultPhoneNumber": "+1234567890", "defaultNotificationChannels": null, "defaultNotificationTargets": null, "defaultNotificationRules": null }, "notificationChannels": [] } ``` ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` with insufficient permissions
### Test Escalation Config
URL: https://docs.uptimeify.io/api/escalation/test-escalation-config
Description: Tests the escalation config by sending a test webhook, PagerDuty event, or Pushover notification. All body fields are optional and override DB values for testing.
Summary: `POST /api/escalation-config/:id/test` ## Request Body (all optional) | Field | Type | Description | |-------|------|-------------| | `type` | string | `pagerduty` or `pushover` for dedicated handlers | | `config` | object | PagerDuty config `{routingKey}` or Pushover config `{userKey, apiToken}` | | `webhookUrl` | string | Override webhook URL for test | | `webhookMethod` | string | Override webhook method | | `webhookHeaders` | object | Override webhook headers | | `webhookBodyTemplate` | string | Override webhook body template | | `webhookTimeout` | number | Override webhook timeout | | `expectedStatusCodes` | string | Override expected status codes | ## Example (cURL): Webhook test ```bash curl -X POST "$BASE_URL/api/escalation-config/1/test" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "webhookUrl": "https://httpbin.org/post", "webhookMethod": "POST", "webhookTimeout": 15 }' ``` ## Example (cURL): PagerDuty test ```bash curl -X POST "$BASE_URL/api/escalation-config/1/test" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "type": "pagerduty", "config": { "routingKey": "your-routing-key-here" } }' ``` ## Response (webhook) ```json { "success": true, "statusCode": 200, "message": "Webhook delivered successfully" } ``` ## Response (PagerDuty) ```json { "success": true, "message": "PagerDuty event enqueued successfully" } ``` ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` with insufficient permissions
### Update Escalation Config
URL: https://docs.uptimeify.io/api/escalation/update-escalation-config
Description: Updates the escalation config. The :id parameter is the organization ID. If no config exists, one is upserted.
Summary: `PATCH /api/escalation-config/:id` ## Request Body (all optional) | Field | Type | Description | |-------|------|-------------| | `webhookUrl` | string\|null | Webhook URL. Set to `null` to deactivate the webhook channel. | | `webhookMethod` | string | HTTP method: `POST`, `PUT`, or `PATCH` | | `webhookHeaders` | object\|string | JSON headers object | | `webhookBodyTemplate` | string | JSON body template with `{{variables}}` | | `webhookTimeout` | integer | Timeout in seconds | | `webhookRetryAttempts` | integer | Number of retries | | `webhookRetryDelay` | integer | Seconds between retries | | `expectedStatusCodes` | string | Comma-separated expected status codes | | `isActive` | boolean | Enable or disable the webhook | | `defaultEmail` | string | **Side-effect:** creates/updates an org-level email notification channel | | `defaultPhoneNumber` | string | **Side-effect:** creates/updates an org-level SMS notification channel | ## Example (cURL) ```bash curl -X PATCH "$BASE_URL/api/escalation-config/1" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "webhookUrl": "https://deinkunde.com/webhook", "webhookMethod": "POST", "webhookHeaders": { "Content-Type": "application/json" }, "webhookTimeout": 30, "webhookRetryAttempts": 3, "isActive": true, "defaultEmail": "alerts@deinkunde.com" }' ``` ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` with insufficient permissions ## Response Returns the updated escalation config object. See [Error Codes](/api/error-codes-and-known-pitfalls) for error responses.
### Examples
URL: https://docs.uptimeify.io/api/examples
Description: Here you’ll find step-by-step examples for common API workflows.
Summary: - [Create a customer + website](/api/examples/create-customer-and-website) - [Delete a customer including all websites](/api/examples/delete-customer-and-all-websites) - [Create a customer + all monitor types](/api/examples/create-customer-and-all-monitors) - [Create a customer + multiple monitors](/api/examples/create-customer-and-multiple-monitors)
### Create a customer + all monitor types
URL: https://docs.uptimeify.io/api/examples/create-customer-and-all-monitors
Description: This example is useful if you want to bootstrap a customer with a complete monitoring baseline.
Summary: High-level flow: 1. Create customer 2. Create website 3. Create monitors (one per monitor type) ## 1) Create customer + website Reuse: - [Create a customer + website](/api/examples/create-customer-and-website) ## 2) Create monitors (one per type) Monitor APIs are organized by type. API reference overview: - [Monitors API](/api/monitors) Typical types you might create: - DNS Monitor - ICMP Monitor - SMTP Monitor - SSH Monitor - TCP Port Monitor - FTP Monitor - IMAP/POP Monitor For details and required fields, follow the per-type API docs. ### Example: create a DNS monitor ```bash curl -X POST "$API_BASE_URL/api/dns-monitors" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "customerId": 123, "name": "DNS: deinkunde.com", "hostname": "deinkunde.com", "dnsConfig": { "rrtypes": ["A", "AAAA"], "matchMode": "exact", "expectedValues": { "A": ["93.184.216.34"], "AAAA": ["2606:2800:220:1:248:1893:25c8:1946"] } } }' ``` ### Example: create an ICMP monitor ```bash curl -X POST "$API_BASE_URL/api/icmp-monitors" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "customerId": 123, "name": "Ping: gateway", "hostname": "gateway.deinkunde.com" }' ``` ### Example: create a TCP Port monitor ```bash curl -X POST "$API_BASE_URL/api/tcp-monitors" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "customerId": 123, "name": "Redis Primary", "hostname": "redis.deinkunde.com", "port": 6379, "config": { "expectBanner": "+PONG" } }' ``` ## Notes - Not every installation exposes every monitor type, and required fields can vary (plan/package). - For website (HTTP) checks, start with the website’s own monitoring settings: [Website Configuration API](/api/website-configuration)
### Create a customer + multiple monitors
URL: https://docs.uptimeify.io/api/examples/create-customer-and-multiple-monitors
Description: This example shows how to set up multiple monitors for a single customer, e.g.:
Summary: - Two different hostnames for DNS monitoring - Multiple servers for ICMP - A mix of DNS + ICMP + SMTP ## 1) Create customer + website - [Create a customer + website](/api/examples/create-customer-and-website) ## 2) Create multiple monitors ### Option A: Multiple monitors of the same type (DNS) ```bash # DNS monitor 1 curl -X POST "$API_BASE_URL/api/dns-monitors" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "customerId": 123, "name": "DNS: app.deinkunde.com", "hostname": "app.deinkunde.com", "dnsConfig": { "rrtypes": ["A"], "matchMode": "exact", "expectedValues": { "A": ["93.184.216.34"] } } }' # DNS monitor 2 curl -X POST "$API_BASE_URL/api/dns-monitors" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "customerId": 123, "name": "DNS: mail.deinkunde.com", "hostname": "mail.deinkunde.com", "dnsConfig": { "rrtypes": ["MX", "TXT"], "matchMode": "contains", "expectedValues": { "MX": ["10 mail.deinkunde.com"], "TXT": ["v=spf1 include:_spf.deinkunde.com ~all"] } } }' ``` ### Option B: Mix different monitor types ```bash # ICMP curl -X POST "$API_BASE_URL/api/icmp-monitors" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "customerId": 123, "name": "Ping: edge-1", "hostname": "edge-1.deinkunde.com" }' # SMTP curl -X POST "$API_BASE_URL/api/smtp-monitors" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "customerId": 123, "name": "SMTP: outbound", "hostname": "smtp.deinkunde.com", "port": 587 }' ``` ## Tips - Use consistent naming conventions (prefix by type: `DNS:`, `Ping:`, `SMTP:`). - Start with conservative intervals and tighten later. ## Reference - [Monitoring Types](/monitoring/monitoring-types/uptime) - [Monitors API](/api/monitors)
### Create a customer + website
URL: https://docs.uptimeify.io/api/examples/create-customer-and-website
Description: This example shows a typical onboarding flow:
Summary: 1. Create a **customer** 2. Create a **website** for that customer 3. (Optional) Attach monitoring configuration > If you prefer the UI: you can create customers and websites in the Admin area. The API is useful for automation and bulk setups. ## Prerequisites - An API token with the required scopes - Your API base URL (e.g. `https://your-instance.tld`) See the API introduction first: - [API Introduction](/api/introduction) ## 1) Create customer API reference: - [Customers API](/api/customers) Example (pseudo request): ```bash curl -X POST "$API_BASE_URL/api/customers" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Acme GmbH", "email": "ops@deinkunde.com" }' ``` Store the returned `customerId`. ## 2) Create website for the customer API reference: - [Websites API](/api/websites) ```bash curl -X POST "$API_BASE_URL/api/websites" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "customerId": 123, "name": "Acme Marketing Website", "url": "https://deinkunde.com" }' ``` Important: - `url` must include the protocol (`https://...`). ## 3) Verify the website API reference: - [Get website](/api/websites/get-website) ```bash curl -X GET "$API_BASE_URL/api/websites/456" \ -H "Authorization: Bearer $TOKEN" ``` ## Next steps - Configure monitoring behavior per website: [Website Configuration API](/api/website-configuration) - Learn which monitor types exist: [Monitoring Types](/monitoring/monitoring-types/uptime)
### Delete a customer including all websites
URL: https://docs.uptimeify.io/api/examples/delete-customer-and-all-websites
Description: This example focuses on a safe, explicit deletion flow.
Summary: Depending on your configuration, deleting a customer may or may not automatically delete related websites. To avoid surprises, we recommend deleting websites explicitly first. ## Prerequisites - An API token with the required scopes ## 1) List websites for the customer API reference: - [List websites](/api/websites/list-websites) Pseudo request: ```bash curl -X GET "$API_BASE_URL/api/websites?customerId=123" \ -H "Authorization: Bearer $TOKEN" ``` Collect all website IDs. ## 2) Delete each website API reference: - [Delete website](/api/websites/delete-website) ```bash curl -X DELETE "$API_BASE_URL/api/websites/456" \ -H "Authorization: Bearer $TOKEN" ``` Repeat for all websites of that customer. ## 3) Delete the customer API reference: - [Customers API](/api/customers) ```bash curl -X DELETE "$API_BASE_URL/api/customers/123" \ -H "Authorization: Bearer $TOKEN" ``` ## Notes - If your backend supports cascade deletes, step 2 might be optional, but doing it explicitly keeps automation predictable.
### Incident Management
URL: https://docs.uptimeify.io/api/incident-management
Description: Public REST API for Uptimeify Incident Management (IM): ingest alerts from your own monitoring systems and manage incidents programmatically.
Summary: Incident Management (IM) is a separate domain from Monitoring: a two-level alert/incident model with teams, on-call schedules, and escalation policies. This section covers the public REST endpoints for ingesting alerts and managing incidents. ## Authentication Every endpoint below requires an **organization-wide** API token: ```bash BASE_URL="https://uptimeify.io" TOKEN="wsm_" ``` Create one via [Create Organization Token](/api/api-tokens/create-organization-token) and leave `customerId` unset. A token created **with** a `customerId` (a customer-scoped token) is always rejected with `403 Forbidden` (`imAccessDenied`); Incident Management has no customer-facing surface, by design. Incident Management must also be **activated** for your organization, or every endpoint below returns `403 Forbidden` (`imNotEnabled`). ## Endpoints - [Events Ingest](./events-ingest): `POST /api/im/events` - [List Incidents](./list-incidents): `GET /api/im/incidents` - [Get Incident](./get-incident): `GET /api/im/incidents/:id` - [Create Incident](./create-incident): `POST /api/im/incidents` - [Acknowledge / Update Incident Status](./acknowledge-incident): `POST /api/im/incidents/:id/status` - [Resolve Incident](./resolve-incident): `POST /api/im/incidents/:id/resolve` - [Teams](./teams): `GET /api/im/teams` - [Schedules](./schedules): `GET/POST /api/im/schedules` - [Schedule Overrides](./schedule-overrides): `GET/POST /api/im/schedules/:id/overrides` - [Who Is On Call](./on-call): `GET /api/im/on-call` ## Alert source setup guides Prefer to send alerts from an existing monitoring tool instead of calling the API directly? See the [alert source setup guides](./alert-sources) for step-by-step instructions for [Zabbix](./alert-sources/zabbix), [Datadog](./alert-sources/datadog), [Grafana Alerting](./alert-sources/grafana-alerting), [Prometheus Alertmanager](./alert-sources/prometheus-alertmanager), [Sentry](./alert-sources/sentry), and any [custom webhook](./alert-sources/custom-webhook). Each uses its own per-source ingest URL (`POST /api/im/ingest/:token`), separate from the Events Ingest endpoint above. ## Error codes See [Error codes and known API pitfalls](/api/error-codes-and-known-pitfalls) for the full list of `data.code` values these endpoints can return.
### Acknowledge / Update Incident Status
URL: https://docs.uptimeify.io/api/incident-management/acknowledge-incident
Description: Moves an Incident Management incident between the open statuses (acknowledged, investigating, identified, monitoring). Set status to acknowledged to acknowledge an incident.
Summary: `POST /api/im/incidents/:id/status` Moves an incident between the "open" statuses: `triggered`, `acknowledged`, `investigating`, `identified`, `monitoring`. Any of the four non-`triggered` statuses is a valid target regardless of the incident's current one (not just the "next" status in sequence). This is also how you **acknowledge** an incident: set `status` to `acknowledged`. This endpoint can never reach `resolved` (use [Resolve Incident](./resolve-incident)) and never `merged`. ## Authentication Same as [List Incidents](./list-incidents): any IM-eligible role (`admin`, `editor`, `responder`), or an organization-wide API token. Incident Management must be enabled for the organization. ## Request Body | Field | Type | Required | Description | |-------|------|----------|--------------| | `status` | string | Yes | Target status. One of `acknowledged`, `investigating`, `identified`, `monitoring`. | ## Example (cURL) Acknowledge an incident: ```bash curl -X POST "$BASE_URL/api/im/incidents/42/status" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "status": "acknowledged" }' ``` ## Response `200 OK`: the updated incident row (same shape as [List Incidents](./list-incidents)' `items`). ```json { "id": 42, "organizationId": 1, "teamId": 3, "title": "Database connection pool exhausted", "customerId": null, "primarySourceId": 7, "severity": "sev1", "severityManual": false, "status": "acknowledged", "mergedIntoId": null, "escalationPolicyId": 5, "currentTier": null, "escalationEpoch": 1, "acknowledgedBy": "u_abc123", "acknowledgedAt": "2026-07-17T09:20:00.000Z", "snoozedUntil": null, "autoResolve": true, "resolvedBy": null, "resolveNote": null, "createdBy": null, "sourceKind": "alert", "triggeredAt": "2026-07-17T09:12:00.000Z", "resolvedAt": null } ``` Transitioning to `acknowledged` additionally cancels any pending escalation jobs for the incident's current escalation cycle and re-arms the ack-timeout reminder. ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` (`imAccessDenied`) when using a customer-scoped token, or a session without an IM-eligible role - `403 Forbidden` (`imNotEnabled`) when Incident Management is not enabled for the organization - `400 Bad Request` (`invalidRequestBody`) when `:id` is not a positive integer, or `status` is missing or not one of the four valid targets - `404 Not Found` (`imIncidentNotFound`) when the incident does not exist, or belongs to another organization - `422 Unprocessable Entity` (`imIncidentInvalidStatusTransition`) when `status` is not a legal target from the incident's current status, e.g. the incident is already `resolved`/`merged`, or `status` equals its current status - `409 Conflict` (`imIncidentStatusConflict`) when the incident's status changed concurrently between the read and the write, safe to retry
### Alert Source Setup Guides
URL: https://docs.uptimeify.io/api/incident-management/alert-sources
Description: Step-by-step guides for wiring Zabbix, Datadog, Grafana Alerting, Prometheus Alertmanager, Sentry, or any custom webhook to Uptimeify Incident Management.
Summary: Each guide below walks through configuring one monitoring/alerting tool to send its alerts to an Uptimeify **alert source**: a per-tool ingest endpoint with a built-in field mapping, so alerts land in Incident Management already normalized (title, severity, status, host) without writing a custom integration. ## Guides - [Zabbix](./zabbix) - [Datadog](./datadog) - [Grafana Alerting](./grafana-alerting) - [Prometheus Alertmanager](./prometheus-alertmanager) - [Sentry](./sentry) - [Custom webhook](./custom-webhook) - [Email](./email) The **Email** guide works differently from the rest: an email source has no JSON webhook payload, so the "How ingestion works" and "Severity mapping" sections below (both webhook-specific) don't apply to it; see that guide directly. ## How ingestion works 1. In the Uptimeify dashboard, go to **Incident Management → Alert Sources → New** and pick the preset matching your tool. Give it a name and, optionally, assign it to a team. 2. Uptimeify generates a unique ingest URL: ``` https:///api/im/ingest/ ``` The `` is shown **once**, immediately after the source is created. Copy it right away. It is never shown again in cleartext. If you lose it, open the source's **Settings** tab and rotate the token to get a fresh URL (also shown only once). 3. Configure your tool (see the tool-specific guide) to send an HTTP `POST` request with a JSON body to that URL whenever it fires or resolves an alert. 4. Uptimeify parses the payload using the preset's field mapping (or your own edited mapping, from the source's **Payload Mapping** tab) and queues it for asynchronous processing into Incident Management. This is a different mechanism from the [Events Ingest](../events-ingest) endpoint (`POST /api/im/events`): that endpoint is authenticated with an organization-wide API token and has no preset field mapping (every request maps through a single auto-provisioned `api`-type source). The per-tool ingest URL above has no separate authentication (the token embedded in the URL **is** the credential, so treat the full URL like a password), but comes with a ready-made mapping for the tool you picked. ## Request limits The same limits apply to every ingest URL, regardless of preset: | Limit | Value | Failure | |-------|-------|---------| | Body size | 256 KB | `413 Payload Too Large` | | JSON nesting depth | 20 levels | `422 Unprocessable Entity` (`payload_too_deep`) | | Body must be valid JSON |, | `422 Unprocessable Entity` (`invalid_json`) | An empty request body is treated as `{}`. ## Response A successful request always returns `202 Accepted` immediately: the alert is queued, not yet processed: ```json { "accepted": true } ``` An unknown, wrong, or rotated-away token returns `404 Not Found` (`not_found`). Every kind of authentication failure returns this same response, by design. It never reveals whether a token used to exist. A `503 Service Unavailable` (`unavailable`) means the alert could not be queued and is safe to retry. ## Severity mapping Every preset except **Custom webhook** (which has no severity selector) maps the vendor's raw severity value onto one of Uptimeify's four incident severities: | Uptimeify severity | Meaning | |---------------------|---------| | `sev1` | Worst / critical | | `sev2` | High | | `sev3` | Warning | | `sev4` | Low / informational | A vendor severity value that isn't in the map, or a missing severity field, falls back to the alert source's own configured default severity, not automatically to `sev4`.
### Custom webhook
URL: https://docs.uptimeify.io/api/incident-management/alert-sources/custom-webhook
Description: Send alerts from any tool that can POST JSON to Uptimeify Incident Management, and define your own field mapping in the dashboard.
Summary: The **Custom webhook** preset is the starting point for any monitoring or alerting tool that doesn't have a dedicated preset: a firewall appliance, an internal script, a niche SaaS product, anything that can send an HTTP `POST` request with a JSON body. Unlike the other presets, it ships with only a minimal default mapping; you fill in the rest yourself once you can see your tool's real payload shape. ## 1. Create the alert source in Uptimeify Go to **Incident Management → Alert Sources → New**, pick the **Custom webhook** preset, name it, and create it. On the success screen, copy the ingest URL (`https:///api/im/ingest/`). It is shown once and never displayed again in cleartext. If you lose it, rotate the token from the source's **Settings** tab. ## 2. Point your tool at the ingest URL Configure your tool's webhook/notification target to send an HTTP `POST` request with a JSON body to the ingest URL, whenever it fires or resolves an alert. There is no fixed request schema on the receiving end. Send whatever your tool produces. ```bash curl -X POST "https:///api/im/ingest/" \ -H "Content-Type: application/json" \ -d '{ "title": "Disk usage above 90% on db-primary", "status": "open" }' ``` ## 3. Adjust the field mapping to your payload The default mapping only extracts a title and a status: reasonable defaults for a generic payload, but almost certainly not everything you want: 1. Send a real (or representative test) payload from your tool once, so you can see its exact field names. The source's **Live activity** view on the alert source detail page shows the most recent raw payloads received. 2. Open the source's **Payload Mapping** tab and edit the selectors: add `severity`, `host`, and `dedupKey` selectors pointing at your payload's actual field names (dot/bracket paths like `data.host.name` or `alerts[0].id` are supported, and `a || b` fallback chains for fields that aren't always present). Add a `severityMap` if your tool sends its own severity vocabulary, so it maps onto Uptimeify's `sev1` to `sev4` scale. 3. Save, then use the **Payload Mapping** tab's test panel to send a sample payload through your edited mapping and confirm the extracted title/severity/status/host look right before relying on it for real alerts. ## Default field mapping | Selector | Normalized field | Notes | |---|---|---| | `title \|\| message` | Title | Tries `title` first, falls back to `message` | | `status` | Status: values in `resolveValues` resolve the alert, anything else keeps it open | Default `resolveValues`: `resolved`, `closed` | | *(none by default)* | Severity | Every alert uses the source's configured default severity until you add a `severity` selector and `severityMap` | | *(none by default)* | Host | Empty until you add a `host` selector | | *(none by default)* | Dedup key | Falls back to a deterministic hash of the title + host until you add a `dedupKey` selector | ## Sample payload This is the shape of payload the default mapping expects out of the box: ```json { "title": "Disk usage above 90% on db-primary", "message": "Disk usage above 90% on db-primary", "status": "open" } ``` You can send this exact payload against your source's mapping from the **Payload Mapping** tab in the dashboard, a good way to confirm the default mapping works before you start customizing selectors for your real tool.
### Datadog
URL: https://docs.uptimeify.io/api/incident-management/alert-sources/datadog
Description: Send Datadog Monitor alerts to Uptimeify Incident Management via a Datadog webhook integration, pre-mapped to title, alert type, and transition state.
Summary: Uptimeify's Datadog preset maps Datadog's Monitor webhook notification payload directly: alert title, alert type, transition state, and hostname are pre-mapped. ## 1. Create the alert source in Uptimeify Go to **Incident Management → Alert Sources → New**, pick the **Datadog** preset, name it, and create it. On the success screen, copy the ingest URL (`https:///api/im/ingest/`). It is shown once and never displayed again in cleartext. If you lose it, rotate the token from the source's **Settings** tab. ## 2. Add a Webhooks integration in Datadog 1. In Datadog, go to **Integrations → Webhooks** and click **New**. 2. Give the webhook a name (for example `uptimeify`) and set the **URL** to the ingest URL you copied in step 1. 3. Set the **Payload** to Datadog's default monitor-notification payload, unchanged, since Uptimeify's preset expects the standard fields (`alert_id`, `alert_title`, `alert_type`, `alert_transition`, `hostname`, …). If you have customized the payload template on this webhook before, reset it to Datadog's default, or edit your Uptimeify source's **Payload Mapping** tab to match your custom fields instead. 4. Save the webhook. ## 3. Notify the webhook from your monitors Datadog only calls a webhook integration when a monitor's notification message references it explicitly: 1. Open (or create) a Datadog **Monitor**. 2. In the **Notify your team** section of the monitor's message, add `@webhook-uptimeify` (replace `uptimeify` with whatever name you gave the webhook in step 2). 3. Save the monitor. Datadog now calls the webhook on every state transition (Triggered, Warn, Recovered, …) the monitor's notification rules cover. Repeat steps 2 and 3 for every monitor you want to appear in Uptimeify, or add `@webhook-uptimeify` to a notification default your monitors already share. ## Default field mapping | Datadog field | Normalized field | |---|---| | `alert_title` | Title | | `alert_type` | Severity (via the map below) | | `alert_transition` | Status: `Recovered` resolves the alert, anything else keeps it open | | `hostname` | Host | | `alert_id` | Dedup key: groups notifications for the same monitor event | ### Severity map | Datadog `alert_type` | Uptimeify severity | |---|---| | `error` | `sev1` | | `warning` | `sev3` | | `success` | `sev4` | | `info` | `sev4` | ## Sample payload This is the shape of payload Uptimeify's Datadog preset expects: ```json { "id": "4707056510925961466", "alert_id": "1234567", "alert_title": "[Triggered] High CPU usage on host web-01", "alert_type": "error", "alert_transition": "Triggered", "alert_status": "Alert", "alert_metric": "system.cpu.user", "date": "1752739200000", "event_type": "metric_alert_monitor", "hostname": "web-01.prod.example.com", "org": { "id": "123456", "name": "Example Org" }, "priority": "normal", "tags": "env:prod,service:web", "title": "[Triggered] High CPU usage on host web-01", "url": "https://app.datadoghq.com/event/event?id=4707056510925961466" } ``` You can send this exact payload against your source's mapping from the **Payload Mapping** tab in the dashboard to verify the setup before wiring up a real monitor.
### Email
URL: https://docs.uptimeify.io/api/incident-management/alert-sources/email
Description: Forward or route alert emails from any monitoring tool, ticketing system, or mailbox to a dedicated inbound address, and turn them into incidents automatically.
Summary: The **Email** alert source turns emails you forward or route to a dedicated inbound address into incidents. Use it for any monitoring or alerting tool that can only notify by email (no webhook option), or for anywhere alerts already land in an inbox: a shared ops mailbox, a legacy paging system, a vendor that only offers email notifications. Unlike the webhook presets on this section's other pages, an Email source has no JSON payload. It parses the email's subject and plain-text body with its own selector language (below), not the dot/bracket `Selector` paths those guides use. ## 1. Create the alert source in Uptimeify Go to **Incident Management → Alert Sources → New** and pick the **Email** option (alongside the webhook presets), name it, and create it. On the success screen, copy the inbound address. It has the shape `@alerts.uptimeify.io` and is shown once, never displayed again in cleartext. If you lose it, rotate the token from the source's **Settings** tab to get a fresh address. ## 2. Point your alerts at the address There is no fixed sender or vendor. Anything that can deliver a plain-text email works: - Set the address as the notification/contact target in a monitoring tool that only supports email alerting. - Add a forwarding rule in your mailbox (Gmail, Outlook, a shared ops alias, …) that forwards matching alert emails to it. - Point an existing mail-routing rule or relay at it, if alerts already flow through one. Only the **plain-text body** is read. An HTML-only email with no plain-text part resolves to an empty body for selector purposes. The subject is still available. ## Default field mapping (zero configuration) A freshly created Email source works with no further setup, using these defaults: | Field | Default source | Notes | |---|---|---| | Title | Subject header, verbatim | | | Status | `open`, unless the subject contains the whole word `resolved`, `recovered`, or `cleared` (case-insensitive) | Only the **subject** is scanned for these, never the body, so a firing alert's body prose (e.g. "checks are not ok") can never be mistaken for a resolution | | Severity | The source's configured default severity | Every email uses it until you add a `severity` selector + `severityMap` | | Host | Empty | Until you add a `host` selector | | Dedup key | A deterministic hash of the title + sender address | Until you add a `dedupKey` selector | ## The email selector language Email selectors are a small, separate syntax from the JSON `Selector` paths used by webhook sources. An email has no JSON structure to walk: | Selector | Resolves to | |---|---| | `subject` | The email's Subject header, verbatim | | `body` | The plain-text body, verbatim | | `body.line(N)` | The Nth line of the plain-text body, 1-indexed (`body.line(1)` is the first line) | | `subject =~ /pattern/` | Regex match against the subject: the first capturing group `(...)` if the pattern has one, otherwise the whole match | | `body =~ /pattern/` | Same, matched against the plain-text body | A selector may chain fallbacks with `||`, same convention as the webhook `Selector` paths: the first candidate that resolves to a non-empty value wins: ``` subject =~ /Status: (\w+)/ || body.line(1) ``` A missing field, an out-of-range line, or an invalid/non-matching regex all resolve to nothing rather than erroring. A malformed or unexpected email is normal, reachable input, not a bug. ## Customizing the mapping There is currently no dedicated mapping editor in the dashboard for Email sources (the **Payload Mapping** tab's selector editor targets the JSON webhook shape). To configure selectors, use the API: 1. `GET /api/im/sources/:id` to read the source's current `payloadMapping`. You need the FULL object back, not just the part you're changing (see the warning below). 2. `PATCH /api/im/sources/:id` with the complete `payloadMapping`, adding or editing an `emailSelectors` key inside it: ```bash curl -X PATCH "https:///api/im/sources/" \ -H "Co…
### Grafana Alerting
URL: https://docs.uptimeify.io/api/incident-management/alert-sources/grafana-alerting
Description: Send Grafana Alerting notifications to Uptimeify Incident Management via a Webhook contact point, mapped from the alert group's first alert.
Summary: Uptimeify's Grafana preset maps Grafana Alerting's webhook contact-point payload directly: the alert name, severity label, status, and instance are pre-mapped from the first alert in the notification group. ## 1. Create the alert source in Uptimeify Go to **Incident Management → Alert Sources → New**, pick the **Grafana** preset, name it, and create it. On the success screen, copy the ingest URL (`https:///api/im/ingest/`). It is shown once and never displayed again in cleartext. If you lose it, rotate the token from the source's **Settings** tab. ## 2. Add a Webhook contact point in Grafana 1. In Grafana, go to **Alerting → Contact points** and click **Add contact point**. 2. Give it a name (for example `uptimeify`) and set **Integration** to **Webhook**. 3. Set the **URL** to the ingest URL you copied in step 1, and leave the **HTTP method** as `POST`. 4. Leave the payload as Grafana's default webhook body. Uptimeify's preset reads the standard fields (`alerts[]`, `status`, `commonLabels`, …) it already sends. Save the contact point. ## 3. Route alerts to the contact point A contact point only receives notifications once a **notification policy** routes to it: 1. Go to **Alerting → Notification policies**. 2. Either point the **default policy** at your new contact point (every alert routes there), or add a nested policy with a label matcher (for example `severity =~ ".+"`) that routes matching alerts to it. 3. Save. Grafana now calls the webhook whenever an alert rule matching that policy fires or resolves. ## Default field mapping Grafana groups alerts into one notification; the preset reads all of these from the **first alert** (`alerts[0]`) in that group, falling back to the group-level `title` for the title field: | Grafana field | Normalized field | |---|---| | `alerts[0].labels.alertname` (falls back to the top-level `title`) | Title | | `alerts[0].labels.severity` | Severity (via the map below) | | `alerts[0].status` | Status: `resolved` resolves the alert, anything else (typically `firing`) keeps it open | | `alerts[0].labels.instance` | Host | | `alerts[0].fingerprint` | Dedup key: Grafana's own stable per-alert identity | ### Severity map Grafana has no built-in `severity` label. This only works if your alert rules set one (for example via a `severity` label on the rule): | Grafana `severity` label | Uptimeify severity | |---|---| | `critical` | `sev1` | | `high` | `sev2` | | `warning` | `sev3` | | `info` | `sev4` | If your alert rules don't set a `severity` label, every alert falls back to the source's configured default severity. ## Sample payload This is the shape of payload Uptimeify's Grafana preset expects: ```json { "receiver": "uptimeify-webhook", "status": "firing", "orgId": 1, "alerts": [ { "status": "firing", "labels": { "alertname": "HighCPU", "severity": "critical", "instance": "server1:9090" }, "annotations": { "summary": "CPU usage above 90% on server1" }, "startsAt": "2026-07-17T12:00:00Z", "endsAt": "0001-01-01T00:00:00Z", "generatorURL": "https://grafana.example.com/alerting/grafana/abc123/view", "fingerprint": "8f3b1c9a2d4e5f60", "silenceURL": "https://grafana.example.com/alerting/silence/new", "dashboardURL": "https://grafana.example.com/d/abc123", "panelURL": "https://grafana.example.com/d/abc123?viewPanel=2" } ], "groupLabels": { "alertname": "HighCPU" }, "commonLabels": { "alertname": "HighCPU", "severity": "critical" }, "commonAnnotations": { "summary": "CPU usage above 90% on server1" }, "externalURL": "https://grafana.example.com/", "version": "1", "groupKey": "{}/{alertname=\"HighCPU\"}", "truncatedAlerts": 0, "title": "[FIRING:1] HighCPU", "state": "alerting", "message": "CPU usage above 90% on server1" } ``` You can send this exact payload against your source's mapping from the **Payload Mapping** tab in the dashboard to verify the setup before wiring up a real notification policy.
### Prometheus Alertmanager
URL: https://docs.uptimeify.io/api/incident-management/alert-sources/prometheus-alertmanager
Description: Send Prometheus Alertmanager notifications to Uptimeify Incident Management via a webhook receiver, mapped from commonLabels and the first alert.
Summary: Uptimeify's Alertmanager preset maps Alertmanager's `webhook_config` notification payload directly: the summary/alertname, severity label, status, and instance are pre-mapped. ## 1. Create the alert source in Uptimeify Go to **Incident Management → Alert Sources → New**, pick the **Prometheus Alertmanager** preset, name it, and create it. On the success screen, copy the ingest URL (`https:///api/im/ingest/`). It is shown once and never displayed again in cleartext. If you lose it, rotate the token from the source's **Settings** tab. ## 2. Add a webhook receiver in Alertmanager Add a receiver to your `alertmanager.yml` pointing at the ingest URL: ```yaml receivers: - name: uptimeify webhook_configs: - url: "https:///api/im/ingest/" send_resolved: true ``` `send_resolved: true` is required. Without it, Alertmanager never calls the webhook when an alert resolves, so incidents in Uptimeify would stay open forever even after the underlying problem clears. ## 3. Route alerts to the receiver Reference the receiver from a route in the same file (either as the default route, or a nested route matching specific labels): ```yaml route: receiver: uptimeify # ...existing routing tree, or a nested `routes:` matcher instead of the default ``` Reload or restart Alertmanager to pick up the config change (`amtool check-config` first is a good sanity check). ## Default field mapping Alertmanager groups multiple firing alerts into one notification; the preset reads the title and instance from the **first alert** (`alerts[0]`) in that group, and severity from the group-level `commonLabels`: | Alertmanager field | Normalized field | |---|---| | `alerts[0].annotations.summary` (falls back to `commonLabels.alertname`) | Title | | `commonLabels.severity` | Severity (via the map below) | | `status` (top-level) | Status: `resolved` resolves the alert, anything else (typically `firing`) keeps it open | | `alerts[0].labels.instance` | Host | | `alerts[0].fingerprint` | Dedup key: Alertmanager's own stable per-alert identity | ### Severity map This only works if your alert rules set a `severity` label: | Alertmanager `severity` label | Uptimeify severity | |---|---| | `critical` | `sev1` | | `warning` | `sev3` | | `info` | `sev4` | Any other value, or a missing `severity` label, falls back to the source's configured default severity. There is no `sev2` mapping for Alertmanager, since its own severity convention is typically just `critical`/`warning`/`info`. ## Sample payload This is the shape of payload Uptimeify's Alertmanager preset expects: ```json { "version": "4", "groupKey": "{}:{alertname=\"InstanceDown\"}", "truncatedAlerts": 0, "status": "firing", "receiver": "uptimeify-webhook", "groupLabels": { "alertname": "InstanceDown" }, "commonLabels": { "alertname": "InstanceDown", "severity": "critical", "job": "node" }, "commonAnnotations": { "summary": "Instance 10.0.0.5:9100 down" }, "externalURL": "http://alertmanager.example.com:9093", "alerts": [ { "status": "firing", "labels": { "alertname": "InstanceDown", "severity": "critical", "instance": "10.0.0.5:9100", "job": "node" }, "annotations": { "summary": "Instance 10.0.0.5:9100 down", "description": "10.0.0.5:9100 has been down for more than 5 minutes." }, "startsAt": "2026-07-17T12:00:00.000Z", "endsAt": "0001-01-01T00:00:00Z", "generatorURL": "http://prometheus.example.com:9090/graph?g0.expr=up%7Bjob%3D%22node%22%7D+%3D%3D+0", "fingerprint": "5b6e6e8f7a1c9d3e" } ] } ``` You can send this exact payload against your source's mapping from the **Payload Mapping** tab in the dashboard to verify the setup before reloading Alertmanager with the real receiver.
### Sentry
URL: https://docs.uptimeify.io/api/incident-management/alert-sources/sentry
Description: Send Sentry issue alerts to Uptimeify Incident Management via an internal integration webhook, mapped from the event's level, message, and environment.
Summary: Uptimeify's Sentry preset maps Sentry's issue-alert webhook payload directly: the event message/culprit, level, action, and server/environment are pre-mapped. ## 1. Create the alert source in Uptimeify Go to **Incident Management → Alert Sources → New**, pick the **Sentry** preset, name it, and create it. On the success screen, copy the ingest URL (`https:///api/im/ingest/`). It is shown once and never displayed again in cleartext. If you lose it, rotate the token from the source's **Settings** tab. ## 2. Create an internal integration in Sentry Sentry sends issue-alert webhooks through an **internal integration**, not a plain webhook URL field: 1. In Sentry, go to **Settings → Developer Settings → New Internal Integration**. 2. Give it a name (for example `Uptimeify`). 3. Enable the **Alert Rule Action** toggle, and under **Webhooks** set the **Webhook URL** to the ingest URL you copied in step 1. 4. Save. This creates an alert-rule action you can attach to any alert rule in the organization. ## 3. Attach it to an alert rule 1. Go to **Alerts → Rules**, and create a new rule (or edit an existing one) for the project(s) you want to forward. 2. Under **Then perform these actions**, add **Send a notification via an integration** and select the internal integration you created in step 2. 3. Save the rule. Sentry now calls the webhook whenever the rule's conditions match, for both newly triggered issues and, if the rule includes a resolve condition, resolutions. ## Default field mapping | Sentry field | Normalized field | |---|---| | `data.event.message` (falls back to `data.event.culprit`) | Title | | `data.event.level` | Severity (via the map below) | | `action` (top-level) | Status: `resolved` resolves the alert, anything else (`triggered`, `created`, `ignored`, `assigned`, …) keeps it open | | `data.event.server_name` (falls back to `data.event.environment`) | Host | | `data.event.issue_id` | Dedup key: groups all notifications for the same Sentry issue | ### Severity map | Sentry `level` | Uptimeify severity | |---|---| | `fatal` | `sev1` | | `error` | `sev2` | | `warning` | `sev3` | | `info` | `sev4` | | `debug` | `sev4` | ## Sample payload This is the shape of payload Uptimeify's Sentry preset expects: ```json { "action": "triggered", "data": { "event": { "event_id": "fe208ee2e2e74ae08b3d4b7cdb9b4e3d", "issue_id": "123456789", "level": "error", "culprit": "raven.scripts.runner in main", "message": "This is an example python exception", "platform": "python", "environment": "production", "server_name": "web-01.prod.example.com", "web_url": "https://sentry.io/organizations/example/issues/123456789/", "issue_url": "https://sentry.io/api/0/issues/123456789/" } }, "actor": { "type": "application", "id": "sentry", "name": "Sentry" } } ``` You can send this exact payload against your source's mapping from the **Payload Mapping** tab in the dashboard to verify the setup before wiring up a real alert rule.
### Zabbix
URL: https://docs.uptimeify.io/api/incident-management/alert-sources/zabbix
Description: Send Zabbix trigger alerts to Uptimeify Incident Management via a Webhook media type, using the ready-made media type Uptimeify generates for you.
Summary: Uptimeify's Zabbix preset maps a Zabbix **Webhook** media type's payload directly: trigger name, severity, status, and host are pre-mapped. Uptimeify also gives you a ready-made media type you can import into Zabbix, so you do not have to create it by hand or write the JavaScript yourself. ## 1. Create the alert source in Uptimeify Go to **Incident Management → Alert Sources → New**, pick the **Zabbix** preset, name it, and create it. On the success screen: - Copy the ingest URL (`https:///api/im/ingest/`), shown once. - Use the **download media type** action to save `uptimeify-zabbix-media-type.yaml`. ## 2. Import the media type into Zabbix 1. In Zabbix, go to **Alerts → Media types** (Zabbix 7.x; **Administration → Media types** on 6.x and older) and click **Import**. 2. Select the downloaded `uptimeify-zabbix-media-type.yaml` and confirm. A media type named **Uptimeify** appears. 3. Give it the ingest URL. Either define the global macro `{$UPTIMEIFY.INGEST.URL}` under **Administration → Macros** with the URL from step 1, or open the media type and paste the URL straight into its `ingest_url` parameter. 4. Enable the media type. The ingest URL must be a media type **parameter**. Zabbix resolves macros in parameter values only, never inside the script body, a `{$UPTIMEIFY.INGEST.URL}` written into the JavaScript would be POSTed to as a literal string and no alert would ever arrive. ### What the media type sends The imported media type declares one parameter per field the preset maps, so the payload matches the preset's selectors without any further editing: | Parameter | Zabbix macro | Used for | |---|---|---| | `ingest_url` | `{$UPTIMEIFY.INGEST.URL}` | Where the alert is POSTed | | `event_id` | `{EVENT.ID}` | Dedup key, stays the problem event's id in the recovery message, which pairs the two into one incident | | `event_severity` | `{EVENT.SEVERITY}` | Severity (see map below) | | `trigger_name` | `{EVENT.NAME}` | Title | | `trigger_status` | `{EVENT.STATUS}` | `RESOLVED` resolves the alert, anything else keeps it open | | `host_name` | `{HOST.NAME}` | Host | | `event_source`, `event_value`, `date`, `time` | respective macros | Carried through for context | The script treats HTTP 200/202 as success and throws on anything else, so a failed delivery shows up as a failed alert attempt in Zabbix instead of disappearing silently. A `404` there means the token in your ingest URL matches no alert source. ### Testing the media type Zabbix's **Test** button on a media type passes every parameter through **literally, it does not resolve macros**. If you test straight after importing, `ingest_url` arrives as the string `{$UPTIMEIFY.INGEST.URL}` and the attempt fails with `cannot get URL: URL rejected: Bad hostname`. That is expected, not a misconfiguration. To test properly, replace the `ingest_url` value in the test dialog with your real ingest URL. The other parameters can stay as they are, the preset only needs `trigger_name` and `trigger_status` to produce a usable alert, and the dialog lets you type real values for those too. ## 3. Wire it up to a trigger action 1. Add the new media type to a Zabbix user under **Users → Users → *(user)* → Media**. Zabbix requires a "Send to" address value even for a webhook. Any non-empty value works, the script ignores it. 2. Create or edit a **Trigger action** under **Data collection → Actions → Trigger actions**, with an operation that sends a message to that user via your new media type. 3. Add the **same media type under that action's "Recovery operations"**, not only under "Operations". This is the step that closes incidents: an action with no recovery operation sends the problem and never the recovery, so the Uptimeify incident stays triggered until somebody resolves it by hand. **Reports → Action log** shows whether recovery messages actually went out. 4. Trigger a test problem (or wait for the next real one) and confirm it shows up un…
### Create Incident
URL: https://docs.uptimeify.io/api/incident-management/create-incident
Description: Manually creates an Incident Management incident for a team; escalation arms moments after creation, with the same paging behavior as an alert-sourced incident.
Summary: `POST /api/im/incidents` Creates an incident by hand, outside the alert-ingest pipeline, e.g. for something a human noticed before any monitoring tool did. A manually-created incident is not a lesser incident: escalation tier 1 is armed moments after creation, exactly as it would be for an incident created from an ingested alert. But that arming happens asynchronously after this response is returned, so this response itself reflects the incident row as it was committed, before escalation starts (see `currentTier` below). ## Authentication Requires the same base IM access as every endpoint in this API (an IM-eligible `admin`, `editor`, or `responder` role, or an organization-wide API token; Incident Management must be enabled for the organization). Creating an incident additionally requires the **write bar** for the target team: your role must be `admin` (organization-level), or you must be a *team admin* (an `im_team_member` of that team with `imRole: 'admin'`). An organization-wide API token satisfies the organization-admin bar, since it authenticates as a synthetic `admin` role. ## Request Body | Field | Type | Required | Description | |-------|------|----------|--------------| | `teamId` | number | Yes | The team that owns the incident. Must belong to your organization. | | `title` | string | Yes | Incident title, up to 10,000 characters. | | `severity` | string | Yes | One of `sev1`, `sev2`, `sev3`, `sev4`. Set explicitly: a manual incident has no alert stream to derive severity from, so it is always `severityManual: true`. | | `customerId` | number | No | Scopes the incident to a customer. Must belong to your organization if given. | | `escalationPolicyId` | number | No | Escalation policy to use. Must belong to your organization if given. Omitted (or `null`) resolves to the team's default policy (`im_escalation_policy` with `isDefault: true`), or `null` if the team has none. It never silently falls through to "no policy" when the team has one configured. | ## Example (cURL) ```bash curl -X POST "$BASE_URL/api/im/incidents" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "teamId": 3, "title": "Database connection pool exhausted", "severity": "sev1" }' ``` ## Response `200 OK`: the newly created incident row (same shape as [List Incidents](./list-incidents)' `items`). ```json { "id": 42, "organizationId": 1, "teamId": 3, "title": "Database connection pool exhausted", "customerId": null, "primarySourceId": null, "severity": "sev1", "severityManual": true, "status": "triggered", "mergedIntoId": null, "escalationPolicyId": 5, "currentTier": null, "escalationEpoch": 0, "acknowledgedBy": null, "acknowledgedAt": null, "snoozedUntil": null, "autoResolve": true, "resolvedBy": null, "resolveNote": null, "createdBy": "u_abc123", "sourceKind": "manual", "triggeredAt": "2026-07-17T09:12:00.000Z", "resolvedAt": null } ``` `sourceKind` is `manual` and `primarySourceId` is `null`: a manually-created incident has no backing alert source. Arming the initial escalation and any outbound-integration fan-out happen best-effort after the incident row is committed: the incident is guaranteed to exist even if paging or an outbound integration fails, but a failure there is logged server-side, not surfaced in this response. Because escalation arms after this response is returned, `currentTier` is always `null` in the create response. Poll [List Incidents](./list-incidents) or [Get Incident](./get-incident) to see the tier once escalation has started. ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` (`imAccessDenied`) when using a customer-scoped token, or a session without an IM-eligible role - `403 Forbidden` (`imNotEnabled`) when Incident Management is not enabled for the organization - `403 Forbidden` (`imTeamWriteDenied`) when your role is not `admin` and you are not a team admin of `teamId` - `400 Bad Request` (`invalidRequestBody`) when `teamId` is missing or not a positive integ…
### Events Ingest
URL: https://docs.uptimeify.io/api/incident-management/events-ingest
Description: Public alert-ingest endpoint for Incident Management. Post any JSON alert payload from your own monitoring or alerting system; it is queued and processed asynchronously into an incident.
Summary: `POST /api/im/events` Accepts an arbitrary JSON alert payload from your own monitoring or alerting system and queues it for asynchronous processing into Incident Management. This is the primary integration endpoint for pushing alerts from third-party systems. ## Authentication Requires an **organization-wide** API token (`Authorization: Bearer wsm_...`, created without a `customerId`, see [Create Organization Token](/api/api-tokens/create-organization-token)). A customer-scoped token is rejected with `403 Forbidden` (`imAccessDenied`). Incident Management must also be activated for the organization, or the request is rejected with `403 Forbidden` (`imNotEnabled`). ## Rate limit 600 requests per minute per organization, not per IP. A monitoring system that fans out across many source IPs shares one quota with its organization. ## Request Body Any JSON object. The payload is stored against an `api`-type alert source that is auto-provisioned for your organization the first time you call this endpoint, and mapped into an alert using that source's field mapping. There is no fixed request schema. Send whatever your alerting system produces. Limits, enforced before the payload is queued: | Limit | Value | Failure | |-------|-------|---------| | Body size | 256 KB | `413 Payload Too Large` (`payloadTooLarge`) | | JSON nesting depth | 20 levels | `422 Payload too deep` (`payloadTooDeep`) | | Body must be valid JSON |, | `422 Invalid JSON` (`invalidJson`) | An empty request body is treated as `{}`. ## Example (cURL) ```bash curl -X POST "$BASE_URL/api/im/events" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "title": "Database connection pool exhausted", "severity": "critical", "host": "db-primary-01" }' ``` ## Response `202 Accepted`: the alert has been queued, not yet processed. There is no synchronous incident or alert ID in the response. ```json { "accepted": true } ``` ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` (`imAccessDenied`) when using a customer-scoped token, or a session without an IM-eligible role - `403 Forbidden` (`imNotEnabled`) when Incident Management is not enabled for the organization - `413 Payload Too Large` (`payloadTooLarge`) when the body exceeds 256 KB - `422 Invalid JSON` (`invalidJson`) when the body is not valid JSON - `422 Payload too deep` (`payloadTooDeep`) when the parsed JSON nests more than 20 levels - `429 Too Many Requests` when your organization exceeds 600 requests/minute (see `retryAfter` in the response body) - `503 Service Unavailable` (`unavailable`) when the alert could not be queued, safe to retry
### Get Incident
URL: https://docs.uptimeify.io/api/incident-management/get-incident
Description: Returns the full detail of a single Incident Management incident: the incident row plus its alerts, timeline events, and role assignments.
Summary: `GET /api/im/incidents/:id` Returns the full detail of a single incident: the incident row plus its alerts (a safe, redacted view, see below), timeline events in chronological order, and role assignments. ## Authentication Same as [List Incidents](./list-incidents): any IM-eligible role (`admin`, `editor`, `responder`), or an organization-wide API token. Incident Management must be enabled for the organization. ## Example (cURL) ```bash curl -X GET "$BASE_URL/api/im/incidents/42" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Response ```json { "id": 42, "organizationId": 1, "teamId": 3, "title": "Database connection pool exhausted", "customerId": null, "primarySourceId": 7, "severity": "sev1", "severityManual": false, "status": "triggered", "mergedIntoId": null, "escalationPolicyId": 5, "currentTier": 1, "escalationEpoch": 0, "acknowledgedBy": null, "acknowledgedAt": null, "snoozedUntil": null, "autoResolve": true, "resolvedBy": null, "resolveNote": null, "createdBy": null, "sourceKind": "alert", "triggeredAt": "2026-07-17T09:12:00.000Z", "resolvedAt": null, "alerts": [ { "id": 101, "organizationId": 1, "sourceId": 7, "dedupKey": "db-primary-01:pool-exhausted", "status": "open", "suppressedReason": null, "title": "Database connection pool exhausted", "severity": "sev1", "host": "db-primary-01", "mappedFields": {}, "duplicateCount": 0, "firstSeenAt": "2026-07-17T09:12:00.000Z", "resolvedAt": null, "incidentId": 42 } ], "events": [ { "id": 501, "incidentId": 42, "at": "2026-07-17T09:12:00.000Z", "kind": "alert_received", "actorUserId": null, "payload": {} } ], "assignments": [ { "id": 12, "userId": "u_abc123", "userName": "Jane Doe", "role": "commander", "createdAt": "2026-07-17T09:15:00.000Z" } ] } ``` `alerts[].rawPayload` is never returned by this endpoint: the raw webhook/API payload can carry API keys or other secrets its source's operator put in it. It is also nulled server-side 90 days after ingest by a retention job. `alerts[].mappedFields` is the already-normalized, safe view of the same alert and survives retention. **Retention, in full**: alert payloads (`rawPayload`) are nulled 90 days after ingest; resolved alerts are deleted 90 days after ingest; outbound-delivery (integration forwarding) records are deleted 90 days after they were sent. The incident itself and its timeline events (`events`) are kept in full for 12 months after the incident triggered; after that only a daily aggregate survives (incident counts plus acknowledge and resolve times, per team and severity), for 24 months total. Per-source daily aggregates (alert/dedup/incident counts, as shown on a source's Analytics tab) are retained indefinitely. Deleting the underlying alert rows does not erase that history. ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` (`imAccessDenied`) when using a customer-scoped token, or a session without an IM-eligible role - `403 Forbidden` (`imNotEnabled`) when Incident Management is not enabled for the organization - `400 Bad Request` (`invalidRequestBody`) when `:id` is not a positive integer - `404 Not Found` (`imIncidentNotFound`) when the incident does not exist, or belongs to another organization
### List Incidents
URL: https://docs.uptimeify.io/api/incident-management/list-incidents
Description: Returns a filtered, cursor-paginated list of Incident Management incidents for your organization.
Summary: `GET /api/im/incidents` Returns a filtered, cursor-paginated list of incidents for your organization. Only `im_incident` fields are returned. Alerts, timeline events, and assignments are available on the [Get Incident](./get-incident) endpoint. ## Authentication Requires a session or API token with IM access (`admin`, `editor`, or `responder` role; an organization-wide API token also works). Incident Management must be enabled for the organization. ## Query Parameters | Parameter | Type | Description | |-----------|------|--------------| | `status` | string, repeatable | Filter by status. One or more of `triggered`, `acknowledged`, `investigating`, `identified`, `monitoring`, `resolved`, `merged`. | | `severity` | string, repeatable | Filter by severity. One or more of `sev1`, `sev2`, `sev3`, `sev4`. | | `teamId` | number, repeatable | Filter by owning team ID. | | `customerId` | number, repeatable | Filter by the incident's associated customer ID. | | `sourceId` | number, repeatable | Filter by primary alert source ID. | | `assignedUserId` | string | Only incidents with an active assignment (any role) for this user ID. | | `onCall` | boolean (`true`/`false`/`1`/`0`) | `true` restricts to incidents owned by a team you are currently on-call for. If you are on-call for no team, returns an empty page. `false` or omitted applies no on-call filtering. | | `from` | ISO 8601 date-time | Only incidents triggered at or after this time. | | `to` | ISO 8601 date-time | Only incidents triggered at or before this time. | | `q` | string | Full-text search on the incident title. Whole-word matching only, no prefix search. | | `limit` | number | Page size. Default 50, max 100. | | `cursor` | string | Continuation token from a previous response's `nextCursor`. Treat as opaque: round-trip it exactly, do not construct one yourself. | A repeatable parameter accepts a repeated query key, e.g. `status=triggered&status=acknowledged`. ## Example (cURL) ```bash curl -X GET "$BASE_URL/api/im/incidents?status=triggered&status=acknowledged&severity=sev1&limit=25" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Response ```json { "items": [ { "id": 42, "organizationId": 1, "teamId": 3, "title": "Database connection pool exhausted", "customerId": null, "primarySourceId": 7, "severity": "sev1", "severityManual": false, "status": "triggered", "mergedIntoId": null, "escalationPolicyId": 5, "currentTier": 1, "escalationEpoch": 0, "acknowledgedBy": null, "acknowledgedAt": null, "snoozedUntil": null, "autoResolve": true, "resolvedBy": null, "resolveNote": null, "createdBy": null, "sourceKind": "alert", "triggeredAt": "2026-07-17T09:12:00.000Z", "resolvedAt": null } ], "nextCursor": "1752743520000:42" } ``` `nextCursor` is `null` on the last page. Pass it back as the `cursor` query parameter to fetch the next page. ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` (`imAccessDenied`) when using a customer-scoped token, or a session without an IM-eligible role - `403 Forbidden` (`imNotEnabled`) when Incident Management is not enabled for the organization - `400 Bad Request` (`invalidRequestBody`) when a filter value is invalid, e.g. an unknown `status`/`severity`, a non-numeric `teamId`/`customerId`/`sourceId`, an unparsable `from`/`to`/`cursor`, or an out-of-range `limit`
### Who Is On Call
URL: https://docs.uptimeify.io/api/incident-management/on-call
Description: Returns who is on call right now, one entry per schedule in your organization.
Summary: `GET /api/im/on-call` Returns who is on call **right now**: one entry per [schedule](./schedules) in your organization. This is a timestamp lookup against the materialized shifts (`im_schedule_shift`), the same table the escalation engine reads to decide who to page; it does not recompute rotation math on the fly. ## Authentication Requires the base IM access every endpoint in this API needs (an IM-eligible `admin`, `editor`, or `responder` role, or an organization-wide API token; Incident Management must be enabled for the organization). ## Example (cURL) ```bash curl -X GET "$BASE_URL/api/im/on-call" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Response `200 OK`: an array, one entry per schedule that currently has coverage. A schedule with no one covering right now (a gap) is simply absent from the array. It is not returned as `null` or an error. ```json [ { "teamId": 3, "scheduleId": 7, "userId": "u_abc123", "userName": "Jane Doe", "shiftEndsAt": "2026-07-18T09:00:00.000Z" } ] ``` When a schedule has multiple overlapping rotation layers, the highest layer covering right now wins, by the same "higher layer covers lower" rule the escalation engine itself resolves with, so this endpoint never disagrees with who actually gets paged. `shiftEndsAt` is when the current shift (or override) ends, i.e. the next handover for that schedule. ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` (`imAccessDenied`) when using a customer-scoped token, or a session without an IM-eligible role - `403 Forbidden` (`imNotEnabled`) when Incident Management is not enabled for the organization
### Resolve Incident
URL: https://docs.uptimeify.io/api/incident-management/resolve-incident
Description: Resolves an Incident Management incident from any non-closed status. This is the only endpoint that can move an incident to resolved.
Summary: `POST /api/im/incidents/:id/resolve` Resolves an incident from any non-closed status (`triggered`, `acknowledged`, `investigating`, `identified`, `monitoring`). This is the only endpoint that can move an incident to `resolved`. [Acknowledge / Update Incident Status](./acknowledge-incident) deliberately rejects `resolved` as a target. ## Authentication Same as [List Incidents](./list-incidents): any IM-eligible role (`admin`, `editor`, `responder`), or an organization-wide API token. Incident Management must be enabled for the organization. ## Request Body | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `note` | string | No | `null` | Optional free-text resolution note, up to 10,000 characters. | ## Example (cURL) ```bash curl -X POST "$BASE_URL/api/im/incidents/42/resolve" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "note": "Restarted the connection pool, monitoring for recurrence." }' ``` ## Response `200 OK`: the updated incident row (same shape as [List Incidents](./list-incidents)' `items`). ```json { "id": 42, "organizationId": 1, "teamId": 3, "title": "Database connection pool exhausted", "customerId": null, "primarySourceId": 7, "severity": "sev1", "severityManual": false, "status": "resolved", "mergedIntoId": null, "escalationPolicyId": 5, "currentTier": null, "escalationEpoch": 1, "acknowledgedBy": "u_abc123", "acknowledgedAt": "2026-07-17T09:20:00.000Z", "snoozedUntil": null, "autoResolve": true, "resolvedBy": "u_abc123", "resolveNote": "Restarted the connection pool, monitoring for recurrence.", "createdBy": null, "sourceKind": "alert", "triggeredAt": "2026-07-17T09:12:00.000Z", "resolvedAt": "2026-07-17T09:45:00.000Z" } ``` Resolving also cancels any pending escalation jobs for the incident's current escalation cycle. ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` (`imAccessDenied`) when using a customer-scoped token, or a session without an IM-eligible role - `403 Forbidden` (`imNotEnabled`) when Incident Management is not enabled for the organization - `400 Bad Request` (`invalidRequestBody`) when `:id` is not a positive integer, or `note` is not a string or exceeds 10,000 characters - `404 Not Found` (`imIncidentNotFound`) when the incident does not exist, or belongs to another organization - `422 Unprocessable Entity` (`imIncidentAlreadyClosed`) when the incident is already `resolved` or `merged` - `409 Conflict` (`imIncidentStatusConflict`) when the incident was closed concurrently between the read and the write, safe to retry
### Schedule Overrides
URL: https://docs.uptimeify.io/api/incident-management/schedule-overrides
Description: List and add on-call schedule overrides: one-off \"X covers for Y\" windows on top of a schedule's regular rotation.
Summary: `GET /api/im/schedules/:id/overrides` · `POST /api/im/schedules/:id/overrides` An **override** is a one-off "X covers for Y" window on a [schedule](./schedules): a user substituted in for a fixed time range, on top of whatever the regular rotation would otherwise produce: a holiday swap or a sick-day cover, without editing the rotation itself. Overrides are materialized into shifts by the same background worker that materializes the regular rotation. ## Authentication Requires the base IM access every endpoint in this API needs (an IM-eligible role, or an organization-wide API token; Incident Management must be enabled for the organization). Listing is available to any IM-eligible role. **Adding** an override additionally requires the write bar: your role must be `admin`, or you must be a team admin of the schedule's team. ## List overrides `GET /api/im/schedules/:id/overrides` Returns the schedule's overrides, ordered by `id`. **This order is load-bearing**, not incidental. `im_schedule_override` has no priority column: precedence between overlapping overrides is decided by creation order (later creation wins), and the materializer reads them in this exact order. This endpoint cannot offer reordering, since there is nowhere to persist a reordered precedence. ### Example (cURL) ```bash curl -X GET "$BASE_URL/api/im/schedules/7/overrides" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ### Response `200 OK` ```json [ { "id": 12, "scheduleId": 7, "userId": "u_abc123", "userName": "Jane Doe", "startsAt": "2026-08-01T00:00:00.000Z", "endsAt": "2026-08-08T00:00:00.000Z", "createdAt": "2026-07-20T10:00:00.000Z" } ] ``` ## Add an override `POST /api/im/schedules/:id/overrides` ### Request Body | Field | Type | Required | Description | |-------|------|----------|--------------| | `userId` | string | Yes | The user covering during this window. Must belong to your organization. | | `startsAt` | ISO 8601 date-time | Yes | Window start. | | `endsAt` | ISO 8601 date-time | Yes | Window end. Must be strictly after `startsAt`. | ### Example (cURL) ```bash curl -X POST "$BASE_URL/api/im/schedules/7/overrides" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "userId": "u_abc123", "startsAt": "2026-08-01T00:00:00.000Z", "endsAt": "2026-08-08T00:00:00.000Z" }' ``` ### Response `200 OK`: the newly created override row (same shape as the list above). ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` (`imAccessDenied`) when using a customer-scoped token, or a session without an IM-eligible role - `403 Forbidden` (`imNotEnabled`) when Incident Management is not enabled for the organization - `403 Forbidden` (`imTeamWriteDenied`) (add only) when your role is not `admin` and you are not a team admin of the schedule's team - `404 Not Found` (`imScheduleNotFound`) when `:id` does not exist, or belongs to another organization - `400 Bad Request` (`invalidRequestBody`) (add only) when `userId` is missing, `startsAt`/`endsAt` is missing or not a valid timestamp, or `startsAt` is not strictly before `endsAt` - `404 Not Found` (`userNotFound`) (add only) when `userId` does not belong to your organization
### Schedules
URL: https://docs.uptimeify.io/api/incident-management/schedules
Description: List and create Incident Management on-call schedules: the rotation that decides who is on call, and when, for a team.
Summary: `GET /api/im/schedules` · `POST /api/im/schedules` A **schedule** belongs to exactly one team and defines its on-call rotation: one or more layers of users, each rotating on a `daily`, `weekly`, or `custom` cadence, optionally restricted to specific times of day. A background worker materializes the rotation into concrete shifts (`im_schedule_shift`) roughly 90 days ahead, which is what [Who Is On Call](./on-call) and the escalation engine actually read at runtime. This endpoint manages the rotation's *definition*, not the materialized shifts directly. ## Authentication Requires the base IM access every endpoint in this API needs (an IM-eligible role, or an organization-wide API token; Incident Management must be enabled for the organization). Listing is available to any IM-eligible role. **Creating** a schedule additionally requires the write bar: your role must be `admin`, or you must be a team admin of the schedule's team. An organization-wide API token satisfies the organization-admin bar. ## List schedules `GET /api/im/schedules` Returns every schedule in your organization, with its team's name. ### Example (cURL) ```bash curl -X GET "$BASE_URL/api/im/schedules" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ### Response `200 OK`: an array, ordered by name. ```json [ { "id": 7, "organizationId": 1, "teamId": 3, "teamName": "Platform Team", "name": "Primary On-Call", "timezone": "Europe/Berlin", "rotation": [ { "users": ["u_abc123", "u_def456"], "type": "weekly", "handoverTime": "09:00", "startDate": "2026-01-05" } ], "createdAt": "2026-01-01T00:00:00.000Z", "updatedAt": "2026-01-01T00:00:00.000Z" } ] ``` ## Create a schedule `POST /api/im/schedules` ### Request Body | Field | Type | Required | Description | |-------|------|----------|--------------| | `teamId` | number | Yes | The team the schedule belongs to. Must belong to your organization. | | `name` | string | Yes | Schedule name, up to 120 characters. | | `timezone` | string | Yes | IANA timezone (e.g. `Europe/Berlin`). Every handover and time-of-day restriction is evaluated in this zone, across DST. | | `tierId` | number | No | The escalation tier the schedule belongs to. | A newly created schedule has **no rotation yet**, it is created with `rotationMode: "none"` and no members. Set the cadence and the members with `PATCH /api/im/schedules/:id` (see below); this endpoint does not accept them. ## Rotation cadence The cadence decides how often on-call hands over. It is set on the schedule with `PATCH /api/im/schedules/:id`. | Field | Type | Description | |-------|------|-------------| | `rotationMode` | string | `none` (one continuous shift, nobody hands over), `auto` (the member pool is chunked automatically) or `explicit` (you define the groups). | | `rotationRepeats` | string | `daily`, `weekly`, `biweekly`, `monthly` or `custom`. Must be `null` when `rotationMode` is `none`. | | `customRepeatUnit` | string | Required when `rotationRepeats` is `custom`: `minutes`, `hours`, `days`, `weeks` or `months`. | | `customRepeatValue` | number | Required when `rotationRepeats` is `custom`. Integer ≥ 1, in the unit above. | | `startsOnTime` | string | `"HH:mm"`, the wall-clock time of day handovers happen, in the schedule's timezone. Minute precision. | | `startsOnDayOfWeek` | number | ISO weekday (1 = Monday .. 7 = Sunday). Required for `weekly`, `biweekly` and `custom` + `weeks`. | | `startsOnDateOfMonth` | number | 1-31, clamped to the month's last day. Required for `monthly` and `custom` + `months`. | **The shortest possible shift is one minute** (`customRepeatUnit: "minutes"`, `customRepeatValue: 1`). Handover times are minute-precise at every cadence, since `startsOnTime` is an `"HH:mm"` wall clock. Sub-day cadences (`minutes`, `hours`) step in absolute time rather than wall clock. Across a DST switch they therefore keep handing over every N minutes in real time instead of duplicating or skipping an hour's worth of handovers. ### How far ahead…
### Teams
URL: https://docs.uptimeify.io/api/incident-management/teams
Description: List the Incident Management teams in your organization, with a member count per team.
Summary: `GET /api/im/teams` A **team** is the top-level ownership unit in Incident Management: every [schedule](./schedules), escalation policy, and incident belongs to exactly one team. This endpoint lists your organization's teams. Team and membership *writes* (create a team, invite/remove members) are managed from the dashboard today and are not yet part of this public API's documented surface. ## Authentication Requires the base IM access every endpoint in this API needs: an IM-eligible role (`admin`, `editor`, or `responder`), or an organization-wide API token. Incident Management must be enabled for the organization. ## Example (cURL) ```bash curl -X GET "$BASE_URL/api/im/teams" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Response `200 OK`: an array, ordered by name. ```json [ { "id": 3, "name": "Platform Team", "labelColor": "#4f46e5", "analyticsEnabled": true, "memberCount": 5 } ] ``` ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` (`imAccessDenied`) when using a customer-scoped token, or a session without an IM-eligible role - `403 Forbidden` (`imNotEnabled`) when Incident Management is not enabled for the organization
### Update a schedule
URL: https://docs.uptimeify.io/api/incident-management/update-schedule
Description: Change an on-call schedule's name, timezone, validity range, weekly windows, rotation cadence and rotation membership, in one atomic call.
Summary: `PATCH /api/im/schedules/:id` This is where a schedule's rotation actually gets configured. [Creating a schedule](./schedules) gives you an empty shell (`rotationMode: "none"`, no members); this endpoint sets the cadence, the weekly windows and who is in the rotation. Every field is **optional**. Only the fields present in the body are touched, a request body of `{ "name": "Primary" }` renames the schedule and changes nothing else. ## Authentication Requires the base IM access every endpoint in this API needs, plus the write bar: your role must be `admin`, or you must be a team admin of the schedule's team. An organization-wide API token is treated as an organization admin. Incident Management is currently restricted to platform administrators. While that restriction is in place, organization roles and organization-wide API tokens receive `403 imAccessDenied` on every `/api/im/**` endpoint, including this one. ## Request body | Field | Type | Description | |-------|------|-------------| | `name` | string | Schedule name, up to 120 characters. | | `timezone` | string | IANA timezone (e.g. `Europe/Berlin`). Every handover boundary and weekly window is evaluated in this zone, across DST. | | `effectiveFrom` | string \| null | ISO timestamp. The schedule pages nobody before it. `null` = no start bound. | | `effectiveUntil` | string \| null | ISO timestamp. The schedule pages nobody after it. `null` = no end bound. | | `weeklySchedules` | array | Recurring on-call windows, up to 50. Each: `{ from: "HH:mm", until: "HH:mm", days: number[] }`, `days` as ISO weekdays (1 = Monday .. 7 = Sunday). `until` may be `"24:00"` for end-of-day, and an `until` at or before `from` wraps past midnight. An empty array means 24/7. | | `rotations` | array | **Full replacement** of the rotation groups, up to 50. Each: `{ members: string[] }`, up to 200 members. Array order is the rotation order; member order within a group is preserved. Every member must already be a member of the schedule's team. Omit the field to leave rotations untouched, sending `[]` removes them all. | Plus the cadence fields, `rotationMode`, `rotationRepeats`, `customRepeatUnit`, `customRepeatValue`, `autoRotationSize`, `roundRobinSize`, `startsOnTime`, `startsOnDayOfWeek`, `startsOnDateOfMonth`. They are documented once, with their allowed values, under [Rotation cadence](./schedules#rotation-cadence). `teamId` and `tierId` are **not** patchable. Moving a schedule between teams or tiers would silently re-point who it pages, so it is not a partial update. ### How the cadence is validated Cadence fields are merged onto the schedule's current row and then validated **as a whole**, not field by field. That is what lets you patch a single field, say `roundRobinSize`, without re-sending the rest of the cadence, while still rejecting a combination that cannot work (for example `rotationRepeats: "weekly"` with no `startsOnDayOfWeek` anywhere in the effective config). Validation happens before anything is written. A cadence the materializer would choke on never reaches the database. ### When shifts are recomputed Changing the timezone, the validity range, the weekly windows, any cadence field, or the rotations re-materializes the schedule's shifts. A bare rename does not, it would take the schedule's lock and rewrite rows for nothing. ## Example (cURL) ```bash curl -X PATCH "$BASE_URL/api/im/schedules/7" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "rotationMode": "explicit", "rotationRepeats": "custom", "customRepeatUnit": "minutes", "customRepeatValue": 30, "startsOnTime": "09:00", "rotations": [ { "members": ["u_abc123"] }, { "members": ["u_def456"] } ] }' ``` ## Response `200 OK`: the schedule's configuration after the update. ```json { "id": 7, "displayName": "Primary On-Call", "timezone": "Europe/Berlin", "effectiveFrom": "2026-08-01T00:00:00.000Z", "effectiveUntil": null, "weeklySchedules": [], "rotationMode": "…
### Introduction
URL: https://docs.uptimeify.io/api/introduction
Description: Welcome to the Uptimeify API documentation. With this REST API, you can programmatically manage your monitoring infrastructure: e.g., organizations, customers, websites, and alerts.
Summary: ## Base URL All API requests should be sent to the following base URL: ```text https://uptimeify.io/api ``` ## Authentication The API supports **Bearer Token** authentication for integrations. ### Create API Token 1. Log in to the Uptimeify dashboard. 2. Open **Settings** > **API** in the dashboard. 3. Click on **Create Token**, enter a name, and copy the generated secret. Add the token to the `Authorization` header of your requests: ```http Authorization: Bearer wsm_ ``` Uptimeify API tokens always start with `wsm_`. If your token does not have that prefix, you are likely using the wrong credential (and requests will return `401 Unauthorized`). ### Token Scopes (Organization vs Customer) API tokens can optionally be created with a **Customer Scope**: - **Organization-wide Token**: can read/modify resources across all customers of the organization. - **Customer-Scoped Token**: can only read/modify resources (websites, maintenance windows, etc.) within that specific customer. Customer-scoped tokens are recommended for agencies and external integrations. Requests outside the scope return `403 Forbidden`. > **Note:** Session-based authentication (cookies) is used for the web interface but is not recommended for integrations. ## Response Format All responses are returned in JSON format. ### Success Response ```json { "id": 123, "name": "example-resource", "createdAt": "2023-01-01T12:00:00Z" } ``` ### Error Response Errors are returned with an appropriate HTTP status code and a JSON body containing details. ```json { "statusCode": 400, "statusMessage": "Bad Request", "message": "Validation failed: 'url' is required." } ``` ## Rate Limiting To ensure service stability, the API is rate-limited. - **Limit**: 600 requests per minute per IP - **Header**: `X-RateLimit-Remaining` shows your remaining quota - **Exceeded**: `429 Too Many Requests` ## Pages - [Generate API Token](./generate-api-token) - [Token Scopes (Organization vs Customer)](./token-scopes-organization-vs-customer) - [Success Response](./success-response) - [Error Response](./error-response)
### Error Response
URL: https://docs.uptimeify.io/api/introduction/error-response
Description: Errors are returned with an appropriate HTTP status code and a JSON body containing details.
Summary: ```json { "statusCode": 400, "statusMessage": "Bad Request", "message": "Validation failed: 'url' is required." } ``` ## Rate limiting To ensure service stability, the API is rate-limited. - **Limit**: 600 requests per minute per IP - **Header**: `X-RateLimit-Remaining` shows your remaining quota - **Exceeded**: `429 Too Many Requests`
### Generate API Token
URL: https://docs.uptimeify.io/api/introduction/generate-api-token
Description: Step-by-step guide to generating an Uptimeify API token from the dashboard Settings > API page.
Summary: 1. Log in to the Uptimeify dashboard. 2. Open **Settings** > **API** in the dashboard. 3. Click **Create Token**, enter a name, and copy the generated secret. Add the token to the `Authorization` header of your requests: ```http Authorization: Bearer wsm_ ``` Uptimeify API tokens always start with `wsm_`. If your token does not have that prefix, you are likely using the wrong credential.
### Success Response
URL: https://docs.uptimeify.io/api/introduction/success-response
Description: On success, the API returns a JSON body with the requested data.
Summary: ```json { "id": 123, "name": "example-resource", "createdAt": "2023-01-01T12:00:00Z" } ```
### Token Scopes (Organization vs Customer)
URL: https://docs.uptimeify.io/api/introduction/token-scopes-organization-vs-customer
Description: API tokens can optionally be created with a Customer Scope:
Summary: - **Organization-wide token**: can read/modify resources across all customers of the organization. - **Customer-scoped token**: can only read/modify resources (websites, maintenance windows, etc.) within that specific customer. Customer-scoped tokens are recommended for agencies and external integrations. Requests outside the scope return `403 Forbidden`. > **Note:** Session-based authentication (cookies) is used for the web interface but is not recommended for integrations.
### Maintenance Windows
URL: https://docs.uptimeify.io/api/maintenance-windows
Description: Create and manage maintenance windows to suppress alerts during planned work. Windows can target a single monitor, multiple monitors, an entire customer, or any monitor carrying a given tag.
Summary: Maintenance windows suppress alerts for the covered monitors during a planned time range. While a window is active the affected monitor is treated as **in maintenance**: no alerts are fired and, if the customer has a status page, the service is shown as **Maintenance** instead of **Degraded**. ## Targeting options A window can cover monitors in four ways (mutually exclusive, except that `targets` and `tagIds` may be combined): | Mode | How to specify | |------|----------------| | **Single monitor (legacy)** | One of the legacy ID fields: `websiteId`, `icmpMonitorId`, `smtpMonitorId`, `sshMonitorId`, `ftpMonitorId`, `imapPopMonitorId`, `dnsMonitorId`, `customerIpId`, `customerDomainId` | | **Multiple monitors** | `targets: [{ type, id }, ...]` array | | **By tag (dynamic)** | `tagIds: [number, ...]`: covers all monitors of the window's customer that currently carry the tag; future-tagged monitors are auto-covered | | **Customer-level** | `customerId` alone: covers every monitor belonging to that customer | ## Dynamic tag behavior Tag-based windows are evaluated live by the monitoring worker: every time a check result arrives the worker resolves which maintenance windows apply to that monitor by looking up the monitor's current tags. This means a monitor tagged *after* a window is created is automatically covered without updating the window. Removing a tag from a monitor drops it from coverage instantly. Tag windows are anchored to a single customer. You cannot create an organization-wide tag window that spans multiple customers. ## Endpoints - [List Maintenance Windows](./list) - [Create Maintenance Window](./create) - [Get Maintenance Window](./get) - [Update Maintenance Window](./update) - [Delete Maintenance Window](./delete)
### Create Maintenance Window
URL: https://docs.uptimeify.io/api/maintenance-windows/create
Description: Creates a new maintenance window. Accepts a single monitor (legacy), multiple monitors, tag-based (including org-wide), or customer-level targeting.
Summary: `POST /api/maintenance-windows` ## Body ```json { "name": "Database migration", "startTime": "2026-07-10T22:00:00.000Z", "endTime": "2026-07-11T01:00:00.000Z", "targets": [ { "type": "website", "id": 101 }, { "type": "icmp", "id": 5 } ], "tagIds": [7], "description": "Planned schema migration", "isRecurring": false, "isActive": true } ``` ### Required fields - `name` (string): A friendly label for the window. - `startTime` (ISO 8601 datetime): When the window begins. - `endTime` (ISO 8601 datetime): When the window ends. Must be after `startTime`. - **At least one targeting field** (see below). ### Target selection (mutually exclusive modes) Exactly one targeting mode must be used. `targets` and `tagIds` may be combined within the multi-monitor mode. | Field | Type | Description | |-------|------|-------------| | `websiteId` | number | Legacy: single website monitor | | `icmpMonitorId` | number | Legacy: single ICMP monitor | | `smtpMonitorId` | number | Legacy: single SMTP monitor | | `sshMonitorId` | number | Legacy: single SSH monitor | | `ftpMonitorId` | number | Legacy: single FTP monitor | | `imapPopMonitorId` | number | Legacy: single IMAP/POP monitor | | `dnsMonitorId` | number | Legacy: single DNS monitor | | `customerIpId` | number | Legacy: single customer IP | | `customerDomainId` | number | Legacy: single customer domain | | `targets` | `{ type, id }[]` | Multi-monitor: `type` is one of `website` \| `dns` \| `icmp` \| `smtp` \| `ssh` \| `ftp` \| `imap_pop` | | `tagIds` | number[] | Tag-based: see [Tag-only (org-wide) mode](#tag-only-org-wide-mode) below | | `customerId` | number | Customer-level: covers every monitor of that customer | ### Tag-only (org-wide) mode Sending `tagIds` **without** `customerId`, `targets`, or any legacy field creates an **org-wide** maintenance window. The window dynamically covers every monitor across the entire organization that currently carries the specified tags, across all customers. ```json { "name": "Infra tag freeze", "startTime": "2026-07-10T22:00:00.000Z", "endTime": "2026-07-11T01:00:00.000Z", "tagIds": [7] } ``` - **Requires admin or editor role.** A readonly user attempting this receives `403 Forbidden`. - The tag membership is resolved live at check time: monitors tagged after the window is created are automatically covered. - All `tagIds` must belong to the same organization; mixing tags from different organizations returns `400 mixedTagOrganizations`. ### Combination rules - **Minimum one** targeting field is required. Omitting all is a Zod validation error (standard `400` with a validation body, not a `data.code` error). - **`customerId` (customer-level mode)** cannot be combined with `targets`, `tagIds`, or any legacy field. Combining them is a Zod validation error (standard `400`). - All monitors in `targets` must belong to the **same customer**; mixing customers returns `{ data: { code: "mixedCustomers" } }`. - All `tagIds` must belong to the same organization; mixing organizations returns `{ data: { code: "mixedTagOrganizations" } }`. ### Optional fields | Field | Type | Description | |-------|------|-------------| | `description` | string | Free-text notes (shown in history). | | `isRecurring` | boolean | Default `false`. Set to `true` to enable `recurrencePattern`. | | `recurrencePattern` | object | Required when `isRecurring` is `true`. See [Recurrence](#recurrence). | | `isActive` | boolean | Default `true`. Set to `false` to create a disabled window. | | `timezone` | string | Default `"UTC"`. The IANA zone (e.g. `"Europe/Berlin"`) the recurrence pattern is interpreted in. Case-insensitive spellings and legacy IANA link names (e.g. `"gmt"`, `"Zulu"`) are accepted and stored under the runtime's canonical name, `"utc"` and `"Zulu"` are both stored as `"UTC"`. A raw UTC offset (e.g. `"+05:00"`) is rejected: it carries no daylight-saving rule, so it cannot express what a zone name does. | ### Recurrence ```json { "frequency": "weekly", "interval": 1, "daysOfWeek":…
### Delete Maintenance Window
URL: https://docs.uptimeify.io/api/maintenance-windows/delete
Description: Permanently deletes a maintenance window. Active alert suppression ends immediately.
Summary: `DELETE /api/maintenance-windows/{id}` ## Path Parameters - `id` (required): The numeric ID of the maintenance window to delete. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X DELETE "$BASE_URL/api/maintenance-windows/42" \ -H "Authorization: Bearer $TOKEN" ``` ## Response Returns `204 No Content` on success with an empty body. ## Common errors - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you cannot access the organization or the window is out of scope (global supporter accounts cannot delete maintenance windows) - `404 Not Found` when no maintenance window with the given ID exists
### Get Maintenance Window
URL: https://docs.uptimeify.io/api/maintenance-windows/get
Description: Returns a single maintenance window by ID, including its full target and tag selection.
Summary: `GET /api/maintenance-windows/{id}` ## Path Parameters - `id` (required): The numeric ID of the maintenance window. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/maintenance-windows/42" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Response ```json { "id": 42, "publicId": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa", "organizationId": 10, "customerId": 1, "name": "Weekly deployment", "description": "Rolling update every Monday", "startTime": "2026-07-07T02:00:00.000Z", "endTime": "2026-07-07T04:00:00.000Z", "isRecurring": true, "recurrencePattern": { "frequency": "weekly", "interval": 1, "daysOfWeek": [1] }, "isActive": true, "timezone": "UTC", "targets": [ { "type": "website", "id": 101 }, { "type": "icmp", "id": 5 } ], "tags": [ { "id": 7, "name": "Production", "color": "red" } ], "websiteId": null, "icmpMonitorId": null, "smtpMonitorId": null, "sshMonitorId": null, "ftpMonitorId": null, "imapPopMonitorId": null, "dnsMonitorId": null, "customerIpId": null, "customerDomainId": null, "createdAt": "2026-06-01T09:00:00.000Z", "updatedAt": "2026-06-01T09:00:00.000Z" } ``` The `targets` array lists all explicitly selected monitors. The `tags` array lists the tags whose monitors are dynamically covered. Both may be non-empty simultaneously when the window uses a mixed multi-monitor + tag selection. ## Common errors - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you cannot access the organization or the window belongs to an out-of-scope customer - `404 Not Found` when no maintenance window with the given ID exists
### List Maintenance Windows
URL: https://docs.uptimeify.io/api/maintenance-windows/list
Description: Returns all maintenance windows visible to the authenticated user, scoped by organization and optional customer filter.
Summary: `GET /api/maintenance-windows` ## Query Parameters - `customerId` (optional): Filter windows by customer ID. - `organizationId` (optional): Defaults to your session organization. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/maintenance-windows?customerId=1" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Response ```json [ { "id": 42, "publicId": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa", "organizationId": 10, "customerId": 1, "name": "Weekly deployment", "description": "Rolling update every Monday", "startTime": "2026-07-07T02:00:00.000Z", "endTime": "2026-07-07T04:00:00.000Z", "isRecurring": true, "recurrencePattern": { "frequency": "weekly", "interval": 1, "daysOfWeek": [1] }, "isActive": true, "timezone": "UTC", "targets": [ { "type": "website", "id": 101 }, { "type": "icmp", "id": 5 } ], "tags": [ { "id": 7, "name": "Production", "color": "red" } ], "websiteId": null, "icmpMonitorId": null, "smtpMonitorId": null, "sshMonitorId": null, "ftpMonitorId": null, "imapPopMonitorId": null, "dnsMonitorId": null, "customerIpId": null, "customerDomainId": null, "createdAt": "2026-06-01T09:00:00.000Z", "updatedAt": "2026-06-01T09:00:00.000Z" } ] ``` Each window includes: - `targets`: array of `{ type, id }` objects for all explicitly selected monitors (`type` is one of `website` | `dns` | `icmp` | `smtp` | `ssh` | `ftp` | `imap_pop`). - `tags`: array of tag objects `{ id, name, color }` for tag-based coverage; an empty array when the window does not use tag targeting. - Legacy single-target fields (`websiteId`, `icmpMonitorId`, etc.) remain present for backwards compatibility; they are `null` when multi-target or tag-based selection is used. ## Common errors - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you cannot access the organization
### Preview Tag Coverage Count
URL: https://docs.uptimeify.io/api/maintenance-windows/preview-count
Description: Returns how many distinct monitors a set of tags currently covers, for the tag-only (org-wide) maintenance-window mode.
Summary: `POST /api/maintenance-windows/preview-count` Used by the maintenance-window form to show "this will cover N monitors" before submitting a [tag-only (org-wide) window](/docs/api/maintenance-windows/create#tag-only-org-wide-mode). Coverage is resolved live: it counts every monitor, across every monitor type, currently tagged with at least one of the given tags, the same dynamic membership the org-wide window itself uses at check time. ## Body ```json { "tagIds": [7] } ``` ### Fields | Field | Type | Description | |-------|------|-------------| | `tagIds` | number[] | Required, at least one. Tags to count coverage for. Must belong to your organization. | ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X POST "$BASE_URL/api/maintenance-windows/preview-count" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "tagIds": [7] }' ``` ## Response ```json { "count": 12 } ``` `count` is the number of distinct `(monitorType, monitorId)` pairs currently carrying at least one of the given tags, not the number of tags. ## Common errors | Status | Description | |--------|-------------| | `400` (validation) | `tagIds` is missing or empty. Standard Zod validation error body, not `{ data: { code } }`. | | `401 Unauthorized` | Not logged in. | | `404` `{ data: { code: "tagNotFound" } }` | A `tagId` does not exist in your organization, or you cannot see it. |
### Preview Maintenance Occurrences
URL: https://docs.uptimeify.io/api/maintenance-windows/preview-occurrences
Description: Computes the upcoming occurrences of a (not-yet-saved) recurrence pattern, without creating a window.
Summary: `POST /api/maintenance-windows/preview-occurrences` Pure computation: this endpoint never touches the database. It runs the same zone-aware recurrence engine the alert-suppression path uses (`occurrencesInRange`), so the preview can never drift from what a saved window with the same `startTime`/`endTime`/`recurrencePattern`/ `timezone` will actually suppress. Useful for showing "what does this pattern mean" before submitting [Create Maintenance Window](/docs/api/maintenance-windows/create) or [Update Maintenance Window](/docs/api/maintenance-windows/update). ## Body ```json { "startTime": "2026-08-10T02:00:00.000Z", "endTime": "2026-08-10T03:00:00.000Z", "isRecurring": true, "recurrencePattern": { "frequency": "weekly", "interval": 1, "daysOfWeek": [1, 3] }, "timezone": "Europe/Berlin" } ``` ### Fields | Field | Type | Description | |-------|------|-------------| | `startTime` | ISO 8601 datetime | Required. Start of the first occurrence. | | `endTime` | ISO 8601 datetime | Required. End of the first occurrence. Must be after `startTime`. | | `isRecurring` | boolean | Required. When `false`, the response contains only the single anchor occurrence. | | `recurrencePattern` | object | Required when `isRecurring` is `true`. Same shape as [Create Maintenance Window](/docs/api/maintenance-windows/create#recurrence). | | `timezone` | string | Default `"UTC"`. The IANA zone the pattern is interpreted in, same validation and canonicalization as the `timezone` field on create/update. | The response returns at most 5 upcoming occurrences, looking forward up to 5 years from the current time. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X POST "$BASE_URL/api/maintenance-windows/preview-occurrences" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "startTime": "2026-08-10T02:00:00.000Z", "endTime": "2026-08-10T03:00:00.000Z", "isRecurring": true, "recurrencePattern": { "frequency": "weekly", "interval": 1, "daysOfWeek": [1, 3] }, "timezone": "Europe/Berlin" }' ``` ## Response ```json { "occurrences": [ { "start": "2026-08-10T00:00:00.000Z", "end": "2026-08-10T01:00:00.000Z" }, { "start": "2026-08-12T00:00:00.000Z", "end": "2026-08-12T01:00:00.000Z" }, { "start": "2026-08-17T00:00:00.000Z", "end": "2026-08-17T01:00:00.000Z" } ] } ``` `occurrences` may be an empty array (e.g. a recurrence pattern whose `endRecurrenceDate` has already passed relative to the preview horizon). Sending `isRecurring: true` without a `recurrencePattern` is not an error. The window is treated as non-recurring and the response contains only the single occurrence described by `startTime` and `endTime`, the same fallback the recurrence engine applies to a malformed pattern. ## Common errors | Status | Description | |--------|-------------| | `400` (validation) | `timezone` is a raw UTC offset or not a zone name the runtime recognizes. Standard Zod validation error body, not `{ data: { code } }`. | | `400` `{ data: { code: "invalidWindow" } }` | `endTime` is not after `startTime`. | | `401 Unauthorized` | Not logged in. |
### Update Maintenance Window
URL: https://docs.uptimeify.io/api/maintenance-windows/update
Description: Partially updates a maintenance window. All fields are optional; only supplied fields are changed. When targets or tagIds are provided they replace the existing selection entirely.
Summary: `PATCH /api/maintenance-windows/{id}` ## Path Parameters - `id` (required): The numeric ID of the maintenance window to update. ## Body All fields are optional. Omit a field to leave it unchanged. ```json { "name": "Extended deployment window", "endTime": "2026-07-11T03:00:00.000Z", "targets": [ { "type": "website", "id": 101 }, { "type": "dns", "id": 9 } ], "tagIds": [7, 12], "isActive": true } ``` ### Updatable fields | Field | Type | Notes | |-------|------|-------| | `name` | string | Display label | | `description` | string | Free-text notes | | `startTime` | ISO 8601 datetime | New start time | | `endTime` | ISO 8601 datetime | New end time; must be after `startTime` | | `isActive` | boolean | Enable or disable without deleting | | `isRecurring` | boolean | Toggle recurrence | | `recurrencePattern` | object | Replaces the recurrence pattern; structure identical to create | | `timezone` | string | The IANA zone (e.g. `"Europe/Berlin"`) the recurrence pattern is interpreted in. Case-insensitive spellings and legacy IANA link names (e.g. `"gmt"`, `"Zulu"`) are accepted and stored under the runtime's canonical name, `"utc"` and `"Zulu"` are both stored as `"UTC"`. A raw UTC offset (e.g. `"+05:00"`) is rejected: it carries no daylight-saving rule, so it cannot express what a zone name does. | | `targets` | `{ type, id }[]` | **Replaces** the full set of explicit monitor targets | | `tagIds` | number[] | **Replaces** the full set of tag IDs | | `websiteId` / `icmpMonitorId` / … | number \| null | Legacy single-target fields | | `customerId` | number | Customer anchor (only for tag-only windows) | ### Replace semantics for targets and tagIds When `targets` or `tagIds` is included in the request body, the **entire existing selection** for that field is replaced. To remove all explicit targets send `"targets": []`; to remove all tags send `"tagIds": []`. ### Combination rules PATCH validates target and tag scope using the same resolver as create, but does **not** re-run the create-time Zod superRefine. In practice: - `customerId` cannot be combined with `targets`, `tagIds`, or legacy fields. - All monitors in `targets` must belong to the same customer; mixing returns `{ data: { code: "mixedCustomers" } }`. - An org-wide tag-only window (tags without `customerId`, `targets`, or legacy fields) can be edited by admin or editor users within the organization. ### Readonly users in scope Read-only users assigned to the window's customer may edit maintenance windows scoped to that customer. Global supporter accounts cannot. Readonly users cannot create or update org-wide tag-only windows. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X PATCH "$BASE_URL/api/maintenance-windows/42" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"isActive": false}' ``` ## Response Returns the updated maintenance window object in the same shape as [Get Maintenance Window](./get). ## Common errors | Status | Description | |--------|-------------| | `400` (validation) | Update would leave the window with no targets, `customerId` combined with other target fields, or `timezone` is a raw UTC offset or not a zone name the runtime recognizes. These are Zod validation errors; the response body is a standard validation error, **not** `{ data: { code } }`. | | `400` `{ data: { code: "mixedCustomers" } }` | `targets` contains monitors from different customers. | | `400` `{ data: { code: "mixedTagOrganizations" } }` | `tagIds` contains tags from different organizations. | | `401 Unauthorized` | Not logged in. | | `403 Forbidden` | You cannot access the window (global supporter accounts cannot edit), or you are a readonly user attempting to set an org-wide tag-only scope. | | `404 Not Found` | No maintenance window with the given ID exists. | | `404` `{ data: { code: "tagNotFound" } }` | A `tagId` does not exist in your organization. |
### MCP Server
URL: https://docs.uptimeify.io/api/mcp
Description: Use Uptimeify's free check tools from any AI agent over the Model Context Protocol (MCP).
Summary: Uptimeify exposes a stateless [Model Context Protocol](https://modelcontextprotocol.io) server so AI agents can run our free checks directly. For an overview of what the server can do and which clients it works with, see the [MCP server page on uptimeify.io](https://uptimeify.io/mcp-server). This page is the technical reference. ## Endpoint `POST https://uptimeify.io/mcp`: Streamable HTTP transport, stateless. The anonymous check tools need no authentication; the authenticated tools require a Bearer API token (see below). Discover the server programmatically via the MCP Server Card: `GET https://uptimeify.io/.well-known/mcp/server-card.json` ## Connect an MCP client Point any MCP client that speaks the **Streamable HTTP** transport at the endpoint above. - **Anonymous tools** work with just the URL: no token needed. - **Authenticated tools** require your Uptimeify API token as an `Authorization: Bearer ` header. Create one under **Settings → API tokens**: leave the customer field empty for an organization-wide token (sees all your monitors), or pick a customer to scope the token to that customer only. The token is shown **once** on creation: copy it right away. Clients that support remote HTTP MCP servers with custom headers can point at the URL directly: ```json { "mcpServers": { "uptimeify": { "url": "https://uptimeify.io/mcp", "headers": { "Authorization": "Bearer wsm_your_token_here" } } } } ``` For clients that only speak stdio, bridge to the HTTP endpoint with [`mcp-remote`](https://www.npmjs.com/package/mcp-remote): ```json { "mcpServers": { "uptimeify": { "command": "npx", "args": ["-y", "mcp-remote", "https://uptimeify.io/mcp", "--header", "Authorization: Bearer wsm_your_token_here"] } } } ``` Omit the `Authorization` header entirely if you only need the anonymous check tools. ## Rate limits The anonymous tools run real network probes on your behalf, so they are capped per client IP. You do not need an account to hit these limits, and you do not need one to stay under them: | Scope | Limit | | --- | --- | | `POST /mcp` overall | 120 requests per minute per IP | | Each anonymous check tool | 15-30 calls per minute per IP, depending on how expensive the probe is (`whois` and `domain_expiry` are the strictest at 15) | | Each authenticated tool | 60 calls per minute per IP | The two layers apply together: 120 calls a minute spread across different tools is fine, 120 calls a minute of `whois` is not. Exceeding a limit returns HTTP `429` with a `retryAfter` value in seconds, telling you when the current window ends. Windows are fixed, not sliding, so a client that waits out `retryAfter` gets a full fresh allowance. The limits are enforced per IP, not per token: an API token does not raise the ceiling, and running several agents behind one NAT address makes them share it. If you have a use case that genuinely needs more, [get in touch](https://uptimeify.io/contact) rather than working around it. ## Available tools All tools are anonymous and read-only. They mirror our public web tools: - **check_ssl**: TLS/SSL certificate inspection (`host`, optional `port`) - **check_dns**, **dns_propagation**, **mx_lookup**: DNS resolution and propagation (`domain`) - **spf_check**, **dkim_check** (`domain`, `selector`), **dmarc_check**: mail authentication (`domain`) - **dnsbl_check** (`ip`), **whois** (`domain`), **domain_expiry** (`domain`) - **http_headers** (`url`), **hsts_check** (`domain`), **redirect_check** (`url`) - **port_check** (`host`, `port`), **ping_test** (`host`, optional `port`) - **website_status** (`url`), **response_time** (`url`) - **ip_geolocation** (`query`), **asn_lookup** (`query`), **reverse_dns** (`ip`) ## Authenticated tools (read-only) These tools read your own monitoring data. Send your Uptimeify API token as an `Authorization: Bearer ` header on the MCP request. A **customer-scoped token** sees only its own customer's monitors; an **organization token** sees all customers in the organization.…
### Monitoring Data & Reports
URL: https://docs.uptimeify.io/api/monitoring
Description: API endpoints for retrieving monitoring data, check history, incident details, uptime stats, and PDF reports.
Summary: ## Monitoring Data ### Get Uptime Stats `GET /api/websites/:websitePublicId/uptime-stats` Returns uptime percentages and average response times (day/month/year). ### Get Check History `GET /api/websites/:websitePublicId/check-history` Returns the latest monitoring checks for a website. ### Get Incident History `GET /api/websites/:websitePublicId/incident-history` Returns incidents for a website (latest 100). ### Get Alert History `GET /api/websites/:websitePublicId/alert-history` Returns notification/escalation attempts for incidents of a website. ### Get Monitoring Data `GET /api/websites/:websitePublicId/monitoring-data?range=day|week|month|year` Returns aggregated time series data used for charts. ### Get Incident Details `GET /api/incidents/:incidentPublicId` Returns details of a specific incident. ### List Incidents (Organization) `GET /api/incidents?organizationId=:organizationId` Lists incidents for an organization (scoped by your permissions). ## Reports ### Download PDF Report `GET /api/websites/:websitePublicId/report.pdf?period=last-week|last-month|last-quarter&startDate=YYYY-MM-DD&endDate=YYYY-MM-DD` Downloads a generated PDF report for a website. ## Endpoints - [Get Uptime Stats](./get-uptime-stats) - [Get Check History](./get-check-history) - [Get Incident History](./get-incident-history) - [Get Alert History](./get-alert-history) - [Get Monitoring Data](./get-monitoring-data) - [List Incidents (Organization)](./list-incidents) - [Get Incident Details](./get-incident-details) - [Download PDF Report](./download-report-pdf)
### Monitoring Locations
URL: https://docs.uptimeify.io/api/monitoring-locations
Description: Retrieve the list of countries and locations where monitoring probes are available.
Summary: ## Endpoints - [List Countries](./list-countries): authenticated, grouped by country - [List Public Locations](./list-public-locations): public, unauthenticated ## Available Locations | Code | Name | Country | |------|------|---------| | `de-ber` | Berlin | Germany | | `de-fra` | Frankfurt | Germany | | `de-fsn` | Falkenstein | Germany | | `de-nbg` | Nuremberg | Germany | | `es-vit` | Logroño | Spain | | `fi-hel` | Helsinki | Finland | | `fr-par` | Paris | France | | `it-mil` | Milan | Italy | | `pl-waw` | Warsaw | Poland |
### List Countries with Locations
URL: https://docs.uptimeify.io/api/monitoring-locations/list-countries
Description: Returns countries that have monitoring locations with active workers. Requires authentication.
Summary: `GET /api/monitoring-locations/countries` ## Example (cURL) ```bash curl -X GET "$BASE_URL/api/monitoring-locations/countries" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Response ```json { "countries": [ { "code": "DE", "locations": 4, "workers": 23 }, { "code": "ES", "locations": 1, "workers": 4 }, { "code": "FI", "locations": 1, "workers": 6 }, { "code": "FR", "locations": 1, "workers": 4 }, { "code": "IT", "locations": 1, "workers": 4 }, { "code": "PL", "locations": 1, "workers": 6 } ] } ```
### List Public Monitoring Locations
URL: https://docs.uptimeify.io/api/monitoring-locations/list-public-locations
Description: Public endpoint (no authentication required) that returns all monitoring locations with active workers. Suitable for landing pages and marketing.
Summary: `GET /api/public/monitoring-locations` ## Example (cURL) ```bash curl -X GET "https://uptimeify.io/api/public/monitoring-locations" \ -H "Accept: application/json" ``` ## Response ```json { "locations": [ { "id": 19, "code": "de-ber", "name": "Berlin (DE)", "countryCode": "DE", "activeWorkers": 4 }, { "id": 18, "code": "de-fra", "name": "Frankfurt (DE)", "countryCode": "DE", "activeWorkers": 4 }, { "id": 8, "code": "de-fsn", "name": "Falkenstein (DE)", "countryCode": "DE", "activeWorkers": 6 }, { "id": 7, "code": "de-nbg", "name": "Nuremberg (DE)", "countryCode": "DE", "activeWorkers": 7 }, { "id": 20, "code": "es-vit", "name": "Logroño (ES)", "countryCode": "ES", "activeWorkers": 4 }, { "id": 3, "code": "fi-hel", "name": "Helsinki (FI)", "countryCode": "FI", "activeWorkers": 6 }, { "id": 17, "code": "fr-par", "name": "Paris (FR)", "countryCode": "FR", "activeWorkers": 4 }, { "id": 6, "code": "it-mil", "name": "Milan (IT)", "countryCode": "IT", "activeWorkers": 4 }, { "id": 5, "code": "pl-waw", "name": "Warsaw (PL)", "countryCode": "PL", "activeWorkers": 6 } ] } ``` `activeWorkers` is a live value and varies over time.
### Delete Incident (Manual only)
URL: https://docs.uptimeify.io/api/monitoring/delete-incident
Description: Deletes a manually created (status-page) incident. Monitor-generated incidents cannot be deleted.
Summary: `DELETE /api/incidents/:id` Deletes a **manual** incident (a status-page announcement created by an admin) together with its timeline updates. Monitor-generated incidents are worker-owned and can never be deleted, the endpoint rejects them with `403 notManualIncident`. ## Authentication Requires a valid session with editor/admin rights, scoped to the incident's customer. - Header: `Authorization: Bearer ` ## Path Parameters - `id` (required): The incident's numeric ID or its `publicId` (UUID). ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X DELETE "$BASE_URL/api/incidents/8d1f0c2e-6a2b-4f1e-9b7c-2e5a1d3c4b5a" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example Response ```json { "ok": true } ``` ## Common Errors - `400 Incident id is required` if no ID is supplied - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have write access to the incident's customer - `403 Only manual incidents can be deleted` (`data.code: notManualIncident`) if the incident is monitor-generated - `404 Incident not found` if no incident matches the ID
### Download PDF Report
URL: https://docs.uptimeify.io/api/monitoring/download-report-pdf
Description: Downloads a PDF report for a website.
Summary: `GET /api/websites/:websitePublicId/report.pdf` ## Query Parameters You can either use a predefined `period` or a custom date range. - `period` (optional, default: `last-month`): `last-week`, `last-month`, `last-quarter` - `startDate` / `endDate` (optional): `YYYY-MM-DD` If `startDate` and `endDate` are provided, they take precedence. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -L "$BASE_URL/api/websites/6bfec6f6-245a-47ce-843b-157d97d56f88/report.pdf?period=last-month" \ -H "Authorization: Bearer $TOKEN" \ -o report.pdf ``` ## Response The response is a PDF (`application/pdf`). ## Common Errors - `400 Website public ID (UUID) required` if `:websitePublicId` is invalid - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access to the website - `404 Website not found` if the website does not exist
### Get Alert History
URL: https://docs.uptimeify.io/api/monitoring/get-alert-history
Description: Returns notification/escalation attempts (alert history) for incidents of a website.
Summary: `GET /api/websites/:websitePublicId/alert-history` ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/websites/6bfec6f6-245a-47ce-843b-157d97d56f88/alert-history" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example Response ```json { "alerts": [ { "id": 987, "incidentId": 123, "type": "email", "status": "sent", "channelName": "Ops Email", "sentAt": "2026-02-26T12:11:00.000Z", "errorMessage": null } ], "total": 1 } ``` ## Common Errors - `400 Website public ID (UUID) required` if `:websitePublicId` is invalid - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access to the website
### Get Check History
URL: https://docs.uptimeify.io/api/monitoring/get-check-history
Description: Returns the latest monitoring checks for a website.
Summary: `GET /api/websites/:websitePublicId/check-history` ## Query Parameters - `limit` (optional, default: `50`, max: `200`) - `checkType` (optional, supports values such as `http_status`, `ssl_check`, `combined`, `playwright`, `heartbeat`, `dns`, plus legacy values like `http` and `ssl`) ## Path Parameters - `websitePublicId` (recommended): website public UUID - Backward compatibility: legacy numeric website IDs are still accepted ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/websites/6bfec6f6-245a-47ce-843b-157d97d56f88/check-history?limit=25&checkType=dns" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example Response ```json { "data": [ { "id": "chk_01H...", "status": "success", "errorMessage": null, "warningMessage": null, "timingDns": 12, "diagnostics": null, "checkedAt": "2026-02-26T12:34:56.000Z", "locationId": 7, "locationName": "Nuremberg (DE)", "locationCode": "de-nbg" } ] } ``` ## Common Errors - `400 Invalid Website identifier` if `:websitePublicId` is neither a valid UUID nor a legacy numeric ID - `400 Invalid checkType` if you send an unsupported `checkType` - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access to the website
### Get Incident Details
URL: https://docs.uptimeify.io/api/monitoring/get-incident-details
Description: Returns detailed incident data, including a timeline of failed/recovery checks and alert events.
Summary: `GET /api/incidents/:incidentPublicId` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `incidentPublicId` (Path, required): Incident public UUID. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/incidents/6bfec6f6-245a-47ce-843b-157d97d56f88" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` Note: `evidenceCheck` can be `null`. `screenshotUrl` is only set when `hasScreenshot` is `true`. ## Timeline resolution Every entry in `outageStartedCheck`, `failedChecks` and `recoveryChecks` carries a `resolution`: - `raw`: a single check at a single location. `location`, `statusCode` and `errorMessage` are the ones that location reported. - `1min`: one minute of the monitoring aggregate, used once an incident is older than the raw check retention window (48 hours). `location`, `statusCode` and `errorMessage` are `null`, `responseTimeMs` is the average over the minute, and two extra fields say how many locations were behind it: `totalLocations` and `failedLocations`. The `id` of such an entry is synthetic (`1min-`) and cannot be looked up as a check. The three lists are resolved independently, so an incident that spans the boundary can return `raw` entries for its recent part and `1min` entries for the older one. ## Example Response (excerpt) ```json { "incident": { "id": 123, "websiteId": 101, "type": "downtime", "status": "open", "startedAt": "2026-02-26T12:10:00.000Z", "resolvedAt": null, "statusCode": null, "errorMessage": "Timeout", "responseTimeMs": null }, "timeline": { "outageStartedAt": "2026-02-26T12:08:00.000Z", "outageStartedCheck": { "id": "chk_01H...", "checkedAt": "2026-02-26T12:08:00.000Z", "status": "failure", "statusCode": 503, "errorMessage": "Timeout", "responseTimeMs": null, "location": { "id": 7, "code": "de-nbg", "name": "Nuremberg (DE)" }, "resolution": "raw" }, "confirmationAt": "2026-02-26T12:10:00.000Z", "failedChecks": [ { "id": "1min-2026-02-26T12:09:00.000Z", "checkedAt": "2026-02-26T12:09:00.000Z", "status": "failure", "statusCode": null, "errorMessage": null, "responseTimeMs": 246, "location": null, "resolution": "1min", "totalLocations": 3, "failedLocations": 2 } ], "failedChecksTotal": 2, "alertEvents": [ { "id": 987, "sentAt": "2026-02-26T12:11:00.000Z", "type": "email", "status": "sent", "channelName": "Ops Email", "errorMessage": null } ], "recoveryChecks": [], "recoveryChecksTotal": 0 }, "evidenceCheck": { "id": "chk_01H...", "checkedAt": "2026-02-26T12:08:00.000Z", "diagnostics": null, "hasScreenshot": false, "screenshotUrl": null } } ``` ## Common Errors - `400 Incident public ID (UUID) required` if `:incidentPublicId` is invalid - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if the incident exists but you do not have access - `404 Incident not found` if the incident does not exist
### Get Incident History
URL: https://docs.uptimeify.io/api/monitoring/get-incident-history
Description: Returns incidents for a single website (latest 100), including a computed duration.
Summary: `GET /api/websites/:websitePublicId/incident-history` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `websitePublicId` (Path, required): Website public UUID. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/websites/6bfec6f6-245a-47ce-843b-157d97d56f88/incident-history" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example Response ```json { "incidents": [ { "id": 123, "type": "downtime", "status": "resolved", "startedAt": "Feb 26, 2026, 12:10:00", "endedAt": "Feb 26, 2026, 12:12:30", "duration": "2m 30s", "durationMs": 150000, "details": "HTTP/2 503 - Response time: 1.20s", "isOngoing": false } ], "total": 1 } ``` Note: `startedAt` and `endedAt` are formatted strings (server-side `en-US`). ## Common Errors - `400 Website public ID (UUID) required` if `:websitePublicId` is invalid - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access to the website - `500 Failed to fetch incident history` on unexpected errors
### Get Monitoring Data
URL: https://docs.uptimeify.io/api/monitoring/get-monitoring-data
Description: Returns time series data used for charts (response times, status tracker, uptime percentage).
Summary: `GET /api/websites/:id/monitoring-data` To keep responses small, the endpoint down-samples the returned data points if needed. ## Query Parameters - `range` (optional, default: `day`): `day`, `week`, `month`, `year` - `maxPoints` (optional, default: `300`, max: `2000`): Limits the number of returned data points. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/websites/6bfec6f6-245a-47ce-843b-157d97d56f88/monitoring-data?range=week" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example Response (excerpt) ```json { "responseTimeData": [ { "timestamp": "2026-02-26T12:00:00.000Z", "responseTime": 123, "status": "success", "success": true, "timingDns": 12, "timingTcp": 20, "timingTls": 30, "timingTtfb": 50, "timingTransfer": 11 } ], "statusData": [ { "date": "26.02", "status": "online" } ], "uptimePercentage": "99.95", "checkSuccessRatePercentage": "99.80", "totalChecks": 100, "successfulChecks": 99 } ``` ## Common Errors - `400 Website public ID (UUID) required` if `:websitePublicId` is invalid - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access to the website - `500 Failed to fetch monitoring data` on server errors
### Get Uptime Stats
URL: https://docs.uptimeify.io/api/monitoring/get-uptime-stats
Description: Returns uptime percentages and average response times for the last day, month and year.
Summary: `GET /api/websites/:websitePublicId/uptime-stats` ## Path Parameters - `websitePublicId` (recommended): website public UUID - Backward compatibility: legacy numeric website IDs are still accepted ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/websites/9a3d4d4d-7a4b-4f37-a9df-2a6f6d9d7a10/uptime-stats" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example Response ```json { "day": "100.00", "month": "99.95", "year": "99.90", "dayAvgResponse": 125, "monthAvgResponse": 118, "yearAvgResponse": 120 } ``` ## Common Errors - `400 Invalid Website identifier` if `:websitePublicId` is neither a valid UUID nor a legacy numeric ID - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access to the website
### List Incidents (Organization)
URL: https://docs.uptimeify.io/api/monitoring/list-incidents
Description: Lists incidents for an organization. If organizationId is omitted, the API falls back to the organization from your session.
Summary: `GET /api/incidents` Readonly users only see incidents for customers they are assigned to. ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Query Parameters - `organizationId` (optional): Organization ID. Defaults to your session organization. - `source` (optional): Filter by incident source. `manual` returns status-page announcements created by an admin; `monitor` returns incidents generated automatically by the monitoring workers. Any other value is ignored and all sources are returned. Useful because manual incidents can be far older than the newest monitor incidents and would otherwise fall outside the `limit` window. - `limit` (optional, default: `100`, max: `500`): Maximum number of incidents to return. - `offset` (optional, default: `0`): Pagination offset. - `includeTotal` (optional, default: `0`): If set to `1` (or `true`), the response includes `total`, `limit`, and `offset`. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/incidents?organizationId=1" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` To paginate: ```bash curl -X GET "$BASE_URL/api/incidents?organizationId=1&limit=100&offset=0" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` To list only manually created (status-page) incidents: ```bash curl -X GET "$BASE_URL/api/incidents?organizationId=1&source=manual" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example Response (excerpt) ```json [ { "id": 123, "websiteId": 101, "status": "open", "severity": "downtime", "started_at": "2026-02-26T12:10:00.000Z", "resolved_at": null, "last_notified_at": "2026-02-26T12:11:00.000Z", "error_message": "Timeout", "status_code": null, "response_time_ms": null, "website": { "id": 101, "name": "Main Marketing Site", "url": "https://deinkunde.com", "status": "active" }, "customer": { "id": 12, "email": "ops@deinkunde.com", "company": "Acme Corp" } } ] ``` If `includeTotal=1`, the response shape changes to: ```json { "incidents": [], "total": 1234, "limit": 100, "offset": 0 } ``` ## Common Errors - `400 Organization ID is required` if the API cannot infer an organization - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access to the organization - `500 Failed to fetch incidents` on unexpected errors
### Monitors
URL: https://docs.uptimeify.io/api/monitors
Description: Manage protocol monitors (ICMP, SMTP, SSH, TCP, FTP, IMAP/POP).
Summary: Protocol monitor detail/update/delete/check endpoints use monitor-specific `publicId` UUIDs in the path. ## Resources - [ICMP Monitors](./icmp-monitors/) - [SMTP Monitors](./smtp-monitors/) - [SSH Monitors](./ssh-monitors/) - [TCP Monitors](./tcp-monitors/) - [FTP Monitors](./ftp-monitors/) - [IMAP/POP Monitors](./imap-pop-monitors/) - [DNS Monitors](./dns-monitors/) - [DNSBL Monitoring (Customer IPs)](./dnsbl-monitoring/) - [Domain Expiry Monitoring (Customer Domains)](./domain-expiry-monitoring/)
### DNS Monitors
URL: https://docs.uptimeify.io/api/monitors/dns-monitors
Description: Manage DNS monitors that check DNS resolution/records for a hostname.
Summary: Path-based DNS monitor endpoints use `dnsMonitorPublicId` UUIDs. ## Endpoints - [List DNS Monitors](./list-dns-monitors) - [Create DNS Monitor](./create-dns-monitor) - [Get DNS Monitor](./get-dns-monitor) - [Update DNS Monitor](./update-dns-monitor) - [Delete DNS Monitor](./delete-dns-monitor) - [Trigger DNS Check](./trigger-check-dns-monitor) - [Get DNS Monitor Check History](./get-dns-monitor-check-history)
### Create DNS Monitor
URL: https://docs.uptimeify.io/api/monitors/dns-monitors/create-dns-monitor
Description: Creates a new DNS monitor for a customer.
Summary: `POST /api/dns-monitors` ## Request Body ```json { "customerId": "6bfec6f6-245a-47ce-843b-157d97d56f88", "name": "Example DNS", "hostname": "deinkunde.com", "status": "active", "checkInterval": 30, "timeoutSeconds": 30, "dnsConfig": { "rrtypes": ["A", "AAAA"], "matchMode": "exact", "expectedValues": { "A": ["93.184.216.34"], "AAAA": ["2606:2800:220:1:248:1893:25c8:1946"] }, "triggerOn": { "resolveError": true, "mismatch": true } } } ``` Notes: - `customerId`, `name`, `hostname`, `dnsConfig.rrtypes`, `dnsConfig.matchMode`, and `dnsConfig.expectedValues` are required. - `customerId` accepts either the internal numeric ID or the customer `publicId` UUID. - Global supporters and readonly users cannot create DNS monitors. - `hostname` must be a hostname (no protocol, no path). - `dnsConfig.expectedValues` must contain at least one expected value for every RR type listed in `dnsConfig.rrtypes`. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X POST \ "$BASE_URL/api/dns-monitors" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"customerId":"6bfec6f6-245a-47ce-843b-157d97d56f88","name":"Example DNS","hostname":"deinkunde.com","dnsConfig":{"rrtypes":["A"],"matchMode":"exact","expectedValues":{"A":["93.184.216.34"]},"triggerOn":{"resolveError":true,"mismatch":true}}}' ``` ## Common errors - `400 Invalid Customer identifier` - `400 hostname must be a valid hostname (no protocol, no path)` - `400 Expected values are required for RR type ` - `401 Unauthorized` - `403 Forbidden` (readonly/global supporter or no access) - `404 Customer not found` ## Response Returns the created DNS monitor object. See [Error Codes](/api/error-codes-and-known-pitfalls) for error responses.
### Delete DNS Monitor
URL: https://docs.uptimeify.io/api/monitors/dns-monitors/delete-dns-monitor
Description: Deletes a DNS monitor.
Summary: `DELETE /api/dns-monitors/:dnsMonitorPublicId` ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X DELETE \ "$BASE_URL/api/dns-monitors/11111111-1111-4111-8111-111111111111" \ -H "Authorization: Bearer $TOKEN" ``` ## Common errors - `401 Unauthorized` - `403 Forbidden` - `404 DNS monitor not found` ## Response Returns `204 No Content` on success. See [Error Codes](/api/error-codes-and-known-pitfalls) for error responses.
### Get DNS Monitor
URL: https://docs.uptimeify.io/api/monitors/dns-monitors/get-dns-monitor
Description: Returns a single DNS monitor.
Summary: `GET /api/dns-monitors/:dnsMonitorPublicId` The response contains the DNS monitor itself and does not embed the full customer record. ## Example response ```json { "id": 1, "publicId": "3c741f27-7015-4202-93ea-0f97cc6bc769", "organizationId": 2, "customerId": 2, "customerName": "Zaskoku & Haupt GbR", "name": "haupt.design", "hostname": "haupt.design", "checkInterval": 30, "timeoutSeconds": 30, "dnsConfig": { "rrtypes": ["A"], "matchMode": "exact", "triggerOn": { "mismatch": true, "resolveError": true }, "expectedValues": { "A": ["76.76.21.21"] } }, "status": "active", "notificationPhoneNumber": null, "notificationEmail": null, "lastCheckedAt": "2026-04-03T14:55:00.005Z", "createdAt": "2026-02-23T20:24:54.731Z", "updatedAt": "2026-04-03T14:55:01.193Z" } ``` ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET \ "$BASE_URL/api/dns-monitors/11111111-1111-4111-8111-111111111111" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Common errors - `400 DNS monitor public ID (UUID) required` - `401 Unauthorized` - `403 Forbidden` - `404 DNS monitor not found`
### Get DNS Monitor Alert History
URL: https://docs.uptimeify.io/api/monitors/dns-monitors/get-dns-monitor-alert-history
Description: Returns notification/escalation attempts (alert history) for incidents of a DNS monitor.
Summary: `GET /api/dns-monitors/:dnsMonitorPublicId/alert-history` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `dnsMonitorPublicId` (Path, required): DNS monitor public UUID. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/dns-monitors/11111111-1111-4111-8111-111111111111/alert-history" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example Response ```json { "alerts": [ { "id": 987, "incidentId": 123, "type": "slack", "status": "sent", "channelName": "Ops Slack", "sentAt": "2026-02-26T12:11:00.000Z", "errorMessage": null } ], "total": 1 } ``` Each entry is one delivery attempt to one notification channel. `status` is `sent` or `failed` (with `errorMessage` set on failure); `type` is the channel type (e.g. `email`, `slack`, `opsgenie`). ## Common Errors - `400 DNS monitor public ID (UUID) required` if `:dnsMonitorPublicId` is invalid - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access to the monitor
### Get DNS Monitor Check History
URL: https://docs.uptimeify.io/api/monitors/dns-monitors/get-dns-monitor-check-history
Description: Returns recent DNS check results for a monitor.
Summary: `GET /api/dns-monitors/:dnsMonitorPublicId/check-history` ## Query Parameters - `limit` (optional, default `50`, max `200`) ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET \ "$BASE_URL/api/dns-monitors/11111111-1111-4111-8111-111111111111/check-history?limit=50" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example Response ```json { "data": [] } ``` ## Common errors - `400 DNS monitor public ID (UUID) required` - `401 Unauthorized` - `403 Forbidden`
### Get DNS Monitor Incident History
URL: https://docs.uptimeify.io/api/monitors/dns-monitors/get-dns-monitor-incident-history
Description: Returns incidents for a single DNS monitor (latest 100), including a computed duration.
Summary: `GET /api/dns-monitors/:dnsMonitorPublicId/incident-history` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `dnsMonitorPublicId` (Path, required): DNS monitor public UUID. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/dns-monitors/11111111-1111-4111-8111-111111111111/incident-history" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example Response ```json { "incidents": [ { "id": 123, "type": "downtime", "status": "resolved", "startedAt": "Feb 26, 2026, 12:10:00", "endedAt": "Feb 26, 2026, 12:12:30", "duration": "2m 30s" } ], "total": 1 } ``` `type` reflects the failure category for this monitor; `status` is `active` (ongoing) or `resolved`. Ongoing incidents have a `null` `endedAt`. ## Common Errors - `400 DNS monitor public ID (UUID) required` if `:dnsMonitorPublicId` is invalid - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access to the monitor
### List DNS Monitors
URL: https://docs.uptimeify.io/api/monitors/dns-monitors/list-dns-monitors
Description: Lists DNS monitors for an organization (with pagination).
Summary: `GET /api/dns-monitors` Each item contains the DNS monitor itself and does not embed the full customer record. ## Query Parameters - `organizationId` (optional): defaults to your session organization - `customerId` (optional) - `search` (optional) - `page` (optional, default `1`) - `perPage` (optional, default `50`, max `200`) ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET \ "$BASE_URL/api/dns-monitors?organizationId=1&page=1&perPage=50" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example Response ```json { "items": [ { "id": 5, "publicId": "79fbe9b0-f9c6-4026-86de-1dbebc84d9bb", "organizationId": 2, "customerId": 2, "customerName": "Zaskoku & Haupt GbR", "name": "Primary DNS Monitor", "hostname": "claas.sh", "checkInterval": 30, "timeoutSeconds": 30, "dnsConfig": {}, "allowedCheckCountryCodes": null, "status": "active", "notificationPhoneNumber": null, "notificationEmail": null, "lastCheckedAt": "2026-04-03T14:54:56.561Z", "createdAt": "2026-04-03T14:54:56.383Z", "updatedAt": "2026-04-03T14:54:56.740Z" } ], "total": 0, "page": 1, "perPage": 50 } ``` ## Common errors - `400 Invalid organizationId` when `organizationId` is invalid - `400 Invalid customerId` when `customerId` is invalid - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you cannot access the organization
### Trigger DNS Check
URL: https://docs.uptimeify.io/api/monitors/dns-monitors/trigger-check-dns-monitor
Description: Triggers an immediate DNS check across eligible monitoring locations.
Summary: `POST /api/dns-monitors/:dnsMonitorPublicId/trigger-check` ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X POST \ "$BASE_URL/api/dns-monitors/11111111-1111-4111-8111-111111111111/trigger-check" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example Response ```json { "success": true, "message": "Checks triggered successfully", "dnsMonitorId": 123, "locationCodes": ["de-nbg"], "queueNames": ["dns-monitor-checks-de-nbg"] } ``` ## Common errors - `400 DNS monitor public ID (UUID) required` - `401 Unauthorized` - `403 Forbidden` (requires write access) - `503 No active monitoring locations available`
### Update DNS Monitor
URL: https://docs.uptimeify.io/api/monitors/dns-monitors/update-dns-monitor
Description: Updates a DNS monitor.
Summary: `PATCH /api/dns-monitors/:dnsMonitorPublicId` Notes: - Readonly users may only change `status`. - Global supporters cannot update DNS monitors. - If you send `customerId`, it accepts either the internal numeric ID or the customer `publicId` UUID. ## Request Body (example) ```json { "customerId": "6764e84f-f02a-43e6-a46d-cecaec556723", "status": "maintenance", "name": "Primary DNS Monitor", "hostname": "claas.sh", "checkInterval": 30, "timeoutSeconds": 30, "dnsConfig": { "rrtypes": ["A"], "matchMode": "exact", "expectedValues": { "A": ["76.76.21.21"] }, "triggerOn": { "resolveError": true, "mismatch": true } } } ``` If you send `dnsConfig`, it must include `rrtypes`, `matchMode`, and at least one `expectedValues` entry for every RR type listed. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X PATCH \ "$BASE_URL/api/dns-monitors/11111111-1111-4111-8111-111111111111" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"customerId":"6764e84f-f02a-43e6-a46d-cecaec556723","status":"maintenance","name":"Primary DNS Monitor","hostname":"claas.sh","checkInterval":30,"timeoutSeconds":30,"dnsConfig":{"rrtypes":["A"],"matchMode":"exact","expectedValues":{"A":["76.76.21.21"]},"triggerOn":{"resolveError":true,"mismatch":true}}}' ``` ## Common errors - `400 DNS monitor ID is required` - `400 Invalid status` - `400 Invalid Customer identifier` - `400 Expected values are required for RR type ` - `401 Unauthorized` - `403 Forbidden` - `404 Customer not found` ## Response Returns the updated DNS monitor object. See [Error Codes](/api/error-codes-and-known-pitfalls) for error responses.
### DNSBL Monitoring (Customer IPs)
URL: https://docs.uptimeify.io/api/monitors/dnsbl-monitoring
Description: DNSBL monitoring is configured via customer IPs. These IPs are periodically checked against DNS-based blacklists.
Summary: ## Endpoints - [List Customer IPs (Org)](./list-customer-ips) - [Get Customer IP](./get-customer-ip) - [Update Customer IP](./update-customer-ip) - [Delete Customer IP](./delete-customer-ip) - [List Customer IPs (Customer)](./list-customer-ips-for-customer) - [Create Customer IP (Customer)](./create-customer-ip-for-customer)
### Create Customer Ip For Customer
URL: https://docs.uptimeify.io/api/monitors/dnsbl-monitoring/create-customer-ip-for-customer
Summary: title: Create Customer IP (Customer) description: POST /api/customers/:customerPublicId/ips --- # Create Customer IP (Customer) `POST /api/customers/:customerPublicId/ips` Creates a new customer IP for DNSBL monitoring. ## Request Body ```json { "ipAddress": "203.0.113.10", "label": "Mail Server", "ipFamily": "v4", "status": "active" } ``` Notes: - `customerPublicId` should be the customer public UUID. Legacy numeric customer IDs remain supported for compatibility. - `ipFamily` is optional; if omitted, it is inferred from the IP address. - If `ipFamily` is provided, it must match the IP address version. - Creating an `active` IP may be blocked by quota limits. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X POST "$BASE_URL/api/customers/6bfec6f6-245a-47ce-843b-157d97d56f88/ips" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"ipAddress":"203.0.113.10","label":"Mail Server","status":"active"}' ``` ## Response Returns the newly created customer IP record. ## Common errors - `400 Invalid Customer identifier` when `:customerPublicId` is missing/invalid - `400 Invalid IP address` when `ipAddress` is invalid - `400 IP family mismatch...` when `ipFamily` does not match the IP - `401 Unauthorized` when you are not logged in - `403 Forbidden` for readonly/global supporter users, or when you cannot access the customer - `403 Active IP limit reached...` when creating/activating would exceed your quota - `404 Customer not found` when the customer does not exist - `409 IP already exists for this customer` when the IP is already present
### Delete Customer IP
URL: https://docs.uptimeify.io/api/monitors/dnsbl-monitoring/delete-customer-ip
Description: Deletes a customer IP.
Summary: `DELETE /api/customer-ips/:customerIpPublicId` Notes: - Readonly users and global supporters cannot delete customer IPs. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X DELETE "$BASE_URL/api/customer-ips/11111111-1111-4111-8111-111111111111" \ -H "Authorization: Bearer $TOKEN" ``` ## Example Response ```json { "success": true } ``` ## Common errors - `400 Invalid IP identifier` when `:customerIpPublicId` is neither a valid UUID nor a legacy numeric ID - `401 Unauthorized` when you are not logged in - `403 Forbidden` for readonly/global supporter users, or when you cannot access the customer - `404 IP not found` when the IP does not exist
### Get Customer IP
URL: https://docs.uptimeify.io/api/monitors/dnsbl-monitoring/get-customer-ip
Description: Returns a customer IP including DNSBL status (if available).
Summary: `GET /api/customer-ips/:customerIpPublicId` The response contains the customer IP itself and, if available, a `dnsbl` object. It does not embed the full customer record. ## Path Parameters - `customerIpPublicId` (recommended): customer IP public UUID - Backward compatibility: legacy numeric IDs are still accepted ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/customer-ips/11111111-1111-4111-8111-111111111111" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example response ```json { "id": 1, "publicId": "a19effb9-eb5f-459b-b5d6-2f615b574651", "customerId": 2, "customerName": "Zaskoku & Haupt GbR", "label": "Kurbelix Adminserver", "ipAddress": "77.75.254.185", "ipFamily": "v4", "status": "active", "createdAt": "2026-02-03T15:06:46.469Z", "updatedAt": "2026-02-03T15:06:46.469Z", "dnsbl": { "isListed": true, "listedCount": 1, "listings": [ { "name": "all.s5h.net", "reason": "Listed in all.s5h.net", "listKey": "all.s5h.net", "delistUrl": "http://s5h.net", "resultCode": "127.0.0.2" } ], "lastError": null, "lastCheckedAt": "2026-04-03T15:13:00.083Z", "lastChangedAt": "2026-02-04T10:00:00.121Z", "lastListedAt": "2026-04-03T15:13:00.083Z", "lastCleanAt": null, "lastNotifiedListedAt": "2026-04-02T20:55:04.969Z", "lastNotifiedCleanAt": null } } ``` ## Common errors - `400 Invalid IP identifier` when `:customerIpPublicId` is neither a valid UUID nor a legacy numeric ID - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you cannot access the customer - `404 IP not found` when the IP does not exist
### List Customer IPs (Org)
URL: https://docs.uptimeify.io/api/monitors/dnsbl-monitoring/list-customer-ips
Description: Lists customer IPs within an organization (pagination + search). This is the primary API for DNSBL monitoring configuration.
Summary: `GET /api/customer-ips` ## Query Parameters - `organizationId` (optional): defaults to your session organization - `customerId` (optional): accepts the customer public UUID (recommended) or a legacy numeric customer ID - `search` (optional) - `page` (optional, default `1`) - `perPage` (optional, default `50`, max `200`) ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/customer-ips?organizationId=1&page=1&perPage=50" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example Response ```json { "items": [], "total": 0, "page": 1, "perPage": 50 } ``` ## Common errors - `400 Organization ID required` when the organization cannot be derived - `400` query validation errors (e.g. invalid `organizationId`, `customerId`, `page`, `perPage`) - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you cannot access the organization
### List Customer Ips For Customer
URL: https://docs.uptimeify.io/api/monitors/dnsbl-monitoring/list-customer-ips-for-customer
Summary: title: List Customer IPs (Customer) description: GET /api/customers/:customerPublicId/ips --- # List Customer IPs (Customer) `GET /api/customers/:customerPublicId/ips` Lists IPs for a single customer, including DNSBL status. ## Path Parameters - `customerPublicId` (recommended): Customer public UUID - Legacy compatibility: numeric customer IDs are still accepted ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/customers/6bfec6f6-245a-47ce-843b-157d97d56f88/ips" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Response Returns an array of customer IP objects. Each item may include a `dnsbl` object. ## Common errors - `400 Invalid Customer identifier` when `:customerPublicId` is missing/invalid - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you cannot access the customer - `404 Customer not found` when the customer does not exist
### Update Customer IP
URL: https://docs.uptimeify.io/api/monitors/dnsbl-monitoring/update-customer-ip
Description: Updates a customer IP (e.g. label or status).
Summary: `PATCH /api/customer-ips/:customerIpPublicId` Notes: - Readonly users and global supporters cannot update customer IPs. - Activating an IP can be denied when quota limits are reached. ## Request Body ```json { "label": "Mail Server", "status": "active" } ``` ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X PATCH "$BASE_URL/api/customer-ips/11111111-1111-4111-8111-111111111111" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"label":"Mail Server","status":"active"}' ``` ## Response Returns the updated customer IP record. ## Common errors - `400 Invalid IP identifier` when `:customerIpPublicId` is neither a valid UUID nor a legacy numeric ID - `400` body validation errors (e.g. invalid `status`) - `401 Unauthorized` when you are not logged in - `403 Forbidden` for readonly/global supporter users, or when you cannot access the customer - `403 Active IP limit reached...` when activating would exceed your quota - `404 IP not found` when the IP does not exist
### Domain Expiry Monitoring (Customer Domains)
URL: https://docs.uptimeify.io/api/monitors/domain-expiry-monitoring
Description: Domain expiry monitoring is configured via customer domains.
Summary: ## Endpoints - [Create Customer Domain (Customer)](./create-customer-domain) - [List Customer Domains (Org)](./list-customer-domains) - [Get Customer Domain](./get-customer-domain) - [Update Customer Domain](./update-customer-domain) - [Delete Customer Domain](./delete-customer-domain) - [List Domain Expiry (Websites)](./list-domain-expiry-websites)
### Create Customer Domain
URL: https://docs.uptimeify.io/api/monitors/domain-expiry-monitoring/create-customer-domain
Summary: title: Create Customer Domain (Customer) description: POST /api/customers/:customerPublicId/domains --- # Create Customer Domain (Customer) `POST /api/customers/:customerPublicId/domains` Creates a customer domain for expiry monitoring. ## Request Body ```json { "domainName": "deinkunde.com", "label": "Main Domain", "status": "active", "expiryWarningDays": 30, "expiryErrorDays": 7 } ``` Notes: - `customerPublicId` should be the customer public UUID. Legacy numeric customer IDs remain supported for compatibility. - `domainName` must be a valid domain like `deinkunde.com` (no protocol, no path). - Creating an `active` domain may be blocked by quota limits. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X POST "$BASE_URL/api/customers/6bfec6f6-245a-47ce-843b-157d97d56f88/domains" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"domainName":"deinkunde.com","label":"Main Domain","status":"active","expiryWarningDays":30,"expiryErrorDays":7}' ``` ## Response Returns the newly created customer domain record. ## Common errors - `400 Invalid Customer identifier` when `:customerPublicId` is missing/invalid - `400 Invalid domain name...` when `domainName` is invalid - `401 Unauthorized` when you are not logged in - `403 Forbidden` for readonly/global supporter users, or when you cannot access the customer - `403 Active domain limit reached...` when creating/activating would exceed your quota - `404 Customer not found` when the customer does not exist
### Delete Customer Domain
URL: https://docs.uptimeify.io/api/monitors/domain-expiry-monitoring/delete-customer-domain
Description: Deletes a customer domain.
Summary: `DELETE /api/customer-domains/:customerDomainPublicId` Notes: - Readonly users and global supporters cannot delete customer domains. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X DELETE "$BASE_URL/api/customer-domains/22222222-2222-4222-8222-222222222222" \ -H "Authorization: Bearer $TOKEN" ``` ## Example Response ```json { "success": true } ``` ## Common errors - `400 Invalid Domain identifier` when `:customerDomainPublicId` is neither a valid UUID nor a legacy numeric ID - `401 Unauthorized` when you are not logged in - `403 Forbidden` for readonly/global supporter users, or when you cannot access the domain/customer - `404 Domain not found` when the domain does not exist
### Get Customer Domain
URL: https://docs.uptimeify.io/api/monitors/domain-expiry-monitoring/get-customer-domain
Description: Returns a single customer domain.
Summary: `GET /api/customer-domains/:customerDomainPublicId` The response contains the customer domain itself and does not embed the full customer record. ## Path Parameters - `customerDomainPublicId` (recommended): customer domain public UUID - Backward compatibility: legacy numeric IDs are still accepted Notes: - Some registries/TLDs (e.g. `.de`) may not publish an expiration date via RDAP. In that case, `expiry.domainExpiresAt` can be `null` and `expiry.lastError` may contain an explanatory message. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/customer-domains/22222222-2222-4222-8222-222222222222" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example response ```json { "id": 20, "publicId": "d70884d9-4cef-4fe5-9b15-341b7dd9e196", "customerId": 2, "customerName": "Zaskoku & Haupt GbR", "label": "Primary Domain", "domainName": "cacheassist.io", "status": "active", "expiryWarningDays": 30, "expiryErrorDays": 7, "createdAt": "2026-04-03T15:28:40.602Z", "updatedAt": "2026-04-03T15:28:40.602Z", "expiry": { "domainExpiresAt": null, "domainRegistrar": null, "isExpired": false, "lastError": "RDAP query error for cacheassist.io: fetch failed", "lastCheckedAt": "2026-04-03T15:29:00.200Z", "lastChangedAt": null, "lastNotifiedWarningAt": null, "lastNotifiedErrorAt": null, "lastNotifiedExpiredAt": null } } ``` ## Common errors - `400 Invalid Domain identifier` when `:customerDomainPublicId` is neither a valid UUID nor a legacy numeric ID - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you cannot access the domain/customer - `404 Domain not found` when the domain does not exist
### List Customer Domains (Org)
URL: https://docs.uptimeify.io/api/monitors/domain-expiry-monitoring/list-customer-domains
Description: Lists customer domains within an organization (pagination + search).
Summary: `GET /api/customer-domains` ## Query Parameters - `organizationId` (optional): defaults to your session organization - `customerId` (optional): accepts the customer public UUID (recommended) or a legacy numeric customer ID - `search` (optional) - `page` (optional, default `1`) - `perPage` (optional, default `50`, max `200`) ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/customer-domains?organizationId=1&page=1&perPage=50" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Response Returns a paginated response: ```json { "items": [ { "id": 1, "publicId": "34eb0ec1-315d-45c4-aefe-0e5d12b11183", "customerId": 2, "customerName": "Zaskoku & Haupt GbR", "customerPublicId": "6764e84f-f02a-43e6-a46d-cecaec556723", "label": null, "domainName": "digitalduett.com", "status": "active", "expiryWarningDays": 30, "expiryErrorDays": 7, "createdAt": "2026-02-13T21:21:03.001Z", "updatedAt": "2026-02-13T21:21:03.001Z", "expiry": { "domainExpiresAt": "2026-12-20T20:44:45.000Z", "domainRegistrar": "NameCheap, Inc.", "isExpired": false, "lastError": null, "lastCheckedAt": "2026-04-03T10:59:00.321Z", "lastChangedAt": "2026-04-03T10:59:00.321Z", "lastNotifiedWarningAt": null, "lastNotifiedErrorAt": null, "lastNotifiedExpiredAt": null } } ], "total": 0, "page": 1, "perPage": 50 } ``` Each item includes flat customer fields plus an `expiry` object (or `null`). ## Common errors - `400 Organization ID required` when the organization cannot be derived - `400` query validation errors (e.g. invalid `organizationId`, `customerId`, `page`, `perPage`) - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you cannot access the organization
### List Domain Expiry (Websites)
URL: https://docs.uptimeify.io/api/monitors/domain-expiry-monitoring/list-domain-expiry-websites
Description: Lists websites with latest known domain expiry information.
Summary: `GET /api/domains` ## Query Parameters - `organizationId` (optional): defaults to your session organization - `customerId` (optional) - `search` (optional) - `page` (optional, default `1`) - `perPage` (optional, default `50`, max `200`) ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/domains?organizationId=1&page=1&perPage=50" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Response ```json { "items": [ { "id": 101, "name": "Example Website", "url": "https://deinkunde.com", "status": "active", "customerId": 10, "customerName": "Example Customer", "checkDomainExpiryEnabled": true, "domainExpiryNoticeDays": 30, "domainExpiryErrorDays": 7, "domainExpiresAt": "2026-12-31T00:00:00.000Z", "domainRegistrar": "Example Registrar", "lastDomainCheckAt": "2026-03-03T10:00:00.000Z", "daysUntilExpiry": 303, "expiryStatus": "ok" } ], "total": 1, "page": 1, "perPage": 50 } ``` ## Field notes - `expiryStatus` can be: `ok`, `warning`, `critical`, `expired`, `unknown`, `disabled`. - `daysUntilExpiry` is `null` when no expiration date is available. ## Common errors - `400 Organization ID required` when the organization cannot be derived - `400 Invalid customerId` when `customerId` is invalid - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you cannot access the organization
### Update Customer Domain
URL: https://docs.uptimeify.io/api/monitors/domain-expiry-monitoring/update-customer-domain
Description: Updates a customer domain (e.g. label, status, expiry thresholds).
Summary: `PATCH /api/customer-domains/:customerDomainPublicId` Notes: - Readonly users and global supporters cannot update customer domains. - Activating a disabled domain can be denied when quota limits are reached. ## Request Body ```json { "label": "Main Domain", "status": "active", "expiryWarningDays": 30, "expiryErrorDays": 7 } ``` ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X PATCH "$BASE_URL/api/customer-domains/22222222-2222-4222-8222-222222222222" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"status":"active"}' ``` ## Response Returns the updated customer domain record. ## Common errors - `400` body validation errors (e.g. invalid thresholds) - `400 Invalid Domain identifier` when `:customerDomainPublicId` is neither a valid UUID nor a legacy numeric ID - `401 Unauthorized` when you are not logged in - `403 Forbidden` for readonly/global supporter users, or when you cannot access the domain/customer - `403 Active domain limit reached...` when activating would exceed your quota - `404 Domain not found` when the domain does not exist
### FTP Monitors
URL: https://docs.uptimeify.io/api/monitors/ftp-monitors
Description: Manage FTP monitors.
Summary: Path-based FTP monitor endpoints use `ftpMonitorPublicId` UUIDs. ## Endpoints - [List FTP Monitors](./list-ftp-monitors) - [Create FTP Monitor](./create-ftp-monitor) - [Get FTP Monitor](./get-ftp-monitor) - [Get FTP Monitor Details](./get-ftp-monitor-details) - [Get FTP Monitor Check History](./get-ftp-monitor-check-history) - [Update FTP Monitor (Change Status)](./update-ftp-monitor) - [Delete FTP Monitor](./delete-ftp-monitor) - [Trigger FTP Check](./trigger-check-ftp-monitor)
### Create FTP Monitor
URL: https://docs.uptimeify.io/api/monitors/ftp-monitors/create-ftp-monitor
Description: Creates a new FTP monitor.
Summary: `POST /api/ftp-monitors` ## Authentication This endpoint requires an API token: `Authorization: Bearer ` ## Request Body - `customerId` (required, number | string): internal numeric customer ID or customer `publicId` UUID - `name` (required, string) - `hostname` (required, string): Must be a hostname (no protocol, no path). - `port` (optional, number | null): If omitted/null, the worker uses a default port. - `status` (optional): `active`, `maintenance`, `disabled` (also accepts `inactive` and normalizes it to `disabled`) - `checkInterval` (optional, number): Minutes (min: 1, max: 60) - `timeoutSeconds` (optional, number): Seconds (min: 1, max: 60) - `checkMode` (optional, string): `protocol` | `tcp`. Defaults to `protocol`. - In `tcp` mode the monitor performs a bare TCP port-reachability check: `ftpConfig` is not required and is not stored. In `protocol` mode the full FTP login check runs (existing behavior). - `ftpConfig` (optional, object): FTP connection options (stored as JSON) - Required (`user` + `password`) unless `checkMode` is `tcp`. ### `ftpConfig` The monitoring worker reads these keys: - `user` (optional, string): Defaults to `anonymous` - `password` (optional, string): Defaults to `anonymous@` - `secure` (optional, boolean | `"implicit"`): - `false` (default): plain FTP - `true`: explicit TLS - `"implicit"`: implicit TLS (worker defaults port to `990` when `port` is not provided) ```json { "customerId": "6764e84f-f02a-43e6-a46d-cecaec556723", "name": "FTP Availability", "hostname": "claas.sh", "port": 21, "status": "active", "checkInterval": 5, "timeoutSeconds": 30, "ftpConfig": { "user": "root", "password": "exampleRoot", "secure": "explicit" } } ``` ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X POST \ "$BASE_URL/api/ftp-monitors" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"customerId":"6764e84f-f02a-43e6-a46d-cecaec556723","name":"FTP Availability","hostname":"claas.sh","port":21,"status":"active","checkInterval":5,"timeoutSeconds":30,"ftpConfig":{"user":"root","password":"exampleRoot","secure":"explicit"}}' ``` ## Response ```json { "id": 400, "organizationId": 1, "customerId": 2, "name": "FTP Availability", "hostname": "claas.sh", "port": 21, "status": "active", "checkInterval": 5, "timeoutSeconds": 30, "allowedCheckCountryCodes": null, "notificationPhoneNumber": null, "notificationEmail": null, "lastCheckedAt": null, "createdAt": "2026-02-25T10:00:00.000Z", "updatedAt": "2026-02-25T10:00:00.000Z", "config": { "user": "root", "password": "exampleRoot", "secure": "explicit" }, "ftpConfig": { "user": "root", "password": "exampleRoot", "secure": "explicit" } } ``` ## Common Errors - `400 Invalid Customer identifier` if `customerId` is neither a valid UUID nor a legacy numeric ID - `400 hostname must be a valid hostname (no protocol, no path)` on invalid hostnames - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access or are not allowed to create monitors (e.g. read-only or global supporter) - `404 Customer not found` if the `customerId` does not exist
### Delete FTP Monitor
URL: https://docs.uptimeify.io/api/monitors/ftp-monitors/delete-ftp-monitor
Description: Deletes an FTP monitor.
Summary: `DELETE /api/ftp-monitors/:ftpMonitorPublicId` ## Authentication `Authorization: Bearer ` ## Parameters - `ftpMonitorPublicId` (Path, required): FTP monitor public UUID. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X DELETE \ "$BASE_URL/api/ftp-monitors/55555555-5555-4555-8555-555555555555" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "success": true } ``` ## Common Errors - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have write access (e.g. global supporter or read-only) - `404 FTP monitor not found` if the monitor does not exist
### Get FTP Monitor
URL: https://docs.uptimeify.io/api/monitors/ftp-monitors/get-ftp-monitor
Description: Returns details of a specific FTP monitor.
Summary: `GET /api/ftp-monitors/:ftpMonitorPublicId` The response contains the FTP monitor itself and does not embed the full customer record. ## Authentication `Authorization: Bearer ` ## Parameters - `ftpMonitorPublicId` (Path, required): FTP monitor public UUID. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET \ "$BASE_URL/api/ftp-monitors/55555555-5555-4555-8555-555555555555" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "id": 400, "organizationId": 1, "customerId": 10, "name": "Partner FTP", "hostname": "ftp.deinkunde.com", "port": 21, "status": "active", "checkInterval": 30, "timeoutSeconds": 30, "allowedCheckCountryCodes": null, "notificationPhoneNumber": null, "notificationEmail": null, "lastCheckedAt": null, "createdAt": "2026-02-25T10:00:00.000Z", "updatedAt": "2026-02-25T10:00:00.000Z", "config": { "user": "monitor", "password": "", "secure": false }, "ftpConfig": { "user": "monitor", "password": "", "secure": false } } ``` ## Common Errors - `400 FTP monitor public ID (UUID) required` if `ftpMonitorPublicId` is missing or invalid - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access - `404 FTP monitor not found` if the monitor does not exist
### Get FTP Monitor Alert History
URL: https://docs.uptimeify.io/api/monitors/ftp-monitors/get-ftp-monitor-alert-history
Description: Returns notification/escalation attempts (alert history) for incidents of a FTP monitor.
Summary: `GET /api/ftp-monitors/:ftpMonitorPublicId/alert-history` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `ftpMonitorPublicId` (Path, required): FTP monitor public UUID. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/ftp-monitors/11111111-1111-4111-8111-111111111111/alert-history" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example Response ```json { "alerts": [ { "id": 987, "incidentId": 123, "type": "slack", "status": "sent", "channelName": "Ops Slack", "sentAt": "2026-02-26T12:11:00.000Z", "errorMessage": null } ], "total": 1 } ``` Each entry is one delivery attempt to one notification channel. `status` is `sent` or `failed` (with `errorMessage` set on failure); `type` is the channel type (e.g. `email`, `slack`, `opsgenie`). ## Common Errors - `400 FTP monitor public ID (UUID) required` if `:ftpMonitorPublicId` is invalid - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access to the monitor
### Get FTP Monitor Check History
URL: https://docs.uptimeify.io/api/monitors/ftp-monitors/get-ftp-monitor-check-history
Description: Returns recent FTP check results for a monitor.
Summary: `GET /api/ftp-monitors/:ftpMonitorPublicId/check-history` ## Authentication `Authorization: Bearer ` ## Parameters - `ftpMonitorPublicId` (Path, required): FTP monitor public UUID. - `limit` (Query, optional): Number of results (default: 50, max: 200) ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET \ "$BASE_URL/api/ftp-monitors/55555555-5555-4555-8555-555555555555/check-history?limit=25" \ -H "Authorization: Bearer $TOKEN" ``` ## Example Response ```json { "data": [ { "id": "2fd6d5ab-1e2a-4b4f-9c42-0f3a33a4a1d2", "status": "success", "errorMessage": null, "warningMessage": null, "timingFtp": 123, "diagnostics": { "message": "FTP connection successful" }, "checkedAt": "2026-02-25T10:05:00.000Z", "locationId": 7, "locationName": "Nuremberg (DE)", "locationCode": "de-nbg" } ] } ``` ## Common Errors - `400 FTP monitor public ID (UUID) required` if `ftpMonitorPublicId` is missing or invalid - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access - `404 FTP monitor not found` if the monitor does not exist
### Get FTP Monitor Details
URL: https://docs.uptimeify.io/api/monitors/ftp-monitors/get-ftp-monitor-details
Description: Returns the FTP monitor detail page data in one call (mega endpoint).
Summary: `GET /api/ftp-monitors/:ftpMonitorPublicId/details` ## Authentication `Authorization: Bearer ` ## Parameters - `ftpMonitorPublicId` (Path, required): FTP monitor public UUID. ## Query Parameters - `range` (optional): `day` (default), `week`, `month`, `year` - `date` (optional): Reference date (ISO string) - `startDate` / `endDate` (optional): Override the time window (ISO strings) - `granularity` (optional): When omitted the server may aggregate automatically for large time ranges. Use `granularity=raw` to force raw data. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET \ "$BASE_URL/api/ftp-monitors/55555555-5555-4555-8555-555555555555/details?range=day" \ -H "Authorization: Bearer $TOKEN" ``` ## Example Response (shape) The response is a single object with these top-level keys: ```json { "ftpMonitorId": 400, "monitor": {}, "latestCheck": {}, "uptimeStats": { "day": "100.00", "month": "100.00", "year": "100.00", "dayAvgResponse": 120, "monthAvgResponse": 130, "yearAvgResponse": 140 }, "uptimeStatsMeta": {}, "monitoringData": { "responseTimeData": [], "statusData": [], "uptimePercentage": "100.00", "checkSuccessRatePercentage": "100.00", "totalChecks": 10, "successfulChecks": 10 }, "incidents": { "history": [], "total": 0, "ongoing": 0, "totalDowntime": "0s" }, "alerts": { "history": [], "total": 0, "notificationContext": {} }, "testResultLog": null, "maintenance": { "inMaintenance": false, "activeWindows": [], "allWindows": [] } } ``` ## Common Errors - `400 Invalid FTP monitor public ID (UUID)` if `ftpMonitorPublicId` is invalid - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access - `500 Failed to fetch FTP monitor details` on server errors
### Get FTP Monitor Incident History
URL: https://docs.uptimeify.io/api/monitors/ftp-monitors/get-ftp-monitor-incident-history
Description: Returns incidents for a single FTP monitor (latest 100), including a computed duration.
Summary: `GET /api/ftp-monitors/:ftpMonitorPublicId/incident-history` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `ftpMonitorPublicId` (Path, required): FTP monitor public UUID. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/ftp-monitors/11111111-1111-4111-8111-111111111111/incident-history" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example Response ```json { "incidents": [ { "id": 123, "type": "downtime", "status": "resolved", "startedAt": "Feb 26, 2026, 12:10:00", "endedAt": "Feb 26, 2026, 12:12:30", "duration": "2m 30s" } ], "total": 1 } ``` `type` reflects the failure category for this monitor; `status` is `active` (ongoing) or `resolved`. Ongoing incidents have a `null` `endedAt`. ## Common Errors - `400 FTP monitor public ID (UUID) required` if `:ftpMonitorPublicId` is invalid - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access to the monitor
### List FTP Monitors
URL: https://docs.uptimeify.io/api/monitors/ftp-monitors/list-ftp-monitors
Description: Lists FTP monitors in an organization.
Summary: `GET /api/ftp-monitors` ## Authentication `Authorization: Bearer ` ## Parameters - `organizationId` (Query, optional): Organization ID. Defaults to the current user's organization. - `customerId` (Query, optional): Filter by customer ID. - `search` (Query, optional): Search by monitor name, hostname, or customer. - `page` (Query, optional): Page number (default: 1). - `perPage` (Query, optional): Items per page (default: 50, max: 200). Note: The results are also limited by your customer scope (if your account is restricted to a subset of customers). ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET \ "$BASE_URL/api/ftp-monitors?page=1&perPage=50&search=ftp" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "items": [ { "id": 400, "organizationId": 1, "customerId": 10, "customerName": "Example Customer", "name": "Partner FTP", "hostname": "ftp.deinkunde.com", "port": 21, "status": "active", "checkInterval": 30, "timeoutSeconds": 30, "allowedCheckCountryCodes": null, "config": { "user": "monitor", "password": "", "secure": false }, "notificationPhoneNumber": null, "notificationEmail": null, "lastCheckedAt": null, "createdAt": "2026-02-25T10:00:00.000Z", "updatedAt": "2026-02-25T10:00:00.000Z" } ], "total": 1, "page": 1, "perPage": 50 } ``` Each item contains the FTP monitor itself and a flat `customerName`, not a full embedded customer record. ## Common Errors - `400 Invalid organizationId` if `organizationId` is not a valid number - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access to the organization
### Trigger FTP Check
URL: https://docs.uptimeify.io/api/monitors/ftp-monitors/trigger-check-ftp-monitor
Description: Triggers an immediate check from all eligible monitoring locations.
Summary: `POST /api/ftp-monitors/:ftpMonitorPublicId/trigger-check` ## Authentication `Authorization: Bearer ` ## Parameters - `ftpMonitorPublicId` (Path, required): FTP monitor public UUID. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X POST \ "$BASE_URL/api/ftp-monitors/55555555-5555-4555-8555-555555555555/trigger-check" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "success": true, "message": "Checks triggered successfully", "ftpMonitorId": 400, "locationCodes": ["de-nbg", "fi-hel"], "queueNames": ["ftp-monitor-checks-de-nbg", "ftp-monitor-checks-fi-hel"] } ``` ## Common Errors - `400 FTP monitor public ID (UUID) required` if `ftpMonitorPublicId` is missing or invalid - `400 No eligible monitoring locations for the selected allowed countries` if your country restrictions match no active locations - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have write access - `503 No active monitoring locations available` if no worker locations are active
### Update FTP Monitor (Change Status)
URL: https://docs.uptimeify.io/api/monitors/ftp-monitors/update-ftp-monitor
Description: Updates an FTP monitor and/or changes its status.
Summary: `PATCH /api/ftp-monitors/:ftpMonitorPublicId` ## Authentication `Authorization: Bearer ` ## Parameters - `ftpMonitorPublicId` (Path, required): FTP monitor public UUID. ## Request Body - `status` (optional): `active`, `maintenance`, `disabled` (also accepts `inactive`/`paused` and normalizes them to `disabled`) - `customerId` (optional, number): Must belong to the same organization - `name` (optional, string) - `hostname` (optional, string): Must be a hostname (no protocol, no path) - `port` (optional, number | null) - `checkInterval` (optional, number) - `timeoutSeconds` (optional, number) - `allowedCheckCountryCodes` (optional, string[] | null): ISO-3166-1 alpha-2, e.g. `"DE"`, `"ES"` - `checkMode` (optional, string): `protocol` (default) | `tcp`. Switching to `tcp` clears any stored credentials: the monitor performs a bare TCP port-reachability check and `ftpConfig` is not stored. Switching back to `protocol` re-enables the full FTP login check; supply `ftpConfig`/`config` to set new credentials. - `ftpConfig` (optional, object): Replaces the stored FTP config JSON - `config` (optional, object): Alias of `ftpConfig` Note: Read-only users may only change `status`. Note: If you omit `ftpConfig`/`config`, the current config remains unchanged. ```json { "status": "maintenance" } ``` ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X PATCH \ "$BASE_URL/api/ftp-monitors/55555555-5555-4555-8555-555555555555" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"status":"active","allowedCheckCountryCodes":["DE","ES"],"ftpConfig":{"user":"monitor","password":"","secure":false}}' ``` ## Response ```json { "id": 400, "status": "maintenance", "config": {}, "ftpConfig": {} } ``` ## Common Errors - `400 Invalid status` if `status` is not valid - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have write access (e.g. read-only user tries to change other fields, or global supporter) - `404 FTP monitor not found` if the monitor does not exist
### ICMP Monitors
URL: https://docs.uptimeify.io/api/monitors/icmp-monitors
Description: Manage ICMP (ping) monitors.
Summary: Path-based ICMP monitor endpoints use `icmpMonitorPublicId` UUIDs. ## Endpoints - [List ICMP Monitors](./list-icmp-monitors) - [Create ICMP Monitor](./create-icmp-monitor) - [Get ICMP Monitor](./get-icmp-monitor) - [Get ICMP Monitor Details](./get-icmp-monitor-details) - [Get ICMP Monitor Check History](./get-icmp-monitor-check-history) - [Update ICMP Monitor](./update-icmp-monitor) - [Delete ICMP Monitor](./delete-icmp-monitor) - [Trigger ICMP Check](./trigger-check-icmp-monitor)
### Create ICMP Monitor
URL: https://docs.uptimeify.io/api/monitors/icmp-monitors/create-icmp-monitor
Description: Creates a new ICMP monitor.
Summary: `POST /api/icmp-monitors` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Request Body ```json { "customerId": "6764e84f-f02a-43e6-a46d-cecaec556723", "name": "Ping Gateway", "hostname": "claas.sh", "status": "active", "checkInterval": 5, "timeoutSeconds": 10, "icmpConfig": { "packetSize": 56, "count": 4 } } ``` ### Fields - `customerId` (number | string, required) - Accepts the internal numeric customer ID or the customer `publicId` UUID. - `name` (string, required) - `hostname` (string, required) - Must be a hostname or IP only (no protocol like `https://`, no path like `/ping`). - `status` (string, optional) - Allowed: `active`, `maintenance`, `disabled`, `paused`, `inactive` - Note: `paused` / `inactive` are normalized to `disabled`. - `checkInterval` (number, optional) - `timeoutSeconds` (number, optional) - `icmpConfig` (object, optional) - Stored as the monitor's `config` JSON. ### `icmpConfig` (worker-supported keys) - `packetSize` (number) - Default: `56` - `count` (number) - Default: `3` ## cURL ```bash curl -X POST "https://YOUR_DOMAIN/api/icmp-monitors" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "customerId": "6764e84f-f02a-43e6-a46d-cecaec556723", "name": "Ping Gateway", "hostname": "claas.sh", "status": "active", "checkInterval": 5, "timeoutSeconds": 10, "icmpConfig": { "packetSize": 56, "count": 4 } }' ``` ## Response ```json { "id": 123, "organizationId": 1, "customerId": 2, "name": "Ping Gateway", "hostname": "claas.sh", "port": null, "status": "active", "checkInterval": 5, "timeoutSeconds": 10, "allowedCheckCountryCodes": null, "notificationPhoneNumber": null, "notificationEmail": null, "lastCheckedAt": null, "createdAt": "2026-02-26T12:00:00.000Z", "updatedAt": "2026-02-26T12:00:00.000Z", "config": { "packetSize": 56, "count": 4 }, "icmpConfig": { "packetSize": 56, "count": 4 } } ``` ## Errors - `400 Invalid Customer identifier` - `400` Invalid hostname or invalid request body - `401` Unauthorized - `403` Forbidden (e.g. `readonly` or global supporter) - `404` Customer not found
### Delete ICMP Monitor
URL: https://docs.uptimeify.io/api/monitors/icmp-monitors/delete-icmp-monitor
Description: Deletes an ICMP monitor.
Summary: `DELETE /api/icmp-monitors/:icmpMonitorPublicId` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `icmpMonitorPublicId` (Path, required): ICMP monitor public UUID. ## cURL ```bash curl -X DELETE "https://YOUR_DOMAIN/api/icmp-monitors/22222222-2222-4222-8222-222222222222" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "success": true } ``` ## Errors - `400` ICMP monitor public ID (UUID) required - `401` Unauthorized - `403` Forbidden - `404` ICMP monitor not found
### Get ICMP Monitor
URL: https://docs.uptimeify.io/api/monitors/icmp-monitors/get-icmp-monitor
Description: Returns details of a specific ICMP monitor.
Summary: `GET /api/icmp-monitors/:icmpMonitorPublicId` The response contains the ICMP monitor itself and does not embed the full customer record. ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `icmpMonitorPublicId` (Path, required): ICMP monitor public UUID. ## cURL ```bash curl "https://YOUR_DOMAIN/api/icmp-monitors/22222222-2222-4222-8222-222222222222" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "id": 123, "organizationId": 1, "customerId": 10, "name": "Core Router", "hostname": "10.0.0.1", "port": null, "status": "active", "checkInterval": 30, "timeoutSeconds": 30, "config": { "packetSize": 56, "count": 3 }, "notificationPhoneNumber": null, "notificationEmail": null, "lastCheckedAt": null, "createdAt": "2026-02-26T12:00:00.000Z", "updatedAt": "2026-02-26T12:00:00.000Z", "icmpConfig": { "packetSize": 56, "count": 3 } } ``` ## Errors - `400` ICMP monitor public ID (UUID) required - `401` Unauthorized - `403` Forbidden - `404` ICMP monitor not found
### Get ICMP Monitor Alert History
URL: https://docs.uptimeify.io/api/monitors/icmp-monitors/get-icmp-monitor-alert-history
Description: Returns notification/escalation attempts (alert history) for incidents of a ICMP monitor.
Summary: `GET /api/icmp-monitors/:icmpMonitorPublicId/alert-history` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `icmpMonitorPublicId` (Path, required): ICMP monitor public UUID. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/icmp-monitors/11111111-1111-4111-8111-111111111111/alert-history" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example Response ```json { "alerts": [ { "id": 987, "incidentId": 123, "type": "slack", "status": "sent", "channelName": "Ops Slack", "sentAt": "2026-02-26T12:11:00.000Z", "errorMessage": null } ], "total": 1 } ``` Each entry is one delivery attempt to one notification channel. `status` is `sent` or `failed` (with `errorMessage` set on failure); `type` is the channel type (e.g. `email`, `slack`, `opsgenie`). ## Common Errors - `400 ICMP monitor public ID (UUID) required` if `:icmpMonitorPublicId` is invalid - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access to the monitor
### Get ICMP Monitor Check History
URL: https://docs.uptimeify.io/api/monitors/icmp-monitors/get-icmp-monitor-check-history
Description: Returns recent check results for the ICMP monitor.
Summary: `GET /api/icmp-monitors/:icmpMonitorPublicId/check-history` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `icmpMonitorPublicId` (Path, required): ICMP monitor public UUID. - `limit` (Query, optional): Maximum number of results (default: 50, max: 200). ## cURL ```bash curl -X GET "https://YOUR_DOMAIN/api/icmp-monitors/22222222-2222-4222-8222-222222222222/check-history?limit=50" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "data": [ { "id": "f6b0e5a7-4c4a-4d3b-9f6b-0e5a74c4a4d3", "status": "success", "errorMessage": null, "warningMessage": null, "timingIcmp": 22, "diagnostics": { "min": 11.1, "avg": 22.2, "max": 33.3, "packetLoss": 0 }, "checkedAt": "2026-02-26T12:00:00.000Z", "locationId": 7, "locationName": "Nuremberg (DE)", "locationCode": "de-nbg" } ] } ``` ## Errors - `400` ICMP monitor public ID (UUID) required - `401` Unauthorized - `403` Forbidden - `404` ICMP monitor not found
### Get ICMP Monitor Details
URL: https://docs.uptimeify.io/api/monitors/icmp-monitors/get-icmp-monitor-details
Description: Returns a consolidated payload used by the monitor details page, including the monitor, customer, and aggregated check data for a given time range.
Summary: `GET /api/icmp-monitors/:icmpMonitorPublicId/details` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `icmpMonitorPublicId` (Path, required): ICMP monitor public UUID. ### Time range query parameters The endpoint supports multiple ways to define the time range. Use the simplest option for your use case: - `range` (Query, optional): Preset range. - Allowed: `day` (default), `week`, `month`, `year` - `date` (Query, optional): Reference date used as the default end date. - Example: `2026-02-26` - `startDate` (Query, optional): Start date/time. - `endDate` (Query, optional): End date/time. - `granularity` (Query, optional): Controls aggregation. - Use `raw` to disable aggregation. - Any other value enables aggregation. - If omitted, the server may aggregate automatically for larger ranges. If you provide multiple, the server resolves them according to its internal precedence rules. ## cURL ```bash curl -X GET "https://YOUR_DOMAIN/api/icmp-monitors/22222222-2222-4222-8222-222222222222/details?range=day&granularity=raw" \ -H "Authorization: Bearer $TOKEN" ``` ## Response Top-level fields (current shape): - `icmpMonitorId` - `monitor` - `latestCheck` - `uptimeStats`, `uptimeStatsMeta` - `monitoringData` - `incidents` - `alerts` - `testResultLog` - `maintenance` Example: ```json { "icmpMonitorId": 123, "monitor": { "id": 123, "customerId": 10, "organizationId": 1, "name": "Core Router", "hostname": "10.0.0.1", "status": "active", "checkInterval": 30, "timeoutSeconds": 30, "port": null, "config": { "packetSize": 56, "count": 3 }, "notificationPhoneNumber": null, "notificationEmail": null, "lastCheckedAt": null, "createdAt": "2026-02-26T12:00:00.000Z", "updatedAt": "2026-02-26T12:00:00.000Z" }, "latestCheck": null, "uptimeStats": { "day": "100.00", "month": "100.00", "year": "100.00", "dayAvgResponse": 22, "monthAvgResponse": 22, "yearAvgResponse": 22 }, "uptimeStatsMeta": { "day": { "window": "24h", "downtimeMinutes": 0, "incidents": 0, "checksTotal": 24, "checksFailed": 0 }, "month": { "window": "30d", "downtimeMinutes": 0, "incidents": 0, "checksTotal": 720, "checksFailed": 0 }, "year": { "window": "YTD", "downtimeMinutes": 0, "incidents": 0, "checksTotal": 1000, "checksFailed": 0 } }, "monitoringData": { "responseTimeData": [ { "timestamp": "2026-02-26T12:00:00.000Z", "responseTime": 22, "status": "success", "success": true, "timingDns": 0, "timingTcp": 22, "timingTls": 0, "timingTtfb": 0, "timingTransfer": 0 } ], "statusData": [{ "date": "26.02", "status": "online" }], "uptimePercentage": "100.00", "checkSuccessRatePercentage": "100.00", "totalChecks": 100, "successfulChecks": 100 }, "incidents": { "history": [], "total": 0, "ongoing": 0, "totalDowntime": "0s" }, "alerts": { "history": [], "total": 0, "notificationContext": { "orgDefaultEmail": null, "orgDefaultPhoneNumber": null, "customerEmail": null, "notificationTargets": [] } }, "testResultLog": [], "maintenance": { "inMaintenance": false, "activeWindows": [], "allWindows": [] } } ``` ## Errors - `400` ICMP monitor public ID (UUID) required - `401` Unauthorized - `403` Forbidden - `404` ICMP monitor not found - `500` Failed to fetch ICMP monitor details
### Get ICMP Monitor Incident History
URL: https://docs.uptimeify.io/api/monitors/icmp-monitors/get-icmp-monitor-incident-history
Description: Returns incidents for a single ICMP monitor (latest 100), including a computed duration.
Summary: `GET /api/icmp-monitors/:icmpMonitorPublicId/incident-history` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `icmpMonitorPublicId` (Path, required): ICMP monitor public UUID. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/icmp-monitors/11111111-1111-4111-8111-111111111111/incident-history" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example Response ```json { "incidents": [ { "id": 123, "type": "downtime", "status": "resolved", "startedAt": "Feb 26, 2026, 12:10:00", "endedAt": "Feb 26, 2026, 12:12:30", "duration": "2m 30s" } ], "total": 1 } ``` `type` reflects the failure category for this monitor; `status` is `active` (ongoing) or `resolved`. Ongoing incidents have a `null` `endedAt`. ## Common Errors - `400 ICMP monitor public ID (UUID) required` if `:icmpMonitorPublicId` is invalid - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access to the monitor
### List ICMP Monitors
URL: https://docs.uptimeify.io/api/monitors/icmp-monitors/list-icmp-monitors
Description: Lists ICMP monitors in an organization.
Summary: `GET /api/icmp-monitors` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `organizationId` (Query, optional): Organization ID. Defaults to the current user's organization. - `customerId` (Query, optional): Filter by customer ID. - `search` (Query, optional): Search by monitor name, hostname, or customer. - `page` (Query, optional): Page number (default: 1). - `perPage` (Query, optional): Items per page (default: 50, max: 200). ## cURL ```bash curl "https://YOUR_DOMAIN/api/icmp-monitors?page=1&perPage=50" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "items": [ { "id": 123, "organizationId": 1, "customerId": 10, "customerName": "Example Customer", "name": "Core Router", "hostname": "10.0.0.1", "status": "active", "checkInterval": 30, "timeoutSeconds": 30, "port": null, "config": { "packetSize": 56, "count": 3 }, "notificationPhoneNumber": null, "notificationEmail": null, "lastCheckedAt": null, "createdAt": "2026-02-26T12:00:00.000Z", "updatedAt": "2026-02-26T12:00:00.000Z" } ], "total": 1, "page": 1, "perPage": 50 } ``` Each item contains the ICMP monitor itself and a flat `customerName`, not a full embedded customer record. ## Errors - `400` Invalid query parameters (e.g. `organizationId`, `customerId`) - `401` Unauthorized - `403` Forbidden
### Trigger ICMP Check
URL: https://docs.uptimeify.io/api/monitors/icmp-monitors/trigger-check-icmp-monitor
Description: Triggers an immediate check from all eligible monitoring locations.
Summary: `POST /api/icmp-monitors/:icmpMonitorPublicId/trigger-check` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `icmpMonitorPublicId` (Path, required): ICMP monitor public UUID. ## cURL ```bash curl -X POST "https://YOUR_DOMAIN/api/icmp-monitors/22222222-2222-4222-8222-222222222222/trigger-check" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "success": true, "message": "Checks triggered successfully", "icmpMonitorId": 123, "locationCodes": ["de-nbg", "fi-hel"], "queueNames": ["icmp-monitor-checks-de-nbg", "icmp-monitor-checks-fi-hel"] } ``` ## Errors - `400` ICMP monitor public ID (UUID) required or no eligible locations - `401` Unauthorized - `403` Forbidden - `404` ICMP monitor not found - `503` No active monitoring locations available - `500` Failed to trigger check
### Update ICMP Monitor
URL: https://docs.uptimeify.io/api/monitors/icmp-monitors/update-icmp-monitor
Description: Updates an ICMP monitor. This endpoint is also used to change the monitor status.
Summary: `PATCH /api/icmp-monitors/:icmpMonitorPublicId` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `icmpMonitorPublicId` (Path, required): ICMP monitor public UUID. ## Request Body All fields are optional. - `status` (optional) - Allowed: `active`, `maintenance`, `disabled`, `paused`, `inactive` - Note: `paused` / `inactive` are normalized to `disabled`. - `name`, `hostname`, `checkInterval`, `timeoutSeconds` (optional) - `port` (optional) - Supported by the API, but currently not used by the ICMP worker. - `allowedCheckCountryCodes` (optional) - Normalized to upper-case and de-duplicated. - `icmpConfig` (optional) - Alias for the stored `config` JSON. - `config` (optional) - Alternative way to update the same stored `config` JSON. Note: Read-only users may only change `status` (and sending any other fields will result in `403`). ```json { "status": "maintenance", "allowedCheckCountryCodes": ["DE", "ES"] } ``` Example updating config: ```json { "icmpConfig": { "packetSize": 56, "count": 3 } } ``` ## cURL ```bash curl -X PATCH "https://YOUR_DOMAIN/api/icmp-monitors/22222222-2222-4222-8222-222222222222" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "status": "disabled" }' ``` ## Response ```json { "id": 123, "organizationId": 1, "customerId": 10, "name": "Core Router", "hostname": "10.0.0.1", "port": null, "status": "maintenance", "checkInterval": 30, "timeoutSeconds": 30, "allowedCheckCountryCodes": ["DE", "ES"], "notificationPhoneNumber": null, "notificationEmail": null, "lastCheckedAt": null, "createdAt": "2026-02-26T12:00:00.000Z", "updatedAt": "2026-02-26T12:05:00.000Z", "config": { "packetSize": 56, "count": 3 }, "icmpConfig": { "packetSize": 56, "count": 3 } } ``` ## Errors - `400` Invalid request body, invalid status, or invalid hostname - `401` Unauthorized - `403` Forbidden (e.g. `readonly` or global supporter) - `404` ICMP monitor not found
### IMAP/POP Monitors
URL: https://docs.uptimeify.io/api/monitors/imap-pop-monitors
Description: Manage IMAP/POP monitors.
Summary: Path-based IMAP/POP monitor endpoints use `imapPopMonitorPublicId` UUIDs. ## Endpoints - [List IMAP/POP Monitors](./list-imap-pop-monitors) - [Create IMAP/POP Monitor](./create-imap-pop-monitor) - [Get IMAP/POP Monitor](./get-imap-pop-monitor) - [Update IMAP/POP Monitor](./update-imap-pop-monitor) - [Delete IMAP/POP Monitor](./delete-imap-pop-monitor) - [Trigger IMAP/POP Check](./trigger-check-imap-pop-monitor) - [Get IMAP/POP Monitor Details](./get-imap-pop-monitor-details) - [Get IMAP/POP Monitor Check History](./get-imap-pop-monitor-check-history)
### Create IMAP/POP Monitor
URL: https://docs.uptimeify.io/api/monitors/imap-pop-monitors/create-imap-pop-monitor
Description: Creates a new IMAP/POP monitor.
Summary: `POST /api/imap-pop-monitors` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Request Body ```json { "customerId": "11111111-1111-4111-8111-111111111111", "name": "Mailbox Access", "hostname": "mail.deinkunde.com", "port": 993, "status": "active", "checkInterval": 30, "timeoutSeconds": 30, "imapPopConfig": { "protocol": "imap", "user": "monitor@deinkunde.com", "password": "password", "tls": true, "tlsOptions": { "rejectUnauthorized": true } } } ``` ### Fields - `customerId` (number | string, required) - Accepts either the internal numeric customer ID or the public customer UUID. - `name` (string, required) - `hostname` (string, required) - Must be a hostname only (no protocol like `https://`, no path like `/imap`). - `port` (number | null, optional) - If omitted or `null`, the worker chooses a default port depending on `imapPopConfig.protocol` and `imapPopConfig.tls`. - `status` (string, optional) - Allowed: `active`, `maintenance`, `disabled`, `paused`, `inactive` - Note: `paused` / `inactive` are normalized to `disabled`. - `checkInterval` (number, optional) - `timeoutSeconds` (number, optional) - `checkMode` (string, optional) - Allowed: `protocol`, `tcp`. Defaults to `protocol`. - In `tcp` mode the monitor performs a bare TCP port-reachability check: `imapPopConfig` is not required and is not stored. In `protocol` mode the full IMAP/POP login check runs (existing behavior). - **`port` is required when `checkMode` is `tcp`**. Without it the request fails with `400` / `data.code: "portRequiredForTcp"`. - `imapPopConfig` (object, optional) - Stored as the monitor's `config` JSON. - Required (`user` + `password`) unless `checkMode` is `tcp`. ### `imapPopConfig` (worker-supported keys) - `protocol` (`imap` | `pop3`) - Default: `imap` - `user` (string) - `password` (string) - `tls` (boolean) - Default: `false` - `tlsOptions.rejectUnauthorized` (boolean) - Used by the IMAP worker library; POP3 currently ignores this option. ### Default ports (worker behavior) If `port` is omitted or `null`: - IMAP: - TLS (`tls: true`): `993` - Non-TLS (`tls: false`): `143` - POP3: - TLS (`tls: true`): `995` - Non-TLS (`tls: false`): `110` ## cURL ```bash curl -X POST "https://YOUR_DOMAIN/api/imap-pop-monitors" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "customerId": "11111111-1111-4111-8111-111111111111", "name": "Mailbox Access", "hostname": "mail.deinkunde.com", "port": 993, "status": "active", "checkInterval": 30, "timeoutSeconds": 30, "imapPopConfig": { "protocol": "imap", "user": "monitor@deinkunde.com", "password": "password", "tls": true } }' ``` ## Response ```json { "id": 500, "organizationId": 1, "customerId": 10, "name": "Mailbox Access", "hostname": "mail.deinkunde.com", "port": 993, "checkInterval": 30, "timeoutSeconds": 30, "status": "active", "notificationPhoneNumber": null, "notificationEmail": null, "lastCheckedAt": null, "createdAt": "2026-02-26T12:00:00.000Z", "updatedAt": "2026-02-26T12:00:00.000Z", "config": { "protocol": "imap", "user": "monitor@deinkunde.com", "password": "password", "tls": true }, "imapPopConfig": { "protocol": "imap", "user": "monitor@deinkunde.com", "password": "password", "tls": true } } ``` ## Errors - `400` Invalid hostname or invalid request body - `400` Invalid Customer identifier - `401` Unauthorized - `403` Forbidden (e.g. `readonly` or global supporter) - `404` Customer not found
### Delete IMAP/POP Monitor
URL: https://docs.uptimeify.io/api/monitors/imap-pop-monitors/delete-imap-pop-monitor
Description: Deletes an IMAP/POP monitor.
Summary: `DELETE /api/imap-pop-monitors/:imapPopMonitorPublicId` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `imapPopMonitorPublicId` (Path, required): IMAP/POP monitor public UUID. ## Response ```json { "success": true } ``` ## Errors - `400` IMAP/POP monitor public ID (UUID) required - `401` Unauthorized - `403` Forbidden (e.g. `readonly` or global supporter) - `404` IMAP/POP monitor not found
### Get IMAP/POP Monitor
URL: https://docs.uptimeify.io/api/monitors/imap-pop-monitors/get-imap-pop-monitor
Description: Returns details of a specific IMAP/POP monitor.
Summary: `GET /api/imap-pop-monitors/:imapPopMonitorPublicId` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `imapPopMonitorPublicId` (Path, required): IMAP/POP monitor public UUID. ## Response ```json { "id": 500, "organizationId": 1, "customerId": 10, "name": "Mailbox Access", "hostname": "mail.deinkunde.com", "port": 993, "checkInterval": 30, "timeoutSeconds": 30, "config": { "protocol": "imap", "user": "monitor@deinkunde.com", "password": "password", "tls": true }, "allowedCheckCountryCodes": null, "status": "active", "notificationPhoneNumber": null, "notificationEmail": null, "lastCheckedAt": null, "createdAt": "2026-02-26T12:00:00.000Z", "updatedAt": "2026-02-26T12:00:00.000Z", "customer": { "id": 10, "organizationId": 1, "name": "Example Customer", "email": "ops@deinkunde.com", "allowedCheckCountryCodes": null, "customFields": null, "alertLocationThresholdCount": 1, "createdAt": "2026-02-01T12:00:00.000Z", "updatedAt": "2026-02-01T12:00:00.000Z" }, "imapPopConfig": { "protocol": "imap", "user": "monitor@deinkunde.com", "password": "password", "tls": true } } ``` ## Errors - `400` IMAP/POP monitor public ID (UUID) required - `401` Unauthorized - `403` Forbidden - `404` IMAP/POP monitor not found
### Get IMAP/POP Monitor Alert History
URL: https://docs.uptimeify.io/api/monitors/imap-pop-monitors/get-imap-pop-monitor-alert-history
Description: Returns notification/escalation attempts (alert history) for incidents of a IMAP/POP monitor.
Summary: `GET /api/imap-pop-monitors/:imapPopMonitorPublicId/alert-history` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `imapPopMonitorPublicId` (Path, required): IMAP/POP monitor public UUID. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/imap-pop-monitors/11111111-1111-4111-8111-111111111111/alert-history" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example Response ```json { "alerts": [ { "id": 987, "incidentId": 123, "type": "slack", "status": "sent", "channelName": "Ops Slack", "sentAt": "2026-02-26T12:11:00.000Z", "errorMessage": null } ], "total": 1 } ``` Each entry is one delivery attempt to one notification channel. `status` is `sent` or `failed` (with `errorMessage` set on failure); `type` is the channel type (e.g. `email`, `slack`, `opsgenie`). ## Common Errors - `400 IMAP/POP monitor public ID (UUID) required` if `:imapPopMonitorPublicId` is invalid - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access to the monitor
### Get IMAP/POP Monitor Check History
URL: https://docs.uptimeify.io/api/monitors/imap-pop-monitors/get-imap-pop-monitor-check-history
Description: Returns recent check results for the IMAP/POP monitor.
Summary: `GET /api/imap-pop-monitors/:imapPopMonitorPublicId/check-history` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `imapPopMonitorPublicId` (Path, required): IMAP/POP monitor public UUID. - `limit` (Query, optional): Maximum number of results (default: 50, max: 200). ## cURL ```bash curl -X GET "https://YOUR_DOMAIN/api/imap-pop-monitors/66666666-6666-4666-8666-666666666666/check-history?limit=50" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "data": [ { "id": 123, "status": "success", "errorMessage": null, "warningMessage": null, "timingImapPop": 142, "diagnostics": { "message": "IMAP connection successful" }, "checkedAt": "2026-02-26T12:00:00.000Z", "locationId": 7, "locationName": "Nuremberg (DE)", "locationCode": "de-nbg" } ] } ``` ## Errors - `400` IMAP/POP monitor public ID (UUID) required - `401` Unauthorized - `403` Forbidden - `404` IMAP/POP monitor not found
### Get IMAP/POP Monitor Details
URL: https://docs.uptimeify.io/api/monitors/imap-pop-monitors/get-imap-pop-monitor-details
Description: Consolidated endpoint that returns the IMAP/POP monitor detail page payload in a single call (latest check, uptime stats, incidents, alert history, maintenance windows, chart data, etc.).
Summary: `GET /api/imap-pop-monitors/:imapPopMonitorPublicId/details` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `imapPopMonitorPublicId` (Path, required): IMAP/POP monitor public UUID. - `range` (Query, optional): `day` | `week` | `month` | `year` (default: `day`). - `date` (Query, optional): Reference date as ISO string. - `startDate` (Query, optional): Override start date as ISO string. - `endDate` (Query, optional): Override end date as ISO string. - `granularity` (Query, optional): If set and not `raw`, the backend may aggregate monitoring chart data. ## cURL ```bash curl -X GET "https://YOUR_DOMAIN/api/imap-pop-monitors/66666666-6666-4666-8666-666666666666/details?range=day" \ -H "Authorization: Bearer $TOKEN" ``` ## Response The response is a large object. Key top-level fields: - `imapPopMonitorId` - `monitor` - `latestCheck` - `uptimeStats`, `uptimeStatsMeta` - `monitoringData` - `incidents` - `alerts` - `testResultLog` - `maintenance` Example (truncated): ```json { "imapPopMonitorId": 500, "monitor": { "id": 500, "hostname": "mail.deinkunde.com", "status": "active" }, "latestCheck": { "id": 123, "status": "success", "checkedAt": "2026-02-26T12:00:00.000Z" }, "uptimeStats": { "day": "100.00", "month": "100.00", "year": "100.00", "dayAvgResponse": 142, "monthAvgResponse": 150, "yearAvgResponse": 160 }, "maintenance": { "inMaintenance": false, "activeWindows": [], "allWindows": [] } } ``` ## Errors - `400` Invalid IMAP/POP monitor public ID (UUID) - `401` Unauthorized - `403` Forbidden - `404` IMAP/POP monitor not found - `500` Failed to fetch IMAP/POP monitor details
### Get IMAP/POP Monitor Incident History
URL: https://docs.uptimeify.io/api/monitors/imap-pop-monitors/get-imap-pop-monitor-incident-history
Description: Returns incidents for a single IMAP/POP monitor (latest 100), including a computed duration.
Summary: `GET /api/imap-pop-monitors/:imapPopMonitorPublicId/incident-history` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `imapPopMonitorPublicId` (Path, required): IMAP/POP monitor public UUID. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/imap-pop-monitors/11111111-1111-4111-8111-111111111111/incident-history" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example Response ```json { "incidents": [ { "id": 123, "type": "downtime", "status": "resolved", "startedAt": "Feb 26, 2026, 12:10:00", "endedAt": "Feb 26, 2026, 12:12:30", "duration": "2m 30s" } ], "total": 1 } ``` `type` reflects the failure category for this monitor; `status` is `active` (ongoing) or `resolved`. Ongoing incidents have a `null` `endedAt`. ## Common Errors - `400 IMAP/POP monitor public ID (UUID) required` if `:imapPopMonitorPublicId` is invalid - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access to the monitor
### List IMAP/POP Monitors
URL: https://docs.uptimeify.io/api/monitors/imap-pop-monitors/list-imap-pop-monitors
Description: Lists IMAP/POP monitors in an organization.
Summary: `GET /api/imap-pop-monitors` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `organizationId` (Query, optional): Organization ID. Defaults to the current user's organization. - `customerId` (Query, optional): Filter by customer ID. - `search` (Query, optional): Search by monitor name, hostname, or customer. - `page` (Query, optional): Page number (default: 1). - `perPage` (Query, optional): Items per page (default: 50, max: 200). ## Response ```json { "items": [ { "id": 500, "organizationId": 1, "customerId": 10, "name": "Mailbox Access", "hostname": "mail.deinkunde.com", "port": 993, "checkInterval": 30, "timeoutSeconds": 30, "customerName": "Example Customer", "config": { "protocol": "imap", "user": "monitor@deinkunde.com", "password": "password", "tls": true }, "status": "active", "notificationPhoneNumber": null, "notificationEmail": null, "lastCheckedAt": null, "createdAt": "2026-02-26T12:00:00.000Z", "updatedAt": "2026-02-26T12:00:00.000Z" } ], "total": 1, "page": 1, "perPage": 50 } ``` ## Errors - `400` Invalid query parameters (e.g. `organizationId`, `customerId`) - `401` Unauthorized - `403` Forbidden
### Trigger IMAP/POP Check
URL: https://docs.uptimeify.io/api/monitors/imap-pop-monitors/trigger-check-imap-pop-monitor
Description: Triggers an immediate check from all eligible monitoring locations.
Summary: `POST /api/imap-pop-monitors/:imapPopMonitorPublicId/trigger-check` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `imapPopMonitorPublicId` (Path, required): IMAP/POP monitor public UUID. ## Response ```json { "success": true, "message": "Checks triggered successfully", "imapPopMonitorId": 500, "locationCodes": ["de-nbg", "fi-hel"], "queueNames": ["imap-pop-monitor-checks-de-nbg", "imap-pop-monitor-checks-fi-hel"] } ``` ## Errors - `400` Invalid IMAP/POP monitor public ID (UUID) or no eligible locations - `401` Unauthorized - `403` Forbidden - `404` IMAP/POP monitor not found - `503` No active monitoring locations available - `500` Failed to trigger check
### Update IMAP/POP Monitor
URL: https://docs.uptimeify.io/api/monitors/imap-pop-monitors/update-imap-pop-monitor
Description: Updates an IMAP/POP monitor and/or changes its status.
Summary: `PATCH /api/imap-pop-monitors/:imapPopMonitorPublicId` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `imapPopMonitorPublicId` (Path, required): IMAP/POP monitor public UUID. ## Request Body - `status` (optional) - Allowed: `active`, `maintenance`, `disabled`, `paused`, `inactive` - Note: `paused` / `inactive` are normalized to `disabled`. - `name`, `hostname`, `port`, `checkInterval`, `timeoutSeconds` (optional) - `allowedCheckCountryCodes` (optional) - Normalized to upper-case and de-duplicated. - `checkMode` (optional) - Allowed: `protocol` | `tcp`. Must be one of these two values or the request fails with `400` / `data.code: "invalidCheckMode"`. - Switching to `tcp` clears any stored credentials: the monitor performs a bare TCP port-reachability check and `imapPopConfig` is not stored. Switching back to `protocol` re-enables the full IMAP/POP login check; supply `imapPopConfig`/`config` to set new credentials. - **`port` is required when the resulting `checkMode` is `tcp`** (either just set on this request, or already stored on the monitor). If no port is available, the request fails with `400` / `data.code: "portRequiredForTcp"`. - `imapPopConfig` (optional) - Alias for the stored `config` JSON. - `config` (optional) - Alternative way to update the same stored `config` JSON. Note: Read-only users may only change `status` (and sending any other fields will result in `403`). ```json { "status": "active" } ``` Example updating config: ```json { "imapPopConfig": { "protocol": "pop3", "tls": true, "user": "monitor@deinkunde.com", "password": "password" } } ``` ## Response ```json { "id": 500, "organizationId": 1, "customerId": 10, "name": "Mailbox Access", "hostname": "mail.deinkunde.com", "port": 993, "checkInterval": 30, "timeoutSeconds": 30, "status": "active", "notificationPhoneNumber": null, "notificationEmail": null, "lastCheckedAt": null, "createdAt": "2026-02-26T12:00:00.000Z", "updatedAt": "2026-02-26T12:05:00.000Z", "config": { "protocol": "pop3", "tls": true, "user": "monitor@deinkunde.com", "password": "password" }, "imapPopConfig": { "protocol": "pop3", "tls": true, "user": "monitor@deinkunde.com", "password": "password" } } ``` ## Errors - `400` Invalid request body, invalid status, or invalid hostname - `401` Unauthorized - `403` Forbidden (e.g. `readonly` or global supporter) - `404` IMAP/POP monitor not found
### SMTP Monitors
URL: https://docs.uptimeify.io/api/monitors/smtp-monitors
Description: Manage SMTP monitors.
Summary: Path-based SMTP monitor endpoints use `smtpMonitorPublicId` UUIDs. ## Endpoints - [List SMTP Monitors](./list-smtp-monitors) - [Create SMTP Monitor](./create-smtp-monitor) - [Get SMTP Monitor](./get-smtp-monitor) - [Get SMTP Monitor Details](./get-smtp-monitor-details) - [Get SMTP Monitor Check History](./get-smtp-monitor-check-history) - [Update SMTP Monitor](./update-smtp-monitor) - [Delete SMTP Monitor](./delete-smtp-monitor) - [Trigger Check for SMTP Monitor](./trigger-check-smtp-monitor)
### Create SMTP Monitor
URL: https://docs.uptimeify.io/api/monitors/smtp-monitors/create-smtp-monitor
Description: Creates a new SMTP monitor.
Summary: `POST /api/smtp-monitors` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Request Body ```json { "customerId": "11111111-1111-4111-8111-111111111111", "name": "Outbound SMTP", "hostname": "smtp.deinkunde.com", "port": 587, "status": "active", "checkInterval": 60, "timeoutSeconds": 30, "smtpConfig": { "secure": false, "ignoreTls": false, "requireTls": false, "auth": { "user": "username", "pass": "password" } } } ``` ### Fields - `customerId` (number | string, required) - Accepts either the internal numeric customer ID or the public customer UUID. - `name` (string, required) - `hostname` (string, required) - Must be a hostname only (no protocol like `https://`, no path like `/smtp`). - `port` (number | null, optional) - If omitted or `null`, the worker defaults to `465` when `smtpConfig.secure=true`, otherwise `25`. - `status` (string, optional) - Allowed: `active`, `maintenance`, `disabled`, `paused`, `inactive` - `checkInterval` (number, optional) - `timeoutSeconds` (number, optional) - `checkMode` (string, optional) - Allowed: `protocol`, `tcp`. Defaults to `protocol`. - In `tcp` mode the monitor performs a bare TCP port-reachability check: `smtpConfig` is not required and is not stored. In `protocol` mode the full SMTP protocol/auth check runs (existing behavior). - `smtpConfig` (object, optional) - Stored as the monitor's `config` JSON. ### `smtpConfig` (worker-supported keys) The SMTP worker coerces/uses these keys: - `secure` (boolean, default `false`) - `ignoreTls` (boolean, default `false`) - `requireTls` (boolean, default `false`) - `auth.user` and `auth.pass` (strings) - Credentials are only used when *both* `user` and `pass` are provided. ## cURL ```bash curl -X POST "https://YOUR_DOMAIN/api/smtp-monitors" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "customerId": "11111111-1111-4111-8111-111111111111", "name": "Outbound SMTP", "hostname": "smtp.deinkunde.com", "port": 587, "status": "active", "checkInterval": 60, "timeoutSeconds": 30, "smtpConfig": { "secure": false, "ignoreTls": false, "requireTls": false, "auth": { "user": "username", "pass": "password" } } }' ``` ## Response ```json { "id": 200, "organizationId": 1, "customerId": 10, "name": "Outbound SMTP", "hostname": "smtp.deinkunde.com", "port": 587, "status": "active", "checkInterval": 60, "timeoutSeconds": 30, "notificationEmail": null, "notificationPhoneNumber": null, "lastCheckedAt": null, "createdAt": "2026-01-01T12:00:00.000Z", "updatedAt": "2026-01-01T12:00:00.000Z", "config": { "secure": false, "ignoreTls": false, "requireTls": false, "auth": { "user": "username", "pass": "password" } }, "smtpConfig": { "secure": false, "ignoreTls": false, "requireTls": false, "auth": { "user": "username", "pass": "password" } } } ``` ## Errors - `400` Invalid hostname or invalid request body - `400` Invalid Customer identifier - `401` Unauthorized - `403` Forbidden (e.g. `readonly` or global supporter)
### Delete SMTP Monitor
URL: https://docs.uptimeify.io/api/monitors/smtp-monitors/delete-smtp-monitor
Description: Deletes an SMTP monitor.
Summary: `DELETE /api/smtp-monitors/:smtpMonitorPublicId` ## Authentication Requires a valid session and write access. - Header: `Authorization: Bearer ` ## Parameters - `smtpMonitorPublicId` (Path, required): SMTP monitor public UUID. ## cURL ```bash curl -X DELETE "https://YOUR_DOMAIN/api/smtp-monitors/33333333-3333-4333-8333-333333333333" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "success": true } ``` ## Errors - `400` SMTP monitor public ID (UUID) required - `401` Unauthorized - `403` Forbidden - `404` Not found
### Get SMTP Monitor
URL: https://docs.uptimeify.io/api/monitors/smtp-monitors/get-smtp-monitor
Description: Returns details of a specific SMTP monitor.
Summary: `GET /api/smtp-monitors/:smtpMonitorPublicId` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `smtpMonitorPublicId` (Path, required): SMTP monitor public UUID. ## cURL ```bash curl "https://YOUR_DOMAIN/api/smtp-monitors/33333333-3333-4333-8333-333333333333" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "id": 200, "organizationId": 1, "customerId": 10, "name": "Outbound SMTP", "hostname": "smtp.deinkunde.com", "port": 587, "status": "active", "checkInterval": 60, "timeoutSeconds": 30, "customerName": "Example Customer", "notificationEmail": null, "notificationPhoneNumber": null, "lastCheckedAt": "2026-01-01T12:00:00.000Z", "createdAt": "2026-01-01T12:00:00.000Z", "updatedAt": "2026-01-01T12:00:00.000Z", "config": { "secure": false, "ignoreTls": false, "requireTls": false }, "smtpConfig": { "secure": false, "ignoreTls": false, "requireTls": false } } ``` ## Errors - `400` SMTP monitor public ID (UUID) required - `401` Unauthorized - `403` Forbidden - `404` Not found
### Get SMTP Monitor Alert History
URL: https://docs.uptimeify.io/api/monitors/smtp-monitors/get-smtp-monitor-alert-history
Description: Returns notification/escalation attempts (alert history) for incidents of a SMTP monitor.
Summary: `GET /api/smtp-monitors/:smtpMonitorPublicId/alert-history` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `smtpMonitorPublicId` (Path, required): SMTP monitor public UUID. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/smtp-monitors/11111111-1111-4111-8111-111111111111/alert-history" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example Response ```json { "alerts": [ { "id": 987, "incidentId": 123, "type": "slack", "status": "sent", "channelName": "Ops Slack", "sentAt": "2026-02-26T12:11:00.000Z", "errorMessage": null } ], "total": 1 } ``` Each entry is one delivery attempt to one notification channel. `status` is `sent` or `failed` (with `errorMessage` set on failure); `type` is the channel type (e.g. `email`, `slack`, `opsgenie`). ## Common Errors - `400 SMTP monitor public ID (UUID) required` if `:smtpMonitorPublicId` is invalid - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access to the monitor
### Get SMTP Monitor Check History
URL: https://docs.uptimeify.io/api/monitors/smtp-monitors/get-smtp-monitor-check-history
Description: Returns recent check results for an SMTP monitor.
Summary: `GET /api/smtp-monitors/:smtpMonitorPublicId/check-history` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `smtpMonitorPublicId` (Path, required): SMTP monitor public UUID. ## Query Parameters - `limit` (number, optional) - Default: `50` - Max: `200` ## cURL ```bash curl "https://YOUR_DOMAIN/api/smtp-monitors/33333333-3333-4333-8333-333333333333/check-history?limit=50" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "data": [ { "id": 12345, "status": "success", "errorMessage": null, "warningMessage": null, "timingSmtp": 123, "diagnostics": { "raw": "optional diagnostic payload" }, "checkedAt": "2026-01-01T12:00:00.000Z", "locationId": 7, "locationName": "Nuremberg (DE)", "locationCode": "de-nbg" } ] } ``` ## Errors - `400` SMTP monitor public ID (UUID) required - `401` Unauthorized - `403` Forbidden - `404` Not found
### Get SMTP Monitor Details
URL: https://docs.uptimeify.io/api/monitors/smtp-monitors/get-smtp-monitor-details
Description: Consolidated endpoint that returns the SMTP monitor details page data in one call.
Summary: `GET /api/smtp-monitors/:smtpMonitorPublicId/details` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `smtpMonitorPublicId` (Path, required): SMTP monitor public UUID. ## Query Parameters - `range` (string, optional): `day`, `week`, `month`, `year` (default: `day`) - `date` (string, optional): Reference date (parsed by `new Date(date)`) - `startDate` (string, optional) - `endDate` (string, optional) - `granularity` (string, optional) - If set to `raw`, disables aggregation. ## cURL ```bash curl "https://YOUR_DOMAIN/api/smtp-monitors/33333333-3333-4333-8333-333333333333/details?range=day" \ -H "Authorization: Bearer $TOKEN" ``` ## Response The response is a single object with these top-level keys: - `smtpMonitorId` - `monitor` - `latestCheck` - `uptimeStats` - `uptimeStatsMeta` - `monitoringData` - `incidents` - `alerts` - `testResultLog` - `maintenance` Example (truncated): ```json { "smtpMonitorId": 200, "monitor": { "id": 200, "customerId": 10, "name": "Outbound SMTP", "hostname": "smtp.deinkunde.com", "port": 587, "status": "active", "checkInterval": 60, "timeoutSeconds": 30, "config": { "secure": false, "requireTls": true } }, "latestCheck": { "status": "success", "checkedAt": "2026-01-01T12:00:00.000Z", "timingSmtp": 123, "locationCode": "de-nbg", "locationName": "Nuremberg (DE)" }, "uptimeStats": { "day": 99.9, "month": 99.5, "year": 99.0, "dayAvgResponse": 120, "monthAvgResponse": 140, "yearAvgResponse": 150 }, "maintenance": { "inMaintenance": false, "activeWindows": [], "allWindows": [] } } ``` ## Errors - `400` Invalid SMTP monitor public ID (UUID) - `401` Unauthorized - `403` Forbidden - `404` Not found - `500` Failed to fetch SMTP monitor details
### Get SMTP Monitor Incident History
URL: https://docs.uptimeify.io/api/monitors/smtp-monitors/get-smtp-monitor-incident-history
Description: Returns incidents for a single SMTP monitor (latest 100), including a computed duration.
Summary: `GET /api/smtp-monitors/:smtpMonitorPublicId/incident-history` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `smtpMonitorPublicId` (Path, required): SMTP monitor public UUID. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/smtp-monitors/11111111-1111-4111-8111-111111111111/incident-history" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example Response ```json { "incidents": [ { "id": 123, "type": "downtime", "status": "resolved", "startedAt": "Feb 26, 2026, 12:10:00", "endedAt": "Feb 26, 2026, 12:12:30", "duration": "2m 30s" } ], "total": 1 } ``` `type` reflects the failure category for this monitor; `status` is `active` (ongoing) or `resolved`. Ongoing incidents have a `null` `endedAt`. ## Common Errors - `400 SMTP monitor public ID (UUID) required` if `:smtpMonitorPublicId` is invalid - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access to the monitor
### List SMTP Monitors
URL: https://docs.uptimeify.io/api/monitors/smtp-monitors/list-smtp-monitors
Description: Lists SMTP monitors in an organization.
Summary: `GET /api/smtp-monitors` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `organizationId` (Query, optional): Organization ID. Defaults to the current user's organization. - `customerId` (Query, optional): Filter by customer ID. - `search` (Query, optional): Search by monitor name, hostname, or customer. - `page` (Query, optional): Page number (default: 1). - `perPage` (Query, optional): Items per page (default: 50, max: 200). ## cURL ```bash curl "https://YOUR_DOMAIN/api/smtp-monitors?page=1&perPage=50" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "items": [ { "id": 200, "organizationId": 1, "customerId": 10, "name": "Outbound SMTP", "hostname": "smtp.deinkunde.com", "port": 587, "status": "active", "checkInterval": 60, "timeoutSeconds": 30, "customerName": "Example Customer", "notificationEmail": null, "notificationPhoneNumber": null, "lastCheckedAt": "2026-01-01T12:00:00.000Z", "createdAt": "2026-01-01T12:00:00.000Z", "updatedAt": "2026-01-01T12:00:00.000Z", "config": { "secure": false, "ignoreTls": false, "requireTls": false } } ], "total": 1, "page": 1, "perPage": 50 } ``` ## Errors - `400` Invalid `organizationId` or `customerId` - `401` Unauthorized - `403` Forbidden
### Trigger Check for SMTP Monitor
URL: https://docs.uptimeify.io/api/monitors/smtp-monitors/trigger-check-smtp-monitor
Description: Triggers an immediate check from all eligible monitoring locations.
Summary: `POST /api/smtp-monitors/:smtpMonitorPublicId/trigger-check` ## Authentication Requires a valid session and write access. - Header: `Authorization: Bearer ` ## Parameters - `smtpMonitorPublicId` (Path, required): SMTP monitor public UUID. ## cURL ```bash curl -X POST "https://YOUR_DOMAIN/api/smtp-monitors/33333333-3333-4333-8333-333333333333/trigger-check" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "success": true, "message": "Checks triggered successfully", "smtpMonitorId": 200, "locationCodes": ["de-nbg", "fi-hel"], "queueNames": ["smtp-monitor-checks-de-nbg", "smtp-monitor-checks-fi-hel"] } ``` ## Errors - `400` SMTP monitor public ID (UUID) required - `401` Unauthorized - `403` Forbidden - `404` Not found - `500` Failed to trigger check (the status message may include e.g. "No active monitoring locations available")
### Update SMTP Monitor
URL: https://docs.uptimeify.io/api/monitors/smtp-monitors/update-smtp-monitor
Description: Updates an SMTP monitor.
Summary: `PATCH /api/smtp-monitors/:smtpMonitorPublicId` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `smtpMonitorPublicId` (Path, required): SMTP monitor public UUID. ## Request Body All fields are optional. ```json { "name": "Outbound SMTP (Primary)", "hostname": "smtp.deinkunde.com", "port": 587, "status": "paused", "checkInterval": 60, "timeoutSeconds": 30, "allowedCheckCountryCodes": ["de", "es"], "smtpConfig": { "secure": false, "requireTls": true, "auth": { "user": "username", "pass": "password" } } } ``` ### Notes - `status` accepts `active`, `maintenance`, `disabled`, `paused`, `inactive`. - `paused` and `inactive` are normalized to `disabled`. - `allowedCheckCountryCodes` is normalized to uppercase ISO codes (e.g. `DE`, `US`). Empty lists become `null`. - `checkMode` (string, optional): `protocol` (default) | `tcp`. Switching to `tcp` clears any stored credentials: the monitor performs a bare TCP port-reachability check and `smtpConfig` is not stored. Switching back to `protocol` re-enables the full SMTP protocol/auth check; supply `smtpConfig`/`config` to set new credentials. - SMTP config can be provided as `smtpConfig` or as `config` (alias). If neither is provided, the stored config is not changed. - Users with `readonly` role can only change `status`. ## cURL ```bash curl -X PATCH "https://YOUR_DOMAIN/api/smtp-monitors/33333333-3333-4333-8333-333333333333" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "status": "disabled" }' ``` ## Response ```json { "id": 200, "organizationId": 1, "customerId": 10, "name": "Outbound SMTP (Primary)", "hostname": "smtp.deinkunde.com", "port": 587, "status": "disabled", "checkInterval": 60, "timeoutSeconds": 30, "notificationEmail": null, "notificationPhoneNumber": null, "lastCheckedAt": "2026-01-01T12:00:00.000Z", "createdAt": "2026-01-01T12:00:00.000Z", "updatedAt": "2026-01-01T12:01:00.000Z", "config": { "secure": false, "requireTls": true, "auth": { "user": "username", "pass": "password" } }, "smtpConfig": { "secure": false, "requireTls": true, "auth": { "user": "username", "pass": "password" } } } ``` ## Errors - `400` SMTP monitor public ID (UUID) required - `401` Unauthorized - `403` Forbidden (readonly users may only update `status`; global supporters cannot update) - `404` Not found
### SSH Monitors
URL: https://docs.uptimeify.io/api/monitors/ssh-monitors
Description: Manage SSH monitors.
Summary: Path-based SSH monitor endpoints use `sshMonitorPublicId` UUIDs. ## Endpoints - [List SSH Monitors](./list-ssh-monitors) - [Create SSH Monitor](./create-ssh-monitor) - [Get SSH Monitor](./get-ssh-monitor) - [Get SSH Monitor Details](./get-ssh-monitor-details) - [Get SSH Monitor Check History](./get-ssh-monitor-check-history) - [Update SSH Monitor](./update-ssh-monitor) - [Delete SSH Monitor](./delete-ssh-monitor) - [Trigger Check for SSH Monitor](./trigger-check-ssh-monitor)
### Create SSH Monitor
URL: https://docs.uptimeify.io/api/monitors/ssh-monitors/create-ssh-monitor
Description: Creates a new SSH monitor.
Summary: `POST /api/ssh-monitors` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Request Body ```json { "customerId": "11111111-1111-4111-8111-111111111111", "name": "Bastion Host", "hostname": "ssh.deinkunde.com", "port": 22, "status": "active", "checkInterval": 60, "timeoutSeconds": 30, "sshConfig": { "username": "root", "password": "password", "privateKey": "-----BEGIN OPENSSH PRIVATE KEY-----..." } } ``` ### Fields - `customerId` (number | string, required) - Accepts either the internal numeric customer ID or the public customer UUID. - `name` (string, required) - `hostname` (string, required) - Must be a hostname only (no protocol like `https://`, no path like `/ssh`). - `port` (number | null, optional) - If omitted or `null`, the worker defaults to port `22`. - `status` (string, optional) - Allowed: `active`, `maintenance`, `disabled`, `paused`, `inactive` - `checkInterval` (number, optional) - `timeoutSeconds` (number, optional) - `checkMode` (string, optional) - Allowed: `protocol`, `tcp`. Defaults to `protocol`. - In `tcp` mode the monitor performs a bare TCP port-reachability check: `sshConfig` is not required and is not stored. In `protocol` mode the full SSH login/handshake check runs (existing behavior). - `sshConfig` (object, optional) - Stored as the monitor's `config` JSON. - Required (username + password or privateKey) unless `checkMode` is `tcp`. ### `sshConfig` (worker-supported keys) - `username` (string) - If omitted, the worker uses `anonymous`. - `password` (string) - `privateKey` (string) ## cURL ```bash curl -X POST "https://YOUR_DOMAIN/api/ssh-monitors" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "customerId": "11111111-1111-4111-8111-111111111111", "name": "Bastion Host", "hostname": "ssh.deinkunde.com", "port": 22, "status": "active", "checkInterval": 60, "timeoutSeconds": 30, "sshConfig": { "username": "root", "password": "password" } }' ``` ## Response ```json { "id": 300, "organizationId": 1, "customerId": 10, "name": "Bastion Host", "hostname": "ssh.deinkunde.com", "port": 22, "status": "active", "checkInterval": 60, "timeoutSeconds": 30, "notificationEmail": null, "notificationPhoneNumber": null, "lastCheckedAt": null, "createdAt": "2026-02-26T12:00:00.000Z", "updatedAt": "2026-02-26T12:00:00.000Z", "config": { "username": "root", "password": "password" }, "sshConfig": { "username": "root", "password": "password" } } ``` ## Errors - `400` Invalid hostname or invalid request body - `400` Invalid Customer identifier - `401` Unauthorized - `403` Forbidden (e.g. `readonly` or global supporter)
### Delete SSH Monitor
URL: https://docs.uptimeify.io/api/monitors/ssh-monitors/delete-ssh-monitor
Description: Deletes an SSH monitor.
Summary: `DELETE /api/ssh-monitors/:sshMonitorPublicId` ## Authentication Requires a valid session and write access. - Header: `Authorization: Bearer ` ## Parameters - `sshMonitorPublicId` (Path, required): SSH monitor public UUID. ## cURL ```bash curl -X DELETE "https://YOUR_DOMAIN/api/ssh-monitors/44444444-4444-4444-8444-444444444444" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "success": true } ``` ## Errors - `400` SSH monitor public ID (UUID) required - `401` Unauthorized - `403` Forbidden - `404` Not found
### Get SSH Monitor
URL: https://docs.uptimeify.io/api/monitors/ssh-monitors/get-ssh-monitor
Description: Returns details of a specific SSH monitor.
Summary: `GET /api/ssh-monitors/:sshMonitorPublicId` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `sshMonitorPublicId` (Path, required): SSH monitor public UUID. ## cURL ```bash curl "https://YOUR_DOMAIN/api/ssh-monitors/44444444-4444-4444-8444-444444444444" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "id": 300, "organizationId": 1, "customerId": 10, "name": "Bastion Host", "hostname": "ssh.deinkunde.com", "port": 22, "status": "active", "checkInterval": 60, "timeoutSeconds": 30, "customerName": "Example Customer", "notificationEmail": null, "notificationPhoneNumber": null, "lastCheckedAt": "2026-02-26T12:00:00.000Z", "createdAt": "2026-02-26T12:00:00.000Z", "updatedAt": "2026-02-26T12:00:00.000Z", "config": { "username": "root" }, "sshConfig": { "username": "root" } } ``` ## Errors - `400` SSH monitor public ID (UUID) required - `401` Unauthorized - `403` Forbidden - `404` Not found
### Get SSH Monitor Alert History
URL: https://docs.uptimeify.io/api/monitors/ssh-monitors/get-ssh-monitor-alert-history
Description: Returns notification/escalation attempts (alert history) for incidents of a SSH monitor.
Summary: `GET /api/ssh-monitors/:sshMonitorPublicId/alert-history` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `sshMonitorPublicId` (Path, required): SSH monitor public UUID. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/ssh-monitors/11111111-1111-4111-8111-111111111111/alert-history" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example Response ```json { "alerts": [ { "id": 987, "incidentId": 123, "type": "slack", "status": "sent", "channelName": "Ops Slack", "sentAt": "2026-02-26T12:11:00.000Z", "errorMessage": null } ], "total": 1 } ``` Each entry is one delivery attempt to one notification channel. `status` is `sent` or `failed` (with `errorMessage` set on failure); `type` is the channel type (e.g. `email`, `slack`, `opsgenie`). ## Common Errors - `400 SSH monitor public ID (UUID) required` if `:sshMonitorPublicId` is invalid - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access to the monitor
### Get SSH Monitor Check History
URL: https://docs.uptimeify.io/api/monitors/ssh-monitors/get-ssh-monitor-check-history
Description: Returns recent check results for an SSH monitor.
Summary: `GET /api/ssh-monitors/:sshMonitorPublicId/check-history` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `sshMonitorPublicId` (Path, required): SSH monitor public UUID. ## Query Parameters - `limit` (number, optional) - Default: `50` - Max: `200` ## cURL ```bash curl "https://YOUR_DOMAIN/api/ssh-monitors/44444444-4444-4444-8444-444444444444/check-history?limit=50" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "data": [ { "id": 12345, "status": "success", "errorMessage": null, "warningMessage": null, "timingSsh": 123, "diagnostics": { "message": "SSH connection successful" }, "checkedAt": "2026-02-26T12:00:00.000Z", "locationId": 7, "locationName": "Nuremberg (DE)", "locationCode": "de-nbg" } ] } ``` ## Errors - `400` SSH monitor public ID (UUID) required - `401` Unauthorized - `403` Forbidden - `404` Not found
### Get SSH Monitor Details
URL: https://docs.uptimeify.io/api/monitors/ssh-monitors/get-ssh-monitor-details
Description: Consolidated endpoint that returns the SSH monitor details page data in one call.
Summary: `GET /api/ssh-monitors/:sshMonitorPublicId/details` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `sshMonitorPublicId` (Path, required): SSH monitor public UUID. ## Query Parameters - `range` (string, optional): `day`, `week`, `month`, `year` (default: `day`) - `date` (string, optional): Reference date (parsed by `new Date(date)`) - `startDate` (string, optional) - `endDate` (string, optional) - `granularity` (string, optional) - If set to `raw`, disables aggregation. ## cURL ```bash curl "https://YOUR_DOMAIN/api/ssh-monitors/44444444-4444-4444-8444-444444444444/details?range=day" \ -H "Authorization: Bearer $TOKEN" ``` ## Response The response is a single object with these top-level keys: - `sshMonitorId` - `monitor` - `latestCheck` - `uptimeStats` - `uptimeStatsMeta` - `monitoringData` - `incidents` - `alerts` - `testResultLog` - `maintenance` Example (truncated): ```json { "sshMonitorId": 300, "monitor": { "id": 300, "customerId": 10, "name": "Bastion Host", "hostname": "ssh.deinkunde.com", "port": 22, "status": "active", "checkInterval": 60, "timeoutSeconds": 30, "config": { "username": "root" } }, "latestCheck": { "status": "success", "checkedAt": "2026-02-26T12:00:00.000Z", "timingSsh": 123, "locationCode": "de-nbg", "locationName": "Nuremberg (DE)" }, "uptimeStats": { "day": 99.9, "month": 99.5, "year": 99.0, "dayAvgResponse": 120, "monthAvgResponse": 140, "yearAvgResponse": 150 }, "maintenance": { "inMaintenance": false, "activeWindows": [], "allWindows": [] } } ``` ## Errors - `400` Invalid SSH monitor public ID (UUID) - `401` Unauthorized - `403` Forbidden - `404` Not found - `500` Failed to fetch SSH monitor details
### Get SSH Monitor Incident History
URL: https://docs.uptimeify.io/api/monitors/ssh-monitors/get-ssh-monitor-incident-history
Description: Returns incidents for a single SSH monitor (latest 100), including a computed duration.
Summary: `GET /api/ssh-monitors/:sshMonitorPublicId/incident-history` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `sshMonitorPublicId` (Path, required): SSH monitor public UUID. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/ssh-monitors/11111111-1111-4111-8111-111111111111/incident-history" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example Response ```json { "incidents": [ { "id": 123, "type": "downtime", "status": "resolved", "startedAt": "Feb 26, 2026, 12:10:00", "endedAt": "Feb 26, 2026, 12:12:30", "duration": "2m 30s" } ], "total": 1 } ``` `type` reflects the failure category for this monitor; `status` is `active` (ongoing) or `resolved`. Ongoing incidents have a `null` `endedAt`. ## Common Errors - `400 SSH monitor public ID (UUID) required` if `:sshMonitorPublicId` is invalid - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access to the monitor
### List SSH Monitors
URL: https://docs.uptimeify.io/api/monitors/ssh-monitors/list-ssh-monitors
Description: Lists SSH monitors in an organization.
Summary: `GET /api/ssh-monitors` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `organizationId` (Query, optional): Organization ID. Defaults to the current user's organization. - `customerId` (Query, optional): Filter by customer ID. - `search` (Query, optional): Search by monitor name, hostname, or customer. - `page` (Query, optional): Page number (default: 1). - `perPage` (Query, optional): Items per page (default: 50, max: 200). ## cURL ```bash curl "https://YOUR_DOMAIN/api/ssh-monitors?page=1&perPage=50" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "items": [ { "id": 300, "organizationId": 1, "customerId": 10, "name": "Bastion Host", "hostname": "ssh.deinkunde.com", "port": 22, "status": "active", "checkInterval": 60, "timeoutSeconds": 30, "customerName": "Example Customer", "notificationEmail": null, "notificationPhoneNumber": null, "lastCheckedAt": "2026-02-26T12:00:00.000Z", "createdAt": "2026-02-26T12:00:00.000Z", "updatedAt": "2026-02-26T12:00:00.000Z", "config": { "username": "root" } } ], "total": 1, "page": 1, "perPage": 50 } ``` ## Errors - `400` Invalid `organizationId` or `customerId` - `401` Unauthorized - `403` Forbidden
### Trigger Check for SSH Monitor
URL: https://docs.uptimeify.io/api/monitors/ssh-monitors/trigger-check-ssh-monitor
Description: Triggers an immediate check from all eligible monitoring locations.
Summary: `POST /api/ssh-monitors/:sshMonitorPublicId/trigger-check` ## Authentication Requires a valid session and write access. - Header: `Authorization: Bearer ` ## Parameters - `sshMonitorPublicId` (Path, required): SSH monitor public UUID. ## cURL ```bash curl -X POST "https://YOUR_DOMAIN/api/ssh-monitors/44444444-4444-4444-8444-444444444444/trigger-check" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "success": true, "message": "Checks triggered successfully", "sshMonitorId": 300, "locationCodes": ["de-nbg", "fi-hel"], "queueNames": ["ssh-monitor-checks-de-nbg", "ssh-monitor-checks-fi-hel"] } ``` ## Errors - `400` SSH monitor public ID (UUID) required - `401` Unauthorized - `403` Forbidden - `404` Not found - `500` Failed to trigger check (the status message may include e.g. "No active monitoring locations available")
### Update SSH Monitor
URL: https://docs.uptimeify.io/api/monitors/ssh-monitors/update-ssh-monitor
Description: Updates an SSH monitor.
Summary: `PATCH /api/ssh-monitors/:sshMonitorPublicId` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `sshMonitorPublicId` (Path, required): SSH monitor public UUID. ## Request Body All fields are optional. ```json { "name": "Bastion Host (Primary)", "hostname": "ssh.deinkunde.com", "port": 22, "status": "paused", "checkInterval": 60, "timeoutSeconds": 30, "allowedCheckCountryCodes": ["de", "es"], "sshConfig": { "username": "root", "privateKey": "-----BEGIN OPENSSH PRIVATE KEY-----..." } } ``` ### Notes - `status` accepts `active`, `maintenance`, `disabled`, `paused`, `inactive`. - `paused` and `inactive` are normalized to `disabled`. - `allowedCheckCountryCodes` is normalized to uppercase ISO codes (e.g. `DE`, `US`). Empty lists become `null`. - `checkMode` (string, optional): `protocol` (default) | `tcp`. Switching to `tcp` clears any stored credentials: the monitor performs a bare TCP port-reachability check and `sshConfig` is not stored. Switching back to `protocol` re-enables the full SSH login/handshake check; supply `sshConfig`/`config` to set new credentials. - SSH config can be provided as `sshConfig` or as `config` (alias). If neither is provided, the stored config is not changed. - Users with `readonly` role can only change `status`. ## cURL ```bash curl -X PATCH "https://YOUR_DOMAIN/api/ssh-monitors/44444444-4444-4444-8444-444444444444" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "status": "disabled" }' ``` ## Response ```json { "id": 300, "organizationId": 1, "customerId": 10, "name": "Bastion Host (Primary)", "hostname": "ssh.deinkunde.com", "port": 22, "status": "disabled", "checkInterval": 60, "timeoutSeconds": 30, "notificationEmail": null, "notificationPhoneNumber": null, "lastCheckedAt": "2026-02-26T12:00:00.000Z", "createdAt": "2026-02-26T12:00:00.000Z", "updatedAt": "2026-02-26T12:01:00.000Z", "config": { "username": "root" }, "sshConfig": { "username": "root" } } ``` ## Errors - `400` SSH monitor ID is required / invalid fields - `401` Unauthorized - `403` Forbidden (readonly users may only update `status`; global supporters cannot update) - `404` Not found
### TCP Monitors
URL: https://docs.uptimeify.io/api/monitors/tcp-monitors
Description: Manage TCP monitors.
Summary: Path-based TCP monitor endpoints use `tcpMonitorPublicId` UUIDs. ## Endpoints - [List TCP Monitors](./list-tcp-monitors) - [Create TCP Monitor](./create-tcp-monitor) - [Get TCP Monitor](./get-tcp-monitor) - [Get TCP Monitor Details](./get-tcp-monitor-details) - [Get TCP Monitor Check History](./get-tcp-monitor-check-history) - [Get TCP Monitor Alert History](./get-tcp-monitor-alert-history) - [Get TCP Monitor Incident History](./get-tcp-monitor-incident-history) - [Update TCP Monitor](./update-tcp-monitor) - [Delete TCP Monitor](./delete-tcp-monitor) - [Trigger Check for TCP Monitor](./trigger-check-tcp-monitor)
### Create TCP Monitor
URL: https://docs.uptimeify.io/api/monitors/tcp-monitors/create-tcp-monitor
Description: Creates a new TCP monitor.
Summary: `POST /api/tcp-monitors` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Request Body ```json { "customerId": "11111111-1111-4111-8111-111111111111", "name": "Redis Primary", "hostname": "redis.deinkunde.com", "port": 6379, "status": "active", "checkInterval": 60, "timeoutSeconds": 30, "config": { "expectBanner": "+PONG" } } ``` ### Fields - `customerId` (number | string, required) - Accepts either the internal numeric customer ID or the public customer UUID. - `name` (string, required, max 255 chars) - `hostname` (string, required, max 253 chars) - Must be a hostname only (no protocol like `https://`, no path like `/status`). - `port` (number, **required**, 1-65535) - Unlike other monitor types, TCP monitors have no default port. You must specify the port to connect to. - `status` (string, optional) - Allowed: `active`, `maintenance`, `disabled`, `paused`, `inactive` (`paused`/`inactive` are normalized to `disabled`). Defaults to `active`. - `checkInterval` (number, optional, 1-1440 minutes) - Defaults to `30`. - `timeoutSeconds` (number, optional, 1-60) - Defaults to `30`. - `allowedCheckCountryCodes` (string[], optional) - Restricts checks to these ISO-3166-1 alpha-2 country codes. - `managementType` (string, optional) - Allowed: `managed`, `self_service`. Only organization writers may set this; other callers always get `managed`. - `config` (object, optional) - Stored as the monitor's `config` JSON. TCP monitors carry **no credentials**: the only supported key is `expectBanner`. ### `config` (worker-supported keys) - `expectBanner` (string, optional, max 255 chars) - If set, the worker verifies that the raw bytes received right after the TCP handshake contain this substring (e.g. `+PONG` for Redis, `220` for an SMTP banner). If omitted, the check only verifies that the TCP handshake succeeds. ## cURL ```bash curl -X POST "https://YOUR_DOMAIN/api/tcp-monitors" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "customerId": "11111111-1111-4111-8111-111111111111", "name": "Redis Primary", "hostname": "redis.deinkunde.com", "port": 6379, "status": "active", "checkInterval": 60, "timeoutSeconds": 30, "config": { "expectBanner": "+PONG" } }' ``` ## Response ```json { "id": 300, "publicId": "44444444-4444-4444-8444-444444444444", "organizationId": 1, "customerId": 10, "name": "Redis Primary", "hostname": "redis.deinkunde.com", "port": 6379, "status": "active", "managementType": "managed", "checkInterval": 60, "timeoutSeconds": 30, "notificationEmail": null, "notificationPhoneNumber": null, "lastCheckedAt": null, "createdAt": "2026-02-26T12:00:00.000Z", "updatedAt": "2026-02-26T12:00:00.000Z", "config": { "expectBanner": "+PONG" } } ``` ## Errors - `400` `invalidHostname`: hostname must be a valid hostname (no protocol, no path) - `400` Invalid request body (e.g. missing/out-of-range `port`, invalid `config` keys) - `400` Invalid Customer identifier - `401` Unauthorized - `403` Forbidden (e.g. `readonly` without self-service permission, self-service quota reached, or global supporter) - `404` Customer not found
### Delete TCP Monitor
URL: https://docs.uptimeify.io/api/monitors/tcp-monitors/delete-tcp-monitor
Description: Deletes a TCP monitor.
Summary: `DELETE /api/tcp-monitors/:tcpMonitorPublicId` ## Authentication Requires a valid session and write access. - Header: `Authorization: Bearer ` ## Parameters - `tcpMonitorPublicId` (Path, required): TCP monitor public UUID. ## cURL ```bash curl -X DELETE "https://YOUR_DOMAIN/api/tcp-monitors/44444444-4444-4444-8444-444444444444" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "success": true } ``` ## Errors - `400` TCP monitor public ID (UUID) required - `401` Unauthorized - `403` Forbidden - `404` Not found
### Get TCP Monitor
URL: https://docs.uptimeify.io/api/monitors/tcp-monitors/get-tcp-monitor
Description: Returns details of a specific TCP monitor.
Summary: `GET /api/tcp-monitors/:tcpMonitorPublicId` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `tcpMonitorPublicId` (Path, required): TCP monitor public UUID. ## cURL ```bash curl "https://YOUR_DOMAIN/api/tcp-monitors/44444444-4444-4444-8444-444444444444" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "id": 300, "publicId": "44444444-4444-4444-8444-444444444444", "organizationId": 1, "customerId": 10, "name": "Redis Primary", "hostname": "redis.deinkunde.com", "port": 6379, "status": "active", "managementType": "managed", "checkInterval": 60, "timeoutSeconds": 30, "customerName": "Example Customer", "notificationEmail": null, "notificationPhoneNumber": null, "lastCheckedAt": "2026-02-26T12:00:00.000Z", "createdAt": "2026-02-26T12:00:00.000Z", "updatedAt": "2026-02-26T12:00:00.000Z", "config": { "expectBanner": "+PONG" } } ``` ## Errors - `400` TCP monitor public ID (UUID) required - `401` Unauthorized - `403` Forbidden - `404` Not found
### Get TCP Monitor Alert History
URL: https://docs.uptimeify.io/api/monitors/tcp-monitors/get-tcp-monitor-alert-history
Description: Returns paginated notification/escalation attempts (alert history) for incidents of a TCP monitor.
Summary: `GET /api/tcp-monitors/:tcpMonitorPublicId/alert-history` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `tcpMonitorPublicId` (Path, required): TCP monitor public UUID. ## Query Parameters - `page` (number, optional): Page number (default: `1`). - `limit` (number, optional): Items per page (default: `25`; max `100`). Ignored in favor of export defaults when `format=csv` or `download=1` (default `10000`, max `50000`). - `status` (string, optional): `sent` or `failed`. - `from` / `to` (ISO date string, optional): Filter by `sentAt` range. - `format` (string, optional): `json` (default) or `csv`. - `download` (string, optional): `1` to force an attachment download. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/tcp-monitors/11111111-1111-4111-8111-111111111111/alert-history" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example Response ```json { "data": [ { "id": 987, "incidentId": 123, "type": "tcp", "status": "sent", "channelName": "Ops Slack", "channelType": "slack", "channelConfig": null, "sentAt": "2026-02-26T12:11:00.000Z", "errorMessage": null } ], "total": 1, "page": 1, "pageCount": 1 } ``` Each entry is one delivery attempt to one notification channel. `status` is `sent` or `failed` (with `errorMessage` set on failure); `channelType` is the channel type (e.g. `email`, `slack`, `opsgenie`). `channelConfig` has secrets redacted. When `format=csv` (or `download=1`), the response is a `text/csv` (or `application/json` for `format=json&download=1`) attachment instead of the paginated JSON envelope above. ## Common Errors - `400 TCP monitor public ID (UUID) required` if `:tcpMonitorPublicId` is invalid - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access to the monitor
### Get TCP Monitor Check History
URL: https://docs.uptimeify.io/api/monitors/tcp-monitors/get-tcp-monitor-check-history
Description: Returns paginated check results for a TCP monitor.
Summary: `GET /api/tcp-monitors/:tcpMonitorPublicId/check-history` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `tcpMonitorPublicId` (Path, required): TCP monitor public UUID. ## Query Parameters - `page` (number, optional): Page number (default: `1`). - `limit` (number, optional): Items per page (default: `10`; max `100`). Ignored in favor of export defaults when `format=csv` or `download=1` (default `10000`, max `50000`). - `status` (string, optional): `success` or `failure` (`failure` also matches `timeout`). - `minMs` / `maxMs` (number, optional): Filter by response time range (`timingTotal`). - `from` / `to` (ISO date string, optional): Filter by `checkedAt` range. - `format` (string, optional): `json` (default) or `csv`. - `download` (string, optional): `1` to force an attachment download. ## cURL ```bash curl "https://YOUR_DOMAIN/api/tcp-monitors/44444444-4444-4444-8444-444444444444/check-history?page=1&limit=50" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "data": [ { "id": 12345, "status": "success", "success": true, "errorMessage": null, "checkedAt": "2026-02-26T12:00:00.000Z", "responseTimeMs": 42, "location": { "name": "Nuremberg (DE)", "code": "de-nbg" } } ], "total": 1, "page": 1, "pageCount": 1 } ``` When `format=csv` (or `download=1`), the response is a `text/csv` (or `application/json` for `format=json&download=1`) attachment instead of the paginated JSON envelope above. ## Errors - `400` TCP monitor public ID (UUID) required - `401` Unauthorized - `403` Forbidden - `404` Not found
### Get TCP Monitor Details
URL: https://docs.uptimeify.io/api/monitors/tcp-monitors/get-tcp-monitor-details
Description: Consolidated endpoint that returns the TCP monitor details page data in one call.
Summary: `GET /api/tcp-monitors/:tcpMonitorPublicId/details` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `tcpMonitorPublicId` (Path, required): TCP monitor public UUID. ## Query Parameters - `range` (string, optional): `day`, `week`, `month`, `year` (default: `day`) - `date` (string, optional): Reference date (parsed by `new Date(date)`) - `startDate` (string, optional) - `endDate` (string, optional) - `granularity` (string, optional) - If set to `raw`, disables aggregation. ## cURL ```bash curl "https://YOUR_DOMAIN/api/tcp-monitors/44444444-4444-4444-8444-444444444444/details?range=day" \ -H "Authorization: Bearer $TOKEN" ``` ## Response The response is a single object with these top-level keys: - `tcpMonitorId` - `monitor` - `latestCheck` - `uptimeStats` - `uptimeStatsMeta` - `monitoringData` - `incidents` - `alerts` - `testResultLog` - `maintenance` Example (truncated): ```json { "tcpMonitorId": 300, "monitor": { "id": 300, "customerId": 10, "name": "Redis Primary", "hostname": "redis.deinkunde.com", "port": 6379, "status": "active", "checkInterval": 60, "timeoutSeconds": 30, "config": { "expectBanner": "+PONG" } }, "latestCheck": { "status": "success", "checkedAt": "2026-02-26T12:00:00.000Z", "timingTotal": 123, "locationCode": "de-nbg", "locationName": "Nuremberg (DE)" }, "uptimeStats": { "day": 99.9, "month": 99.5, "year": 99.0, "dayAvgResponse": 120, "monthAvgResponse": 140, "yearAvgResponse": 150 }, "maintenance": { "inMaintenance": false, "activeWindows": [], "allWindows": [] } } ``` ## Errors - `400` Invalid TCP monitor public ID (UUID) - `401` Unauthorized - `403` Forbidden - `404` Not found - `500` Failed to fetch TCP monitor details
### Get TCP Monitor Incident History
URL: https://docs.uptimeify.io/api/monitors/tcp-monitors/get-tcp-monitor-incident-history
Description: Returns paginated incidents for a single TCP monitor, including a computed duration.
Summary: `GET /api/tcp-monitors/:tcpMonitorPublicId/incident-history` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `tcpMonitorPublicId` (Path, required): TCP monitor public UUID. ## Query Parameters - `page` (number, optional): Page number (default: `1`). - `limit` (number, optional): Items per page (default: `25`; max `100`). Ignored in favor of export defaults when `format=csv` or `download=1` (default `10000`, max `50000`). - `status` (string, optional): `open`, `acknowledged`, or `resolved`. - `type` (string, optional): `downtime`, `http_status`, `ssl_expiry`, `ssl_critical`, `ssl_warning`, `response_time`, `dnsbl`, `domain_expiry_warning`, `critical`, or `inconclusive`. - `from` / `to` (ISO date string, optional): Filter by `startedAt` range. - `format` (string, optional): `json` (default) or `csv`. - `download` (string, optional): `1` to force an attachment download. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/tcp-monitors/11111111-1111-4111-8111-111111111111/incident-history" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example Response ```json { "data": [ { "id": 123, "publicId": "55555555-5555-4555-8555-555555555555", "type": "downtime", "status": "resolved", "startedAt": "2026-02-26T12:10:00.000Z", "endedAt": "2026-02-26T12:12:30.000Z", "durationMs": 150000, "duration": "2m 30s", "statusCode": null, "errorMessage": "connect ETIMEDOUT", "details": "connect ETIMEDOUT", "isOngoing": false } ], "total": 1, "page": 1, "pageCount": 1 } ``` `status` is `open`, `acknowledged`, or `resolved`. Ongoing incidents have a `null` `endedAt` and `isOngoing: true`. When `format=csv` (or `download=1`), the response is a `text/csv` (or `application/json` for `format=json&download=1`) attachment instead of the paginated JSON envelope above. ## Common Errors - `400 TCP monitor public ID (UUID) required` if `:tcpMonitorPublicId` is invalid - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access to the monitor
### List TCP Monitors
URL: https://docs.uptimeify.io/api/monitors/tcp-monitors/list-tcp-monitors
Description: Lists TCP monitors in an organization.
Summary: `GET /api/tcp-monitors` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `organizationId` (Query, optional): Organization ID. Defaults to the current user's organization. - `customerId` (Query, optional): Filter by customer ID. - `search` (Query, optional): Search by monitor name, hostname, or customer. - `page` (Query, optional): Page number (default: 1). - `perPage` (Query, optional): Items per page (default: 50, max: 200). ## cURL ```bash curl "https://YOUR_DOMAIN/api/tcp-monitors?page=1&perPage=50" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "items": [ { "id": 300, "publicId": "44444444-4444-4444-8444-444444444444", "organizationId": 1, "customerId": 10, "name": "Redis Primary", "hostname": "redis.deinkunde.com", "port": 6379, "status": "active", "managementType": "managed", "checkInterval": 60, "timeoutSeconds": 30, "customerName": "Example Customer", "notificationEmail": null, "notificationPhoneNumber": null, "lastCheckedAt": "2026-02-26T12:00:00.000Z", "createdAt": "2026-02-26T12:00:00.000Z", "updatedAt": "2026-02-26T12:00:00.000Z", "config": { "expectBanner": "+PONG" }, "tags": [] } ], "total": 1, "page": 1, "perPage": 50 } ``` ## Errors - `400` Invalid `organizationId` or `customerId` - `401` Unauthorized - `403` Forbidden
### Trigger Check for TCP Monitor
URL: https://docs.uptimeify.io/api/monitors/tcp-monitors/trigger-check-tcp-monitor
Description: Triggers an immediate check from one eligible monitoring location.
Summary: `POST /api/tcp-monitors/:tcpMonitorPublicId/trigger-check` ## Authentication Requires a valid session and write access. - Header: `Authorization: Bearer ` ## Parameters - `tcpMonitorPublicId` (Path, required): TCP monitor public UUID. ## Notes - The location is chosen deterministically from the currently active monitoring locations (filtered by the monitor's or customer's `allowedCheckCountryCodes`, if set). - Rate-limited per monitor via a short cooldown; retrying too soon returns `429`. ## cURL ```bash curl -X POST "https://YOUR_DOMAIN/api/tcp-monitors/44444444-4444-4444-8444-444444444444/trigger-check" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "success": true, "message": "Check triggered successfully", "tcpMonitorId": 300, "locationCode": "de-nbg", "queueName": "tcp-monitor-checks-de-nbg" } ``` ## Errors - `400` No eligible monitoring locations for the selected allowed countries - `401` Unauthorized - `403` Forbidden - `404` Not found - `429` Check recently triggered: retry after the cooldown (`retryAfterMs` on the error payload) - `500` / `503` Failed to trigger check (e.g. "No active monitoring locations available")
### Update TCP Monitor
URL: https://docs.uptimeify.io/api/monitors/tcp-monitors/update-tcp-monitor
Description: Updates a TCP monitor.
Summary: `PATCH /api/tcp-monitors/:tcpMonitorPublicId` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Parameters - `tcpMonitorPublicId` (Path, required): TCP monitor public UUID. ## Request Body All fields are optional. ```json { "name": "Redis Primary", "hostname": "redis.deinkunde.com", "port": 6379, "status": "paused", "checkInterval": 60, "timeoutSeconds": 30, "allowedCheckCountryCodes": ["de", "es"], "config": { "expectBanner": "+PONG" } } ``` ### Notes - `status` accepts `active`, `maintenance`, `disabled`, `paused`, `inactive`. - `paused` and `inactive` are normalized to `disabled`. - `port`, if provided, must be an integer between `1` and `65535`. - `allowedCheckCountryCodes` is normalized to uppercase ISO codes (e.g. `DE`, `US`). Empty lists become `null`. - `config` only accepts the `expectBanner` key (merged into the existing stored config). There is no `sshConfig`-style alias and no credential fields. TCP monitors carry no credentials. If omitted, the stored config is not changed. - Users with `readonly` role can only change `status`. ## cURL ```bash curl -X PATCH "https://YOUR_DOMAIN/api/tcp-monitors/44444444-4444-4444-8444-444444444444" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "status": "disabled" }' ``` ## Response ```json { "id": 300, "publicId": "44444444-4444-4444-8444-444444444444", "organizationId": 1, "customerId": 10, "name": "Redis Primary", "hostname": "redis.deinkunde.com", "port": 6379, "status": "disabled", "managementType": "managed", "checkInterval": 60, "timeoutSeconds": 30, "notificationEmail": null, "notificationPhoneNumber": null, "lastCheckedAt": "2026-02-26T12:00:00.000Z", "createdAt": "2026-02-26T12:00:00.000Z", "updatedAt": "2026-02-26T12:01:00.000Z", "config": { "expectBanner": "+PONG" } } ``` ## Errors - `400` TCP monitor ID is required / invalid fields (e.g. `invalidPort`, `invalidConfig`, `invalidStatus`) - `401` Unauthorized - `403` Forbidden (readonly users may only update `status`; global supporters cannot update) - `404` Not found
### Notification Channels
URL: https://docs.uptimeify.io/api/notification-channels
Description: Manage how and where alerts are delivered. Channels can be organization-level (default for all customers), customer-level (override for a specific customer), or website-level (override for a specific monitor).
Summary: ## Authentication All examples assume a bearer token: ```bash BASE_URL="https://uptimeify.io" TOKEN="" ``` ## Channel Types and Config Each channel type has a specific `config` structure: | Type | Config Fields | Notes | |------|--------------|-------| | `email` | `{ email, to }` | Email address and recipient name | | `sms` | `{ phoneNumber }` | Phone number in international format | | `webhook` | `{ url, method?, headers?, bodyTemplate?, timeout?, retryAttempts?, retryDelay?, expectedStatusCodes?, secret? }` | HTTP webhook. `secret` for HMAC signing | | `slack` | `{ secret }` | Slack incoming webhook URL | | `discord` | `{ secret }` | Discord incoming webhook URL | | `pagerduty` | `{ routingKey }` | PagerDuty Events API v2 routing key | | `pushover` | `{ userKey, apiToken }` | Pushover user key and API token | | `opsgenie` | `{ apiKey }` | Opsgenie API key | Secrets (webhook `secret`, Slack/Discord URLs, PagerDuty routing keys, Pushover credentials, Opsgenie API keys) are **encrypted at rest** and **redacted in API responses** (replaced with `null`, with `hasSecret: true` flags). ## Webhook Signature Verification When a `secret` is configured on a webhook channel, Uptimeify signs outgoing payloads so receivers can verify authenticity. **Algorithm:** HMAC-SHA256 **Header:** `X-Webhook-Signature`: hex-encoded HMAC-SHA256 digest **Companion headers sent with every webhook:** | Header | Value | |--------|-------| | `X-Webhook-Signature` | Hex-encoded HMAC-SHA256 digest | | `X-Webhook-Signature-Algorithm` | `sha256` | | `X-Webhook-Timestamp` | ISO 8601 timestamp (e.g. `2026-05-05T12:00:00.000Z`) | | `X-Webhook-Attempt` | 1-based retry number (e.g. `1`, `2`, `3`) | | `X-Webhook-Event` | Event type: `alert`, `recovery`, or `dnsbl` | **Verification steps:** 1. Read the raw request body as a string (do not parse as JSON first). 2. Compute HMAC-SHA256 using the `secret` you configured in the channel as the key and the raw body string as the message. 3. Compare the result (hex-encoded) with the value of the `X-Webhook-Signature` header using a constant-time comparison. 4. Optionally check `X-Webhook-Timestamp` for replay protection. ```javascript // Node.js verification example const crypto = require('crypto') function verifySignature(rawBody, signature, secret) { const expected = crypto .createHmac('sha256', secret) .update(rawBody) .digest('hex') return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expected) ) } ``` ```python # Python verification example def verify_signature(raw_body, signature, secret): expected = hmac.new( secret.encode('utf-8'), raw_body.encode('utf-8'), hashlib.sha256 ).hexdigest() return hmac.compare_digest(signature, expected) ``` ## Scope Resolution When listing channels, the scope determines which channels are returned: - **Organization-level** (`organizationId` only, no `customerId`/`websiteId`): Default channels for all customers - **Customer-level** (`customerId` set, no `websiteId`): Customer-specific overrides - **Website-level** (`websiteId` set): Per-monitor overrides ## Endpoints - [List Notification Channels](./list-notification-channels) - [Create Notification Channel](./create-notification-channel) - [Update Notification Channel](./update-notification-channel) - [Delete Notification Channel](./delete-notification-channel) - [Test Notification Channel](./test-notification-channel)
### Create Notification Channel
URL: https://docs.uptimeify.io/api/notification-channels/create-notification-channel
Description: Creates a new notification channel. Secrets in config are encrypted server-side.
Summary: `POST /api/notification-channels` ## Request Body | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `type` | string | Yes | - | Channel type (see full list below) | | `name` | string | Yes | - | Display name | | `config` | object\|string | Yes | - | Channel configuration (see type table). Pass as JSON object or JSON string. | | `organizationId` | number | No | from session | Organization scope | | `customerId` | number | No | null | Customer scope | | `websiteId` | number | No | null | Website scope. Requires `sourceChannelId`. | | `sourceChannelId` | number\|string | Conditional | null | Parent channel ID for website overrides | | `category` | string | No | `direct` | `direct` or `integration` | | `priority` | number | No | 1 | Lower = higher priority | | `delaySeconds` | number | No | 0 | Delay before sending alert | | `conditions` | object\|string | No | null | Alert conditions (e.g., `{"onlyFullService": true, "minIncidentDuration": 300}`) | | `isActive` | boolean | No | true | Whether the channel is active | ## Channel types Direct (`category: "direct"`): `email`, `sms`, `webhook`. Integrations (`category: "integration"`): `incident_management`, `slack`, `discord`, `teams`, `pagerduty`, `opsgenie`, `allquiet`, `telegram`, `googlechat`, `mattermost`, `rocketchat`, `matrix`, `lark`, `dingtalk`, `wecom`, `ilert`, `grafanaoncall`, `squadcast`, `incidentio`, `pushover`, `ntfy`, `gotify`, `jira`, `github`, `gitlab`, `linear`, `servicenow`. The `config` fields depend on the type: see [Integrations](/integrations) for what each channel needs. You can validate a channel before saving with the [Test Notification Channel](/api/notification-channels/test-notification-channel) endpoint. ### `incident_management` Uptimeify's own Incident Management is an integration channel like any other, with two differences: - It takes an **empty `config`** (`{}`). There is no endpoint and no credential, confirmed outages are delivered internally. - It is **not testable**: the [Test Notification Channel](/api/notification-channels/test-notification-channel) endpoint does not support this type. What the channel controls is *which* monitors page through Incident Management. Its scope (`customerId` / `websiteId`, both optional, omit both for the whole organization) and its `allowedPackageTypes` decide whose confirmed outages become IM incidents; everything else stays on classic notifications. A monitoring outage only reaches Incident Management when a matching, active channel exists **and** the customer's package has integration alerts enabled. ## Example (cURL): Email channel ```bash curl -X POST "$BASE_URL/api/notification-channels" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "type": "email", "name": "Ops Email", "config": { "email": "ops@deinkunde.com", "to": "Ops Team" }, "organizationId": 1 }' ``` ## Example (cURL): Slack channel ```bash curl -X POST "$BASE_URL/api/notification-channels" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "type": "slack", "name": "Alerts Slack Channel", "config": { "secret": "https://hooks.slack.com/services/T00/B00/xxx" }, "organizationId": 1 }' ``` ## Example (cURL): Webhook channel ```bash curl -X POST "$BASE_URL/api/notification-channels" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "type": "webhook", "name": "Custom Webhook", "config": { "url": "https://deinkunde.com/webhook", "method": "POST", "headers": { "X-Custom-Header": "value" }, "bodyTemplate": "{\"text\": \"{{websiteName}} is {{status}}\"}", "timeout": 30, "retryAttempts": 3, "retryDelay": 60, "expectedStatusCodes": "200,201,204" }, "organizationId": 1 }' ``` ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` when creating channels for an organization you cannot write to - `409 Conflict` when a duplicate website-level override already exists ## Response Returns the…
### Delete Notification Channel
URL: https://docs.uptimeify.io/api/notification-channels/delete-notification-channel
Description: Deletes a notification channel. If the channel was an org-level default, organization defaults are automatically synced.
Summary: `DELETE /api/notification-channels/:id` ## Example (cURL) ```bash curl -X DELETE "$BASE_URL/api/notification-channels/1" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "success": true } ``` ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` when accessing channels outside your scope - `404 Not found` when the channel does not exist
### List Notification Channels
URL: https://docs.uptimeify.io/api/notification-channels/list-notification-channels
Description: Returns notification channels scoped by organization, customer, or website.
Summary: `GET /api/notification-channels` ## Query Parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `organizationId` | number | No | Scope to organization | | `customerId` | number | No | Scope to customer | | `websiteId` | number | No | Scope to website (includes org + customer channels) | ## Example (cURL) ```bash curl -X GET "$BASE_URL/api/notification-channels?organizationId=1" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Response ```json [ { "id": 1, "organizationId": 1, "customerId": null, "websiteId": null, "type": "email", "category": "direct", "name": "Ops Email", "config": { "email": "ops@deinkunde.com", "to": "Ops Team" }, "priority": 1, "delaySeconds": 0, "conditions": null, "isActive": true, "allowedPackageTypes": null } ] ``` ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` when accessing channels outside your scope
### Test Notification Channel
URL: https://docs.uptimeify.io/api/notification-channels/test-notification-channel
Description: Tests a notification channel configuration. Optionally sends a real test message.
Summary: `POST /api/notification-channels/test` ## Request Body | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `type` | string | Yes | - | `webhook`, `slack`, `discord`, `pagerduty`, `pushover`, `opsgenie` | | `config` | object | Yes | `{}` | Channel config with secrets | | `organizationId` | number | No | from session | Organization scope | | `customerId` | number | No | null | Customer scope | | `websiteId` | number | No | null | Website scope | | `dryRun` | boolean | No | false | If true, validates only (no actual send) | ## Example (cURL) ```bash curl -X POST "$BASE_URL/api/notification-channels/test" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "type": "webhook", "config": { "url": "https://deinkunde.com/webhook", "method": "POST", "timeout": 15 }, "dryRun": false }' ``` ## Response ```json { "success": true, "mode": "send", "message": "Webhook delivered successfully", "validation": { "ok": true, "errors": [], "warnings": [] }, "request": { "url": "https://deinkunde.com/webhook", "method": "POST", "timeoutSeconds": 15, "headers": {}, "bodyPreview": "...", "bodySize": 256 }, "response": { "reachable": true, "ok": true, "statusCode": 200, "statusText": "OK", "durationMs": 150, "headers": {}, "bodyPreview": null, "bodySize": null } } ``` HTTP 429 responses are treated as "reachable but rate-limited" (success: true). ## Common errors - `401 Unauthorized` when not authenticated - `400 Bad Request` when config is invalid for the given type
### Update Notification Channel
URL: https://docs.uptimeify.io/api/notification-channels/update-notification-channel
Description: Updates a notification channel.
Summary: `PATCH /api/notification-channels/:id` Config is **merged** with existing secrets, preserving encrypted fields that are not provided. ## Request Body (all optional) | Field | Type | Description | |-------|------|-------------| | `type` | string | Channel type | | `name` | string | Display name | | `config` | object\|string | Merged with existing secrets | | `priority` | number | Priority order | | `delaySeconds` | number | Delay before sending | | `conditions` | object\|string\|null | Alert conditions | | `allowedPackageTypes` | string[]\|null | Package types this channel applies to | | `isActive` | boolean | Whether the channel is active | ## Example (cURL) ```bash curl -X PATCH "$BASE_URL/api/notification-channels/1" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Updated Email Channel", "isActive": true }' ``` ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` when accessing channels outside your scope - `404 Not found` when the channel does not exist ## Response Returns the updated notification channel object. See [Error Codes](/api/error-codes-and-known-pitfalls) for error responses.
### Connected apps (OAuth connections)
URL: https://docs.uptimeify.io/api/oauth-connections
Description: List and revoke the OAuth/MCP applications connected to your account, such as Claude's remote MCP connector.
Summary: Uptimeify lets you connect third-party apps (like Claude Desktop / Claude.ai) via [MCP OAuth](/api/mcp). These two endpoints let the signed-in user list and revoke their own connections. They power the **Settings → Connected apps** page in the dashboard. Both endpoints are **session-authenticated** (browser cookie), not token-based: they are not part of the `wsm_`/`wsma_` API-token surface, and every operation is strictly scoped to the caller's own connections. ## List connections `GET /api/oauth/connections` Returns the caller's own live OAuth connections, one entry per connected client. "Live" means the grant still has an open refresh window. Returns an empty array when the user has no connections. ### Example (cURL) ```bash curl -X GET "$BASE_URL/api/oauth/connections" \ -H "Cookie: $SESSION_COOKIE" \ -H "Accept: application/json" ``` ### Response ```json [ { "client_id": "claude-desktop", "client_name": "Claude", "connected_at": "2026-06-01T09:12:00.000Z", "last_active": "2026-07-20T14:03:00.000Z", "scope": "read-only" } ] ``` | Field | Type | Description | | --- | --- | --- | | `client_id` | string | The OAuth client identifier. | | `client_name` | string | Human-readable application name. | | `connected_at` | string (ISO 8601) | When the connection was first authorized. | | `last_active` | string (ISO 8601) | Most recent token activity for this client. | | `scope` | string | Always `"read-only"`: connected apps can never create, edit, or delete anything. | ## Revoke a connection `DELETE /api/oauth/connections/:clientId` Revokes the caller's own grant for the given client: deletes its access/refresh tokens and consent record immediately. The app loses access right away; reconnecting requires the user to go through OAuth consent again. ### Example (cURL) ```bash curl -X DELETE "$BASE_URL/api/oauth/connections/claude-desktop" \ -H "Cookie: $SESSION_COOKIE" ``` ### Response ```json { "revoked": true, "client_id": "claude-desktop" } ``` ## Common errors | Status | `data.code` | Meaning | | --- | --- | --- | | 401 | `unauthorized` | No active session (not signed in). Both endpoints. | | 400 | `missingClientId` | `DELETE` only: the `:clientId` path parameter is missing. | | 404 | `unknownOauthConnection` | `DELETE` only: the caller has no connection for that `client_id` (already revoked, expired, or never connected). See [error codes](/api/error-codes-and-known-pitfalls). |
### Organization & Billing
URL: https://docs.uptimeify.io/api/organization
Description: Path-based organization endpoints use organizationPublicId UUIDs.
Summary: ## Endpoints - [Get Organization Details](./get-organization-details) - [Update Organization](./update-organization) - [List Package Configs](./list-package-configs) - [Upsert Package Config](./upsert-package-config) - [Delete Package Config](./delete-package-config) - [Get Billing Details](./get-billing-details) - [Update Billing Details](./update-billing-details) - [List Invoices](./list-invoices) - [Error codes and known API pitfalls](/api/error-codes-and-known-pitfalls)
### Organization SMTP
URL: https://docs.uptimeify.io/api/organization-smtp
Description: Configure custom SMTP settings so your organization's notification emails are sent through your own mail server. All SMTP endpoints require admin role.
Summary: ## Authentication All endpoints accept API bearer tokens or session cookies: ```bash BASE_URL="https://uptimeify.io" TOKEN="" ``` ## Endpoints - [Test SMTP Connection](./test-smtp) - [Get SMTP Send Logs](./get-smtp-logs)
### Get SMTP Send Logs
URL: https://docs.uptimeify.io/api/organization-smtp/get-smtp-logs
Description: Returns recent SMTP send event logs from Redis. Admin-only.
Summary: `GET /api/organization/smtp/logs` ## Query Parameters | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `limit` | number | 100 | Max events to return (1-500) | ## Example (cURL) ```bash curl -X GET "$BASE_URL/api/organization/smtp/logs?limit=50" \ -H "Cookie: $SESSION_COOKIE" \ -H "Accept: application/json" ``` ## Response ```json { "events": [ { "type": "smtp_send", "timestamp": "2026-04-15T12:00:00.000Z", "to": "admin@deinkunde.com", "subject": "Website Down: deinkunde.com", "status": "sent" } ] } ``` Returns `{ "events": [] }` if no logs exist or on Redis errors (fails open). ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` when not an admin
### Test SMTP Connection
URL: https://docs.uptimeify.io/api/organization-smtp/test-smtp
Description: Tests the SMTP configuration by sending a test email. All parameters are optional. When omitted, stored config values are used. This allows testing new credentials before saving them. Admin-only.
Summary: `POST /api/organization/smtp/test` The test also updates the SMTP config's `lastTestedAt`, `lastTestStatus`, and `lastTestError` fields. ## Request Body (all optional) | Field | Type | Description | |-------|------|-------------| | `toEmail` | string | Recipient email address (max 320 chars). Defaults to current user's email. | | `subject` | string | Email subject (1-200 chars). Defaults to `SMTP Test ()`. | | `message` | string | Email body (1-2000 chars). Defaults to a standard test message. | | `host` | string\|null | SMTP server hostname (1-255 chars). Overrides stored config. | | `port` | number\|null | SMTP server port (1-65535). Overrides stored config. | | `username` | string\|null | SMTP username (1-200 chars). Overrides stored config. | | `password` | string\|null | SMTP password (1-500 chars). Overrides stored config (not persisted). | | `tlsMode` | string\|null | `ssl`, `starttls`, or `none`. Overrides stored config. | | `fromName` | string\|null | Sender display name (1-200 chars). Overrides stored config. | | `fromEmail` | string\|null | Sender email address (max 320 chars). Overrides stored config. | | `replyTo` | string\|null | Reply-to email address (max 320 chars). Overrides stored config. | ## Example (cURL): Using stored config ```bash curl -X POST "$BASE_URL/api/organization/smtp/test" \ -H "Cookie: $SESSION_COOKIE" \ -H "Content-Type: application/json" \ -d '{}' ``` ## Example (cURL): Testing new credentials ```bash curl -X POST "$BASE_URL/api/organization/smtp/test" \ -H "Cookie: $SESSION_COOKIE" \ -H "Content-Type: application/json" \ -d '{ "host": "smtp.deinkunde.com", "port": 587, "tlsMode": "starttls", "username": "alerts@deinkunde.com", "password": "secret-password", "fromName": "Uptimeify Alerts", "fromEmail": "alerts@deinkunde.com", "toEmail": "admin@deinkunde.com" }' ``` ## Response (success) ```json { "success": true, "messageId": "" } ``` ## Common errors - `400 Missing recipient email (toEmail)` when no recipient and user has no email - `400 SMTP config incomplete (host/port required)` when host or port is missing - `400 SMTP config incomplete (username required)` when username is needed but missing - `400 SMTP password missing (username is set)` when password is needed but missing - `400 SMTP config incomplete (fromEmail required)` when fromEmail is missing - `403 Forbidden` when not an admin - `502 Failed to send SMTP test email` when the SMTP connection fails
### Apply Report Default
URL: https://docs.uptimeify.io/api/organization/apply-report-default
Description: Writes a package's monthlyReportsDefault onto every existing customer currently on that package, once.
Summary: `POST /api/organizations/:id/package-configs/:packageType/apply-report-default` [Upsert Package Config](/api/organization/upsert-package-config)'s `monthlyReportsDefault` is a **template**: it only seeds `monthlyReportsEnabled` for customers created from then on. Saving the package config never rewrites customers who already exist on it, on purpose, a configuration save must not silently mutate customer rows. This endpoint is the explicit, one-off action for the other case: propagate the package's current `monthlyReportsDefault` onto every customer that already holds that package, right now. This is a single bulk `UPDATE`, not a loop with per-customer error isolation like [Bulk Actions](/api/customers/bulk-actions): it writes one boolean column with no cascade and no side effects, so per-row isolation buys nothing here. Either the whole set is written, or the request fails before anything is written. ## Path parameter: `:id` `:id` is the organization's **numeric** `id`, the same integer `id` field returned by [Get Organization Details](/api/organization/get-organization-details). This is narrower than the sibling package-config routes, which also resolve `:organizationPublicId` (a UUID). This endpoint does not: it parses the path segment with `Number(...)` directly and rejects anything that is not a positive integer with `400 invalidRequestBody`. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X POST "$BASE_URL/api/organizations/1/package-configs/pro/apply-report-default" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` No request body. ## Response ```json { "updated": 42 } ``` `updated` is the number of customer rows written. A customer "holds" the package if its `packageId` matches this package config's id, or, for customers that were never assigned an id, if its `packageType` string matches. Customers that only happen to share the same `packageType` string without holding this exact config are not included. ## Common errors - `400 invalidRequestBody` `:id` is not a positive integer, or `:packageType` is missing - `401 unauthorized` you are not logged in - `403 forbidden` you are not an admin of this organization and not a global admin - `403 customerScopedTokenForbidden` when called with a customer-scoped API token; this is an organization-wide action and requires an organization-scoped token or a session - `404 notFound` no package config with this `packageType` exists for this organization Authorization note: - Write access is required (organization admin of the target organization, or global admin). - A customer-scoped API token is refused outright. This endpoint writes across every customer holding the package, so there is no customer dimension to narrow it down to; a token bound to a single customer must not reach the rest of the organization.
### Cancel Scheduled Quota Change
URL: https://docs.uptimeify.io/api/organization/cancel-pending-quota-change
Description: Cancels a quota reduction that was scheduled for the end of the current billing period, so the organization keeps its current plan. Answers cancelled:false, not 404, when nothing is scheduled.
Summary: `DELETE /api/organizations/:organizationPublicId/pending-quota-change` ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" ORG_ID="" curl -X DELETE "$BASE_URL/api/organizations/$ORG_ID/pending-quota-change" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` Notes: - This endpoint takes no request body. It closes whichever quota reduction is currently scheduled for the organization; there is never more than one. - A quota reduction does not take effect immediately. Under AGB § 10.5 it is scheduled for the end of the current billing period and is returned as `pendingQuotaChange` (see [Update Organization](/api/organization/update-organization) and [Get Organization Details](/api/organization/get-organization-details)). This endpoint is what calls it off before it fires. - **This is the only way to keep the current plan.** Direction is classified against the organization's *current*, still higher tier, so re-sending the current tier through `PATCH` counts as an unchanged request and leaves the scheduled reduction in place. Only a genuine price increase supersedes one, and that changes the plan rather than restoring it. - Cancelling is free and always allowed until the change applies. Afterwards there is nothing left to cancel: the reduction is already the organization's plan, and going back up is an ordinary quota increase through [Update Organization](/api/organization/update-organization). - Requires organization write access (organization admin or global admin) from an unrestricted session or token, the same authorization as [Update Organization](/api/organization/update-organization). A customer-scoped API token cannot call this endpoint, even with `admin`-level role, because cancelling changes what the organization pays from the next period onwards. ## Response ```json { "cancelled": true } ``` | Value | Meaning | | --- | --- | | `true` | A scheduled reduction was open and has been cancelled. The organization keeps its current plan. | | `false` | Nothing was scheduled, so nothing changed. **This is a success, not an error**, the endpoint is deliberately idempotent so that a repeated call (a double-clicked button, a retried request) does not surface a failure. | ## Common errors - `401 Unauthorized` (`data.code: unauthorized`) when you are not logged in - `400 Invalid Organization identifier` when `:organizationPublicId` is neither a legacy integer id nor a valid UUID public identifier - `403 Forbidden` (`data.code: forbidden`) when you do not have write access to the organization - `403 Forbidden` (`data.code: customerScopedTokenForbidden`) when called with a customer-scoped API token; this is an organization-wide action and requires an organization-scoped token or a session - `404 Organization not found` (no `data.code`) when `:organizationPublicId` does not resolve to an existing organization Note that there is no "nothing to cancel" error, that case is `200` with `cancelled: false`. See [Error Codes](/api/error-codes-and-known-pitfalls) for the full list.
### Delete Package Config
URL: https://docs.uptimeify.io/api/organization/delete-package-config
Description: Deletes a package config for an organization. Deletion is only allowed if no customers are currently assigned to that package. The package is resolved directly from the path parameter, so values like test1 work as long as they match the stored packageType.
Summary: `DELETE /api/package-configs/:packageType` ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X DELETE "$BASE_URL/api/package-configs/pro" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` Notes: - The organization is derived automatically from your authenticated session or API token. - The legacy route `DELETE /api/organizations/:organizationPublicId/package-configs/:packageType` remains supported for compatibility. - The plural org-less alias `DELETE /api/organizations/package-configs/:packageType` is also supported. - Global admins need an active organization context in the authenticated session for the org-less route. - The in-use check evaluates each customer's package assignment, not the package name. Customers that merely carry the same `packageType` string without being assigned to this config do not block deletion. ## Response ```json { "success": true } ``` ## Common errors - `404 Package not found` when the config does not exist - `409 Package is still in use by customers` when customers are assigned to this package - `400 Package type is required` when `:packageType` is missing - `400 Organization ID is required in the authenticated session` when no organization can be derived from the current session/token - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you cannot access the organization Authorization note: - Write access is required (organization admin or global admin).
### Download Data Export
URL: https://docs.uptimeify.io/api/organization/download-data-export
Description: Issues a short-lived download URL for a finished organization data export.
Summary: `GET /api/organization/data-export/{exportId}/download` Returns a presigned URL for the export ZIP. The URL is valid for 5 minutes; the object key itself is never exposed and the storage bucket is not browsable, so knowing an export id is not access, an authenticated administrator of the owning organization is. ## Query parameters | Parameter | Type | Description | | --- | --- | --- | | `redirect` | `1` | Respond with `302` to the presigned URL instead of returning JSON. Convenient for `curl -L` and browser links. | ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" EXPORT_ID="0f0f2f6a-2b3b-4a2f-9a7c-3e5f8c1d2b44" # Straight to the file curl -L -o export.zip \ "$BASE_URL/api/organization/data-export/$EXPORT_ID/download?redirect=1" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "url": "https://..your-objectstorage.com/data-exports/org-12/0f0f2f6a-...zip?X-Amz-Signature=...", "expiresInSeconds": 300, "sizeBytes": 18446721, "filename": "uptimeify-export-0f0f2f6a-2b3b-4a2f-9a7c-3e5f8c1d2b44.zip" } ``` Each call increments `downloadCount` and updates `lastDownloadedAt` on the export record. ## Common errors - `400 User must belong to an organization` when no organization can be derived from the session or token - `401 Unauthorized` when you are not authenticated - `403 Forbidden` when your role cannot administer the organization - `404 Export not found` when the id does not belong to your organization - `409 Export is not downloadable` (`data.code: exportNotReady`) while the export is queued, generating or failed - `410 Export download window has expired` (`data.code: exportExpired`) after the 7-day window
### Get Billing Details
URL: https://docs.uptimeify.io/api/organization/get-billing-details
Description: Returns billing information and payment methods for the organization in your authenticated session.
Summary: `GET /api/organization/billing` ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/organization/billing" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` Notes: - The organization is derived automatically from your authenticated session or API token. - The legacy route `GET /api/organizations/:organizationPublicId/billing` remains supported for compatibility. - The plural org-less alias `GET /api/organizations/billing` is also supported. - Global admins need an active organization context in the authenticated session for the org-less route. ## Response ```json { "billingEmail": "billing@deinkunde.com", "paymentMethod": "mollie" } ``` ## Common errors - `400 Organization ID is required in the authenticated session` when no organization can be derived from the current session/token - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you cannot access the organization
### Get Organization Details
URL: https://docs.uptimeify.io/api/organization/get-organization-details
Description: Returns details of the organization from your authenticated session.
Summary: `GET /api/organization` ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/organization" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` Notes: - The organization is derived automatically from your authenticated session or API token. - The legacy route `GET /api/organizations/:organizationPublicId` remains supported for compatibility. - The plural org-less alias `GET /api/organizations` is also supported. - Global admins need an active organization context in the authenticated session for the org-less route. - Billing fields, including the `quota*` fields and `pendingQuotaChange`, are only present for an organization admin or a global admin. Other members receive the response without them. ## Response ```json { "id": 1, "name": "My Org", "companyName": "My Company GmbH", "street": "Sample St. 1", "postalCode": "12345", "city": "Berlin", "country": "DE", "vatId": "DE123456789", "billingEmail": "billing@deinkunde.com", "createdAt": "2023-01-01T00:00:00.000Z", "pendingQuotaChange": { "effectiveAt": "2026-08-01T00:00:00.000Z", "quotaWebsitesLimit": 25, "quotaSmsIncluded": 100, "quotaMonthlyPriceCents": 6900 } } ``` `pendingQuotaChange` describes a quota reduction that has been requested but does not take effect until the end of the current billing period (AGB § 10.5). It is `null` when nothing is scheduled, which is the normal case. | Field | Meaning | | --- | --- | | `effectiveAt` | The first instant at which the new plan applies: the next UTC month start for a monthly billing cycle, the end of the paid term for a yearly one. | | `quotaWebsitesLimit` | The monitor limit that will apply from then on. `null` means no limit. | | `quotaSmsIncluded` | The included SMS allowance that will apply from then on. | | `quotaMonthlyPriceCents` | The monthly price that will apply from then on, in cents. | While a change is pending, the organization's own `quota*` fields still describe the **current, higher** plan, that is the point of the clause, and billing continues at that plan for the whole period. Use [Cancel Scheduled Quota Change](/api/organization/cancel-pending-quota-change) to call it off, or [Update Organization](/api/organization/update-organization) to raise the plan instead. ## Common errors - `400 Organization ID is required in the authenticated session` when no organization can be derived from the current session/token - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you cannot access the organization
### List Data Exports
URL: https://docs.uptimeify.io/api/organization/list-data-exports
Description: Returns the recent data export requests of your organization with status, size, retention window and expiry.
Summary: `GET /api/organization/data-export` Returns the 20 most recent export requests, newest first. Poll this (or `GET /api/organization/data-export/{exportId}`) while an export is `queued` or `generating`. ## Response ```json { "exports": [ { "id": "0f0f2f6a-2b3b-4a2f-9a7c-3e5f8c1d2b44", "status": "ready", "format": "json_csv", "includeCheckHistory": true, "recipientEmails": ["admin@youragency.com"], "sizeBytes": 18446721, "retentionCutoff": "2025-03-30T09:12:44.101Z", "expiresAt": "2026-08-06T09:19:02.884Z", "downloadCount": 1, "lastDownloadedAt": "2026-07-30T10:02:11.900Z", "error": null, "manifest": { "generatedAt": "2026-07-30T09:12:52.006Z", "retentionCutoff": "2025-03-30T09:12:44.101Z", "includeCheckHistory": true, "packages": [ { "packageType": "fullservice", "displayName": "Full Service", "dataRetentionMonths": 16, "cutoff": "2025-03-30T09:12:44.101Z" } ], "sections": [ { "path": "customers/customers", "rows": 42 }, { "path": "monitors/websites", "rows": 311 }, { "path": "checks/website-checks", "rows": 100000, "truncated": true } ], "redactedFields": ["passwords and passphrases", "webhook secrets and signing keys"] }, "createdAt": "2026-07-30T09:12:44.101Z", "startedAt": "2026-07-30T09:12:45.010Z", "completedAt": "2026-07-30T09:19:02.884Z" } ], "cooldownHours": 6 } ``` ## Statuses | Status | Meaning | | --- | --- | | `queued` | Accepted, waiting for the builder | | `generating` | The bundle is being built | | `ready` | Downloadable until `expiresAt` | | `failed` | Build failed; `error` carries the reason | | `expired` | Download window closed, the file has been deleted | ## Single export `GET /api/organization/data-export/{exportId}` returns one export in the same shape (without the `exports` wrapper). ## Common errors - `400 User must belong to an organization` when no organization can be derived from the session or token - `401 Unauthorized` when you are not authenticated - `403 Forbidden` when you cannot read the organization - `404 Export not found` when the id does not belong to your organization
### List Invoices
URL: https://docs.uptimeify.io/api/organization/list-invoices
Description: Lists all invoices for the organization (Mollie).
Summary: `GET /api/mollie/invoices` ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET \ "$BASE_URL/api/mollie/invoices" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` Notes: - The organization is derived automatically from your authenticated session or API token. - The legacy route `GET /api/organizations/:organizationPublicId/mollie/invoices` remains supported for compatibility. - The `pdfUrl` values in the response use the org-less download route `/api/invoices/:invoiceId/pdf`. - Global admins need an active organization context in the authenticated session for the org-less route. ## Response ```json { "invoices": [ { "id": "inv_123456", "reference": "INV-2024-001", "status": "paid", "issuedAt": "2024-01-15T10:00:00.000Z", "netAmount": { "value": "29.99", "currency": "EUR" }, "pdfUrl": "/api/invoices/inv_123456/pdf" }, { "id": "inv_123455", "reference": "INV-2023-128", "status": "paid", "issuedAt": "2023-12-15T10:00:00.000Z", "netAmount": { "value": "29.99", "currency": "EUR" }, "pdfUrl": "/api/invoices/inv_123455/pdf" } ] } ``` ## Common errors - `400 Organization ID is required in the authenticated session` when no organization can be derived from the current session/token - `401 Unauthorized` when you are not logged in - `403 Not authorized` when you cannot access the organization
### List Package Configs
URL: https://docs.uptimeify.io/api/organization/list-package-configs
Description: Returns all package configurations for the organization in your authenticated session. Each config also includes a usedByCustomerCount field.
Summary: `GET /api/package-configs` ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/package-configs" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` Notes: - The organization is derived automatically from your authenticated session or API token. - The legacy route `GET /api/organizations/:organizationPublicId/package-configs` remains supported for compatibility. - Global admins need an active organization context in the authenticated session for the org-less route. - `usedByCustomerCount` counts the customers actually assigned to that config, not the customers whose `packageType` string happens to match its name. ## Response ```json [ { "id": 10, "packageType": "pro", "maxUrls": 100, "dataRetentionMonths": 12, "checkIntervalMinutes": 1, "checkLocations": 3, "notificationDelayMinutes": 0, "reminderDelayMinutes": 10, "alertConsecutiveChecks": 3, "alertLocationThreshold": "majority", "alertLocationThresholdCount": 2, "alertReminderInterval": 60, "enableSslCheck": true, "enableHttpsCheck": true, "enableStatusCheck": true, "enableSizeCheck": true, "enableResponseTimeCheck": true, "enableKeywordCheck": false, "enableEmailAlerts": true, "enableSmsAlerts": true, "enableWebhookAlerts": true, "enableIntegrationAlerts": true, "enablePostRequestEscalation": false, "enableMaintenanceWindows": true, "enablePdfReports": true, "notes": null, "createdAt": "2026-02-26T12:00:00.000Z", "updatedAt": "2026-02-26T12:00:00.000Z", "usedByCustomerCount": 4 } ] ``` ## Common errors - `400 Organization ID is required in the authenticated session` when no organization can be derived from the current session/token - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you cannot access the organization
### Create Report
URL: https://docs.uptimeify.io/api/organization/reports/create-report
Description: Creates an organization-level report configuration. Requires a plan that includes organization reports and available report quota.
Summary: `POST /api/organization/reports` Creates a recurring organization report. The organization is derived from your authenticated session or API token. Requires organization **admin** (API tokens act as organization admins). Read-only users cannot create reports. ## Request Body | Field | Type | Required | Notes | |---|---|---|---| | `name` | string | yes | 1-200 chars. | | `enabled` | boolean | no | Default `true`. | | `frequency` | `"daily" \| "weekly" \| "monthly"` | no | Default `monthly`. | | `weekday` | integer 0-6 | when `weekly` | 0 = Sunday. | | `dayOfMonth` | integer 1-28 | when `monthly` | Capped at 28. | | `sendHour` | integer 0-23 | no | Default `2`, in `timezone`. | | `timezone` | string | no | IANA tz, default `Europe/Berlin`. | | `scopeMode` | `"all" \| "customers" \| "tags"` | no | Default `all`. | | `scopeCustomerIds` | integer[] | when `customers` | Must belong to your organization. | | `scopeTagIds` | integer[] | when `tags` | Must belong to your organization. | | `inclusionMode` | `"all" \| "problems" \| "threshold"` | no | Default `all`. | | `problemSignals` | object | no | `{ incident, downtimeMinutes, sslDaysLt, responseBreach }`. | | `thresholdUptimeLt` | number | when `threshold` | e.g. `99.9`. | | `thresholdResponseGt` | integer (ms) | when `threshold` | | | `sendWhenEmpty` | boolean | no | Send an "all clear" report when no site matches. Default `false`. | | `sections` | object | no | Six booleans toggling report sections. | | `monitorTypes` | string[] | no | Which monitor types the report covers. One or more of `website`, `dns`, `dnsbl`, `domain`, `icmp`, `smtp`, `ssh`, `tcp`, `ftp`, `imap`. Default: all 10. Must contain at least one entry. | | `sectionsByType` | object | no | Per-type section toggles: `{ "": { "": boolean } }`. Legal `sectionKey`s depend on the type: see [Section keys by monitor type](#section-keys-by-monitor-type) below. Sending a section key that isn't legal for its type is rejected with `400 Bad Request`. | | `groupByCustomer` | boolean | no | Sub-group each monitor type's table by customer. Default `false`. | | `format` | `"email" \| "email_pdf"` | no | Default `email_pdf`. | | `recipientEmails` | string[] | no | Agency-side email recipients (max 50). | | `recipientUserIds` | string[] | no | Organization team member user ids (max 50). | ### Section keys by monitor type | Monitor type | Legal `sectionKey`s | |---|---| | `website`, `dns`, `icmp`, `smtp`, `ssh`, `tcp`, `ftp`, `imap` | `fleetSummary`, `worstPerformers`, `perMonitorTable`, `incidentLog` | | `website` (additionally) | `sslExpiry` | | `dnsbl` | `dnsblStatus` | | `domain` | `domainExpiry` | ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X POST "$BASE_URL/api/organization/reports" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Weekly Ops Digest", "frequency": "weekly", "weekday": 1, "scopeMode": "all", "inclusionMode": "problems", "problemSignals": { "incident": true, "downtimeMinutes": 5, "sslDaysLt": 14, "responseBreach": true }, "format": "email_pdf", "recipientEmails": ["ops@agency.io"] }' ``` ## Response ```json { "id": "3f1c9c1e-8d2a-4c7e-9b1a-2d5f6a7b8c90", "name": "Weekly Ops Digest", "enabled": true, "frequency": "weekly", "weekday": 1, "dayOfMonth": null, "sendHour": 2, "timezone": "Europe/Berlin", "scopeMode": "all", "inclusionMode": "problems", "monitorTypes": ["website", "dns", "dnsbl", "domain", "icmp", "smtp", "ssh", "tcp", "ftp", "imap"], "sectionsByType": {}, "groupByCustomer": false, "format": "email_pdf", "recipientEmails": ["ops@agency.io"], "recipientUserIds": [] } ``` ## Common errors - `401 Unauthorized`: not authenticated. - `403 Forbidden` (`forbidden`): not an organization admin. - `400` (`invalidReportSchedule`): `weekly` without `weekday`, or `monthly` without `dayOfMonth`. - `400` (`invalidReportScope`): scope ids are not owned by your organization, or `threshold` mode without a thresh…
### Delete Report
URL: https://docs.uptimeify.io/api/organization/reports/delete-report
Description: Permanently deletes an organization report configuration and its scheduling. Past report runs are not deleted.
Summary: `DELETE /api/organization/reports/:id` Deletes a report configuration. `:id` is the report's `id` (a UUID). The organization is derived from your authenticated session or API token, and the report must belong to it. Requires organization **admin** (API tokens act as organization admins). Read-only users cannot delete reports. This stops future scheduled runs of the report. Historical [report runs](/api/organization/reports/list-report-runs) already generated for this report are not affected and remain retrievable. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" REPORT_ID="3f1c9c1e-8d2a-4c7e-9b1a-2d5f6a7b8c90" curl -X DELETE "$BASE_URL/api/organization/reports/$REPORT_ID" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "success": true } ``` ## Common errors - `401 Unauthorized`: not authenticated. - `403 Forbidden` (`forbidden`): not an organization admin. - `404 Not Found` (`notFound`): no report with this id exists for your organization.
### Download Report PDF
URL: https://docs.uptimeify.io/api/organization/reports/download-report-pdf
Description: Redirects to a short-lived signed URL for the archived PDF of a report run.
Summary: `GET /api/organization/report-runs/:id/pdf` Redirects (`302`) to a time-limited, signed object-storage URL for the PDF archived for a report run. `:id` is the run's `id` (a UUID), as returned by [List Report Runs](/api/organization/reports/list-report-runs). The run must belong to your organization. Available to any organization role, including **read-only** users. Only runs with `format: "email_pdf"` that completed successfully archive a PDF. Check `hasPdf` on the run before calling this endpoint. `format: "email"` runs, and runs that failed or were skipped, have no PDF and return `404`. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" RUN_ID="9a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d" curl -X GET "$BASE_URL/api/organization/report-runs/$RUN_ID/pdf" \ -H "Authorization: Bearer $TOKEN" \ -L -o report.pdf ``` ## Response `302 Found` with a `Location` header pointing at a signed, short-lived URL for the PDF object. No response body. Follow the redirect (`-L` in cURL, or your HTTP client's default redirect handling) to download the file. ## Common errors - `401 Unauthorized`: not authenticated. - `403 Forbidden` (`forbidden`): you do not have access to this organization. - `404 Not Found` (`reportRunNotFound`): no run with this id exists for your organization. - `404 Not Found` (`reportRunNoPdf`): the run has no archived PDF (it was an email-only report, or generation failed/was skipped).
### Get Report
URL: https://docs.uptimeify.io/api/organization/reports/get-report
Description: Returns a single organization report configuration by its public id.
Summary: `GET /api/organization/reports/:id` Returns one report configuration. `:id` is the report's `id` (a UUID), as returned by [Create Report](/api/organization/reports/create-report) or [List Reports](/api/organization/reports/list-reports). The report must belong to your organization. Available to any organization role, including **read-only** users. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" REPORT_ID="3f1c9c1e-8d2a-4c7e-9b1a-2d5f6a7b8c90" curl -X GET "$BASE_URL/api/organization/reports/$REPORT_ID" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Response ```json { "id": "3f1c9c1e-8d2a-4c7e-9b1a-2d5f6a7b8c90", "name": "Weekly Ops Digest", "enabled": true, "frequency": "weekly", "weekday": 1, "dayOfMonth": null, "sendHour": 2, "timezone": "Europe/Berlin", "scopeMode": "all", "scopeCustomerIds": [], "scopeTagIds": [], "inclusionMode": "problems", "problemSignals": { "incident": true, "downtimeMinutes": 5, "sslDaysLt": 14, "responseBreach": true }, "thresholdUptimeLt": null, "thresholdResponseGt": null, "sendWhenEmpty": false, "sections": { "fleetSummary": true, "worstPerformers": true, "perSiteTable": true, "incidentLog": true, "sslExpiry": true, "groupByCustomer": false }, "monitorTypes": ["website", "dns", "dnsbl", "domain", "icmp", "smtp", "ssh", "tcp", "ftp", "imap"], "sectionsByType": {}, "groupByCustomer": false, "format": "email_pdf", "recipientEmails": ["ops@agency.io"], "recipientUserIds": [], "createdAt": "2026-06-01T02:00:00.000Z", "updatedAt": "2026-06-01T02:00:00.000Z" } ``` ## Common errors - `401 Unauthorized`: not authenticated. - `403 Forbidden` (`forbidden`): you do not have access to this organization. - `404 Not Found` (`notFound`): no report with this id exists for your organization.
### List Report Runs
URL: https://docs.uptimeify.io/api/organization/reports/list-report-runs
Description: Returns the organization's report generation/delivery history, optionally filtered to a single report.
Summary: `GET /api/organization/report-runs` Lists report runs for the organization, newest first. A run is created every time a report is generated: on schedule or via [Send Report Now](/api/organization/reports/send-report-now). The organization is derived from your authenticated session or API token. Available to any organization role, including **read-only** users. ## Query Parameters | Field | Type | Required | Notes | |---|---|---|---| | `reportId` | string (uuid) | no | Restrict to runs of one report. If the id does not resolve to a report in your organization, returns `[]`. | | `limit` | integer | no | Default `50`, capped at `200`. | | `offset` | integer | no | Default `0`. | ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/organization/report-runs?reportId=3f1c9c1e-8d2a-4c7e-9b1a-2d5f6a7b8c90&limit=20" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Response ```json [ { "id": "9a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d", "reportId": "3f1c9c1e-8d2a-4c7e-9b1a-2d5f6a7b8c90", "reportName": "Weekly Ops Digest", "periodStart": "2026-05-25T00:00:00.000Z", "periodEnd": "2026-06-01T00:00:00.000Z", "periodLabel": "May 25 - Jun 1, 2026", "status": "sent", "trigger": "schedule", "sentAt": "2026-06-01T02:00:12.000Z", "recipients": ["ops@agency.io"], "hasPdf": true, "summary": { "sitesIncluded": 42, "incidents": 3 }, "createdAt": "2026-06-01T02:00:00.000Z" } ] ``` `status` is one of `pending`, `generating`, `sent`, `failed`, `skipped_empty`. `trigger` is `schedule` or `manual`. `hasPdf` is `true` only when a PDF was archived for this run. Use it to decide whether to call [Download Report PDF](/api/organization/reports/download-report-pdf). ## Common errors - `401 Unauthorized`: not authenticated. - `403 Forbidden` (`forbidden`): you do not have access to this organization.
### List Reports
URL: https://docs.uptimeify.io/api/organization/reports/list-reports
Description: Returns all organization report configurations plus the organization's report entitlement (plan feature flag and quota).
Summary: `GET /api/organization/reports` Lists the organization's recurring report configurations, newest first. The organization is derived from your authenticated session or API token. Available to any organization role, including **read-only** users. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/organization/reports" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Response ```json { "entitlement": { "enabled": true, "limit": 10 }, "reports": [ { "id": "3f1c9c1e-8d2a-4c7e-9b1a-2d5f6a7b8c90", "name": "Weekly Ops Digest", "enabled": true, "frequency": "weekly", "weekday": 1, "dayOfMonth": null, "sendHour": 2, "timezone": "Europe/Berlin", "scopeMode": "all", "scopeCustomerIds": [], "scopeTagIds": [], "inclusionMode": "problems", "problemSignals": { "incident": true, "downtimeMinutes": 5, "sslDaysLt": 14, "responseBreach": true }, "thresholdUptimeLt": null, "thresholdResponseGt": null, "sendWhenEmpty": false, "sections": { "fleetSummary": true, "worstPerformers": true, "perSiteTable": true, "incidentLog": true, "sslExpiry": true, "groupByCustomer": false }, "monitorTypes": ["website", "dns", "dnsbl", "domain", "icmp", "smtp", "ssh", "tcp", "ftp", "imap"], "sectionsByType": {}, "groupByCustomer": false, "format": "email_pdf", "recipientEmails": ["ops@agency.io"], "recipientUserIds": [], "createdAt": "2026-06-01T02:00:00.000Z", "updatedAt": "2026-06-01T02:00:00.000Z" } ] } ``` `entitlement.enabled` reflects whether the organization's plan includes organization reports; `entitlement.limit` is the maximum number of report configurations allowed. Both are derived from the organization's active pricing/custom-pricing rows, independent of how many reports currently exist. ## Common errors - `401 Unauthorized`: not authenticated. - `403 Forbidden` (`forbidden`): you do not have access to this organization.
### Send Report Now
URL: https://docs.uptimeify.io/api/organization/reports/send-report-now
Description: Queues an immediate, out-of-schedule generation and delivery of an organization report.
Summary: `POST /api/organization/reports/:id/send-now` Enqueues a manual run of a report configuration, independent of its `frequency`/`sendHour` schedule. `:id` is the report's `id` (a UUID). The organization is derived from your authenticated session or API token, and the report must belong to it. Requires organization **admin** (API tokens act as organization admins). Read-only users cannot trigger sends. This endpoint only queues the job. It does not wait for generation or delivery to complete. Poll [List Report Runs](/api/organization/reports/list-report-runs) (filtered by `reportId`) to see the resulting run once its `status` moves past `pending`. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" REPORT_ID="3f1c9c1e-8d2a-4c7e-9b1a-2d5f6a7b8c90" curl -X POST "$BASE_URL/api/organization/reports/$REPORT_ID/send-now" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "queued": true, "jobId": "orgreport-manual-3f1c9c1e-8d2a-4c7e-9b1a-2d5f6a7b8c90-1b6e..." } ``` ## Common errors - `401 Unauthorized`: not authenticated. - `403 Forbidden` (`forbidden`): not an organization admin. - `404 Not Found` (`notFound`): no report with this id exists for your organization.
### Update Report
URL: https://docs.uptimeify.io/api/organization/reports/update-report
Description: Updates an organization report configuration. All fields are optional; at least one must be provided.
Summary: `PATCH /api/organization/reports/:id` Partially updates a report configuration. `:id` is the report's `id` (a UUID). The organization is derived from your authenticated session or API token, and the report must belong to it. Requires organization **admin** (API tokens act as organization admins). Read-only users cannot update reports. Only send the fields you want to change. Cross-field validation (schedule and scope) is re-run against the **merged** result. A `PATCH` that only changes `frequency` to `weekly` without also sending `weekday` fails if the existing report has no `weekday` set. ## Request Body Same fields as [Create Report](/api/organization/reports/create-report), all optional, but at least one field must be present: | Field | Type | Notes | |---|---|---| | `name` | string | 1-200 chars. | | `enabled` | boolean | | | `frequency` | `"daily" \| "weekly" \| "monthly"` | | | `weekday` | integer 0-6 \| null | Required (effectively) when the resulting `frequency` is `weekly`. | | `dayOfMonth` | integer 1-28 \| null | Required (effectively) when the resulting `frequency` is `monthly`. | | `sendHour` | integer 0-23 | | | `timezone` | string | IANA tz. | | `scopeMode` | `"all" \| "customers" \| "tags"` | | | `scopeCustomerIds` | integer[] | Must belong to your organization. | | `scopeTagIds` | integer[] | Must belong to your organization. | | `inclusionMode` | `"all" \| "problems" \| "threshold"` | | | `problemSignals` | object | `{ incident, downtimeMinutes, sslDaysLt, responseBreach }`. | | `thresholdUptimeLt` | number \| null | | | `thresholdResponseGt` | integer (ms) \| null | | | `sendWhenEmpty` | boolean | | | `sections` | object | Six booleans toggling report sections. | | `monitorTypes` | string[] | One or more of `website`, `dns`, `dnsbl`, `domain`, `icmp`, `smtp`, `ssh`, `tcp`, `ftp`, `imap`. Cannot be sent as an empty array. | | `sectionsByType` | object | Per-type section toggles: `{ "": { "": boolean } }`. Legal `sectionKey`s depend on the type: see [Section keys by monitor type](/api/organization/reports/create-report#section-keys-by-monitor-type). Sending a section key that isn't legal for its type is rejected with `400 Bad Request`. | | `groupByCustomer` | boolean | Sub-group each monitor type's table by customer. | | `format` | `"email" \| "email_pdf"` | | | `recipientEmails` | string[] | Max 50. | | `recipientUserIds` | string[] | Max 50. | ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" REPORT_ID="3f1c9c1e-8d2a-4c7e-9b1a-2d5f6a7b8c90" curl -X PATCH "$BASE_URL/api/organization/reports/$REPORT_ID" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "enabled": false, "recipientEmails": ["ops@agency.io", "cs@agency.io"] }' ``` ## Response ```json { "id": "3f1c9c1e-8d2a-4c7e-9b1a-2d5f6a7b8c90", "name": "Weekly Ops Digest", "enabled": false, "frequency": "weekly", "weekday": 1, "dayOfMonth": null, "sendHour": 2, "timezone": "Europe/Berlin", "scopeMode": "all", "inclusionMode": "problems", "monitorTypes": ["website", "dns", "dnsbl", "domain", "icmp", "smtp", "ssh", "tcp", "ftp", "imap"], "sectionsByType": {}, "groupByCustomer": false, "format": "email_pdf", "recipientEmails": ["ops@agency.io", "cs@agency.io"], "recipientUserIds": [] } ``` ## Common errors - `401 Unauthorized`: not authenticated. - `403 Forbidden` (`forbidden`): not an organization admin. - `404 Not Found` (`notFound`): no report with this id exists for your organization. - `400` (`invalidReportSchedule`): the resulting `weekly` config has no `weekday`, or the resulting `monthly` config has no `dayOfMonth`. - `400` (`invalidReportScope`): scope ids are not owned by your organization, or the resulting `threshold` mode has no threshold. - `400 Bad Request`: a `sectionsByType` entry contains a `sectionKey` that isn't legal for its monitor type, or `monitorTypes` is sent as an empty array.
### Request Data Export
URL: https://docs.uptimeify.io/api/organization/request-data-export
Description: Queues a complete export of your organization's content as JSON and CSV, delivered as a downloadable ZIP and announced by email.
Summary: `POST /api/organization/data-export` Starts a full export of everything your organization holds in Uptimeify: customers, monitors of every type, incidents and incident updates, maintenance windows, notification channels, tags, status pages, reports and their run history, billing records, team members, API token metadata, incident-management configuration and history, and (unless you opt out) the raw check history behind your monitors. The request returns immediately with `202 Accepted`. The bundle is built in the background; when it is ready, an email with a download link goes to the recipients, and the export appears as `ready` in `GET /api/organization/data-export`. ## Request body | Field | Type | Default | Description | | --- | --- | --- | --- | | `includeCheckHistory` | boolean | `true` | Include raw per-check rows. This is the bulk of the bundle; set `false` for a configuration-only export. | | `recipientEmails` | string[] | administrators of the organization | Who receives the "export is ready" email. Every address must belong to a member of this organization. Max 20. | ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X POST "$BASE_URL/api/organization/data-export" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "includeCheckHistory": true }' ``` ## Response ```json { "id": "0f0f2f6a-2b3b-4a2f-9a7c-3e5f8c1d2b44", "status": "queued", "format": "json_csv", "includeCheckHistory": true, "recipientEmails": ["admin@youragency.com"], "sizeBytes": null, "retentionCutoff": null, "expiresAt": null, "downloadCount": 0, "lastDownloadedAt": null, "error": null, "manifest": null, "createdAt": "2026-07-30T09:12:44.101Z", "startedAt": null, "completedAt": null } ``` ## What the bundle contains A ZIP with both formats plus an index: - `manifest.json`, every section with its row count, the retention cutoff per package, and completeness flags - `README.txt`, what is included, what is redacted, what is incomplete - `json/.json`, full-fidelity records - `csv/.csv`, the same records, flat and spreadsheet-ready Three rules bound the contents: - **Retention.** Time-series data (check history, alert deliveries, SMS log) reaches back as far as the data retention of the packages your customers are on. Older rows are not withheld, they no longer exist. - **Credentials.** Passwords, webhook secrets, integration and ingest tokens, API token hashes and SMTP passwords are replaced with `[redacted]`. The surrounding record is exported in full. - **Rolling windows.** `organization/audit-log` holds administrative actions (who did what, from which IP and user agent). It is a rolling recent-activity window (30 days by default), not a permanent record. Its `note` in `manifest.json` states the window the export covers. Raw check history is capped per monitor type (100,000 newest rows by default). A capped section is flagged with `truncated` in `manifest.json`; pull the remainder per monitor via `GET /api/websites/{id}/check-history?format=csv` and the equivalent endpoints of the other monitor types. `truncated` also marks a section whose source could not be read while the export ran. Such a section carries a `note` beginning with `INCOMPLETE` and zero rows, which is not a claim that the section is empty. Request a new export to retry. ## Rate limits - One export at a time per organization (`409 exportAlreadyRunning`). - One request per organization every 6 hours (`429 exportCooldown`). - A finished export stays downloadable for 7 days, then the file is deleted and the record flips to `expired`. ## Common errors - `400 User must belong to an organization` when no organization can be derived from the session or token - `400 Recipients must be members of this organization` (`data.code: invalidExportRecipient`) when `recipientEmails` contains an outside address - `401 Unauthorized` when you are not authenticated - `403 Forbidden` when your role cannot administer the organ…
### Update Billing Details
URL: https://docs.uptimeify.io/api/organization/update-billing-details
Description: Updates billing information for the organization in your authenticated session.
Summary: `PATCH /api/organization/billing` ## Request Body ```json { "billingEmail": "billing@deinkunde.com" } ``` ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X PATCH "$BASE_URL/api/organization/billing" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d '{ "billingEmail": "billing@deinkunde.com" }' ``` ## Notes - `billingEmail` is currently the only supported field for this endpoint. - The organization is derived automatically from your authenticated session or API token. - The legacy route `PATCH /api/organizations/:organizationPublicId/billing` remains supported for compatibility. - The plural org-less alias `PATCH /api/organizations/billing` is also supported. - Global admins need an active organization context in the authenticated session for the org-less route. ## Common errors - `400 Invalid request body` when the payload does not match the schema - `400 Organization ID is required in the authenticated session` when no organization can be derived from the current session/token - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you do not have admin access to update billing settings ## Response Returns the updated organization billing details. See [Error Codes](/api/error-codes-and-known-pitfalls) for error responses.
### Update Organization
URL: https://docs.uptimeify.io/api/organization/update-organization
Description: Updates the organization from your authenticated session.
Summary: `PATCH /api/organization` ## Request Body ```json { "name": "New Name", "companyName": "New Company Name", "street": "New Street", "postalCode": "54321", "city": "New City", "country": "US", "vatId": "US123", "countryCode": "AT", "billingEmail": "new@deinkunde.com", "requireMfaAdmins": true, "requireMfaEditors": false, "requireMfaReadonly": false, "requireMfaCustomers": false, "defaultNotificationChannels": { "email": true, "sms": false, "webhook": true, "integrations": false }, "defaultNotificationTargets": { "email": "both", // customer, organization, both "sms": "organization", "webhook": "organization", "integrations": "customer" } } ``` ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X PATCH "$BASE_URL/api/organization" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d '{ "name": "New Name", "billingEmail": "new@deinkunde.com" }' ``` Notes: - The organization is derived automatically from your authenticated session or API token. - The legacy route `PATCH /api/organizations/:organizationPublicId` remains supported for compatibility. - The plural org-less alias `PATCH /api/organizations` is also supported. - Global admins need an active organization context in the authenticated session for the org-less route. - `countryCode` is the organization's billing country as an ISO-3166-1 alpha-2 code (two letters, case-insensitive on input, always stored and returned uppercased). It determines VAT treatment: reverse charge under AGB § 10.2 requires this to be an EU member state other than Germany. - Writing `vatId` or `countryCode` (either one, in the same request or separately) resets `vatIdStatus` to `unverified`. A previous `valid` verdict never survives an edit to either field, re-validate with [Validate VAT ID](/api/organization/validate-vat-id) after the change to restore reverse-charge eligibility. - **Quota changes are not symmetric.** A change whose `quotaMonthlyPriceCents` is **higher** than the organization's current one takes effect immediately, and the difference is charged pro rata for the rest of the current billing period. A change whose price is **lower** does **not** take effect immediately: under AGB § 10.5 it is scheduled for the end of the current billing period, and no credit is issued for the unused part of the higher plan. Until it fires, the organization keeps its current plan and every quota field on the organization still describes that plan. Direction is decided by price alone, never by the monitor limit, so a tier that costs less for *more* monitors is still a reduction. - A scheduled reduction is returned as `pendingQuotaChange` on both this endpoint and [Get Organization Details](/api/organization/get-organization-details); it is `null` when nothing is scheduled. There is at most one scheduled change per organization: a second reduction replaces the first, and an increase supersedes it (otherwise you would be silently dropped back at the period boundary). To call one off without changing plan, use [Cancel Scheduled Quota Change](/api/organization/cancel-pending-quota-change), re-sending the current tier here counts as unchanged and will not do it. - A reduction below the organization's current monitor usage is rejected up front with `400`. The same check runs again when the change is applied: if the organization has grown past the new limit in the meantime, the reduction is abandoned rather than applied, and the organization keeps the higher plan. - `requireMfaAdmins`, `requireMfaEditors`, `requireMfaReadonly` and `requireMfaCustomers` (all boolean) independently force two-step verification for four different audiences, set any subset of the four to roll MFA out incrementally. **A user's audience is not simply their role.** Every user in the organization is either a **team member** or a **customer user**: - **Team member**: an `admin`, or an `editor`/`readonly` member with no explicit customer assignme…
### Upsert Package Config
URL: https://docs.uptimeify.io/api/organization/upsert-package-config
Description: Creates a new package config (by :packageType) or updates an existing one. packageType is a free-form identifier chosen by the organization, and customer endpoints can later reference that same key.
Summary: `PATCH /api/package-configs/:packageType` This endpoint is the source of truth for alerting-related defaults like `alertConsecutiveChecks` and feature flags like `enableEmailAlerts`. ## Request Body All fields are optional and can be updated over time. If you want a human-friendly package label in the UI, set `displayName` in addition to the technical `packageType` key. ```json { "displayName": "Pro Care", "maxUrls": 100, "dataRetentionMonths": 12, "checkIntervalMinutes": 1, "checkLocations": 3, "notificationDelayMinutes": 0, "reminderDelayMinutes": 10, "alertConsecutiveChecks": 3, "alertLocationThreshold": "majority", "alertLocationThresholdCount": 2, "alertReminderInterval": 60, "enableEmailAlerts": true, "enableSmsAlerts": true, "enableWebhookAlerts": true, "enableIntegrationAlerts": true, "enablePostRequestEscalation": false, "enableMaintenanceWindows": true, "enablePdfReports": true, "monthlyReportsDefault": true, "allowSelfService": true, "maxSelfServiceUrls": 10, "notes": "Default for PRO customers" } ``` ### Monitor ownership defaults `allowSelfService` (default `false`) and `maxSelfServiceUrls` (default `0`) are the package-tier defaults for the [managed vs. self-service](/monitoring/managed-vs-self-service) model. Every customer on the package inherits them unless the customer carries its own non-`null` override (see [Update Customer](/api/customers/update-customer)). `maxSelfServiceUrls` caps a customer's **total** self-service monitors across all monitor types. The `enable*` alert flags double as the inherited channel-type policy for the same model. ### `alertConsecutiveChecks` An integer between **1 and 10** (values outside that range are rejected with `400` and `data.code` `invalidRequestBody`). It is the number of consecutive failing cycles required before an incident opens, and it also bounds how many consecutive successful cycles are required before one closes. A very large value therefore does not just delay alerting, it delays recovery: at a 5-minute cadence a value of 999 would keep an incident open for 83 hours of uninterrupted green checks. The cap matches the one the dashboard has always enforced. ### `monthlyReportsDefault` `monthlyReportsDefault` (boolean, default `true`) is a **template**, not a live switch: it seeds a new customer's `monthlyReportsEnabled` at creation time. It never rewrites an existing customer, whose own value always wins once set. To push a changed default onto customers **already** on this package, call [Apply Report Default](/api/organization/apply-report-default) afterwards. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X PATCH "$BASE_URL/api/package-configs/pro" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d '{ "displayName":"Pro Care", "maxUrls":100, "dataRetentionMonths":12, "checkIntervalMinutes":1, "checkLocations":3, "notificationDelayMinutes":0, "reminderDelayMinutes":10, "alertConsecutiveChecks":3, "alertLocationThreshold":"majority", "alertLocationThresholdCount":2, "alertReminderInterval":60, "enableEmailAlerts":true, "enableSmsAlerts":true, "enableWebhookAlerts":true, "enableIntegrationAlerts":true, "enableMaintenanceWindows":true, "enablePdfReports":true, "monthlyReportsDefault":true, "notes":"Default for PRO customers" }' ``` ## Response Returns the created/updated package config. ```json { "id": 10, "packageType": "pro", "displayName": "Pro Care", "maxUrls": 100, "dataRetentionMonths": 12, "checkIntervalMinutes": 1, "checkLocations": 3, "notificationDelayMinutes": 0, "reminderDelayMinutes": 10, "alertConsecutiveChecks": 3, "alertLocationThreshold": "majority", "alertLocationThresholdCount": 2, "alertReminderInterval": 60, "enableEmailAlerts": true, "enableSmsAlerts": true, "enableWebhookAlerts": true, "enableIntegrationAlerts": true, "enableMaintenanceWindows": true, "enablePdfReports": true, "monthlyReportsDefault": true, "notes": "Default for PRO customers"…
### Validate VAT ID
URL: https://docs.uptimeify.io/api/organization/validate-vat-id
Description: Validates the organization's stored VAT identification number against the EU VIES register and persists the verdict. Only a valid VAT ID from another EU member state qualifies the organization for reverse-charge invoicing under AGB § 10.2.
Summary: `POST /api/organizations/:organizationPublicId/vat-id/validate` ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" ORG_ID="" curl -X POST "$BASE_URL/api/organizations/$ORG_ID/vat-id/validate" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` Notes: - This endpoint takes no request body. It validates whatever `vatId` and `countryCode` are currently stored on the organization, set them first with [Update Organization](/api/organization/update-organization) if needed. - Validation is a deliberate, explicit action, not a side effect of saving the organization. The EU VIES service is slow and occasionally unavailable per member state, so a `PATCH` to the organization must not fail or hang on it. Saving a new `vatId` or `countryCode` resets `vatIdStatus` to `unverified`; calling this endpoint is what turns it back into a verdict. - Reverse charge under AGB § 10.2 applies only when the organization's country is an EU member state other than Germany, a VAT ID is stored, and `vatIdStatus` is exactly `valid`. Every other combination bills at the standard rate. - Requires organization write access (organization admin or global admin) from an unrestricted session or token, the same authorization as [Update Organization](/api/organization/update-organization). A customer-scoped API token cannot call this endpoint, even with `admin`-level role. ## Response ```json { "vatIdStatus": "valid", "vatIdValidatedAt": "2026-07-30T09:15:00.000Z", "vatIdValidationNote": null, "taxTreatment": "reverse_charge" } ``` `vatIdStatus` is one of: | Value | Meaning | | --- | --- | | `unverified` | Not yet validated, or the stored `vatId`/`countryCode` changed since the last validation (see above). Never returned by this endpoint, it is the pre-validation default. | | `valid` | VIES confirmed the VAT ID. The only status that grants reverse charge. | | `invalid` | VIES rejected the VAT ID, its country prefix does not belong to an EU VAT country, or it does not match the organization's stored `countryCode`. | | `unavailable` | VIES could not give a verdict, a timeout, an HTTP error, or a member state's registry reporting itself down (`MS_UNAVAILABLE`). Deliberately distinct from `invalid`: an outage must never be read as "the VAT ID is wrong," since that would strip a legitimate customer of reverse charge. Retry later. | `taxTreatment` reflects the resulting billing treatment and is one of `standard` or `reverse_charge`. `vatIdValidationNote` is a short, human-readable reason (e.g. why a check came back `invalid` or `unavailable`); `null` on a `valid` result. ## Common errors - `401 Unauthorized` (`data.code: unauthorized`) when you are not logged in - `400 Invalid Organization identifier` when `:organizationPublicId` is neither a legacy integer id nor a valid UUID public identifier - `403 Forbidden` (`data.code: forbidden`) when you do not have write access to the organization - `403 Forbidden` (`data.code: customerScopedTokenForbidden`) when called with a customer-scoped API token; this is an organization-wide action and requires an organization-scoped token or a session - `404 Organization not found` (no `data.code`) when `:organizationPublicId` does not resolve to an existing organization - `404 Organization not found` (`data.code: organizationNotFound`) when the organization is deleted in the narrow window between resolving `:organizationPublicId` and the endpoint's own lookup, a race, not the typical not-found response - `422 No VAT ID stored for this organization` (`data.code: vatIdMissing`) when the organization has no `vatId` set, store one first with [Update Organization](/api/organization/update-organization) - `409 The VAT ID or country changed while validation was in flight` (`data.code: vatIdChangedDuringValidation`) when `vatId` or `countryCode` was edited concurrently while VIES was being queried, the in-flight verdict is discarded rather than written to stale data. Safe…
### Service Status
URL: https://docs.uptimeify.io/api/service-status
Description: Public, unauthenticated endpoint that returns Uptimeify's own platform availability, check and alert delivery percentages, component states, and the hash-chained frozen monthly totals.
Summary: `GET /api/service-status` No authentication required. Returns no customer data, only the platform's own delivery figures, published component states, and the frozen monthly totals behind them. ## Example (cURL) ```bash curl -X GET "https://uptimeify.io/api/service-status" \ -H "Accept: application/json" ``` ## Response ```json { "checkDelivery": { "pct": 99.97, "coveragePct": 99.94, "insufficientData": false, "windowDays": 90 }, "alertDelivery": { "pct": 97.31, "sampleSize": 2454, "endpointFailures": 140, "latencyP90Ms": 1727, "latencySampleSize": 812, "insufficientData": false, "latencyInsufficientData": false }, "components": [ { "key": "de-fra", "displayName": "Frankfurt (DE)", "state": "operational", "coveragePct": 99.94 }, { "key": "alerting", "displayName": "Alert Delivery", "state": "operational", "coveragePct": 100 } ], "frozenMonths": [ { "month": "2026-06-01", "sliKind": "check_delivery", "scopeKey": "mesh", "numerator": 4318204, "denominator": 4319850, "unknownUnits": 612, "rowHash": "9f2c1a7e4b8d0f3a6c5e2b1d8a7f4e9c0b3d6a1f8e5c2b9d7a4f1e8c5b2d9a6f" }, { "month": "2026-06-01", "sliKind": "measurement_coverage", "scopeKey": "mesh", "numerator": 43176, "denominator": 43200, "unknownUnits": 24, "rowHash": "4d1b8e0cc0a2f7593e6b4c1d8a05f2e7b9c3d6a1f8e5c2b9d7a4f1e8c5b2d9a6" } ] } ``` Notes: - `checkDelivery.pct` and `alertDelivery.pct` are trailing `windowDays` figures (currently 90 days), truncated to two decimals, never rounded up. - **`checkDelivery.pct` counts scheduled CYCLES, not checks.** Every monitor is due one check per check interval, and that is one cycle. A cycle counts as delivered if at least one check row exists for that monitor, from **any** of our locations, inside its minute plus a two-minute tolerance; a cycle that produced no check anywhere is the only thing that lowers the figure. The `numerator` and `denominator` of a `check_delivery` row are therefore whole cycle counts, fulfilled over due. **Which location ran a given cycle is not part of this figure.** The scheduler visits one rotating location per cycle rather than all of them at once, so a location that has not come round yet has not failed at anything, and pricing its share as a shortfall reported outages that had not happened. Losing a location appears in `components[]` instead, per component, with its own `coveragePct`. - **`unknownUnits` is NOT in the denominator of any percentage on this endpoint.** A percentage is measured over what the platform could actually account for; `denominator` is the expectation over the recorded units and `unknownUnits` is the expectation over the units that were never recorded, a gap in the per-minute recorder, a stalled rollup. The two are separate axes because "we were unavailable" and "we could not measure" are different facts: folding the second into the first would publish an outage that did not happen and make the figure a measure of the recorder's own reliability. - **`coveragePct` is the second half of every claim and is never omitted.** It is the share of the window the platform can account for, `0`-`100`, truncated to two decimals; `null` only when nothing was owed in the window yet. `checkDelivery.coveragePct` covers the whole mesh; `components[].coveragePct` is per component and is `null` for a component that has never been measured at all. For the conservative reading (the share of the window positively confirmed as delivered), multiply the two: `pct x coveragePct / 100`. - From any `frozenMonths` row you can recompute coverage yourself as `denominator / (denominator + unknownUnits)`. Rows with `"sliKind": "measurement_coverage"` state it directly in minutes instead: `numerator` is the minutes recorded, `denominator` the minutes owed. That kind exists because a day on which the recorder never ran leaves `check_delivery` at `0/0` (with no recorded minute there is no observed cycle rate to price an unrecorded one with), so only a minute-based row keeps such a day visible. - **Alert delivery is two f…
### Status Pages
URL: https://docs.uptimeify.io/api/status-pages
Description: Create and manage public status pages that display the operational status of your monitored websites, recent incidents, and maintenance windows.
Summary: Status pages can be public (accessible by anyone) or restricted to customer members. Custom domains are supported with DNS verification. ## Authentication All examples assume a bearer token: ```bash BASE_URL="https://uptimeify.io" TOKEN="" ``` ## Endpoints - [List Status Pages](./list-status-pages) - [Create Status Page](./create-status-page) - [Update Status Page](./update-status-page) - [Delete Status Page](./delete-status-page) - [Get Public Status Page](./get-public-status-page) - [Get Status Page Design](./get-status-page-design) - [Update Status Page Design](./update-status-page-design) - [List Status Page Subscribers](./list-subscribers) - [Delete Status Page Subscriber](./delete-subscriber) - [Export Status Page Subscribers](./export-subscribers) - [Add Custom Domain](./add-status-page-domain) - [Remove Custom Domain](./remove-status-page-domain) - [Verify Status Page Domain](./verify-status-page-domain) - [Activate Status Page Domain](./activate-status-page-domain)
### Activate Status Page Domain
URL: https://docs.uptimeify.io/api/status-pages/activate-status-page-domain
Description: Activates a verified custom domain for a status page. Admin-only.
Summary: `POST /api/status-pages/domains/activate` ## Request Body | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `domainId` | number | Yes | - | The verified domain ID to activate | ## Example (cURL) ```bash curl -X POST "$BASE_URL/api/status-pages/domains/activate" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "domainId": 42 }' ``` ## Response ```json { "activated": true, "domain": { "id": 42, "hostname": "status.deinkunde.com", "status": "active", "role": "status_page", "isPrimary": false, "verificationToken": "abc123-def456-ghi789", "verifiedAt": "2026-05-01T12:00:00.000Z", "createdAt": "2026-05-01T11:00:00.000Z", "updatedAt": "2026-05-01T12:30:00.000Z" } } ``` ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` when not an admin - `400 Bad Request` when the domain is not yet verified
### Add Custom Domain to Status Page
URL: https://docs.uptimeify.io/api/status-pages/add-status-page-domain
Description: Adds a custom hostname to an existing status page and returns the DNS TXT record needed for verification. Admin-only.
Summary: `POST /api/status-pages/domains` ## Request Body | Field | Type | Required | Description | |-------|------|----------|-------------| | `statusPageId` | number \| string | Yes | The status page ID or publicId | | `hostname` | string | Yes | The custom hostname (e.g. `status.deinkunde.com`) | ## Example (cURL) ```bash curl -X POST "$BASE_URL/api/status-pages/domains" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "statusPageId": 1, "hostname": "status.deinkunde.com" }' ``` ## Response ```json { "domain": { "id": 42, "hostname": "status.deinkunde.com", "status": "pending", "role": "status_page", "verificationToken": "abc123-def456-ghi789", "verifiedAt": null, "createdAt": "2026-05-01T11:00:00.000Z", "updatedAt": "2026-05-01T11:00:00.000Z" }, "dns": { "txtName": "_uptimeify-verify.status.deinkunde.com", "txtValue": "abc123-def456-ghi789" } } ``` ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` when not an admin - `404 Not found` when the status page does not exist - `409 Conflict` when the hostname is already in use - `422 Unprocessable Entity` when the hostname is reserved or invalid
### Create Status Page
URL: https://docs.uptimeify.io/api/status-pages/create-status-page
Description: Creates a new status page. Requires admin role.
Summary: `POST /api/status-pages` ## Request Body | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `customerId` | number | Yes | - | Customer ID (must belong to your organization) | | `name` | string | Yes | - | Display name (1-120 chars). Slug auto-generated from name. | | `slug` | string | No | auto | URL slug (1-120 chars, auto-normalized to lowercase-hyphens) | | `description` | string | No | null | Description (max 1000 chars) | | `visibility` | string | No | `public` | `public` or `customer_members_only` | | `isPublished` | boolean | No | true | Whether the page is publicly visible | | `customDomainHostname` | string | No | null | Custom domain (3-253 chars). Creates a pending DNS verification record. | | `hiddenMonitors` | array | No | `[]` | Monitors to hide from this status page. Each entry is `{ "type": "http"\|"dns"\|"icmp"\|"smtp"\|"ssh"\|"ftp"\|"imap_pop", "id": }`. Omit or send `[]` to show all monitors (the default). Monitors added later appear automatically. Maximum 500 entries. | ## Example (cURL) ```bash curl -X POST "$BASE_URL/api/status-pages" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "customerId": 5, "name": "Production Status", "description": "Real-time status of our production services", "visibility": "public", "hiddenMonitors": [{ "type": "http", "id": 42 }] }' ``` ## Response ```json { "statusPage": { "id": 1, "publicId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "organizationId": 1, "customerId": 5, "name": "Production Status", "slug": "production-status", "description": "Real-time status of our production services", "visibility": "public", "isPublished": true, "showRecentIncidents": false, "showRecentMaintenance": false, "customDomainId": null, "createdAt": "2026-01-15T10:00:00.000Z", "updatedAt": "2026-01-15T10:00:00.000Z" }, "dns": null } ``` If `customDomainHostname` is provided, the response includes DNS verification instructions: ```json { "dns": { "txtName": "_uptimeify-verify.status.deinkunde.com", "txtValue": "abc123-def456-ghi789" } } ``` ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` when not an admin - `409 Conflict` when slug is already taken
### Delete Status Page
URL: https://docs.uptimeify.io/api/status-pages/delete-status-page
Description: Permanently deletes a status page. Requires admin role.
Summary: `DELETE /api/status-pages/:id` ## Example (cURL) ```bash curl -X DELETE "$BASE_URL/api/status-pages/1" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "deleted": true, "id": 1 } ``` ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` when not an admin - `404 Not found` when the status page does not exist
### Delete Status Page Subscriber
URL: https://docs.uptimeify.io/api/status-pages/delete-subscriber
Description: Permanently deletes a subscriber and its stored consent evidence. Requires admin or editor role.
Summary: `DELETE /api/status-pages/:id/subscribers/:subscriberId` This is a **hard delete**, not the public one-click unsubscribe. The public unsubscribe link keeps the subscriber row with `status: "unsubscribed"` as proof that sending must stop; this endpoint exists to answer erasure requests (GDPR Art. 17), so it removes the row entirely, email address, consent timestamp, IP address, and user agent included. ## Path Parameters | Parameter | Description | |-----------|-------------| | `id` | Status page ID or `publicId` (UUID) | | `subscriberId` | Subscriber ID (positive integer) | ## Example (cURL) ```bash curl -X DELETE "$BASE_URL/api/status-pages/db58058e-4b58-4d97-a314-3bb8e279a182/subscribers/42" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "ok": true } ``` ## Common errors - `401 Unauthorized` when you are not authenticated - `400 User must belong to an organization` (`data.code: userMustBelongToOrg`) when no organization can be derived from the session or token - `403 Forbidden` when your role is not admin or editor, or your customer scope does not include this status page - `400 Invalid Status page identifier` when `id` is not a valid numeric ID or UUID (no `data.code`) - `404 Status page not found` when `id` is well-formed but no status page has that ID (no `data.code`) - `404 Status page not found` (`data.code: statusPageNotFound`) when `id` resolves to a real status page, but it belongs to a different organization, or is outside your customer scope - `400 Invalid subscriber id` (`data.code: invalidSubscriberId`) when `subscriberId` does not parse as a positive integer - `404 Subscriber not found` (`data.code: subscriberNotFound`) when `subscriberId` does not exist, or belongs to a different status page than `id` `id` is resolved and scope-checked before `subscriberId` is parsed, so a bad `id` is always the error you see first.
### Export Status Page Subscribers
URL: https://docs.uptimeify.io/api/status-pages/export-subscribers
Description: Downloads every subscriber of a status page as CSV. Requires admin or editor role.
Summary: `GET /api/status-pages/:id/subscribers/export` Unlike [List Status Page Subscribers](./list-subscribers), the export is not capped, it returns every row for the page. Each export is recorded in the organization's audit log (page ID and row count only, never the addresses themselves), since this is a bulk download of your subscribers' personal data. ## Path Parameter | Parameter | Description | |-----------|-------------| | `id` | Status page ID or `publicId` (UUID) | ## Example (cURL) ```bash curl "$BASE_URL/api/status-pages/db58058e-4b58-4d97-a314-3bb8e279a182/subscribers/export" \ -H "Authorization: Bearer $TOKEN" \ -o subscribers.csv ``` ## Response `Content-Type: text/csv; charset=utf-8`, `Content-Disposition: attachment; filename="status-page--subscribers.csv"` (`` is the status page's numeric ID, not its `publicId`). ```csv "email","status","locale","created_at","confirmed_at" "reader@example.com","confirmed","en","2026-08-01T12:00:00.000Z","2026-08-01T12:05:00.000Z" "other@example.com","pending","de","2026-08-02T09:30:00.000Z","" ``` Rows are ordered newest-first by `created_at`. Every field is quoted and embedded quotes are doubled per RFC 4180. A value that would otherwise start with `=`, `+`, `-`, or `@`, or with these characters when preceded by whitespace (tab, space, or carriage return), is prefixed with a single quote so it renders as inert text instead of being evaluated. Subscriber emails are visitor-supplied, so this guards against CSV/formula injection when the file is opened in Excel or Sheets. ## Common errors - `401 Unauthorized` when you are not authenticated - `400 User must belong to an organization` (`data.code: userMustBelongToOrg`) when no organization can be derived from the session or token - `403 Forbidden` when your role is not admin or editor, or your customer scope does not include this status page - `400 Invalid Status page identifier` when `id` is not a valid numeric ID or UUID (no `data.code`) - `404 Status page not found` when `id` is well-formed but no status page has that ID (no `data.code`) - `404 Status page not found` (`data.code: statusPageNotFound`) when `id` resolves to a real status page, but it belongs to a different organization, or is outside your customer scope
### Get Public Status Page
URL: https://docs.uptimeify.io/api/status-pages/get-public-status-page
Description: Two endpoints serve the public status page view:
Summary: - `GET /api/status-pages/public/:slug`: lookup by URL slug - `GET /api/status-pages/public/by-id/:id`: lookup by numeric ID No authentication required for `visibility: public` pages. Customer-member authentication required for `visibility: customer_members_only` pages. ## Response ```json { "statusPage": { "id": 1, "name": "Production Status", "slug": "production-status", "description": "Real-time status of our production services", "visibility": "public", "isPublished": true, "showRecentIncidents": true, "showRecentMaintenance": true, "overallState": "operational", "productName": "Northwind Monitoring", "createdAt": "2026-01-15T10:00:00.000Z", "updatedAt": "2026-01-15T10:00:00.000Z" }, "websites": [ { "id": 101, "name": "Main Site", "url": "https://deinkunde.com", "status": "active", "state": "operational", "inMaintenance": false, "openIncidents": 0, "lastCheckedAt": "2026-05-01T12:00:00.000Z" } ], "maintenanceHistory": [], "incidentHistory": [] } ``` `state` values: `operational`, `warning` (SSL-only incidents), `degraded` (non-SSL incidents), `maintenance` (active maintenance window). `overallState` is the most severe state across all websites. `productName` is your organization's white-label product name, or `null` when you have not set one (or have hidden it). It is what the page's own footer uses for the "Powered by" line. This response no longer contains `organizationId` or `customerId`. They were internal row identifiers that this public endpoint served to every visitor of every published page; nothing in the rendered page used them. If you were reading them, identify the page by `id` or `slug` instead, both are still returned. ## Common errors - `404 Not found` when the status page does not exist or is not published - `403 Forbidden` for `customer_members_only` pages when the user is not a member
### Get Status Page Design
URL: https://docs.uptimeify.io/api/status-pages/get-status-page-design
Description: Returns the current visual design configuration for a status page. Requires admin role.
Summary: `GET /api/status-pages/:id/design` ## Path Parameter | Parameter | Description | |-----------|-------------| | `id` | Status page ID or `publicId` (UUID) | ## Example (cURL) ```bash curl "$BASE_URL/api/status-pages/db58058e-4b58-4d97-a314-3bb8e279a182/design" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "designConfig": { "layout": "timeline", "colorScheme": "dark", "accentColor": "#f59e0b", "headerStyle": "simple", "fontFamily": "system", "cardRadius": "md", "pageWidth": "lg", "customTitle": "", "customSubtitle": "", "showPoweredBy": true, "showUptimeStats": true, "showServiceUrls": false, "showLastChecked": false, "showHistory": true } } ``` If no design has been saved yet all fields reflect the defaults. ## Common errors - `401 Unauthorized`: not authenticated - `403 Forbidden`: not an admin - `404 Not found`: status page does not exist
### List Status Pages
URL: https://docs.uptimeify.io/api/status-pages/list-status-pages
Description: Returns all status pages for the organization. Each page includes its custom domain info if configured.
Summary: `GET /api/status-pages` ## Example (cURL) ```bash curl -X GET "$BASE_URL/api/status-pages" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Response ```json { "statusPages": [ { "id": 1, "publicId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "organizationId": 1, "customerId": 5, "customerPublicId": "f7e6d5c4-b3a2-1098-7654-321fedcba098", "name": "Production Status", "slug": "production-status", "description": "Real-time status of our production services", "visibility": "public", "isPublished": true, "showRecentIncidents": true, "showRecentMaintenance": true, "customDomainId": null, "createdAt": "2026-01-15T10:00:00.000Z", "updatedAt": "2026-01-15T10:00:00.000Z" } ] } ``` ## Common errors - `401 Unauthorized` when not authenticated
### List Status Page Subscribers
URL: https://docs.uptimeify.io/api/status-pages/list-subscribers
Description: Returns the email and RSS subscribers of a status page, plus exact counts by status. Requires admin or editor role.
Summary: `GET /api/status-pages/:id/subscribers` ## Path Parameter | Parameter | Description | |-----------|-------------| | `id` | Status page ID or `publicId` (UUID) | ## Example (cURL) ```bash curl "$BASE_URL/api/status-pages/db58058e-4b58-4d97-a314-3bb8e279a182/subscribers" \ -H "Authorization: Bearer $TOKEN" ``` ## Response ```json { "total": 12, "confirmed": 9, "pending": 2, "unsubscribed": 1, "subscribers": [ { "id": 42, "email": "reader@example.com", "status": "confirmed", "locale": "en", "createdAt": "2026-08-01T12:00:00.000Z", "confirmedAt": "2026-08-01T12:05:00.000Z" } ] } ``` `status` is one of `pending`, `confirmed`, or `unsubscribed`. `confirmedAt` is `null` until the subscriber confirms their address. `total`, `confirmed`, `pending`, and `unsubscribed` are exact counts computed independently of the `subscribers` array; `subscribers` itself is capped at 5000 rows, newest first, use [Export Status Page Subscribers](./export-subscribers) to retrieve every row on a larger page. ## Common errors - `401 Unauthorized` when you are not authenticated - `400 User must belong to an organization` (`data.code: userMustBelongToOrg`) when no organization can be derived from the session or token - `403 Forbidden` when your role is not admin or editor, or your customer scope does not include this status page - `400 Invalid Status page identifier` when `id` is not a valid numeric ID or UUID (no `data.code`) - `404 Status page not found` when `id` is well-formed but no status page has that ID (no `data.code`) - `404 Status page not found` (`data.code: statusPageNotFound`) when `id` resolves to a real status page, but it belongs to a different organization, or is outside your customer scope The last two rows share the same message but differ in `data.code`: a malformed or entirely unknown `id` never carries a `data.code`; only an `id` that resolves to a row you may not see does.
### Remove Custom Domain from Status Page
URL: https://docs.uptimeify.io/api/status-pages/remove-status-page-domain
Description: Removes a custom domain from a status page. Deletes the domain record; the status page's customDomainId is automatically set to null. Admin-only.
Summary: `DELETE /api/status-pages/domains/:id` ## Path Parameters | Parameter | Type | Description | |-----------|------|-------------| | `id` | number | The domain ID to remove | ## Example (cURL) ```bash curl -X DELETE "$BASE_URL/api/status-pages/domains/42" \ -H "Authorization: Bearer $TOKEN" ``` ## Response `204 No Content` ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` when not an admin - `404 Not found` when the domain does not exist - `422 Unprocessable Entity` when the domain ID is invalid
### Update Status Page
URL: https://docs.uptimeify.io/api/status-pages/update-status-page
Description: Updates a status page. At least one field must be provided. Requires admin role.
Summary: `PATCH /api/status-pages/:id` ## Request Body (all optional) | Field | Type | Description | |-------|------|-------------| | `customerId` | number | Move status page to another customer | | `name` | string | Display name (1-120 chars) | | `slug` | string | URL slug (1-120 chars, 409 on conflict) | | `description` | string\|null | Description (max 1000 chars). `null` clears it. | | `visibility` | string | `public` or `customer_members_only` | | `isPublished` | boolean | Publish or unpublish the page | | `showRecentIncidents` | boolean | Show recent incidents section | | `showRecentMaintenance` | boolean | Show recent maintenance section | | `designConfig` | object | Visual design settings (layout, colors, typography, …). See [Update Status Page Design](./update-status-page-design) for all fields. | | `hiddenMonitors` | array | Monitors to hide from this status page. Each entry is `{ "type": "http"\|"dns"\|"icmp"\|"smtp"\|"ssh"\|"ftp"\|"imap_pop", "id": }`. Omit or send `[]` to show all monitors (the default). Monitors added later appear automatically. Maximum 500 entries. | When present, `hiddenMonitors` replaces the stored list in full; omit the field to leave it unchanged; send `[]` to show all monitors again. ## Example (cURL) ```bash curl -X PATCH "$BASE_URL/api/status-pages/1" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Production Status - Updated", "isPublished": true, "showRecentIncidents": true, "hiddenMonitors": [{ "type": "http", "id": 42 }] }' ``` ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` when not an admin - `404 Not found` when the status page does not exist - `409 Conflict` when slug is already taken ## Response Returns the updated status page object. See [Error Codes](/api/error-codes-and-known-pitfalls) for error responses.
### Update Status Page Design
URL: https://docs.uptimeify.io/api/status-pages/update-status-page-design
Description: Updates the visual design configuration of a status page. Only the fields you provide are changed. All other settings keep their current values. Requires admin role.
Summary: `PATCH /api/status-pages/:id/design` ## Path Parameter | Parameter | Description | |-----------|-------------| | `id` | Status page ID or `publicId` (UUID) | ## Request Body (all optional) ### Layout | Field | Type | Values | Default | Description | |-------|------|--------|---------|-------------| | `layout` | string | `classic` `cards` `minimal` `sleek` `board` `split` `timeline` `compact` | `classic` | Visual layout template | | `pageWidth` | string | `sm` `md` `lg` `xl` | `lg` | Max content width: sm = 672 px, md = 896 px, lg = 1024 px, xl = 1280 px | ### Colors | Field | Type | Values | Default | Description | |-------|------|--------|---------|-------------| | `colorScheme` | string | `light` `dark` `auto` | `auto` | Color mode. `auto` follows the visitor's system preference. | | `accentColor` | string | hex, e.g. `#6366f1` | `#6366f1` | Brand accent color used for highlights, borders, and gradients | ### Typography & Style | Field | Type | Values | Default | Description | |-------|------|--------|---------|-------------| | `fontFamily` | string | `system` `mono` | `system` | `system` = default sans-serif, `mono` = monospace | | `cardRadius` | string | `none` `md` `xl` | `md` | Card corner radius: none = sharp, md = rounded, xl = pill | ### Header | Field | Type | Values | Default | Description | |-------|------|--------|---------|-------------| | `headerStyle` | string | `simple` `centered` `hero` | `simple` | `simple` = left-aligned, `centered` = centered, `hero` = full gradient banner | | `customTitle` | string | max 120 chars | `""` | Overrides the status page name in the header. Leave empty to use the page name. | | `customSubtitle` | string | max 200 chars | `""` | Optional tagline below the title | ### Display options | Field | Type | Default | Description | |-------|------|---------|-------------| | `showUptimeStats` | boolean | `true` | Show uptime percentage and service counts | | `showServiceUrls` | boolean | `false` | Show the monitored URL below each service name | | `showLastChecked` | boolean | `false` | Show the last check timestamp per service | | `showHistory` | boolean | `true` | Show the recent incidents & maintenance section | | `showPoweredBy` | boolean | `true` | Show a "Powered by ``" line in the footer. It names your organization's product name and never Uptimeify; with no product name set, nothing is rendered. | ## Example: switch to dark timeline layout ```bash curl -X PATCH "$BASE_URL/api/status-pages/db58058e-4b58-4d97-a314-3bb8e279a182/design" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "layout": "timeline", "colorScheme": "dark", "accentColor": "#f59e0b", "pageWidth": "lg" }' ``` ## Example: minimal, no branding, full width ```bash curl -X PATCH "$BASE_URL/api/status-pages/db58058e-4b58-4d97-a314-3bb8e279a182/design" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "layout": "minimal", "colorScheme": "light", "pageWidth": "xl", "showPoweredBy": false, "showUptimeStats": false, "customTitle": "System Status", "customSubtitle": "Live overview of all services" }' ``` ## Response ```json { "designConfig": { "layout": "timeline", "colorScheme": "dark", "accentColor": "#f59e0b", "headerStyle": "simple", "fontFamily": "system", "cardRadius": "md", "pageWidth": "lg", "customTitle": "", "customSubtitle": "", "showPoweredBy": true, "showUptimeStats": true, "showServiceUrls": false, "showLastChecked": false, "showHistory": true } } ``` ## Common errors - `400 Bad Request`: invalid field value (e.g. unknown layout name or malformed hex color) - `401 Unauthorized`: not authenticated - `403 Forbidden`: not an admin - `404 Not found`: status page does not exist
### Verify Status Page Domain
URL: https://docs.uptimeify.io/api/status-pages/verify-status-page-domain
Description: Verifies DNS TXT records for a custom status page domain. Admin-only.
Summary: `POST /api/status-pages/domains/verify` ## Request Body | Field | Type | Required | Description | |-------|------|----------|-------------| | `domainId` | number | Yes | The domain ID to verify | ## Example (cURL) ```bash curl -X POST "$BASE_URL/api/status-pages/domains/verify" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "domainId": 42 }' ``` ## Response (success) ```json { "verified": true, "status": "verified", "domain": { "id": 42, "hostname": "status.deinkunde.com", "status": "verified", "role": "status_page", "verificationToken": "abc123-def456-ghi789", "verifiedAt": "2026-05-01T12:00:00.000Z", "createdAt": "2026-05-01T11:00:00.000Z", "updatedAt": "2026-05-01T12:00:00.000Z" } } ``` ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` when not an admin - `404 Not found` when the domain does not exist
### Tags
URL: https://docs.uptimeify.io/api/tags
Description: Organize your monitors with tags: create, assign, filter, and manage across all monitor types.
Summary: Tags let you label any monitor (website, DNS, ICMP, SMTP, SSH, FTP, IMAP/POP) with one or more color-coded tags and then filter any monitor list by tag. Tags are organization-scoped; readonly members can create and use their own tags but cannot see or modify tags created by others. ## Endpoints - [List Tags](./list-tags) - [Create Tag](./create-tag) - [Update Tag](./update-tag) - [Delete Tag](./delete-tag) - [Assign Tag to Monitor](./assign-tag) - [Remove Tag from Monitor](./remove-tag) - [List Monitor Tags](./list-monitor-tags)
### Assign Tag to Monitor
URL: https://docs.uptimeify.io/api/tags/assign-tag
Description: Assigns an existing tag to a monitor. The caller needs read access to the monitor and visibility of the tag. The operation is idempotent.
Summary: `POST /api/monitor-tags` ## Body ```json { "monitorType": "website", "monitorId": 101, "tagId": 1 } ``` - `monitorType` (required): Type of the monitor. One of: `website` | `dns` | `icmp` | `smtp` | `ssh` | `tcp` | `ftp` | `imap_pop` | `customer_domain` | `customer_ip`. Use `customer_domain` for domain (SSL/expiry) monitors and `customer_ip` for DNSBL/blacklist monitors. - `monitorId` (required): Numeric ID of the monitor. - `tagId` (required): Numeric ID of the tag to assign. This operation is **idempotent**: calling it again when the tag is already assigned returns the existing assignment without error. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X POST "$BASE_URL/api/monitor-tags" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"monitorType":"website","monitorId":101,"tagId":1}' ``` ## Response ```json { "monitorType": "website", "monitorId": 101, "tagId": 1, "createdAt": "2026-06-29T12:00:00.000Z" } ``` ## Common errors - `400 Bad Request` when `monitorType` is not a supported type - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you do not have read access to the monitor or cannot see the tag - `404 Not Found` when the monitor or tag does not exist
### Create Tag
URL: https://docs.uptimeify.io/api/tags/create-tag
Description: Creates a new tag in your organization. Readonly members may create tags that are visible only to themselves.
Summary: `POST /api/tags` ## Body ```json { "name": "Production", "color": "red" } ``` - `name` (required): Display label for the tag (max 50 characters). - `color` (required): One of the palette keys: `slate` | `red` | `amber` | `green` | `teal` | `blue` | `indigo` | `violet` | `pink` | `gray`. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X POST "$BASE_URL/api/tags" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"Production","color":"red"}' ``` ## Response ```json { "id": 1, "publicId": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa", "organizationId": 10, "name": "Production", "color": "red", "createdBy": 42, "createdAt": "2026-06-29T10:00:00.000Z", "updatedAt": "2026-06-29T10:00:00.000Z" } ``` ## Common errors - `400 invalidTagColor` when `color` is not a recognized palette key - `400 Bad Request` when `name` is missing or exceeds the character limit - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you cannot access the organization
### Delete Tag
URL: https://docs.uptimeify.io/api/tags/delete-tag
Description: Deletes a tag and removes it from all monitors it was assigned to. Only the tag owner or an admin may delete a tag.
Summary: `DELETE /api/tags/{id}` Path parameter `{id}` is the tag's numeric `id`. Deleting a tag cascades: all monitor-tag assignments for that tag are also removed automatically. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" TAG_ID=1 curl -X DELETE "$BASE_URL/api/tags/$TAG_ID" \ -H "Authorization: Bearer $TOKEN" ``` ## Response Returns `204 No Content` on success. ## Common errors - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you are not the tag owner or an admin - `404 Not Found` when the tag does not exist or is not visible to you
### List Monitor Tags
URL: https://docs.uptimeify.io/api/tags/list-monitor-tags
Description: Returns all tags assigned to a specific monitor. Readonly members see only their own tags.
Summary: `GET /api/monitor-tags` ## Query Parameters - `monitorType` (required): Type of the monitor. One of: `website` | `dns` | `icmp` | `smtp` | `ssh` | `tcp` | `ftp` | `imap_pop` | `customer_domain` | `customer_ip`. Use `customer_domain` for domain (SSL/expiry) monitors and `customer_ip` for DNSBL/blacklist monitors. - `monitorId` (required): Numeric ID of the monitor. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/monitor-tags?monitorType=website&monitorId=101" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Response ```json [ { "id": 1, "publicId": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa", "organizationId": 10, "name": "Production", "color": "red", "createdBy": 42, "createdAt": "2026-06-01T08:00:00.000Z", "updatedAt": "2026-06-01T08:00:00.000Z" } ] ``` ## Common errors - `400 Bad Request` when `monitorType` or `monitorId` is missing or invalid - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you do not have read access to the monitor - `404 Not Found` when the monitor does not exist
### List Tags
URL: https://docs.uptimeify.io/api/tags/list-tags
Description: Returns all tags visible to the caller. Readonly members see only tags they created themselves.
Summary: `GET /api/tags` ## Query Parameters - `organizationId` (optional): Defaults to your session organization. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/tags" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Response ```json [ { "id": 1, "publicId": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa", "organizationId": 10, "name": "Production", "color": "red", "createdBy": 42, "createdAt": "2026-06-01T08:00:00.000Z", "updatedAt": "2026-06-01T08:00:00.000Z" }, { "id": 2, "publicId": "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb", "organizationId": 10, "name": "Staging", "color": "amber", "createdBy": 42, "createdAt": "2026-06-10T09:00:00.000Z", "updatedAt": "2026-06-10T09:00:00.000Z" } ] ``` ## Common errors - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you cannot access the organization
### Remove Tag from Monitor
URL: https://docs.uptimeify.io/api/tags/remove-tag
Description: Removes a tag assignment from a monitor. The caller must have read access to the monitor.
Summary: `DELETE /api/monitor-tags` ## Body ```json { "monitorType": "website", "monitorId": 101, "tagId": 1 } ``` - `monitorType` (required): Type of the monitor. One of: `website` | `dns` | `icmp` | `smtp` | `ssh` | `tcp` | `ftp` | `imap_pop` | `customer_domain` | `customer_ip`. Use `customer_domain` for domain (SSL/expiry) monitors and `customer_ip` for DNSBL/blacklist monitors. - `monitorId` (required): Numeric ID of the monitor. - `tagId` (required): Numeric ID of the tag to remove. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X DELETE "$BASE_URL/api/monitor-tags" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"monitorType":"website","monitorId":101,"tagId":1}' ``` ## Response Returns `204 No Content` on success. ## Common errors - `400 Bad Request` when `monitorType` is not a supported type - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you do not have read access to the monitor - `404 Not Found` when the assignment does not exist
### Update Tag
URL: https://docs.uptimeify.io/api/tags/update-tag
Description: Updates an existing tag's name or color. Only the tag owner or an admin may update a tag.
Summary: `PATCH /api/tags/{id}` Path parameter `{id}` is the tag's numeric `id`. ## Body All fields are optional; send only the fields you want to change. ```json { "name": "Critical", "color": "violet" } ``` - `name` (optional): New display label (max 50 characters). - `color` (optional): One of the palette keys: `slate` | `red` | `amber` | `green` | `teal` | `blue` | `indigo` | `violet` | `pink` | `gray`. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" TAG_ID=1 curl -X PATCH "$BASE_URL/api/tags/$TAG_ID" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"Critical","color":"violet"}' ``` ## Response ```json { "id": 1, "publicId": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa", "organizationId": 10, "name": "Critical", "color": "violet", "createdBy": 42, "createdAt": "2026-06-01T08:00:00.000Z", "updatedAt": "2026-06-29T11:00:00.000Z" } ``` ## Common errors - `400 invalidTagColor` when `color` is not a recognized palette key - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you are not the tag owner or an admin - `404 Not Found` when the tag does not exist or is not visible to you
### Users
URL: https://docs.uptimeify.io/api/users
Description: Manage users within your organization.
Summary: ## Endpoints - [List Users](./list-users) - [Create User](./create-user) - [Get User](./get-user) - [Update User](./update-user) - [Delete User](./delete-user) - [Reset User MFA](./reset-mfa)
### Create User
URL: https://docs.uptimeify.io/api/users/create-user
Description: Creates a new user in the organization.
Summary: `POST /api/users` ## Request Body ```json { "email": "jane@deinkunde.com", "firstName": "Jane", "lastName": "Doe", "role": "user", // "admin" or "user" "password": "temporaryPassword123", // Optional, user can set it later "customerIds": ["11111111-1111-4111-8111-111111111111", "22222222-2222-4222-8222-222222222222"] // Optional: Limit access to specific customers via public IDs } ``` Notes: - `customerIds` accepts customer public IDs and legacy numeric IDs. - Public IDs are the preferred format for new integrations. ## Response Returns the created user object. See [Error Codes](/api/error-codes-and-known-pitfalls) for error responses.
### Delete User
URL: https://docs.uptimeify.io/api/users/delete-user
Description: Removes a user from the organization.
Summary: `DELETE /api/users/:id` ## Response On success: `204 No Content`.
### Get User
URL: https://docs.uptimeify.io/api/users/get-user
Description: Returns the details of a specific user.
Summary: `GET /api/users/:id` ## Response ```json { "id": "user_123", "name": "Max Mustermann", "email": "max@deinkunde.com", "role": "admin", "isActive": true, "createdAt": "2023-01-01T00:00:00Z" } ```
### List Users
URL: https://docs.uptimeify.io/api/users/list-users
Description: Lists all users in the organization.
Summary: `GET /api/users` ## Response ```json { "id": "user_123", "name": "Max Mustermann", "email": "max@deinkunde.com", "role": "admin", "isActive": true, "createdAt": "2023-01-01T00:00:00Z" } ```
### Reset User MFA
URL: https://docs.uptimeify.io/api/users/reset-mfa
Description: Clears another user's two-step verification, so they can set it up again from scratch.
Summary: `POST /api/users/:id/mfa/reset` An organization admin (or global admin) can force-reset another user's two-step verification. This deletes the user's stored second factor and turns `twoFactorEnabled` off for their account, it does not enroll a new factor. The user is emailed a notification and must set up two-step verification again the next time it is required. You cannot reset your own second factor through this endpoint. Removing your own factor goes through account security settings instead, and requires your password. **API tokens cannot call this endpoint.** It requires a real, logged-in user session (an organization admin or global admin), an `Authorization: Bearer` API token is always rejected, even an organization-scoped one with admin-level access. This applies to agent access tokens too. A leaked token must never be able to strip an organization's MFA protection by resetting members' factors one by one. ## Request Body None. ## Example (cURL) Call this endpoint from an authenticated browser session (the admin's own login session cookie), not with an API token: ```bash BASE_URL="https://uptimeify.io" curl -X POST "$BASE_URL/api/users/user_123/mfa/reset" \ -H "Cookie: better-auth.session_token=" \ -H "Accept: application/json" ``` ## Response ```json { "ok": true } ``` ## Common errors - `401 unauthorized` when you are not authenticated - `403 nonUserSessionForbidden` when called with an API token or agent access token instead of a real user session, see above - `403 customerScopedTokenForbidden` when called with a customer-scoped API token; this is an organization-wide action and requires an organization-scoped token or a session - `404 userNotFound` when the target `:id` does not resolve to a user - `403 insufficientPermissions` when the caller may not reset this user's second factor, see below Permission rules (`data.code: insufficientPermissions`): - You can never reset your own second factor through this endpoint. - Global admins can reset any user. - Global supporters (read-only across all organizations) can never reset a user. - Organization admins can reset users in their own organization only; the target must belong to the same organization as the caller. - Regular (non-admin) users can never reset another user's second factor.
### Update User
URL: https://docs.uptimeify.io/api/users/update-user
Description: Updates the details and permissions of a user.
Summary: `PATCH /api/users/:id` ## Request Body ```json { "firstName": "Jane", "lastName": "Smith", "role": "admin", "isActive": true, "customerIds": ["11111111-1111-4111-8111-111111111111", "22222222-2222-4222-8222-222222222222"] // Update the list of assigned customers via public IDs } ``` Notes: - `customerIds` accepts customer public IDs and legacy numeric IDs. - Public IDs are the preferred format for new integrations. ## Response Returns the updated user object. See [Error Codes](/api/error-codes-and-known-pitfalls) for error responses.
### Configurations
URL: https://docs.uptimeify.io/api/website-configuration
Description: Manage advanced monitoring settings for your websites.
Summary: ## Authentication All examples assume a bearer token: ```bash BASE_URL="https://uptimeify.io" TOKEN="" ``` ## Check Configuration ### Update Check Settings `PATCH /api/websites/:websiteId/check-config` Configure which aspects of the website should be monitored. #### Request Body ```json { "checkSslEnabled": true, "checkHttpsRedirectEnabled": true, "checkStatusEnabled": true, "checkSizeEnabled": true, "checkResponseTimeEnabled": true, "checkKeywordEnabled": false, "checkDomainExpiryEnabled": true, "minPageSize": 1024, "maxPageSize": 5242880 } ``` Example (cURL): ```bash curl -X PATCH \ "$BASE_URL/api/websites/101/check-config" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"checkSslEnabled":true,"checkHttpsRedirectEnabled":true,"checkStatusEnabled":true,"checkSizeEnabled":true,"checkResponseTimeEnabled":true,"checkKeywordEnabled":false}' ``` ## Alerting Configuration Alerting-related defaults and feature flags are configured via **organization package configs**. See: - [List Package Configs](../organization/list-package-configs) - [Upsert Package Config](../organization/upsert-package-config) - [Delete Package Config](../organization/delete-package-config) ## Notification Channels ## Maintenance Windows ### List Maintenance Windows `GET /api/maintenance-windows?websiteId=:websiteId` ### Get Maintenance Window `GET /api/maintenance-windows/:id` ### Create Maintenance Window `POST /api/maintenance-windows` ### Update Maintenance Window `PATCH /api/maintenance-windows/:id` ### Delete Maintenance Window `DELETE /api/maintenance-windows/:id` ### Check Maintenance (Website) `GET /api/maintenance-windows/check/:websiteId` ### Check Maintenance (Batch) `POST /api/maintenance-windows/check/batch` ## Endpoints - [Update Check Settings](./update-check-settings) - [List Maintenance Windows](./list-maintenance-windows) - [Get Maintenance Window](./get-maintenance-window) - [Create Maintenance Window](./create-maintenance-window) - [Update Maintenance Window](./update-maintenance-window) - [Delete Maintenance Window](./delete-maintenance-window) - [Check Maintenance (Website)](./check-maintenance-window) - [Check Maintenance (Batch)](./check-maintenance-window-batch)
### Check Maintenance (Website)
URL: https://docs.uptimeify.io/api/website-configuration/check-maintenance-window
Description: Checks if a website is currently in an active maintenance window.
Summary: `GET /api/maintenance-windows/check/:websiteId` This endpoint requires authentication via browser session or API token. ## Parameters - `websiteId` (Path, required): Internal numeric website ID or `websitePublicId` (UUID). ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" API_TOKEN="wsm_your_real_api_token" curl -X GET "$BASE_URL/api/maintenance-windows/check/cd11a84d-96a2-41aa-a957-b1db8ee01b72" \ -H "Authorization: Bearer $API_TOKEN" \ -H "Accept: application/json" ``` ## Example Response ```json { "inMaintenance": true, "activeWindows": [ { "id": 5, "name": "Weekly maintenance", "startTime": "2026-02-25T02:00:00.000Z", "endTime": "2026-02-25T04:00:00.000Z", "description": "Planned downtime" } ] } ``` ## Common errors - `401 Unauthorized` when no valid session or API token is sent - `403 Forbidden` when the website is outside the allowed customer/organization scope - `400 Invalid Website identifier` when `:websiteId` is neither a positive integer ID nor a UUID - `404 Website not found` when the provided `websitePublicId` does not resolve to a website
### Check Maintenance (Batch)
URL: https://docs.uptimeify.io/api/website-configuration/check-maintenance-window-batch
Description: Batch-check multiple websites for active maintenance windows.
Summary: `POST /api/maintenance-windows/check/batch` This endpoint requires authentication via browser session or API token. ## Request Body ```json { "websiteIds": [101, "cd11a84d-96a2-41aa-a957-b1db8ee01b72", 103] } ``` ### Fields - `websiteIds` ((number|string)[], required): numeric website IDs or `websitePublicId` UUIDs ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" API_TOKEN="wsm_your_real_api_token" curl -X POST "$BASE_URL/api/maintenance-windows/check/batch" \ -H "Authorization: Bearer $API_TOKEN" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d '{"websiteIds":[101,"cd11a84d-96a2-41aa-a957-b1db8ee01b72",103]}' ``` ## Example Response ```json { "results": { "101": { "inMaintenance": true, "activeWindows": [ { "id": 5, "name": "Weekly maintenance", "startTime": "2026-02-25T02:00:00.000Z", "endTime": "2026-02-25T04:00:00.000Z", "description": "Planned downtime" } ] }, "cd11a84d-96a2-41aa-a957-b1db8ee01b72": { "inMaintenance": false, "activeWindows": [] } } } ``` ## Common errors - `400 websiteIds array required` when `websiteIds` is missing/empty - `400 All websiteIds must be valid identifiers` when any entry is neither a positive integer ID nor a UUID - `401 Unauthorized` when no valid session or API token is sent - `403 Forbidden` when at least one website is outside the allowed customer/organization scope - `404 Website not found` when a provided `websitePublicId` does not resolve to a website
### Create Maintenance Window
URL: https://docs.uptimeify.io/api/website-configuration/create-maintenance-window
Description: Creates a new maintenance window.
Summary: `POST /api/maintenance-windows` Important: You must provide **exactly one** target ID (e.g. `websiteId` *or* `icmpMonitorId`, `smtpMonitorId`, `sshMonitorId`, `ftpMonitorId`, `imapPopMonitorId`). All target IDs and `customerId` accept either the internal numeric ID or the matching public UUID. ## Authentication Requires a valid session or API token. - Header: `Authorization: Bearer ` Note: Global supporter users are not allowed to create maintenance windows (`403`). Read-only users are allowed. ## Request Body ```json { "websiteId": "521e3338-4597-4d1d-8eeb-dc56d271e71c", "name": "Server Upgrade", "description": "Planned downtime", "startTime": "2026-02-25T02:00:00.000Z", "endTime": "2026-02-25T04:00:00.000Z", "isRecurring": true, "recurrencePattern": { "frequency": "weekly", "interval": 1, "daysOfWeek": [1] }, "isActive": true } ``` ### Fields Target (exactly one required): - `websiteId` (number|string) - `icmpMonitorId` (number|string) - `smtpMonitorId` (number|string) - `sshMonitorId` (number|string) - `ftpMonitorId` (number|string) - `imapPopMonitorId` (number|string) - `customerId` (number|string, optional) Identifier rule: Internal numeric IDs or public UUIDs are accepted. Other fields: - `name` (string, required) - `description` (string, optional) - Max length: 2000 - If omitted (or empty), it is stored as `null`. - `startTime` (string/date, required) - `endTime` (string/date, required) - Must be after `startTime`. - `isRecurring` (boolean, optional) - Default: `false` - `recurrencePattern` (object, optional) - Stored as JSON. - Typical keys include `frequency`, `interval`, `daysOfWeek`, `dayOfMonth`, `endRecurrenceDate`. - `isActive` (boolean, optional) - Default: `true` - `timezone` (string, optional) - Default: `"UTC"` - The IANA zone (e.g. `"Europe/Berlin"`) the recurrence pattern is interpreted in. - Case-insensitive spellings and legacy IANA link names (e.g. `"gmt"`, `"Zulu"`) are accepted and stored under the runtime's canonical name, `"utc"` and `"Zulu"` are both stored as `"UTC"`. - A raw UTC offset (e.g. `"+05:00"`) is rejected: it carries no daylight-saving rule, so it cannot express what a zone name does. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X POST \ "$BASE_URL/api/maintenance-windows" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"websiteId":"521e3338-4597-4d1d-8eeb-dc56d271e71c","name":"Server Upgrade","description":"Planned downtime","startTime":"2026-02-25T02:00:00.000Z","endTime":"2026-02-25T04:00:00.000Z","isRecurring":true,"recurrencePattern":{"frequency":"weekly","interval":1,"daysOfWeek":[1]},"isActive":true}' ``` ## Example Response ```json { "id": 5, "websiteId": 101, "icmpMonitorId": null, "smtpMonitorId": null, "sshMonitorId": null, "ftpMonitorId": null, "imapPopMonitorId": null, "customerId": 12, "name": "Server Upgrade", "description": "Planned downtime", "startTime": "2026-02-25T02:00:00.000Z", "endTime": "2026-02-25T04:00:00.000Z", "isRecurring": true, "recurrencePattern": { "frequency": "weekly", "interval": 1, "daysOfWeek": [1] }, "isActive": true, "timezone": "UTC", "createdBy": "", "createdAt": "2026-02-20T10:00:00.000Z", "updatedAt": "2026-02-20T10:00:00.000Z" } ``` ## Common Errors - `400 Exactly one target ID must be provided` if no or multiple target IDs are sent - `400 Invalid Website identifier` or the corresponding target error if an identifier is neither an integer ID nor a UUID - `400 End time must be after start time` if `endTime <= startTime` - `400` (validation) if `timezone` is a raw UTC offset or not a zone name the runtime recognizes - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access (or are a global supporter)
### Delete Maintenance Window
URL: https://docs.uptimeify.io/api/website-configuration/delete-maintenance-window
Description: Deletes a maintenance window.
Summary: `DELETE /api/maintenance-windows/:id` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` Note: Global supporter users are not allowed to delete maintenance windows (`403`). Read-only users are allowed. ## Parameters - `id` (Path, required): Maintenance window ID. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X DELETE \ "$BASE_URL/api/maintenance-windows/5" \ -H "Authorization: Bearer $TOKEN" ``` ## Example Response ```json { "success": true } ``` ## Common Errors - `400 Invalid maintenance window ID` if `:id` is invalid - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access - `404 Maintenance window not found` if the maintenance window does not exist
### Get Maintenance Window
URL: https://docs.uptimeify.io/api/website-configuration/get-maintenance-window
Description: Returns a single maintenance window by ID (including its target relation).
Summary: `GET /api/maintenance-windows/:id` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` Note (API token scope): If you use a customer-scoped API token, the window must belong to that customer. ## Parameters - `id` (Path, required): Maintenance window ID. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/maintenance-windows/5" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example Response ```json { "id": 5, "websiteId": 101, "icmpMonitorId": null, "smtpMonitorId": null, "sshMonitorId": null, "ftpMonitorId": null, "imapPopMonitorId": null, "customerId": 12, "name": "Weekly maintenance", "description": "Planned downtime", "startTime": "2026-02-25T02:00:00.000Z", "endTime": "2026-02-25T04:00:00.000Z", "isRecurring": true, "recurrencePattern": { "frequency": "weekly", "interval": 1, "daysOfWeek": [1] }, "isActive": true, "timezone": "UTC", "createdBy": "", "createdAt": "2026-02-20T10:00:00.000Z", "updatedAt": "2026-02-20T10:00:00.000Z", "website": { "id": 101, "url": "https://deinkunde.com" } } ``` Note: Depending on the target, the response may also include one of these relations: `icmpMonitor`, `smtpMonitor`, `sshMonitor`, `ftpMonitor`, `imapPopMonitor`. Nested `customer` objects are not returned. ## Common errors - `400 Invalid maintenance window ID` when `:id` is invalid - `404 Maintenance window not found` when the window does not exist - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you cannot access the target/customer - `500 Maintenance window target is missing` if the window has no valid target
### List Maintenance Windows
URL: https://docs.uptimeify.io/api/website-configuration/list-maintenance-windows
Description: Returns maintenance windows scoped to your organization by default.
Summary: `GET /api/maintenance-windows` You can filter by a specific target (e.g. `websiteId`) or by customer/organization. ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` ## Query Parameters - `websiteId` (optional) - `customerId` (optional) - `organizationId` (optional, defaults to your session organization) - `activeOnly` (optional): set to `true` to only return active windows - Target filters (optional, exactly one is typically used when narrowing down): - `icmpMonitorId`, `smtpMonitorId`, `sshMonitorId`, `ftpMonitorId`, `imapPopMonitorId` Note: When `websiteId` is provided, access is enforced for that website. Note (API token scope): If you use a customer-scoped API token, results are restricted to that customer. Providing a different `customerId` returns `403`. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET \ "$BASE_URL/api/maintenance-windows?websiteId=101" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example Response ```json [ { "id": 5, "websiteId": 101, "icmpMonitorId": null, "smtpMonitorId": null, "sshMonitorId": null, "ftpMonitorId": null, "imapPopMonitorId": null, "customerId": 12, "customerName": "Acme Corp", "name": "Weekly maintenance", "description": "Planned downtime", "startTime": "2026-02-25T02:00:00.000Z", "endTime": "2026-02-25T04:00:00.000Z", "isRecurring": true, "recurrencePattern": { "frequency": "weekly", "interval": 1, "daysOfWeek": [1] }, "isActive": true, "timezone": "UTC", "createdBy": "", "createdAt": "2026-02-20T10:00:00.000Z", "updatedAt": "2026-02-20T10:00:00.000Z", "website": { "id": 101, "url": "https://deinkunde.com" } } ] ``` Note: The list endpoint only includes the `website` relation (when `websiteId` is set). It does not include nested `customer` objects or monitor relations. A lightweight top-level `customerName` is included for display/filtering. ## Common Errors - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access
### Update Check Settings
URL: https://docs.uptimeify.io/api/website-configuration/update-check-settings
Description: Configure which aspects of the website should be monitored.
Summary: `PATCH /api/websites/:websiteId/check-config` ## Request Body ```json { "checkSslEnabled": true, "checkHttpsRedirectEnabled": true, "checkStatusEnabled": true, "checkSizeEnabled": true, "checkResponseTimeEnabled": true, "checkKeywordEnabled": false, "checkDomainExpiryEnabled": true, "minPageSize": 1024, "maxPageSize": 5242880 } ``` ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X PATCH \ "$BASE_URL/api/websites/101/check-config" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"checkSslEnabled":true,"checkHttpsRedirectEnabled":true,"checkStatusEnabled":true,"checkSizeEnabled":true,"checkResponseTimeEnabled":true,"checkKeywordEnabled":false,"checkDomainExpiryEnabled":true,"minPageSize":1024,"maxPageSize":5242880}' ``` ## Example Response ```json { "success": true } ``` ## Common errors - `400 Invalid website ID` when `:websiteId` is invalid - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you do not have write access to the website - `404 Website not found` when the website does not exist
### Update Maintenance Window
URL: https://docs.uptimeify.io/api/website-configuration/update-maintenance-window
Description: Updates an existing maintenance window.
Summary: `PATCH /api/maintenance-windows/:id` ## Authentication Requires a valid session. - Header: `Authorization: Bearer ` Note: Global supporter users are not allowed to update maintenance windows (`403`). Read-only users are allowed. ## Parameters - `id` (Path, required): Maintenance window ID. ## Request Body All fields are optional. ```json { "name": "Server Upgrade (updated)", "description": null, "startTime": "2026-02-25T02:00:00.000Z", "endTime": "2026-02-25T04:00:00.000Z", "isRecurring": true, "recurrencePattern": { "frequency": "weekly", "interval": 1, "daysOfWeek": [1] }, "isActive": true, "timezone": "Europe/Berlin" } ``` Notes: - You cannot change the target IDs (`websiteId`, `icmpMonitorId`, ...). Only the window fields can be updated. - `description` can be set to `null` to clear it. - If you update `startTime` and/or `endTime`, `endTime` must be after `startTime`. - `timezone` (string, optional): the IANA zone the recurrence pattern is interpreted in. Case-insensitive spellings and legacy IANA link names (e.g. `"gmt"`, `"Zulu"`) are accepted and stored under the runtime's canonical name, `"utc"` and `"Zulu"` are both stored as `"UTC"`. A raw UTC offset (e.g. `"+05:00"`) is rejected: it carries no daylight-saving rule, so it cannot express what a zone name does. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X PATCH \ "$BASE_URL/api/maintenance-windows/5" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"Server Upgrade (updated)","description":null,"startTime":"2026-02-25T02:00:00.000Z","endTime":"2026-02-25T04:00:00.000Z","isRecurring":true,"recurrencePattern":{"frequency":"weekly","interval":1,"daysOfWeek":[1]},"isActive":true}' ``` ## Example Response ```json { "id": 5, "websiteId": 101, "icmpMonitorId": null, "smtpMonitorId": null, "sshMonitorId": null, "ftpMonitorId": null, "imapPopMonitorId": null, "customerId": 12, "name": "Server Upgrade (updated)", "description": null, "startTime": "2026-02-25T02:00:00.000Z", "endTime": "2026-02-25T04:00:00.000Z", "isRecurring": true, "recurrencePattern": { "frequency": "weekly", "interval": 1, "daysOfWeek": [1] }, "isActive": true, "timezone": "Europe/Berlin", "createdBy": "", "createdAt": "2026-02-20T10:00:00.000Z", "updatedAt": "2026-02-21T11:00:00.000Z" } ``` ## Common Errors - `400 Invalid maintenance window ID` if `:id` is invalid - `400 End time must be after start time` if you update the time range to an invalid value - `400` (validation) if `timezone` is a raw UTC offset or not a zone name the runtime recognizes - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have access - `404 Maintenance window not found` if the maintenance window does not exist
### Website Management
URL: https://docs.uptimeify.io/api/websites
Description: Manage monitored websites.
Summary: Path-based website endpoints use `websitePublicId` UUIDs. ## Endpoints - [List Websites](./list-websites) - [Create Website](./create-website) - [Get Website](./get-website) - [Get Website Details](./get-website-details) - [Update Website](./update-website) - [Change Status](./change-status) - [Delete Website](./delete-website)
### Change Website Status
URL: https://docs.uptimeify.io/api/websites/change-status
Description: Changes the monitoring status of a website.
Summary: `PATCH /api/websites/:websitePublicId` There is no dedicated `.../status` endpoint. Status changes are part of `PATCH /api/websites/:websitePublicId`. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X PATCH "$BASE_URL/api/websites/9a3d4d4d-7a4b-4f37-a9df-2a6f6d9d7a10" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d '{ "status": "maintenance" }' ``` ## Request Body ```json { "status": "active" } ``` Allowed values: - `active` - `inactive` (you may also send `paused`, it will be mapped to `inactive`) - `maintenance` ## Response Returns the updated website record. ## Common errors - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you cannot write to the website - `403 Active website limit reached...` when switching to `active` would exceed the customer/package limit
### Create Website
URL: https://docs.uptimeify.io/api/websites/create-website
Description: Creates a new website monitor for a customer.
Summary: `POST /api/websites` ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X POST "$BASE_URL/api/websites" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d '{ "customerId": "059e1469-0f05-4c93-bd4d-89c45bb2afd9", "name": "New Landing Page", "url": "https://landing.deinkunde.com", "monitoringType": "combined", "checkInterval": 5 }' ``` ## Request Body ### Core fields | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `customerId` | number\|string | Yes | - | Customer public ID (preferred) or legacy numeric ID | | `name` | string | Yes | - | Display name (1-255 chars) | | `url` | string | Yes* | - | URL to monitor (1-2048 chars). Required for non-heartbeat monitors. For DNS monitors, use a hostname without protocol. | | `monitoringType` | string | No | `combined` | `combined`, `http_status`, `ssl_check`, `playwright`, `heartbeat`, `dns` | | `status` | string | No | `active` | `active`, `inactive`, `maintenance` (`paused` accepted, mapped to `inactive`) | | `checkInterval` | number | No | 30 | Check interval in minutes (1-60, min depends on package) | | `timeoutSeconds` | number | No | 30 | Request timeout in seconds (1-60) | | `expectedStatusCodes` | string | No | `200,301,302` | Comma-separated expected HTTP status codes | | `allowedCheckCountryCodes` | string[]\|null | No | org default | Array of 2-letter country codes to restrict monitoring locations | | `searchTerm` | string\|null | No | null | Keyword to search for in response body (max 255 chars) | | `customFields` | object\|null | No | null | Custom field values as key-value pairs | | `managementType` | string | No | `managed` | Ownership class: `managed` or `self_service`: see [Managed vs. Self-Service](/monitoring/managed-vs-self-service). Only organization admins may choose it; customer-scoped creators always get `self_service` (requires `allowSelfService` and free quota). | ### Authentication | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `authMode` | string | No | `none` | `none`, `authorization_header`, `basic` | | `authorizationHeader` | string\|null | No | null | Required when `authMode` is `authorization_header` (1-4096 chars). Encrypted at rest. | | `basicAuthUsername` | string\|null | No | null | Required when `authMode` is `basic` (1-255 chars) | | `basicAuthPassword` | string\|null | No | null | Required when `authMode` is `basic` (1-4096 chars). Encrypted at rest. | ### HTTP Request Configuration | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `httpMethod` | string | No | `GET` | `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, `OPTIONS` | | `customHeaders` | object\|null | No | null | Custom HTTP headers as key-value pairs. Keys: 1-100 chars, values: max 8192 chars. Encrypted at rest. | | `requestBody` | string\|null | No | null | Request body for POST/PUT/PATCH requests (max 100KB). Encrypted at rest. | | `followRedirects` | boolean | No | true | Whether to follow HTTP redirects | | `cookieHandling` | string | No | `none` | `none` or `jar` (maintain cookie jar across redirects) | ### mTLS (Mutual TLS) | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `mtlsEnabled` | boolean | No | false | Enable mutual TLS authentication | | `mtlsClientCert` | string\|null | No | null | Required when `mtlsEnabled` is true (1-100000 chars). Encrypted at rest. | | `mtlsClientKey` | string\|null | No | null | Required when `mtlsEnabled` is true (1-100000 chars). Encrypted at rest. | ### Playwright Monitoring | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `playwrightScript` | string\|null | No* | null | Required when `monitoringType` is `playwright` (1-20000 chars) | | `playwrightEnv` | object\|null…
### Delete Website
URL: https://docs.uptimeify.io/api/websites/delete-website
Description: Irrevocably removes a website and its monitoring history.
Summary: `DELETE /api/websites/:websitePublicId` ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X DELETE "$BASE_URL/api/websites/9a3d4d4d-7a4b-4f37-a9df-2a6f6d9d7a10" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Response ```json { "success": true } ``` ## Common errors - `400 Website public ID (UUID) required` when `:websitePublicId` is invalid - `401 Unauthorized` when you are not logged in - `500 Failed to delete website` on server errors
### Get Website
URL: https://docs.uptimeify.io/api/websites/get-website
Description: Returns a single website (basic/sanitized). Sensitive fields like encrypted credentials are removed.
Summary: `GET /api/websites/:websitePublicId` If you need the full “mega” representation (including additional computed/status fields), use: - `GET /api/websites/:websitePublicId/details` ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/websites/9a3d4d4d-7a4b-4f37-a9df-2a6f6d9d7a10" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Response ```json { "id": 101, "organizationId": 1, "customerId": 1, "name": "Main Marketing Site", "url": "https://deinkunde.com", "status": "active", "monitoringType": "combined", "checkInterval": 1, "createdAt": "2026-02-26T12:00:00.000Z", "updatedAt": "2026-02-26T12:00:00.000Z" } ``` ## Common errors - `400 Website public ID (UUID) required` when `:websitePublicId` is missing/invalid - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you cannot access the website - `404 Not Found` when the website does not exist
### Get Website Details
URL: https://docs.uptimeify.io/api/websites/get-website-details
Description: Returns the website detail page data in one call (mega endpoint). This includes monitoring stats, alert history, incident history, monitoring chart data, and maintenance windows.
Summary: `GET /api/websites/:websitePublicId/details` ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/websites/9a3d4d4d-7a4b-4f37-a9df-2a6f6d9d7a10/details?range=day" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Query Parameters - `range` (optional): `day` | `week` | `month` | `year` (default: `day`) - `date` (optional): reference date for calendar views - `startDate` / `endDate` (optional): custom date window for zoomed views - `granularity` (optional): e.g. `minute` for non-aggregated data (when supported) ## Response ```json { "website": { "id": 101, "name": "Main Marketing Site", "url": "https://deinkunde.com", "status": "active", "monitoringType": "combined", "customerId": 1 }, "uptimeStats": { "day": "100.00", "month": "99.95", "year": "99.90", "dayAvgResponse": 125, "monthAvgResponse": 118, "yearAvgResponse": 120 }, "monitoringData": { "responseTimeData": [], "statusData": [], "uptimePercentage": "99.95", "checkSuccessRatePercentage": "99.80", "totalChecks": 100, "successfulChecks": 99 }, "incidents": { "history": [], "total": 0, "ongoing": 0, "totalDowntime": "0m" }, "alerts": { "history": [], "total": 0, "notificationContext": null }, "maintenance": { "inMaintenance": false, "activeWindows": [], "allWindows": [] } } ``` ## Common errors - `400 Invalid website public ID (UUID)` when `:websitePublicId` is invalid - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you have no access to the website - `500 Failed to fetch website details` on server errors
### List Websites
URL: https://docs.uptimeify.io/api/websites/list-websites
Description: Lists websites within an organization (paginated). Results are scoped by your session/permissions.
Summary: `GET /api/websites` ## Query Parameters - `organizationId` (optional): Defaults to your session organization. - `customerId` (optional): Filter websites by customer. - `search` (optional): Search by website/customer fields. - `monitoringType` (optional): `combined` | `http_status` | `ssl_check` | `playwright` | `heartbeat` | `dns` - `excludeMonitoringType` (optional): same values as `monitoringType` - `page` (optional, default: `1`) - `perPage` (optional, default: `50`, max: `200`) ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X GET "$BASE_URL/api/websites?organizationId=1&page=1&perPage=50" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Response ```json { "items": [ { "id": 101, "publicId": "11111111-1111-4111-8111-111111111111", "customerId": 1, "customerPublicId": "22222222-2222-4222-8222-222222222222", "customerName": "Acme Corp", "name": "Main Marketing Site", "url": "https://deinkunde.com", "status": "active", "monitoringType": "combined", "checkInterval": 1, "createdAt": "2026-02-26T12:00:00.000Z" } ], "total": 1, "page": 1, "perPage": 50 } ``` Each item contains flat `customerName` and `customerPublicId` fields, not a full embedded `customer` object. ## Common errors - `400 Invalid customerId` when `customerId` is invalid - `400 Invalid monitoringType` when `monitoringType` is not supported - `401 Unauthorized` when you are not logged in - `403 Forbidden` when you cannot access the organization
### Trigger Website Check
URL: https://docs.uptimeify.io/api/websites/trigger-check
Description: Triggers an immediate check of a website across eligible monitoring locations.
Summary: `POST /api/websites/:websitePublicId/trigger-check` ## Authentication Requires a valid session with write access to the website. - Header: `Authorization: Bearer ` ## Parameters - `websitePublicId` (Path, required): Website public UUID. ## Example (cURL) ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X POST \ "$BASE_URL/api/websites/6bfec6f6-245a-47ce-843b-157d97d56f88/trigger-check" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" ``` ## Example Response ```json { "success": true, "message": "Check triggered successfully", "websiteId": 123 } ``` The check is enqueued for the website's active monitoring locations; results land in the check history a few seconds later. ## Common Errors - `400 Website public ID (UUID) required` if `:websitePublicId` is invalid - `401 Unauthorized` if you are not authenticated - `403 Forbidden` if you do not have write access to the website - `503 No active monitoring locations available`
### Update Website
URL: https://docs.uptimeify.io/api/websites/update-website
Description: Updates a website. Supports both partial updates (only sent fields are changed) and full updates (all required fields must be present).
Summary: `PATCH /api/websites/:websitePublicId` A request is treated as a **full update** when `customerId`, `name`, and `url` are all present in the body. Otherwise, it is treated as a partial update. ## Example (cURL): Partial update ```bash BASE_URL="https://uptimeify.io" TOKEN="" curl -X PATCH "$BASE_URL/api/websites/9a3d4d4d-7a4b-4f37-a9df-2a6f6d9d7a10" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d '{ "name": "Updated Website Name", "checkInterval": 5, "timeoutSeconds": 10 }' ``` ## Example (cURL): Full update with HTTP configuration ```bash curl -X PATCH "$BASE_URL/api/websites/9a3d4d4d-7a4b-4f37-a9df-2a6f6d9d7a10" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d '{ "customerId": 5, "name": "API Endpoint Monitor", "url": "https://api.deinkunde.com/health", "httpMethod": "POST", "customHeaders": { "X-API-Key": "abc123", "Content-Type": "application/json" }, "requestBody": "{\"check\": true}", "followRedirects": false, "cookieHandling": "jar", "mtlsEnabled": true, "mtlsClientCert": "-----BEGIN CERTIFICATE-----\n...", "mtlsClientKey": "-----BEGIN PRIVATE KEY-----\n..." }' ``` ## Request Body All fields are optional for partial updates. For full updates, `customerId`, `name`, and `url` are required. ### Core fields | Field | Type | Description | |-------|------|-------------| | `customerId` | number\|string | Customer public ID (preferred) or legacy numeric ID | | `name` | string | Display name (1-255 chars) | | `url` | string | URL to monitor (1-2048 chars). For DNS monitors, use hostname without protocol. | | `monitoringType` | string | `combined`, `http_status`, `ssl_check`, `playwright`, `heartbeat`, `dns` | | `status` | string | `active`, `inactive`, `maintenance` (`paused` → `inactive`) | | `checkInterval` | number | Check interval in minutes (1-60) | | `timeoutSeconds` | number | Request timeout in seconds (1-60) | | `expectedStatusCodes` | string | Comma-separated expected HTTP status codes | | `allowedCheckCountryCodes` | string[]\|null | Array of 2-letter country codes, or null to reset to org default | | `searchTerm` | string\|null | Keyword to search for (null clears it) | | `customFields` | object\|null | Custom field values | | `managementType` | string | Ownership class: `managed` or `self_service`: see [Managed vs. Self-Service](/monitoring/managed-vs-self-service). Changing the class is organization-admin-only; flipping to `self_service` requires `allowSelfService` and free quota. | ### Authentication | Field | Type | Description | |-------|------|-------------| | `authMode` | string | `none`, `authorization_header`, `basic`. Setting to `none` clears auth credentials. | | `authorizationHeader` | string\|null | Required when `authMode` is `authorization_header`. Encrypted at rest. | | `basicAuthUsername` | string\|null | Required when `authMode` is `basic`. | | `basicAuthPassword` | string\|null | Required when `authMode` is `basic`. Encrypted at rest. | ### HTTP Request Configuration | Field | Type | Description | |-------|------|-------------| | `httpMethod` | string | `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, `OPTIONS` | | `customHeaders` | object\|null | Custom HTTP headers (keys: 1-100 chars, values: max 8192 chars). Null clears them. Encrypted at rest. | | `requestBody` | string\|null | Request body for POST/PUT/PATCH (max 100KB). Null clears it. Encrypted at rest. | | `followRedirects` | boolean | Whether to follow HTTP redirects | | `cookieHandling` | string | `none` or `jar` (maintain cookie jar across redirects) | ### mTLS (Mutual TLS) | Field | Type | Description | |-------|------|-------------| | `mtlsEnabled` | boolean | Enable mutual TLS authentication. Setting to false clears cert and key. | | `mtlsClientCert` | string\|null | Client certificate (max 100KB). Required when `mtlsEnabled` is true. Encrypted at rest. | | `mtlsClientKey` | string\|null | Client…
### Whitelabel & Branding
URL: https://docs.uptimeify.io/api/whitelabel
Description: Customize your organization's product name, theme colors, logos, favicons, and custom domains. All whitelabel endpoints require admin or global admin role.
Summary: ## Authentication All whitelabel endpoints require session authentication (not API tokens): ```bash BASE_URL="https://uptimeify.io" ``` ## Endpoints ### Branding - [Get Branding](./get-branding) - [Update Branding](./update-branding) - [Upload Branding Asset](./upload-branding) ### Domains - [List Domains](./list-domains) - [Add Domain](./add-domain) - [Verify Domain](./verify-domain) - [Activate Domain](./activate-domain) - [Delete Domain](./delete-domain)
### Activate Domain
URL: https://docs.uptimeify.io/api/whitelabel/activate-domain
Description: Activates a verified custom domain. Optionally sets it as the primary domain. Admin-only.
Summary: `POST /api/organization/whitelabel/domains/activate` ## Request Body | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `domainId` | number | Yes | - | The verified domain ID to activate | | `makePrimary` | boolean | No | auto | Set this domain as primary. Auto-set to `true` if no primary exists. | ## Example (cURL) ```bash curl -X POST "$BASE_URL/api/organization/whitelabel/domains/activate" \ -H "Cookie: $SESSION_COOKIE" \ -H "Content-Type: application/json" \ -d '{ "domainId": 2, "makePrimary": true }' ``` ## Response ```json { "activated": true, "domain": { "id": 2, "hostname": "app.deinkunde.com", "status": "active", "role": "app", "isPrimary": true, "verificationToken": "xyz789-abc456", "verifiedAt": "2026-04-15T12:30:00.000Z", "createdAt": "2026-04-15T12:00:00.000Z", "updatedAt": "2026-04-15T13:00:00.000Z" } } ``` ## Common errors - `400 Domain must be verified before activation` - `404 Domain not found`
### Add Domain
URL: https://docs.uptimeify.io/api/whitelabel/add-domain
Description: Adds a custom domain for whitelabeling. Creates a pending DNS verification record. The first domain is automatically set as primary. Requires admin role.
Summary: `POST /api/organization/whitelabel/domains` ## Request Body | Field | Type | Required | Description | |-------|------|----------|-------------| | `hostname` | string | Yes | Custom domain hostname (3-253 chars). No wildcards. | ## Example (cURL) ```bash curl -X POST "$BASE_URL/api/organization/whitelabel/domains" \ -H "Cookie: $SESSION_COOKIE" \ -H "Content-Type: application/json" \ -d '{ "hostname": "app.deinkunde.com" }' ``` ## Response ```json { "domain": { "id": 2, "hostname": "app.deinkunde.com", "status": "pending", "role": "app", "isPrimary": false, "verificationToken": "xyz789-abc456", "verifiedAt": null, "createdAt": "2026-04-15T12:00:00.000Z", "updatedAt": "2026-04-15T12:00:00.000Z" }, "dns": { "txtName": "_uptimeify-verify.app.deinkunde.com", "txtValue": "xyz789-abc456" } } ``` Add a DNS TXT record with the `txtName` and `txtValue`, then call [Verify Domain](./verify-domain). ## Common errors - `400 Invalid hostname` when hostname format is wrong - `400 This hostname is reserved` when hostname matches the main platform - `400 Wildcard domains are not supported` - `409 Hostname already exists`
### Delete Domain
URL: https://docs.uptimeify.io/api/whitelabel/delete-domain
Description: Deletes a custom domain. If the deleted domain was primary, the next available domain is promoted to primary. Admin-only.
Summary: `DELETE /api/organization/whitelabel/domains/:id` ## Example (cURL) ```bash curl -X DELETE "$BASE_URL/api/organization/whitelabel/domains/2" \ -H "Cookie: $SESSION_COOKIE" ``` ## Response ```json { "success": true } ``` ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` when not an admin - `404 Domain not found`
### Get Branding
URL: https://docs.uptimeify.io/api/whitelabel/get-branding
Description: Returns the organization's branding configuration, including product name, theme colors, and signed URLs for logos and favicons. Requires admin role.
Summary: `GET /api/organization/whitelabel/branding` ## Example (cURL) ```bash curl -X GET "$BASE_URL/api/organization/whitelabel/branding" \ -H "Cookie: $SESSION_COOKIE" \ -H "Accept: application/json" ``` ## Response ```json { "branding": { "productName": "Acme Monitor", "hideProductName": false, "logoObjectKey": "branding/org-1/logo/light/1710000000-logo.svg", "logoObjectKeyLight": "branding/org-1/logo/light/1710000000-logo.svg", "logoObjectKeyDark": "branding/org-1/logo/dark/1710000000-logo-dark.svg", "hideLogos": false, "faviconObjectKey": "branding/org-1/favicon/light/1710000000-favicon.ico", "faviconObjectKeyLight": "branding/org-1/favicon/light/1710000000-favicon.ico", "faviconObjectKeyDark": "branding/org-1/favicon/dark/1710000000-favicon-dark.ico", "themePrimary": "#43B1AE", "themeSecondary": null, "logoUrl": "https://s3.deinkunde.com/branding/org-1/logo/light/1710000000-logo.svg?...", "logoLightUrl": "https://s3.deinkunde.com/branding/org-1/logo/light/1710000000-logo.svg?...", "logoDarkUrl": "https://s3.deinkunde.com/branding/org-1/logo/dark/1710000000-logo-dark.svg?...", "faviconUrl": "https://s3.deinkunde.com/branding/org-1/favicon/light/1710000000-favicon.ico?...", "faviconLightUrl": "https://s3.deinkunde.com/branding/org-1/favicon/light/1710000000-favicon.ico?...", "faviconDarkUrl": "https://s3.deinkunde.com/branding/org-1/favicon/dark/1710000000-favicon-dark.ico?...", "updatedAt": "2026-03-01T10:00:00.000Z" } } ``` ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` when not an admin
### List Domains
URL: https://docs.uptimeify.io/api/whitelabel/list-domains
Description: Returns all custom app domains for the organization. Only domains with role app are returned. Requires admin role.
Summary: `GET /api/organization/whitelabel/domains` ## Example (cURL) ```bash curl -X GET "$BASE_URL/api/organization/whitelabel/domains" \ -H "Cookie: $SESSION_COOKIE" \ -H "Accept: application/json" ``` ## Response ```json { "domains": [ { "id": 1, "hostname": "app.deinkunde.com", "status": "active", "role": "app", "isPrimary": true, "verificationToken": "abc123-def456", "verifiedAt": "2026-01-15T10:00:00.000Z", "createdAt": "2026-01-14T08:00:00.000Z", "updatedAt": "2026-01-15T10:00:00.000Z" } ] } ``` ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` when not an admin
### Update Branding
URL: https://docs.uptimeify.io/api/whitelabel/update-branding
Description: Updates the organization's branding configuration. All fields are optional: only sent fields are updated. Setting productName or theme colors to null clears them. Requires admin role.
Summary: `PATCH /api/organization/whitelabel/branding` Theme colors accept CSS custom property tokens (e.g. `--color-red-500`) or hex colors (e.g. `#43B1AE`). Invalid values are rejected. ## Request Body (all optional) | Field | Type | Description | |-------|------|-------------| | `productName` | string\|null | Product display name (1-80 chars). `null` clears it. | | `hideProductName` | boolean | Hide the product name in the UI | | `hideLogos` | boolean | Hide all logos in the UI | | `themePrimary` | string\|null | Primary theme color (hex or CSS variable). `null` clears it. | | `themeNeutral` | string\|null | Neutral/secondary theme color (hex or CSS variable). `null` clears it. | ## Example (cURL) ```bash curl -X PATCH "$BASE_URL/api/organization/whitelabel/branding" \ -H "Cookie: $SESSION_COOKIE" \ -H "Content-Type: application/json" \ -d '{ "productName": "Acme Monitor", "themePrimary": "#43B1AE", "hideProductName": false }' ``` ## Response ```json { "branding": { "productName": "Acme Monitor", "hideProductName": false, "hideLogos": false, "themePrimary": "#43B1AE", "themeSecondary": null, "updatedAt": "2026-04-15T12:00:00.000Z" } } ``` ## Common errors - `401 Unauthorized` when not authenticated - `403 Forbidden` when not an admin
### Upload Branding Asset
URL: https://docs.uptimeify.io/api/whitelabel/upload-branding
Description: Uploads a logo or favicon as a base64 data URL. Assets are stored in S3 and the branding record is updated automatically. Requires admin role.
Summary: `POST /api/organization/whitelabel/branding/upload` **Logo** types: `logo`, `logoLight`, `logoDark`: accepts PNG, JPEG, WebP, SVG (max 2 MB). **Favicon** types: `favicon`, `faviconLight`, `faviconDark`: accepts PNG, SVG, ICO (max 512 KB). Legacy `logo` is treated as `logoLight`, and `favicon` as `faviconLight`. ## Request Body | Field | Type | Required | Description | |-------|------|----------|-------------| | `kind` | string | Yes | `logo`, `logoLight`, `logoDark`, `favicon`, `faviconLight`, `faviconDark` | | `fileName` | string | Yes | Original file name (1-200 chars) | | `dataUrl` | string | Yes | Base64 data URL (`data:*/*;base64,...`) | ## Example (cURL) ```bash curl -X POST "$BASE_URL/api/organization/whitelabel/branding/upload" \ -H "Cookie: $SESSION_COOKIE" \ -H "Content-Type: application/json" \ -d '{ "kind": "logoLight", "fileName": "logo.svg", "dataUrl": "data:image/svg+xml;base64,PHN2Zy..." }' ``` ## Response ```json { "success": true, "uploaded": { "kind": "logo", "objectKey": "branding/org-1/logo/light/1710000000-logo.svg", "contentType": "image/svg+xml", "bytes": 1234, "publicUrl": "https://s3.deinkunde.com/branding/org-1/logo/light/1710000000-logo.svg?..." }, "branding": { "productName": "Acme Monitor", "hideProductName": false, "logoUrl": "https://s3.deinkunde.com/...", "themePrimary": "#43B1AE", "updatedAt": "2026-04-15T12:00:00.000Z" } } ``` ## Common errors - `400 Invalid dataUrl` when the data URL format is wrong - `400 Unsupported contentType` when the content type is not allowed for the asset kind - `413 File too large` when exceeding size limits - `503 Whitelabel asset storage is not configured` when S3 is not set up
### Verify Domain
URL: https://docs.uptimeify.io/api/whitelabel/verify-domain
Description: Verifies DNS TXT records for a custom domain. Admin-only.
Summary: `POST /api/organization/whitelabel/domains/verify` ## Request Body | Field | Type | Required | Description | |-------|------|----------|-------------| | `domainId` | number | Yes | The domain ID to verify | ## Example (cURL) ```bash curl -X POST "$BASE_URL/api/organization/whitelabel/domains/verify" \ -H "Cookie: $SESSION_COOKIE" \ -H "Content-Type: application/json" \ -d '{ "domainId": 2 }' ``` ## Response (success) ```json { "verified": true, "status": "verified", "domain": { "id": 2, "hostname": "app.deinkunde.com", "status": "verified", "role": "app", "isPrimary": false, "verificationToken": "xyz789-abc456", "verifiedAt": "2026-04-15T12:30:00.000Z", "createdAt": "2026-04-15T12:00:00.000Z", "updatedAt": "2026-04-15T12:30:00.000Z" } } ``` ## Common errors - `400 TXT verification failed (token not found)` when DNS record is missing or has wrong value - `400 Domain has no verification token` - `404 Domain not found`
## Examples
### Examples
URL: https://docs.uptimeify.io/examples
Description: Practical examples for common workflows with customers, websites, and monitors.
Summary: This page has moved into the API documentation. Continue here: - [Examples (API)](/api/examples) - [Create a customer + website](/api/examples/create-customer-and-website) - [Delete a customer including all websites](/api/examples/delete-customer-and-all-websites) - [Create a customer + all monitor types](/api/examples/create-customer-and-all-monitors) - [Create a customer + multiple monitors](/api/examples/create-customer-and-multiple-monitors)
### Create a customer + all monitor types
URL: https://docs.uptimeify.io/examples/create-customer-and-all-monitors
Description: Example automation: create one website and then create one monitor of each type.
Summary: This page has moved into the API documentation. Continue here: - [Create a customer + all monitor types (API)](/api/examples/create-customer-and-all-monitors)
### Create a customer + multiple monitors
URL: https://docs.uptimeify.io/examples/create-customer-and-multiple-monitors
Description: Example: create a customer and set up multiple monitors (same type and mixed types).
Summary: This page has moved into the API documentation. Continue here: - [Create a customer + multiple monitors (API)](/api/examples/create-customer-and-multiple-monitors)
### Create a customer + website
URL: https://docs.uptimeify.io/examples/create-customer-and-website
Description: End-to-end example: create a customer and then create the first website.
Summary: This page has moved into the API documentation. Continue here: - [Create a customer + website (API)](/api/examples/create-customer-and-website)
### Delete a customer including all websites
URL: https://docs.uptimeify.io/examples/delete-customer-and-all-websites
Description: Safe deletion flow: remove websites first, then delete the customer.
Summary: This page has moved into the API documentation. Continue here: - [Delete a customer including all websites (API)](/api/examples/delete-customer-and-all-websites)
## Incidents
### Incidents
URL: https://docs.uptimeify.io/incidents
Description: Incidents are created automatically when monitoring detects a problem with a website or service. They are the foundation for alerting, reporting, and the public Status Pages.
Summary: ## Where to find incidents - **Incidents overview**: `/incidents` - **Per website / monitor**: most detail pages include an incident history tab ## Incident lifecycle Incidents have two primary states: - `open`: the issue is still ongoing - `resolved`: the service recovered and the incident was closed (automatically, once checks pass again) ## What an incident contains Depending on the check type, an incident can include: - **Type / severity**: for example `downtime`, `http_status`, `performance`, `ssl_warning`, `ssl_expiry`, `ssl_handshake` - **Start / resolved time** - **HTTP status code** (if applicable) - **Response time (ms)** (if applicable) - **Error message**: network errors, timeouts, parsing failures, etc. - **Last notified timestamp**: used for notification reminders **`ssl_handshake`**: the server aborted the TLS handshake (for example with an `internal_error` alert) and never established a secure connection, so the site is unreachable over HTTPS. ### How certificate problems are classified A certificate problem is only a warning while visitors can still reach the site. Once a browser *rejects* the certificate, the site is unreachable, and the incident is treated like any other outage: - **`downtime`** means the certificate is rejected right now: it has expired, it was revoked, or a real browser refused the page (for example `net::ERR_CERT_COMMON_NAME_INVALID` for a hostname the certificate is not valid for). Visitors see a full-page interstitial instead of your site, so this is an outage and alerts at critical severity. The error message on the incident still names the certificate cause. - **`ssl_expiry`**, the certificate is still valid but has reached the critical threshold before expiry. Nobody is blocked yet; this is a warning. - **`ssl_warning`**, a certificate problem we could not prove is blocking visitors. Also a warning. ## Incident details (timeline & evidence) From the incidents list, open the **details modal** to see: - A **timeline** of confirmation → outage → recovery - The **evidence check** (the monitoring check that served as proof) - An optional **traceroute excerpt** - An optional **screenshot** (when available) This answers "what exactly happened?" without digging through raw logs. ## How incidents map to status page state If the customer has a public [status page](/status-pages), open incidents change the displayed service state: | Incident situation | Status page `state` | |--------------------|---------------------| | No open incidents | `operational` | | Only **SSL warning** incidents open | `warning` | | Any other open incident (downtime, http_status, performance, …) | `degraded` | | Active maintenance window, no open incidents | `maintenance` | The overall page banner reflects the most severe state across all services. See [Status Pages → How the public state is derived](/status-pages#how-the-public-state-is-derived). ## Inconclusive checks (browser verification timeouts) Some sites use bot protection / browser verification that can time out. In these cases Uptimeify may mark a check as **inconclusive** rather than treating it as full downtime. So you don't get misleading "everything is down" alerts and incidents from a protection challenge rather than a real outage.
## Integrations
### Integrations
URL: https://docs.uptimeify.io/integrations
Description: Uptimeify delivers alerts to the tools you already use, chat, on-call, issue trackers and more, plus firewall allowlisting and a full REST API.
Summary: ## Notification channels Deliver alerts to where your team already works. Channels are configured per customer and attached to your notification rules. - Manage in the app under the customer's **Notifications** settings. - Automate with the [Notification Channels API](/api/notification-channels) (create, list, update, delete, and **test** a channel). Every channel below is supported out of the box. ### Direct | Channel | Delivers via | You provide | |---------|--------------|-------------| | **Email** | E-mail | Recipient address(es); falls back to the customer/org email | | **SMS** | Text message | Phone number(s); falls back to the customer/org phone | | **Webhook** | Your HTTP endpoint | Endpoint URL (optional custom headers and JSON body template) | ### Chat & collaboration | Channel | You provide | |---------|-------------| | **Slack** | Incoming webhook URL (optional username, icon, color) | | **Microsoft Teams** | Incoming webhook URL | | **Discord** | Incoming webhook URL (optional username, color) | | **Telegram** | Bot token + chat ID | | **Google Chat** | Incoming webhook URL | | **Mattermost** | Incoming webhook URL | | **Rocket.Chat** | Incoming webhook URL | | **Matrix** | Homeserver URL + room ID + access token | | **Lark / Feishu** | Incoming webhook URL | | **DingTalk** | Incoming webhook URL (access token in the URL) | | **WeCom** | Incoming webhook URL (key in the URL) | ### On-call & incident response | Channel | You provide | |---------|-------------| | **PagerDuty** | Events API v2 routing key | | **Opsgenie** | API key + region (EU/US), optional priority | | **ilert** | Integration key | | **Grafana OnCall** | Incoming webhook URL | | **Squadcast** | Incoming webhook URL (token in the URL) | | **incident.io** | Alert source URL + bearer token | | **All Quiet** | Inbound webhook URL (the URL is the secret) | ### Push & lightweight | Channel | You provide | |---------|-------------| | **Pushover** | User key + API token | | **ntfy** | Topic URL (optional access token) | | **Gotify** | Server URL + app token | ### Issue trackers & ITSM | Channel | You provide | |---------|-------------| | **Jira** | Base URL + email + project key + issue type + API token | | **GitHub** | Repository + token (PAT); optional Enterprise API base URL | | **GitLab** | Project ID + token; optional self-hosted base URL | | **Linear** | Team ID + API key | | **ServiceNow** | Instance URL + username + password | ## Escalation policies Define multi-step, time-based escalation so an unacknowledged alert moves to the next responder automatically. - Configure and test via the [Escalation API](/api/escalation). ## Firewall allowlisting If your site sits behind a firewall or WAF, allowlist our monitoring nodes so checks aren't blocked. - See [Firewall allowlisting](/integrations/firewall) for the dashboard and `GET /api/ips` endpoint. - The full node list is published at [/ips.txt](https://uptimeify.io/ips.txt). Whitelisting other providers? See [Monitoring IP whitelists](https://uptimeify.io/compare/uptimerobot/probe-ips). ## API & automation Everything above, and the rest of the platform, is scriptable. - Start with the [API Documentation](/api). - Generate a token under your account and authenticate with `Authorization: Bearer wsm_`.
### Firewall allowlisting (Monitoring node IPs)
URL: https://docs.uptimeify.io/integrations/firewall
Description: If your website is protected by a firewall or WAF, you may need to allowlist our monitoring nodes so we can reach your endpoint reliably.
Summary: ## Dashboard (UI) You can view the current IP allowlist in the dashboard sidebar under “IP Addresses”. ## API (Automation) To fetch the current list programmatically, use the authenticated endpoint: - `GET /api/ips` ### Authentication Use an API token generated in the dashboard. ```bash BASE_URL="https://uptimeify.io" TOKEN="wsm_" curl -H "Authorization: Bearer $TOKEN" "$BASE_URL/api/ips" ``` ### Response ```json { "ipv4": ["203.0.113.10"], "ipv6": ["2001:db8::10"], "locations": { "de-nbg": ["203.0.113.10"] }, "updated_at": "2026-01-01T00:00:00.000Z", "documentation": "https://uptimeify.io/docs/integrations/firewall" } ``` Notes: - The IP list can change over time. Don’t hard-code it permanently. - Prefer periodic syncing on your side (e.g. via a scheduled job). ## How to recognise our checks in your logs Besides the source IP, every check identifies itself in two ways. Either is enough to allowlist on, and both are stable, use them if you would rather not track a changing IP list. ### User-Agent Simple checks (HTTP, SSL, redirects) send: ``` SiteMonitorBot/1.0 (+https://uptimeify.io) ``` Browser-based checks (screenshots and Playwright monitors) send a normal Chrome User-Agent with that token **appended**: ``` Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36 SiteMonitorBot/1.0 (+https://uptimeify.io) ``` The browser prefix is deliberate. A Playwright monitor walks a real user journey, a login, a checkout, and many WAFs block a bare bot User-Agent outright. That would make us report an outage your visitors are not experiencing. So we keep the browser shape and add our name to it, rather than pretending to be nothing but a bot. If you match on User-Agent, match on the substring `SiteMonitorBot`, never on the full string, which carries a Chrome version that changes with every browser update. ### Request header Every check also sends: ``` X-Uptimeify-Monitor: 1 ``` This is the more reliable of the two: a CDN or reverse proxy can rewrite a User-Agent, and bot mitigation sometimes strips it. The header is a constant and never contains a monitor, customer or account identifier, nothing about your account is exposed to anyone reading your access log. ### Rate Checks arrive at the interval you configured, from each location you enabled. They do not crawl: a simple check is one request, a Playwright monitor is exactly the journey your script describes. ## Which bot is this, and how do I stop it Allowlisting is one direction. If you would rather block us, or you are looking at a client that is not a monitoring check at all, the full account is on [uptimeify.io/robot](https://uptimeify.io/robot): every automated client we operate, what each one requests and how often, the addresses it comes from, and ready-made rules for Cloudflare, nginx and Apache. Two clients besides `SiteMonitorBot` appear there. `UptimeifyStatusBot` reads the public status pages other companies publish about themselves. `UptimeifyToolsBot` fetches an address someone entered into one of our free tools or passed to our MCP server, once, with no schedule behind it. None of the three reads `robots.txt`. A monitoring check is ordered by the site owner, so a `Disallow` written by a third party would switch off a check somebody on your own team relies on. Blocking works at your CDN, WAF or web server.
## Maintenance
### Maintenance
URL: https://docs.uptimeify.io/maintenance
Description: Maintenance windows let you plan work (deployments, updates, migrations) without triggering noisy alerts. During an active window the affected target is treated as in maintenance. Alerts are suppressed and, if the customer has a status page, the service is shown as Maintenance instead of Degraded.
Summary: ## Create a maintenance window Go to **Dashboard → Maintenance** and create a new window. Read-only users can create, edit, reactivate, and delete maintenance windows for the customers they are assigned to. Global support accounts cannot modify maintenance windows. | Setting | Description | |---------|-------------| | **Target** | One or more: a **website**, a **service monitor** (ICMP / SMTP / SSH / FTP / IMAP-POP), a **DNS monitor**, a set of **tags**, or the entire **customer**. See [Apply to multiple monitors or by tag](#apply-to-multiple-monitors-or-by-tag). | | **Name** | A friendly label, e.g. "Weekly updates". | | **Description** | Optional notes shown in history. | | **Start / End** | The window's time range. Times are interpreted in your timezone. | | **Active** | Toggle to enable/disable the window without deleting it. | ## Apply to multiple monitors or by tag A single maintenance window can cover many monitors at once: **Multi-monitor select in the form** In the maintenance window form, open the **Target** selector and pick any combination of websites and service monitors. All selected monitors are added to the window; they must all belong to the same customer. **Bulk "Set maintenance" from monitor lists** On any monitor list page (e.g. *Dashboard → Web*, *Dashboard → Server*) you can select multiple rows using the checkboxes, then choose **Set maintenance** from the bulk-action toolbar. This creates a single maintenance window covering all selected monitors at once, pre-populating the form with those monitors as targets. **Dynamic by-tag windows** Instead of (or in addition to) selecting individual monitors, you can select one or more **tags**. A tag-based window covers every monitor that currently carries the tag, and it stays current automatically: - A monitor **tagged after** the window is created is automatically covered; no update needed. - **Removing a tag** from a monitor drops it from coverage immediately. **Org-wide scope:** When you select tags with *no* explicit monitors and no customer context, the window is **org-wide**. It covers all monitors across the entire organization that carry those tags, regardless of which customer they belong to. Creating or editing an org-wide tag window requires **admin or editor** role; readonly users will receive a permission error. Tag coverage is evaluated live by the monitoring worker at the moment each check result arrives, so there is no lag between tagging a monitor and it being protected by the window. ## One-time vs recurring A window is either **one-time** (default) or **recurring**. Recurring windows carry a recurrence pattern: | Field | Meaning | |-------|---------| | `frequency` | `daily`, `weekly`, or `monthly` | | `interval` | Repeat every *N* days/weeks/months (e.g. `2` = every other week) | | `daysOfWeek` | For weekly: which days (`0` = Sunday … `6` = Saturday) | | `dayOfMonth` | For monthly: day of month (`1`-`31`) | | `endRecurrenceDate` | Optional date when the recurrence stops | The **Start / End** times define the window's length within each occurrence. ## Effect on status pages If the target's customer has a status page: - A target with an **active maintenance window** (and no open incidents) is shown as **Maintenance**. - Maintenance windows can also appear in **Recent History** (controlled by the status page's *Show recent maintenance* toggle, see [Status Pages](/status-pages#recent-history)). Maintenance does not override real outages: if there is an **open incident** during a maintenance window, the service is shown as **Degraded**, not Maintenance. See [Incidents](/incidents). ## Troubleshooting ### Window does not show as "active" - Verify **Active** is enabled. - Confirm the current time is between **Start** and **End**. - Check the start/end times are correct for your timezone. - For recurring windows, confirm today matches the recurrence pattern (`frequency`, `daysOfWeek`/`d…
## Monitoring
### Monitoring
URL: https://docs.uptimeify.io/monitoring
Description: Monitor the availability and performance of your websites and services.
Summary: In this section you will find information about all monitor types and how to configure them. **Website & HTTP:** Uptime, Keyword, Response Time, Page Size, HTTPS Redirect, SSL **Network & Services:** ICMP, SMTP, IMAP/POP, SSH, TCP Port, FTP **DNS & Domain:** DNS, DNSBL, Domain Expiry **Advanced:** Heartbeat (Cron), Playwright (Synthetic Browser) For a full categorized overview see [Monitoring Types](/monitoring/monitoring-types). ## Getting started - Create your first monitor: [Set up an Uptime monitor](/monitoring/monitoring-types/uptime) - Overview of all available monitor types: [Monitoring Types](/monitoring/monitoring-types)
### Incident Management
URL: https://docs.uptimeify.io/monitoring/incident-management
Description: Incident Management (IM) is Uptimeify's on-call and incident-response layer: teams, on-call schedules, escalation policies, and alert ingestion from your own tools, separate from customer-facing Monitoring.
Summary: Incident Management (IM) is a separate domain from Monitoring: a two-level alert/incident model built for **your own** on-call response, not for paging your clients. Where Monitoring watches your clients' websites and services and tells *them* (or you, on their behalf) when something is down, IM is the tool your own team uses to make sure the right person on your side gets woken up, whether the trigger is a monitoring outage, a webhook from Datadog or Grafana, or an email forwarded from a vendor. Incident Management has no customer-facing surface. Customer (`readonly`-role) logins never see it, in any phase, it is an internal tool for your organization's admins, editors, and responders. ## The model The top-level ownership unit, every schedule, policy, and incident belongs to exactly one team. On-call rotations: who covers a team, and when, including one-off overrides. Escalation policies: the ordered tiers that decide who gets paged, how, and when to escalate further. The sev1-sev4 scale that drives urgency, paging speed, and which rule chain fires. How SMS and voice paging draw on your existing SMS quota, and what happens when it runs out. How long alert payloads, resolved alerts, and outbound-delivery records are kept. ## Two-level alert/incident model An inbound signal (a webhook payload, a forwarded email, an API call, or a bridged monitoring outage) becomes an **alert** (`im_alert`). Alerts are deduplicated and grouped into an **incident** (`im_incident`), the object your team actually responds to: acknowledges, works, and resolves. Multiple alerts (a flapping check re-firing every minute) collapse into one incident, so your on-call engineer sees one page, not twenty. ## Getting started Incident Management must be activated per organization before any of its endpoints or pages work, an organization admin turns it on from **Dashboard → Incidents**. Once active, see the [Incident Management API](/api/incident-management) to ingest alerts and manage incidents programmatically, or the concept pages above to understand the on-call model itself.
### Policies
URL: https://docs.uptimeify.io/monitoring/incident-management/policies
Description: An escalation policy is the ordered set of tiers that decides who gets paged when an incident opens, how many times, and when to move to the next tier.
Summary: An **escalation policy** belongs to exactly one [team](/monitoring/incident-management/teams) and decides what happens when one of that team's incidents opens: who gets paged, in what order, and when the engine gives up waiting and escalates further. ## Tiers A policy is an ordered list of **tiers** (`tierOrder`). Tier 1 fires after its own `delayMinutes` from the incident opening (`0` means immediately); if nobody acknowledges in time, the engine advances to tier 2, then tier 3, and so on. Each tier has: | Field | Meaning | |---|---| | `delayMinutes` | How long to wait, from the previous tier firing, before this tier fires. | | `targetType` | What this tier pages: `schedule` (whoever is currently on call for a given [schedule](/monitoring/incident-management/schedules)), `user` (a specific person), `team` (every member of a team), or `channel` (a notification channel, e.g. a Slack webhook). | | `repeatCount` / `repeatIntervalMinutes` | How many times, and how often, this tier re-pages its target before escalating onward, a repeat is a second (or third) attempt at the *same* tier, not a new one. | | `conditions` | An optional time-window restriction (recurring, wall-clock, IANA-zone-aware), a tier can be scoped to fire only during specific hours or weekdays, evaluated at the moment it would fire. `null` means the tier always fires. | ## Defaults A team can mark one policy `isDefault: true`. An incident created without an explicit policy, whether ingested from an alert source or created manually, resolves to the team's default policy. A team with no default policy configured pages nobody until one is set or a policy is chosen explicitly per incident. ## Acknowledgment timeout and reminders Two policy-level settings shape what happens after paging starts: - **`ackTimeoutMinutes`**, if set, an incident that was acknowledged but sees no further progress for this long resumes escalating from its current tier, rather than staying quiet indefinitely because someone tapped "acknowledge" and then walked away. - **`reminderIntervalMinutes`**, if set, an unresolved incident gets a recurring reminder notification at this interval, independent of the tier chain, so a long-running incident doesn't fade from view. Both are `null` (off) unless configured. ## Quota-aware paging SMS and voice tiers are subject to the organization's shared [SMS/voice quota](/monitoring/incident-management/quota-and-overage). When that quota is exhausted, a paid tier is skipped in favor of the next tier immediately rather than waiting out its delay, and free channels (push, email) keep notifying regardless.
### Quota & Overage
URL: https://docs.uptimeify.io/monitoring/incident-management/quota-and-overage
Description: SMS and voice paging in Incident Management draw on the same monthly quota as your customer-facing SMS alerts. Here's exactly what happens when it runs out.
Summary: Incident Management doesn't have a separate telco quota. SMS and voice calls sent by IM (acknowledgment pages, escalation notifications) draw on **the same monthly SMS quota** your organization already has for customer-facing Monitoring alerts, one pool, one price per unit. A voice call costs exactly what an SMS costs and counts as one unit against the same allowance; see [pricing](/pricing) for the included allowance per plan and the per-unit overage rate. ## What happens at the included limit As long as your organization is under its included monthly allowance, every SMS or voice send goes out normally, no distinction, no extra step. Once you're at or over the included allowance, what happens next depends on a single organization-level setting: **overage**. - **Overage off (the default)**, a paid send beyond the allowance is blocked. It is not silently dropped: the engine treats it as "no telco spend available right now" and falls back, as described below. You are never billed automatically for going over. - **Overage on** (opted in from your Incident Management settings), a send beyond the allowance goes out and is billed per unit, up to an optional monthly safety cap (a hard ceiling on overage spend) if you've set one. Once that cap would be exceeded, sends block again exactly as if overage were off. ## What happens when a send is blocked A blocked SMS/voice send never means the incident goes unpaged. Three things happen at once: 1. **Free channels keep going.** Push and email notifications for that same rule chain are unaffected and continue to fire, quota only ever gates the paid (SMS/voice) channels. 2. **Escalation skips ahead immediately.** Rather than waiting out the blocked tier's delay, the engine promotes the next escalation tier right away, the point of escalating is to reach *someone*, and a paid channel that can't send is a reason to try the next tier sooner, not later. 3. **One admin alert per organization per month.** The first time a send is blocked in a given calendar month, your organization's admins get an email explaining that the quota is exhausted, which incident triggered it, and how to fix it (enable overage, or raise the safety cap). This is deliberately throttled to one email per month, a single severe incident can otherwise exhaust quota (and would otherwise trigger this alert) many times in a few minutes. None of this requires any action from you to keep incidents visible: the worst case is that paging is slower (next tier instead of a retried SMS) and quieter (no SMS/voice at all until the next billing period, unless you opt in to overage), never invisible. ## Where to change it The overage opt-in (`overageEnabled`) is configured per organization from your Incident Management settings (**Dashboard → Incidents → Settings**) or via `PATCH /api/im/org-settings`. The optional monthly safety cap and per-unit overage price are part of your organization's existing SMS billing settings (**Dashboard → Billing**), the same cap that already applies to overage on customer-facing Monitoring SMS alerts, since both draw from the one pool.
### Retention
URL: https://docs.uptimeify.io/monitoring/incident-management/retention
Description: How long Incident Management keeps raw alert payloads, resolved alerts, outbound-delivery records and the incidents themselves, and what's kept indefinitely.
Summary: Incident Management retains different pieces of an incident's history for different lengths of time. In short: the per-source daily aggregates are permanent, an incident keeps its full detail for a year and lives on as a daily aggregate for a second year, and the raw, potentially sensitive material behind it goes sooner than either. | Data | Retention | |---|---| | Alert raw payload (`im_alert.rawPayload`) | Nulled out 90 days after ingest | | Resolved alerts (the `im_alert` row itself) | Deleted 90 days after ingest | | Outbound-delivery records (integration forwarding history) | Deleted 90 days after they were sent | | Incidents, timeline events, assignments | Deleted 12 months after the incident was triggered | | Daily incident aggregate (volume, MTTA, MTTR per day, team and severity) | Deleted 24 months after the day it covers | | Per-source daily aggregates (alert/dedup/incident counts) | Retained indefinitely | ## Why the raw payload is nulled, not the alert `rawPayload` is the exact, unmodified body your source sent. A vendor's webhook can carry API keys or other secrets its operator put in it, so it isn't something to keep around forever just because the alert it produced still matters. `mappedFields` is the already-normalized, safe view the same payload maps onto (title, severity, host, etc.). It is separate from `rawPayload` and survives it; [Get Incident](/api/incident-management/get-incident) always returns `mappedFields`, never `rawPayload`, regardless of age. ## Why aggregates outlive the rows they're computed from A source's daily counts (shown on its Analytics tab) are retained indefinitely specifically so that deleting the underlying resolved-alert rows after 90 days doesn't erase your longer-term history of how a source or team is trending. You lose the individual alert once it ages out; you keep the shape of the trend. ## Why incidents keep 12 months of detail and 24 months of shape The same idea applied to incidents, one step at a time. For twelve months an incident is kept whole: its timeline, its assignments and the alerts it was built from, so a quarterly review, an annual report or an audit finds the real thing rather than a summary of it. That is twice the six-month window most incident tools open by default. Past twelve months the question people actually ask changes from "what happened in this incident" to "what did last year look like", and that question is answered by one row per day, team and severity carrying the volume, the acknowledgement latency and the resolution latency. Those rows are written nightly, before anything is deleted, and nothing is ever removed that they have not already recorded. They are kept for 24 months, so a year-over-year comparison always has both years in it. Two consequences worth knowing. An incident older than twelve months disappears from the incident list and from the API, including one that was somehow never resolved: the window is a clock, not a status. And percentiles cannot be recomputed from the aggregate, only totals and averages, so a report older than twelve months shows mean time to acknowledge and mean time to resolve rather than a p95.
### Schedules
URL: https://docs.uptimeify.io/monitoring/incident-management/schedules
Description: A schedule defines a team's on-call rotation (who covers it, and when) plus one-off overrides for swaps and time off.
Summary: A **schedule** belongs to exactly one [team](/monitoring/incident-management/teams) and answers one question: who is on call, right now? An escalation policy tier can target a schedule directly, so "page whoever is on call" is a first-class routing target, not something you have to hand-maintain as a list of names. ## The rotation A schedule has exactly **one** rotation. Stacking coverage is done at the escalation tier (a tier can target several schedules) rather than inside a single schedule. A rotation period puts a **group** of members on call as *concurrent peers*: they are equals and do not cut each other. Which of them a given incident actually reaches is decided when the page happens, not here. How the group is formed: - **`none`**, no handover at all. Everyone in the schedule is on call for its whole active period, as one continuous shift. - **`auto`**, the member pool is chunked automatically into groups of the size you give. - **`explicit`**, you define the groups and their order yourself. How often it hands over: - **`daily`**, **`weekly`**, **`biweekly`**, **`monthly`**, at the time of day, weekday or day of month you set. - **`custom`**, every N `minutes`, `hours`, `days`, `weeks` or `months`. **The shortest shift is one minute**, and handover times are minute-precise at every cadence. Cadences below a day (`minutes`, `hours`) advance in absolute time rather than by wall clock, so a daylight-saving switch neither duplicates nor skips an hour's worth of handovers. Independently of the rotation, a schedule can carry **weekly windows** (recurring wall-clock windows such as "weekdays 09:00-18:00"), so business-hours-only coverage needs no separate schedule. Outside those windows the schedule simply has nobody on call. ## Materialization A background worker precomputes each schedule's rotation into concrete shift rows, up to 90 days ahead. This is deliberate: answering "who is on call right now" at escalation time is then an indexed timestamp lookup, not a live timezone/DST computation, the one place this must never disagree with what actually happened is exactly the moment someone is being paged. A fast cadence is precomputed less far ahead, because the number of rows is the horizon divided by the cadence, a one-minute rotation over 90 days would be 129,600 shifts per member. Such a schedule is kept roughly 41 hours ahead instead and topped up every hour. That is a limit on how far a *preview* reaches, not on coverage: paging always reads the shift that is current now. ## Overrides An **override** is a one-off "X covers for Y" window on top of the regular rotation, a holiday swap or sick-day cover, without editing the rotation itself. Overrides have no priority field: precedence between overlapping overrides is decided by creation order (the later-created one wins), and this is also the order the materializer reads them in. ## Timezone Every schedule has an IANA timezone. Handovers and restriction windows are evaluated in it, across daylight-saving transitions, a `09:00` handover means 09:00 in the schedule's own zone, not UTC. ## API See [Schedules](/api/incident-management/schedules), [Schedule Overrides](/api/incident-management/schedule-overrides), and [Who Is On Call](/api/incident-management/on-call).
### Severity
URL: https://docs.uptimeify.io/monitoring/incident-management/severity
Description: Incident Management uses a four-level sev1-sev4 scale that drives paging urgency, escalation speed, and which notification rule chain fires.
Summary: Every Incident Management alert and incident carries a **severity**, one of: | Severity | Meaning | Urgency | |---|---|---| | `sev1` | Critical | High | | `sev2` | Error | High | | `sev3` | Warning | Low | | `sev4` | Info | Low | `sev1`/`sev2` page at **high urgency**, `sev3`/`sev4` at **low urgency**, urgency selects which of a user's notification rule chains fires (e.g. "push immediately, SMS after 5 minutes" for high urgency vs. a slower, quieter chain for low). Severity also drives which of your outbound integrations receive the incident, if you route by minimum severity. ## Where severity comes from An incident's severity is either: - **Derived from its alerts** (`severityManual: false`, the default), the incident's severity is the highest (most critical) severity across every alert grouped into it. A new `sev1` alert landing on an already-`sev3` incident bumps the whole incident to `sev1`; the incident never *quietly* de-escalates as its most severe alert resolves. - **Set by a human** (`severityManual: true`), a [manually created incident](/api/incident-management/create-incident) has no alert stream to derive severity from, so its creator sets it directly, and it stays sticky (the alert-derived `max()` logic never overrides it). An alert source's [payload mapping](/api/incident-management/alert-sources) can map a vendor's own severity/priority field onto `sev1`-`sev4` (with a configurable default for unmapped values), so an alert from Datadog, Grafana, or a custom webhook carries the right severity from the moment it's ingested. ## Routing rules and channels A routing rule or notification channel can specify a `minSeverity`, the incident must be at least this severe (this critical or worse) to trigger it. The default is `sev2`, so routine `sev3`/`sev4` alerts don't, by default, reach every configured integration.
### Teams
URL: https://docs.uptimeify.io/monitoring/incident-management/teams
Description: A team is Incident Management's top-level ownership unit, every schedule, escalation policy, and incident belongs to exactly one team.
Summary: A **team** is the top-level ownership unit in Incident Management. Every [schedule](/monitoring/incident-management/schedules), every [escalation policy](/monitoring/incident-management/policies), and every incident belongs to exactly one team, there is no cross-team or org-wide incident, by design. A small organization might run everything through a single "Default Team"; a larger one splits by service, product, or on-call rotation. ## Membership and roles A user becomes a team member with an `im_team_member.imRole` of either: - **`member`**, can be scheduled, paged, and assigned to incidents owned by the team. - **`admin`** (team admin), additionally may edit the team, and create/edit/delete its schedules, escalation policies, alert sources, and routing rules. Team-admin is scoped to that one team, it does not grant org-wide write access. An organization-level `admin` role always has full write access to every team, without needing team membership at all. ## Write access Mutating a team or its membership requires one of: - the organization's own `admin` role, or - a global admin (cross-org support access), or - a user who is themselves a team admin **of that specific team**. The same bar applies to creating a schedule, escalation policy, alert source, or routing rule under a team: org-admin, or admin of that team. ## Analytics A team has an `analyticsEnabled` flag ("Enable analytics") that turns on response/resolution metric tracking (MTTA/MTTR) for that team. It defaults to off for a new team. ## API See [Teams](/api/incident-management/teams) for the public read endpoint.
### Managed vs. Self-Service Monitors
URL: https://docs.uptimeify.io/monitoring/managed-vs-self-service
Description: Every monitor has an ownership class that controls who may edit it and who receives its alerts.
Summary: Every monitor, website, DNS, ICMP, SMTP, SSH, FTP, IMAP/POP, domain expiry, and DNSBL, carries an **ownership class** (`managementType`) with one of two values: - **`managed`** (default): the organization runs the monitor on the customer's behalf. It is **read-only in the customer portal**; customers request edits through the change-request flow. Alerts follow the organization's full escalation setup, including org-level integrations (webhooks, OpsGenie, org SMTP). - **`self_service`**: the customer owns the monitor. Customer users (including `readonly`-role portal logins) can **create, edit, and delete** their own self-service monitors, bounded by a package quota. Alerts go to the **customer's recipients only**. Org-level integration channels are skipped. Existing monitors were classified `managed`, so nothing changed for them until you explicitly flip a monitor. ## Who can do what | Action | Customer portal user | Organization admin | |---|---|---| | View a monitor | ✓ (own customer scope) | ✓ | | Edit/delete a `self_service` monitor | ✓ (own monitors) | ✓ | | Edit/delete a `managed` monitor | ✗, unless the customer has the `canEditManaged` exception | ✓ | | Create monitors | ✓ as `self_service`, if allowed and within quota | ✓ (any class) | | Change a monitor's class (`managed` ⇄ `self_service`) | ✗, always organization-only | ✓ | | Request a change / request managed status | ✓ (change request) | approves/rejects | Customers can never grant themselves rights: a customer-scoped API caller's writes to permission fields are ignored server-side, and flipping the class of a monitor is rejected with `403` (`managementTypeOrgOnly`). ## Permissions and inheritance Self-service behavior is configured at two tiers with a *null-means-inherit* model: 1. **Package config** (`PATCH /api/package-configs/:packageType`) sets the defaults for every customer on the package: `allowSelfService` (default `false`) and `maxSelfServiceUrls` (default `0`), plus the channel-permission defaults `enableEmailAlerts`, `enableSmsAlerts`, `enableWebhookAlerts`, `enableIntegrationAlerts`, `enablePostRequestEscalation`. 2. **Customer overrides** (`POST`/`PATCH /api/customers`) can set the same fields per customer. `null` (or omitting the field) means *inherit from the package config*. The per-customer `canEditManaged` flag additionally allows a specific customer to edit `managed` monitors without owning them. Resolution is always: customer value → package default → platform default, and it fails closed (absence = least privilege). ## The self-service quota `maxSelfServiceUrls` bounds a customer's **total** number of `self_service` monitors **across all monitor types**: five self-service websites plus three self-service ICMP monitors count as eight. When the quota is reached, creating another self-service monitor (or flipping an existing monitor to `self_service`) fails with `403` (`selfServiceQuotaReached`). ## Change requests For `managed` monitors, the portal shows a **Request change** action instead of edit controls. A request has a `kind`: - `change`: free-text ask ("please raise the check interval"). - `request_managed`: the customer asks the organization to take over responsibility for a monitor. Accepting this request flips the monitor to `managed` automatically. Requests land in the organization's change-request inbox (**Dashboard → Change Requests**), where an org admin accepts or rejects them. Open requests are capped at 10 per customer. See the [Change Requests API](/api/change-requests) for the endpoints. ## What this means for alert delivery The ownership class drives escalation routing at the worker level: - `managed`: unchanged, full org escalation (org integrations + customer recipients per your escalation config). - `self_service`: notifications are delivered to the customer's own recipients only. Organization-level integration channels (channels not bound to a customer or monitor) are skipped, so your ops tooling isn't paged for monitors a…
### Monitoring Types
URL: https://docs.uptimeify.io/monitoring/monitoring-types
Description: Uptimeify supports a wide range of monitor types: from HTTP uptime, keyword, and SSL checks to DNS, network services, and synthetic browser flows. Pick the type that matches what you need to watch.
Summary: ## Website & HTTP HTTP/HTTPS availability and status-code checks for your websites. Verify expected text is present (or absent) in the response body. Alert when a site responds slower than your threshold. Track the response payload size and catch unexpected bloat. Confirm HTTP correctly redirects to HTTPS (with HSTS). Catch expiring or invalid TLS certificates before they break trust. ## Network & Services Ping a host to check reachability and packet loss. Check that mail servers accept connections on the SMTP port. Monitor inbound mail retrieval services. Verify SSH endpoints are listening and reachable. Check that any raw TCP port (databases, caches, queues) is open and accepting connections. Check that FTP servers respond on their configured port. ## DNS & Domain Watch A, AAAA, MX, TXT, CNAME and other records for changes. Detect when your IPs land on DNS blocklists. Get warned well before a domain registration lapses. ## Advanced Be alerted when a scheduled job stops checking in. Synthetic browser flows that test real user journeys.
### DNS Monitor
URL: https://docs.uptimeify.io/monitoring/monitoring-types/dns
Description: The DNS Monitor checks DNS responses for a hostname (e.g. deinkunde.com) on a fixed interval. This helps you detect unexpected record changes (A/AAAA), missing MX records, or TXT changes (SPF/DMARC / domain verification).
Summary: ## How it works - We resolve the selected **record types (RRTypes)** (e.g. `A`, `AAAA`, `MX`, `TXT`). - Optionally, we compare the resolved values against your **Expected Values**. - Depending on your trigger settings, we can create incidents/alerts for: - **Resolve Error** (e.g. `NXDOMAIN`, timeout) - **Mismatch** (resolved values differ from Expected Values) ## Configuration ### Basic settings - **Hostname**: Hostname only, without protocol or path (e.g. `deinkunde.com`, not `https://deinkunde.com/path`). - **Check interval**: How often to check (e.g. every 30 minutes). - **Status**: - `active`: checks run normally. - `maintenance`: checks still run, but alerting may be dampened depending on alert logic. - `disabled`: checks do not run. ### DNS checks - **RRTypes**: Comma-separated list, e.g. `A, AAAA, MX, TXT`. - **Match mode**: - `exact`: values must match exactly. - `contains`: expected values must be contained in the response. - **Expected values**: a list per RRType (one value per line). Empty fields mean “do not validate”. - **Triggers**: - `resolveError`: alert on DNS resolve errors. - `mismatch`: alert when Expected Values do not match. ## Monitoring locations DNS checks run from our monitoring locations. If a monitor does not specify its own restrictions, (if configured) the **customer-level allowed countries** apply.
### DNSBL Monitoring
URL: https://docs.uptimeify.io/monitoring/monitoring-types/dnsbl
Description: Monitor the reputation of your IP addresses on blocklists.
Summary: **Status: Lab** This feature is currently in the **Lab phase**. This means we are still actively optimizing intervals and the selection of lists. DNSBL (DNS-based Blocklist) Monitoring regularly checks whether your server's IP address is listed on one of the known "Blacklists" (Blocklists/RBLs). Being listed on such a list often has serious consequences for email delivery (mails land in spam or are rejected). ## How it works We use our own open-source engine **[uptimeify-dnsbl](https://www.npmjs.com/package/uptimeify-dnsbl)** to check your IP address against over 50 international spam databases. ### Check Interval Currently, we perform the check **every 180 minutes (3 hours)**. - **Reason**: DNSBL operators often block excessive queries ("Rate Limiting"). A 3-hour rhythm is a good compromise between timeliness and "Good Citizenship" towards list operators. - *Note*: If there is a real need for more frequent checks, please contact us. ## What happens if a match is found? If we find your IP on a list (e.g., Spamhaus, Barracuda, SORBS): 1. **Notification**: You receive an alert via your configured channels. 2. **Details**: We show you the specific reason (e.g., "Listed in SBL" or "Dynamic IP Range"). 3. **Solution**: Where available, we provide a direct **Delisting Link** through which you can request removal from the list operator. ## Supported Lists We query a curated list of reliable providers. A complete overview can be found on the page [Used Lists](/monitoring/monitoring-types/dnsbl/lists).
### Used RBL Lists
URL: https://docs.uptimeify.io/monitoring/monitoring-types/dnsbl/lists
Description: We currently monitor your IP addresses against the following lists. This selection covers the most important and reliable international anti-spam databases.
Summary: We use **[uptimeify-dnsbl](https://www.npmjs.com/package/uptimeify-dnsbl)** for this. ## Spamhaus Project *Probably the most important RBL worldwide.* - Spamhaus ## Other Important Lists - Barracuda (BRBL) - SpamCop - SORBS (Aggregate) - UCEPROTECT (Level 1, 2) - Hostkarma - Backscatterer - Invaluement SIP - SpamCannibal - DroneBL - Spam Eating Monkey (SEM-BLACK) - URIBL Black - RV-SOFT Technology - ZapBL - Suomispam Reputation - Kempt.net - Korea Services - NiX Spam - Passive Spam Block List (PSBL) - InterServer RBL - all.s5h.net - Abuse.ch (Combined, Drone, Spam) - 0spam (RBL, Blocklist) - Singular TTK PTE - SpamRats (Spam, Dyna, NoPtr) - Spamsources Fabel - Virus RBL JP - Woody's SMTP Blacklist - WPBL - Gweep (Proxy, Relays) - Digibase Spambot - Lashback UBL - WormRBL - Team Cymru Bogons - Nether.net Relays - Imp.ch Spam RBL - Mailspike Z - Anonmails.de - Pedantic.org - GBUdb Truncate ## Usage Notes We perform the queries in an "Aggregated" manner. This means we do not query each list individually one after the other, but parallelized and optimized to keep response time low and avoid timeouts.
### Domain Expiry Monitoring
URL: https://docs.uptimeify.io/monitoring/monitoring-types/domain-expiry
Description: An expired domain is one of the most severe incidents that can happen to a website. Once a domain expires, the entire online presence becomes unreachable, and in the worst case, the domain is registered by a third party. Our Domain Expiry Monitoring helps you proactively prevent this.
Summary: ## How it works We use the **RDAP protocol** (Registration Data Access Protocol), the modern successor to WHOIS, to query the registration data of your domains directly from the responsible registries. RDAP delivers structured, reliable data and is supported by all major TLDs. ### Check Interval Currently, we perform the check **every 12 hours** (configurable via environment variable). - **Reason**: Domain registration data changes infrequently. A 12-hour rhythm provides timely detection while remaining respectful towards registry rate limits. - *Note*: If there is a real need for more frequent checks, please contact us. ## Two monitoring paths Domain Expiry Monitoring works through two independent channels: ### 1. Website-based checks If the **Domain Expiry Check** is enabled for a website, we automatically extract the domain from the URL and check its expiry date. Results are stored alongside the regular monitoring data. ### 2. Registered customer domains Domains can also be registered independently in the **Admin → Domains** section. This is useful for: - Domains that are not actively monitored as websites (e.g., parked domains, mail-only domains). - Tracking domains with individual warning thresholds per domain. - Per-domain notification overrides (separate email/phone contacts). ## Thresholds Two configurable thresholds determine when alerts are triggered: | Threshold | Default | Purpose | |---|---|---| | **Warning** | 30 days | Early heads-up that renewal is due soon. | | **Critical** | 7 days | Urgent alert: domain expires within days. | These thresholds can be configured individually per website or per registered domain. ## What happens when a threshold is breached? 1. **Warning**: You receive an alert via your configured notification channels when the domain enters the warning window. 2. **Critical**: An urgent alert is triggered if the domain expires within the critical threshold. 3. **Expired**: If the domain has already expired, we flag it immediately. 4. **Recovery**: When a domain is renewed and no longer within the warning window, open incidents are automatically resolved and a recovery notification is sent. ## Monitored Data For each domain, we record and display: - **Expiry Date**: When the domain registration expires. - **Registrar**: Which registrar manages the domain (e.g., "INWX", "Hetzner", "GoDaddy"). - **Days Until Expiry**: Calculated in real time. - **Current Status**: OK, Warning, Critical, or Expired. - **Last Checked**: Timestamp of the most recent RDAP lookup. ## Supported TLDs RDAP is supported by all major gTLDs (.com, .net, .org, etc.) and many ccTLDs (.de, .at, .ch, .nl, .uk, etc.). If a TLD is not supported by RDAP, we skip the check silently. No false alerts are generated. ## Troubleshooting If we report a domain expiry issue: 1. Check with your registrar whether the domain is set to **auto-renew**. 2. Verify that the payment method on file with your registrar is still valid. 3. For domains managed by third parties, confirm that the responsible person is aware of the upcoming renewal.
### FTP Monitor
URL: https://docs.uptimeify.io/monitoring/monitoring-types/ftp
Description: The FTP Monitor checks whether your FTP service is reachable and responsive. This helps detect outages on file transfer infrastructure used by legacy integrations, batch exports, or partner uploads.
Summary: ## How it works - We connect to your FTP endpoint and verify that the service responds. - If the server cannot be reached or does not respond within the configured timeout, the check fails. ## Configuration ### Basic settings - **Hostname**: Hostname only, without protocol or path. - **Port**: Optional (default is typically 21). - **Check interval** and **Timeout**. - **Status**: `active`, `maintenance`, `disabled`. ### Advanced settings If your server requires authentication or encryption (e.g. FTPS), advanced options can be configured via the monitor settings. ### Monitoring locations FTP checks run from our monitoring locations. Monitor-level **Allowed Check Countries** (or customer-level defaults) control which locations are used. ### Port-only mode (no credentials) By default (**Check Mode**: `protocol`), the monitor connects to your FTP endpoint and verifies that the service responds as expected. Set **Check Mode** to `tcp` to skip that protocol validation. The monitor only opens a bare TCP connection to `hostname:port`, without any FTP handshake, and no credentials are required or stored. Use `tcp` mode when you only need to confirm the port is reachable.
### Heartbeat Monitoring (Cron)
URL: https://docs.uptimeify.io/monitoring/monitoring-types/heartbeat
Description: Heartbeat monitoring (also known as Cron Monitoring) works in reverse: instead of us checking your server, your server (or script) notifies us that it is alive.
Summary: This is perfect for monitoring: - **Daily Backups:** Ensure your database backups actually ran. - **Background Jobs:** Monitor workers, import scripts, or periodic tasks. - **Intranet Devices:** Monitor devices behind a firewall that can send outbound requests. ## How it works 1. You create a **Heartbeat Monitor** in the dashboard. 2. We give you a unique **Heartbeat URL**. 3. You configure your script (Cronjob, Worker) to call this URL when it finishes successfully. 4. We expect a ping within your configured **Interval** (plus a **Grace Period**). 5. If we don't receive a ping in time, we send an alert: "Heartbeat missing". ## Configuration ### Expected Interval How often do you expect the ping? - *Example:* For a daily backup, set this to **24 hours** (1440 minutes). - *Example:* For a minutely worker, set this to **1 minute**. ### Grace Period How much delay is acceptable? - *Example:* If your backup usually takes 10 minutes but sometimes 30, set the Grace Period to **30 minutes**. - We will only alert if `Last Ping Time + Interval + Grace Period < NOW`. ## Usage Examples ### Linux Cronjob (Backup Script) ```bash #!/bin/bash # Run backup ... pg_dump dbname > backup.sql # If successful, ping the heartbeat if [ $? -eq 0 ]; then curl -m 10 --retry 3 https://ping.uptimeify.io/ping/YOUR_TOKEN fi ``` ### PowerShell (Windows) ```powershell # Run task... Write-Output "Task running..." # Ping Heartbeat Invoke-RestMethod -Uri "https://ping.uptimeify.io/ping/YOUR_TOKEN" ``` ### Node.js Worker ```javascript await doImport(); // Ping await fetch('https://ping.uptimeify.io/ping/YOUR_TOKEN'); ```
### HTTPS Redirect Check
URL: https://docs.uptimeify.io/monitoring/monitoring-types/https-redirect
Description: Security and SEO are indispensable today. The HTTPS Redirect Check ensures that visitors accessing your website unencrypted via http:// are automatically redirected to the secure https:// version.
Summary: ## Why is this important? - **Security**: Prevents Man-in-the-Middle attacks. - **SEO**: Search engines like Google prefer HTTPS and penalize sites without correct redirection. - **User Trust**: Users see the lock symbol in the browser. ## How it works This check is an option within the Uptime Monitor. When enabled: 1. We explicitly call the URL with `http://`. 2. We expect a status code `301` (Permanent Redirect) or `302` (Found) as well as `307/308`. 3. We check if the redirect target starts with `https://`. ## Configuration You can find this setting in the advanced options of your monitor: - **Enable HTTPS Redirect Check**: Enable this option to enforce the redirect.
### ICMP Monitor
URL: https://docs.uptimeify.io/monitoring/monitoring-types/icmp
Description: The ICMP Monitor checks whether a host is reachable on the network. This is ideal for infrastructure components that do not expose HTTP endpoints (e.g. routers, firewalls, VPN gateways, databases, or internal services).
Summary: ## How it works - We send ICMP echo requests (“ping”) to your **hostname**. - The monitor is considered healthy when the target responds within the configured timeout. - We record latency and detect sustained reachability problems. ## Configuration ### Basic settings - **Name**: A human-friendly label. - **Hostname**: Hostname or IP address (no protocol, no path). - **Check interval**: How often to check. - **Timeout**: How long to wait for a response. - **Status**: - `active`: checks run normally. - `maintenance`: checks still run, but alerting may be dampened depending on alert logic. - `disabled`: checks do not run. ### Monitoring locations ICMP checks run from our monitoring locations. - If you set **Allowed Check Countries** on the monitor, checks run only from matching locations. - If the monitor has no restrictions, (if configured) the **customer-level allowed countries** apply.
### IMAP/POP Monitor
URL: https://docs.uptimeify.io/monitoring/monitoring-types/imap-pop
Description: The IMAP/POP Monitor verifies that your mail server is reachable for clients. This is useful for detecting authentication and connectivity issues that impact inbox access.
Summary: ## How it works - We connect to your IMAP or POP endpoint and validate that the service responds. - If the server cannot be reached or does not respond in time, the check fails. ## Configuration ### Basic settings - **Hostname**: Hostname only, without protocol or path. - **Port**: Optional (commonly 143/993 for IMAP, 110/995 for POP). - **Check interval** and **Timeout**. - **Status**: `active`, `maintenance`, `disabled`. ### Advanced settings Protocol selection (IMAP vs POP) and other advanced options (e.g. encryption/auth requirements) can be configured via the monitor settings. ### Monitoring locations IMAP/POP checks run from our monitoring locations. You can restrict execution using **Allowed Check Countries** (monitor-level) or by using the customer-level defaults. ### Port-only mode (no credentials) By default (**Check Mode**: `protocol`), the monitor logs in to your IMAP or POP endpoint and validates the connection. Set **Check Mode** to `tcp` to skip authentication entirely. The monitor only opens a bare TCP connection to `hostname:port`, without an IMAP/POP handshake or login, and no credentials are required or stored. Use `tcp` mode when you only need to confirm the port is reachable. Unlike SSH, SMTP, and FTP, **IMAP/POP requires an explicit Port when Check Mode is `tcp`**. Since there's no way to infer IMAP vs. POP without one, the monitor can't fall back to a protocol default port.
### Keyword Monitor
URL: https://docs.uptimeify.io/monitoring/monitoring-types/keyword
Description: Sometimes a page is technically \\\"online\\\" (Status Code 200) but doesn't show the desired content: for example, a white page, a database error message, or \\\"Out of Stock\\\". The Keyword Monitor (also called \\\"Content Monitor\\\") solves this problem.
Summary: ## How it works The Keyword Monitor downloads the HTML body of your page and searches it for a specific text (string). It is useful for ensuring that: - The database connection is working (check for dynamic content). - The CMS renders correctly. - No maintenance mode is displayed. ## Configuration Modes You can run the monitor in two modes: ### 1. "Must Contain" (Keyword Present) Alert if the word is **NOT** found. - **Use Case**: Check for "Welcome", "Imprint", or the company name in the footer. - **Example**: Check for the name of a bestseller product on a shop page. ### 2. "Must Not Contain" (Keyword Absent) Alert if the word **IS** found. - **Use Case**: Check for error messages. - **Examples**: "MySQL Error", "404 Not Found" (in text), "Hacked by", "Maintenance Mode". ## Setup Steps 1. Create a new monitor or edit an existing one. 2. Under "Advanced Settings", select the **Keyword Check** option. 3. Enter the search term (Case-sensitive!). 4. Save the monitor.
### Page Size Check
URL: https://docs.uptimeify.io/monitoring/monitoring-types/page-size
Description: The Page Size Check monitors the size of your website's HTML response content (body) in bytes. Unexpected changes in file size can indicate serious issues that are missed by a pure status code check (200 OK).
Summary: ## Use Cases - **Hacks / Defacements**: Attackers often inject code, significantly increasing the page size. - **Empty Pages**: A database error could cause only the header to render (page suddenly very small), although the server reports Status 200. - **Performance**: Accidentally including huge scripts or CSS files in the HTML. ## Configuration You can define thresholds: ### Min Page Size Alert if the page is smaller than X bytes. - *Recommendation*: Set this to approx. 80-90% of your page's normal size to detect partially empty renderings. ### Max Page Size Alert if the page is larger than Y bytes. - *Recommendation*: Protects against "code bloat" or injections. The check is considered failed if the actual size is outside this range.
### Playwright Monitor
URL: https://docs.uptimeify.io/monitoring/monitoring-types/playwright
Description: For critical processes like Login, Checkout, or Registration, a simple HTTP check is often insufficient. The Playwright Monitor loads your website in a real browser (Headless Chromium) and executes a script defined by you.
Summary: This simulates a real user and uncovers JavaScript errors, broken buttons, or UI issues. ## How it works We execute your Playwright script in our secure cloud environment. - **Success**: The script runs to completion without errors. - **Error**: An `expect` check fails or a timeout occurs. We automatically save a **screenshot** and the error message. ## Example: Testing Login Here is a simple script to test a login process: ```javascript test('User can login', async ({ page }) => { // 1. Navigation await page.goto('https://app.deinkunde.com/login'); // 2. Fill form (Using Env Variables) await page.fill('input[name="email"]', 'monitor-user@deinkunde.com'); await page.fill('input[name="password"]', process.env.MONITOR_PASSWORD); // 3. Submit await page.click('button[type="submit"]'); // 4. Verification (Wait for dashboard element) await expect(page).toHaveURL(/dashboard/); await expect(page.locator('.welcome-message')).toContainText('Hello'); }); ``` ## Environment Variables (Secrets) You should never store passwords, API keys, or sensitive data directly in the script ("hardcoding"). Instead, use **Environment Variables**. ### Setup 1. Navigate to **Monitor Settings**. 2. Open the **Advanced Settings** section. 3. Find the **Environment Variables** section. 4. Enter the Key and Value. - Example Key: `PASSWORD` - Example Value: `SecretPassword123!` ### Usage in Script In Playwright code, access values via `process.env.KEY`. The values are injected into the secure worker environment only at runtime. ```javascript test('Secure Login', async ({ page }) => { await page.goto('https://deinkunde.com/login'); // Secure access via process.env await page.fill('#password', process.env.PASSWORD); await page.click('#submit'); }); ``` ### Security - Values are stored **encrypted** in our database. - They are not visible in plain text in the frontend editor after saving (depending on permissions). - They do not appear in the screenshot log (unless you explicitly `console.log` them). ## Tips for Stable Tests - Use `data-testid` attributes for selectors where possible.
### Response Time Check
URL: https://docs.uptimeify.io/monitoring/monitoring-types/response-time
Description: In addition to pure availability, speed is crucial for user experience. The Response Time Check measures how long your server takes to respond to a request.
Summary: ## What is measured? We typically measure the **Time to First Byte (TTFB)** as well as the time for the complete transfer of the HTML document. ## Configuration You can set thresholds for warnings: - **Threshold (ms)**: If the response time exceeds this value (e.g., 2000ms), an alarm is triggered or the status is marked as "Degraded", depending on configuration. ## Analysis In the monitor details, you will find charts showing the development of response time over time. This helps you to: - Identify performance bottlenecks at specific times (e.g., during backups). - See the impact of code deployments on speed.
### SMTP Monitor
URL: https://docs.uptimeify.io/monitoring/monitoring-types/smtp
Description: The SMTP Monitor verifies that your SMTP server is reachable and responsive. This helps you detect outages of outbound mail gateways and mail delivery infrastructure before users report missing emails.
Summary: ## How it works - We connect to your SMTP server and validate that it responds as expected. - If the server cannot be reached or does not respond in time, we mark the check as failed. ## Configuration ### Basic settings - **Hostname**: Hostname only, without protocol or path. - **Port**: Optional. Use your SMTP port (commonly 25 / 587 / 465). - **Check interval** and **Timeout**. - **Status**: `active`, `maintenance`, `disabled`. ### Advanced settings Depending on your use case, advanced options (e.g. encryption/auth requirements) can be configured via the monitor settings. ### Monitoring locations SMTP checks run from our monitoring locations. You can restrict execution using **Allowed Check Countries** (monitor-level) or by using the customer-level defaults. ### Port-only mode (no credentials) By default (**Check Mode**: `protocol`), the monitor connects to your SMTP server and validates that it responds as expected. Set **Check Mode** to `tcp` to skip that protocol validation. The monitor only opens a bare TCP connection to `hostname:port`, without any SMTP handshake, and no credentials are required or stored. Use `tcp` mode when you only need to confirm the port is reachable.
### SSH Monitor
URL: https://docs.uptimeify.io/monitoring/monitoring-types/ssh
Description: The SSH Monitor checks whether your SSH service is reachable. It is commonly used to detect firewall issues, network outages, or server crashes affecting administrative access.
Summary: ## How it works - We attempt to connect to your SSH service. - If the server cannot be reached, rejects connections unexpectedly, or times out, the check fails. ## Configuration ### Basic settings - **Hostname**: Hostname or IP address. - **Port**: Optional (default is typically 22). - **Check interval** and **Timeout**. - **Status**: `active`, `maintenance`, `disabled`. ### Monitoring locations SSH checks run from our monitoring locations. Use **Allowed Check Countries** to limit where checks run from. ### Port-only mode (no credentials) By default (**Check Mode**: `protocol`), the monitor logs in over SSH with the credentials you provide and verifies a successful handshake/login. Set **Check Mode** to `tcp` to skip authentication entirely. The monitor only opens a bare TCP connection to `hostname:port`, without attempting an SSH handshake or login, and no credentials are required or stored. Use `tcp` mode when you only need to confirm the port is reachable (e.g. a firewall or network check) without giving Uptimeify SSH credentials.
### SSL Monitor
URL: https://docs.uptimeify.io/monitoring/monitoring-types/ssl
Description: Expired SSL certificates are a common cause of downtime and loss of user trust. Browsers display warnings like \\\"Connection not secure\\\". Our SSL Monitor helps you proactively prevent this.
Summary: ## How it works The SSL Monitor regularly checks the configuration of your TLS/SSL certificate. It is often integrated into the Uptime Monitor but can also be configured separately. ## Monitored Metrics ### 1. Expiration (Expiry) This is the most critical check. We alert you well before the expiration date so you can renew the certificate. - **Notification**: By default, we remind you **30, 14, 7, and 1 day(s)** before expiration (configurable). ### 2. Validity & Chain of Trust We check if the certificate: - Is signed by a trusted Root CA. - Has not been revoked (OCSP/CRL Check). - Is issued for the correct hostname (domain). ## Troubleshooting If we report an SSL error: 1. Check the expiration date. 2. Check if "Intermediate Certificates" are correctly installed (Chain incomplete error). 3. Ensure the hostname in the certificate (CN or SAN) matches the monitored URL.
### TCP Port Monitor
URL: https://docs.uptimeify.io/monitoring/monitoring-types/tcp-port
Description: The TCP Port Monitor checks whether a raw TCP port is open and accepting connections. It is commonly used for databases, caches, message queues, and other non-HTTP services that don't speak a protocol we have a dedicated monitor for.
Summary: ## How it works - We attempt to open a TCP connection to your `hostname:port`. - If the connection cannot be established or times out, the check fails. - If you configure **Expect Banner**, we also verify that the raw bytes received right after the handshake contain that substring (e.g. `+PONG` for Redis, `220` for an SMTP banner). Otherwise the check fails even though the port is open. ## Configuration ### Basic settings - **Hostname**: Hostname or IP address (no protocol, no path). - **Port**: Required. Unlike other monitor types, there is no default port. You must specify the exact port to connect to (e.g. `6379` for Redis, `5432` for PostgreSQL, `27017` for MongoDB). - **Expect Banner**: Optional substring the worker must find in the bytes received immediately after the TCP handshake. - **Check interval** and **Timeout**. - **Status**: `active`, `maintenance`, `disabled`. ### Monitoring locations TCP checks run from our monitoring locations. Use **Allowed Check Countries** to limit where checks run from. ### No credentials TCP Port monitors carry no credentials or authentication. They only verify that the port accepts connections (and optionally that the initial banner matches). For services that require authentication before you can confirm they're healthy, use a protocol-specific monitor (e.g. SSH, SMTP, IMAP/POP) where one exists.
### Uptime Monitor
URL: https://docs.uptimeify.io/monitoring/monitoring-types/uptime
Description: The Uptime Monitor is the core of your monitoring strategy. It regularly checks if your website or API endpoint is accessible to your customers. We recommend setting up at least one Uptime Monitor for every publicly accessible page.
Summary: ## How it works We send HTTP or HTTPS requests to your URL at the interval you choose (e.g., every 30 seconds or every minute). We consider the monitor "Online" if: 1. The server responds (no timeout). 2. The **Status Code** matches expectations (default `2xx`, e.g., 200 OK). If an error is detected, we verify it from nine European locations across Germany, Spain, France, Italy, Poland and Finland to avoid false alarms before sending a notification. ## Create a website ### Where is this in the UI? In the dashboard, navigate to **Websites** and click **New website** / **Create website**. ### Name Choose a name that is easy to recognize for your team (e.g. "Marketing Website", "Shop API", "Customer Portal"). ### URL Uptimeify **automatically prepends `https://`** if you enter a plain hostname (e.g. `deinkunde.com`), so you don't need to type the protocol yourself, but you can always paste the full URL (e.g. `https://deinkunde.com`) as well. Tips: - Prefer the canonical target URL (usually `https://…`). - If you want to verify HTTP→HTTPS behavior, also use the **HTTPS Redirect** monitor type. ### Status You can keep a website active or temporarily disable it (e.g. during migrations). A disabled website is not monitored. ## Configuration ### Basic Settings - **URL**: The full address (e.g., `https://deinkunde.com`). - **Check Interval**: How often to check (e.g., 60s). ### Advanced Settings - **Expected Status Codes**: By default, we check for `200-299`. You can adjust this, e.g., to `200,301,302` if redirects should be considered a success. - **Timeout**: Maximum time to wait for a response (Default: 30s). - **HTTP Method**: Default is `GET`. You can also choose `HEAD`, `POST`, etc. - **Request Body & Headers**: Send JSON data or authentication tokens (e.g., `Authorization: Bearer ...`). ## Optional checks When configuring an Uptime Monitor, you can enable additional checks alongside the basic availability test: - **SSL**: Monitor certificate validity and expiration date → see [SSL Monitor](/monitoring/monitoring-types/ssl) - **Response Time**: Set performance thresholds and get alerted when your site slows down (see [Response Time](#response-time) below) - **Keyword / Content Validation**: Verify that specific content is present in the response body - **Page Size**: Alert on unexpected size changes that may indicate missing resources or bloat ## Common pitfalls ### URL vs. hostname (DNS/ICMP) The Uptime Monitor accepts a plain hostname (Uptimeify adds `https://` automatically) or a full URL. Some other monitor types work with a **hostname without protocol** and do not perform HTTP requests. - DNS Monitor: hostname without protocol (e.g. `deinkunde.com`) → see [DNS Monitor](/monitoring/monitoring-types/dns) - ICMP Monitor: hostname without protocol (e.g. `server.deinkunde.com`) → see [ICMP Monitor](/monitoring/monitoring-types/icmp) ### Authentication / bot protection If your site uses Basic Auth, token auth, or bot protection, a simple HTTP check may fail. - Configure auth headers or Basic Auth credentials in the **Request Headers** field. - For complex login flows, **Playwright** monitors are often the more robust choice. ## Response Time In addition to status, we also record the response time (Time to First Byte + Download). - You can configure alerts if the response time exceeds a threshold (e.g., > 2000ms), even if the status is 200 OK. ## Next steps - Investigate outages and view history: [Incidents](/incidents)
### Using Tags
URL: https://docs.uptimeify.io/monitoring/tags
Description: Organize and filter monitors with color-coded tags across all monitor types.
Summary: Tags let you label any monitor, website, DNS, ICMP, SMTP, SSH, FTP, or IMAP/POP, with one or more color-coded badges. You can then filter every monitor list by tag and instantly see which monitors belong to a group (e.g. "Production", "Client A", "Staging"). ## Creating Tags Open **Settings → Tags** and click **New Tag**. Provide: - **Name**: a short label, up to 50 characters. - **Color**: choose from the fixed palette: `slate`, `red`, `amber`, `green`, `teal`, `blue`, `indigo`, `violet`, `pink`, or `gray`. Tags are organization-scoped: every member with sufficient permissions can see and use them. ### Readonly member rule Readonly members **can** create tags, but those tags are visible only to themselves. A readonly member cannot see, update, or delete tags created by other users. They can assign their own tags to any monitor they have read access to. ## Color Palette | Key | Color | |-----|-------| | `slate` | Slate / neutral gray | | `red` | Red | | `amber` | Amber / orange | | `green` | Green | | `teal` | Teal | | `blue` | Blue | | `indigo` | Indigo | | `violet` | Violet / purple | | `pink` | Pink | | `gray` | Gray | ## Assigning Tags Tags can be assigned in two places: ### From the monitor list (inline) In any monitor list (Websites, DNS, ICMP, etc.) find the **Tags** column. Click the tag chip area on a row to open the tag picker and toggle tags on or off for that monitor. ### From the monitor detail page Open a monitor's detail page and locate the **Tags** section. Use the tag picker to add or remove tags. Changes are saved immediately. ## The Tags Column The Tags column is visible by default in every monitor list. To get a more compact view, toggle the column off using the **Columns** menu at the top of the list. Your preference is saved per list. ## Filtering by Tag At the top of any monitor list, open the **Filter** menu and select one or more tags. The list narrows to show only monitors that carry **all** of the selected tags (AND logic). To clear the filter, deselect all tags or click **Reset filters**. ## Managing Tags Go to **Settings → Tags** to see the full tag list. From there you can: - **Edit** a tag's name or color (owner or admin only). - **Delete** a tag: this removes the tag from every monitor it was assigned to (cascade delete). ## API Reference See the [Tags API](../api/tags) for programmatic access to create, update, delete, and assign tags.
## Status Pages
### Status Pages
URL: https://docs.uptimeify.io/status-pages
Description: Status pages let you communicate service health to your customers transparently: live overall state, a per-service breakdown, and a short history of recent incidents and maintenance. Each page is fully brandable and can run on your own domain (e.g. status.deinkunde.com).
Summary: 8 layouts, color schemes, accent color, custom title, and toggles for what to show. Serve a page on `status.deinkunde.com` with DNS verification and automatic HTTPS. Create, update, design, and manage domains programmatically. ## What a status page shows - **Overall state**: a single banner: Operational, Degraded, or Maintenance. - **Services**: the customer's monitored websites and service monitors, each with its own state. - **Uptime statistics**: optional per-service uptime percentages. - **Recent history**: optional list of recent incidents and maintenance windows. ### How the public state is derived Uptimeify computes each service's public state from two live signals. It never exposes raw check data: | Condition | Public state | |-----------|--------------| | One or more **open incidents** | **Degraded** | | **Active maintenance window** and no open incidents | **Maintenance** | | Neither | **Operational** | The overall banner reflects the worst state across all services. See [Incidents](/incidents) and [Maintenance](/maintenance) for how those signals are produced. ## Create a status page In the app, go to **Dashboard → Status Pages → Create**. A page always belongs to exactly one **customer**. It shows that customer's services. | Setting | Description | |---------|-------------| | **Customer** | Required. The customer whose services the page displays. | | **Public name** | The page title shown to visitors. | | **Slug** | Used for the friendly URL. Auto-generated from the name; lowercase letters, numbers, and hyphens. | | **Description** | Optional intro text shown under the title. | | **Visibility** | `public` (anyone can view) or `customer_members_only` (login + access to the customer required). | | **Published** | Toggle off to hide the page without deleting it. | You can do all of this via the [API](/api/status-pages/create-status-page) too. ## URLs Every status page is reachable at: - `/status/`: friendly URL based on the configured slug. - `/status/`: stable URL based on the page ID (always available, even if the slug changes). With a [custom domain](/status-pages/custom-domains) configured and active, the page is also served at the apex of that hostname, e.g. `https://status.deinkunde.com/`. ## Visibility & publishing - **`public`**: the page is reachable by anyone with the link. Best for customer-facing status. - **`customer_members_only`**: visitors must be logged in and have access to the customer. Best for internal or NDA-bound services. - **Published** is independent of visibility: an unpublished page returns *not found* regardless of visibility, which is useful while you are still setting it up. ## Recent history The **Recent History** section is controlled by two independent toggles: - **Show recent incidents** - **Show recent maintenance** These only affect the *history list*. Live indicators (such as a service currently being in maintenance) are always shown, independent of these flags. The [design settings](/status-pages/design) additionally control the history time window (`historyDays`). ## Troubleshooting ### "Status page not found" - Confirm the page is **Published**. - If visibility is `customer_members_only`, you must be logged in and have access to that customer. - Double-check the slug. It is normalized to lowercase-with-hyphens on save. ### A service shows the wrong state - **Stuck on Degraded?** There is still an open incident for that service. Resolve it or wait for automatic recovery (see [Incidents](/incidents)). - **Expected Maintenance but see Operational?** Verify the maintenance window is **active** and the current time is within its start/end (see [Maintenance](/maintenance)).
### Custom Domains
URL: https://docs.uptimeify.io/status-pages/custom-domains
Description: Connect a hostname like status.deinkunde.com to a status page so it runs entirely under your own brand. A hostname can be bound to exactly one status page, and the certificate is issued automatically once the domain is verified and active.
Summary: ## How it works A custom domain moves through three states: 1. **Pending**: added, waiting for DNS verification. 2. **Verified**: the TXT record was found; ready to activate. 3. **Active**: the page is served on the hostname over HTTPS. ``` Add hostname → Add TXT record → Verify → Activate → Point CNAME (pending) (your DNS) (verified) (active) (traffic) ``` ## Step 1: Add the hostname In the app: **Dashboard → Status Pages → (page) → Custom Domain → Add**, enter `status.deinkunde.com`. Via the API: [Add Custom Domain](/api/status-pages/add-status-page-domain). Uptimeify returns a TXT verification record: - **TXT name**: `_uptimeify-verify.status.deinkunde.com` - **TXT value**: the token shown in the app / API response ## Step 2: Add the TXT record Create the TXT record in your DNS provider exactly as shown. Keep it in place. It is also re-checked over time. DNS changes can take anywhere from a few minutes to a few hours to propagate. If verification fails immediately, wait and retry. ## Step 3: Verify Click **Verify** (or call [Verify Status Page Domain](/api/status-pages/verify-status-page-domain)). Uptimeify looks up the TXT record and checks the token matches. On success the domain becomes **Verified**. ## Step 4: Activate Click **Activate** (or call [Activate Status Page Domain](/api/status-pages/activate-status-page-domain)). The domain becomes **Active** and a TLS certificate is provisioned automatically (on-demand HTTPS): no certificate upload required. ## Step 5: Point traffic to Uptimeify Finally, point the hostname at Uptimeify so visitors actually reach the page. Add the CNAME shown in the app for `status.deinkunde.com`. Once DNS resolves, `https://status.deinkunde.com/` serves your status page. Keep both records in place: the **TXT** record (used for ongoing verification) and the **CNAME** (routes visitor traffic). Removing the TXT record can cause the domain to fail re-verification. ## Removing a domain Remove the binding in the app or via [Remove Custom Domain](/api/status-pages/remove-status-page-domain). The status page stays reachable on its `/status/` and `/status/` URLs. ## Troubleshooting ### "TXT verification failed (token not found)" - Confirm the TXT **name** is exactly `_uptimeify-verify.`. - Confirm the TXT **value** matches the token from Uptimeify (no extra quotes or spaces). - Give DNS more time to propagate, then verify again. - Check you didn't add the record to the wrong zone (e.g. apex vs subdomain). ### The page doesn't load on my domain - Make sure the domain is **Active**, not just Verified. - Confirm the **CNAME** points to the target shown in the app. - The first request after activation may be slightly slower while the certificate is issued.
### Design & Branding
URL: https://docs.uptimeify.io/status-pages/design
Description: Every status page has a visual design configuration. Edit it in the app under Dashboard → Status Pages → (page) → Design, or via the design API. Changes are merged: you only send the fields you want to change, and the rest keep their current values.
Summary: ## Layouts Choose one of eight layout presets. They all show the same data (overall state, services, optional stats and history) but differ in structure and density. Preview each one live in the design editor. | Layout | Best for | |--------|----------| | `classic` | The default. A prominent hero banner with the overall state, followed by the service list. | | `cards` | Each service rendered as a card in a responsive grid. | | `minimal` | Stripped-back, text-first presentation with minimal chrome. | | `sleek` | A modern hero banner with an accent-color gradient. | | `board` | A dashboard-style board to see many services at a glance. | | `split` | A full-width header with a two-column body (status next to history). | | `timeline` | Emphasizes a chronological timeline of incidents and maintenance. | | `compact` | Dense layout that fits many services into little vertical space. | ## Appearance | Option | Values | Default | Description | |--------|--------|---------|-------------| | `colorScheme` | `light`, `dark`, `auto` | `auto` | `auto` follows the visitor's system preference. | | `accentColor` | hex color `#rrggbb` | `#6366f1` | Used for highlights, links, and gradients. | | `headerStyle` | `simple`, `centered`, `hero` | `simple` | How prominent the page header is. | | `fontFamily` | `system`, `mono` | `system` | `mono` gives a technical, monospaced look. | | `cardRadius` | `none`, `md`, `xl` | `md` | Corner rounding of cards and panels. | | `pageWidth` | `sm`, `md`, `lg`, `xl` | `lg` | Maximum content width. | ## Content & sections | Option | Type | Default | Description | |--------|------|---------|-------------| | `customTitle` | string (≤120) | - | Overrides the page title in the header. | | `customSubtitle` | string (≤200) | - | A subtitle shown under the title. | | `showUptimeStats` | boolean | `true` | Show per-service uptime percentages. | | `showServiceUrls` | boolean | `false` | Show each service's URL next to its name. | | `showLastChecked` | boolean | `false` | Show the "last checked" timestamp per service. | | `showHistory` | boolean | `true` | Show the recent history section. | | `historyDays` | number | - | How many days of history to include (bounded by the platform min/max). | | `showPoweredBy` | boolean | `true` | Show a "Powered by ``" line in the footer. It names **you**, never Uptimeify: with no product name set, the line is not rendered at all. | `showHistory` controls whether the history *section* is rendered. The separate **Show recent incidents** and **Show recent maintenance** toggles (on the page's main settings) control *what* goes into that section. See [Recent history](/status-pages#recent-history). ## Example (API) ```bash curl -X PATCH "$BASE_URL/api/status-pages//design" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "layout": "sleek", "colorScheme": "dark", "accentColor": "#10b981", "showServiceUrls": true, "showPoweredBy": false }' ``` Only the fields you send are changed; everything else keeps its current value. See [Update Status Page Design](/api/status-pages/update-status-page-design) for the full reference. ## White-labeling A status page never names Uptimeify. The footer's copyright line names the operator of the page (your product name, or the status page's own name if you have not set one), and if neither exists, no copyright line is rendered. Nothing on the page falls back to the vendor's name. White-labeling is not a plan feature; it applies on every paid plan. For a fully branded page: 1. Set your **product name** under Branding. It is what the copyright line and the "Powered by" line say, and it is the only thing either of them will ever say. 2. Configure a [custom domain](/status-pages/custom-domains) so the page runs on your hostname. 3. Upload a **favicon** under Branding. On your own domain this is what visitors see in the browser tab and on an iOS home screen. Without one the page carries no icon a…