openapi: 3.1.0 info: title: FLUX Monitoring API version: 1.0.0 description: | Version 1 of the FLUX Monitoring API. Tokens are created in the dashboard, expire automatically, and are limited to the scopes selected at creation. servers: - url: https://monitoring.fluxcdn.cloud/api/v1 description: Production security: - bearerToken: [] tags: - name: Account - name: Monitors - name: Incidents paths: /account: get: tags: [Account] summary: Read workspace usage and limits operationId: getAccount x-required-scope: account:read responses: '200': description: Workspace summary content: application/json: schema: type: object required: [data] properties: data: $ref: '#/components/schemas/Account' '401': { $ref: '#/components/responses/Unauthenticated' } '403': { $ref: '#/components/responses/InsufficientScope' } '429': { $ref: '#/components/responses/RateLimited' } /monitors: get: tags: [Monitors] summary: List monitored services operationId: listMonitors x-required-scope: monitors:read parameters: - $ref: '#/components/parameters/Cursor' - $ref: '#/components/parameters/PerPage' responses: '200': description: Cursor-paginated monitored services content: application/json: schema: $ref: '#/components/schemas/MonitorCollection' '401': { $ref: '#/components/responses/Unauthenticated' } '403': { $ref: '#/components/responses/InsufficientScope' } '429': { $ref: '#/components/responses/RateLimited' } post: tags: [Monitors] summary: Create a monitored service operationId: createMonitor x-required-scope: monitors:write parameters: - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/MonitorInput' responses: '201': description: Monitored service created headers: Location: { schema: { type: string, format: uri } } content: application/json: schema: $ref: '#/components/schemas/MonitorResponse' '400': { $ref: '#/components/responses/InvalidIdempotencyKey' } '401': { $ref: '#/components/responses/Unauthenticated' } '403': { $ref: '#/components/responses/InsufficientScope' } '409': { $ref: '#/components/responses/IdempotencyConflict' } '422': { $ref: '#/components/responses/ValidationFailed' } '429': { $ref: '#/components/responses/RateLimited' } /monitors/{monitor}: parameters: - $ref: '#/components/parameters/MonitorId' get: tags: [Monitors] summary: Read a monitored service operationId: getMonitor x-required-scope: monitors:read responses: '200': description: Monitored service content: application/json: schema: { $ref: '#/components/schemas/MonitorResponse' } '401': { $ref: '#/components/responses/Unauthenticated' } '403': { $ref: '#/components/responses/InsufficientScope' } '404': { $ref: '#/components/responses/NotFound' } put: tags: [Monitors] summary: Replace monitored service settings operationId: updateMonitor x-required-scope: monitors:write parameters: - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/MonitorInput' } responses: '200': description: Monitored service updated content: application/json: schema: { $ref: '#/components/schemas/MonitorResponse' } '400': { $ref: '#/components/responses/InvalidIdempotencyKey' } '401': { $ref: '#/components/responses/Unauthenticated' } '403': { $ref: '#/components/responses/InsufficientScope' } '404': { $ref: '#/components/responses/NotFound' } '409': { $ref: '#/components/responses/IdempotencyConflict' } '422': { $ref: '#/components/responses/ValidationFailed' } delete: tags: [Monitors] summary: Delete a monitored service operationId: deleteMonitor x-required-scope: monitors:write parameters: - $ref: '#/components/parameters/IdempotencyKey' responses: '204': { description: Monitored service deleted } '400': { $ref: '#/components/responses/InvalidIdempotencyKey' } '401': { $ref: '#/components/responses/Unauthenticated' } '403': { $ref: '#/components/responses/InsufficientScope' } '404': { $ref: '#/components/responses/NotFound' } '409': { $ref: '#/components/responses/IdempotencyConflict' } /incidents: get: tags: [Incidents] summary: List incidents operationId: listIncidents x-required-scope: incidents:read parameters: - $ref: '#/components/parameters/Cursor' - $ref: '#/components/parameters/PerPage' responses: '200': description: Cursor-paginated incidents content: application/json: schema: type: object required: [data, meta] properties: data: type: array items: { $ref: '#/components/schemas/Incident' } meta: { $ref: '#/components/schemas/CursorMeta' } '401': { $ref: '#/components/responses/Unauthenticated' } '403': { $ref: '#/components/responses/InsufficientScope' } /incidents/{incident}: get: tags: [Incidents] summary: Read an incident operationId: getIncident x-required-scope: incidents:read parameters: - name: incident in: path required: true schema: { type: string, pattern: '^[0-9A-HJKMNP-TV-Z]{26}$' } responses: '200': description: Incident content: application/json: schema: type: object required: [data] properties: data: { $ref: '#/components/schemas/Incident' } '401': { $ref: '#/components/responses/Unauthenticated' } '403': { $ref: '#/components/responses/InsufficientScope' } '404': { $ref: '#/components/responses/NotFound' } components: securitySchemes: bearerToken: type: http scheme: bearer bearerFormat: FLUX API token parameters: MonitorId: name: monitor in: path required: true schema: { type: string, pattern: '^[0-9A-HJKMNP-TV-Z]{26}$' } Cursor: name: cursor in: query schema: { type: string } PerPage: name: per_page in: query schema: { type: integer, minimum: 1, maximum: 100, default: 25 } IdempotencyKey: name: Idempotency-Key in: header required: true description: A unique 8–100 character key. Reuse it only when retrying the same request body. schema: { type: string, minLength: 8, maxLength: 100, pattern: '^[A-Za-z0-9_.:-]+$' } schemas: MonitorInput: type: object additionalProperties: false required: [name, type, url, method, interval_seconds, timeout_seconds, retry_count, confirmation_attempts, slow_threshold_ms, expected_status_min, expected_status_max, max_redirects, auth_type, execution_mode, consensus_threshold] properties: name: { type: string, minLength: 2, maxLength: 100 } type: { type: string, enum: [http, keyword, json, ping, tcp, udp, dns, ssl, domain, heartbeat] } url: { type: string, maxLength: 2048, description: Public destination URL or hostname. Private and reserved destinations are rejected. } method: { type: string, enum: [GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS] } interval_seconds: { type: integer, minimum: 120, description: Must be one of the intervals enabled by the administrator. } timeout_seconds: { type: integer, minimum: 2, maximum: 60 } retry_count: { type: integer, minimum: 0, maximum: 5 } confirmation_attempts: { type: integer, minimum: 1, maximum: 5 } slow_threshold_ms: { type: integer, minimum: 100, maximum: 60000 } expected_status_min: { type: integer, minimum: 100, maximum: 599 } expected_status_max: { type: integer, minimum: 100, maximum: 599 } follow_redirects: { type: boolean, default: false } max_redirects: { type: integer, minimum: 0 } auth_type: { type: string, enum: [none, basic, bearer] } auth_username: { type: [string, 'null'], maxLength: 255 } auth_secret: { type: [string, 'null'], maxLength: 2048, writeOnly: true } headers: { type: [string, 'null'], description: One HTTP header per line. Reserved forwarding and authorization headers are rejected. } body: { type: [string, 'null'], maxLength: 65535 } group: { type: [string, 'null'], maxLength: 60 } tags: { type: [string, 'null'], description: Comma-separated labels. } execution_mode: { type: string, enum: [local, single_location, multi_location] } locations: type: array items: { type: string, maxLength: 32 } consensus_threshold: { type: integer, minimum: 1, maximum: 5 } keyword: { type: [string, 'null'], maxLength: 500 } keyword_mode: { type: [string, 'null'], enum: [present, absent, null] } keyword_case_sensitive: { type: boolean } json_pointer: { type: [string, 'null'] } json_operator: { type: [string, 'null'], enum: [equals, not_equals, exists, not_exists, null] } json_expected: { type: [string, 'null'] } dns_record_type: { type: [string, 'null'], enum: [A, AAAA, CNAME, MX, TXT, NS, CAA, null] } dns_expected: { type: [string, 'null'] } port: { type: [integer, 'null'], minimum: 1, maximum: 65535 } udp_payload: { type: [string, 'null'], pattern: '^[A-Fa-f0-9]*$' } udp_expected: { type: [string, 'null'], pattern: '^[A-Fa-f0-9]*$' } minimum_tls_days: { type: [integer, 'null'], minimum: 1, maximum: 365 } minimum_domain_days: { type: [integer, 'null'], minimum: 1, maximum: 365 } heartbeat_grace_seconds: { type: [integer, 'null'], minimum: 60, maximum: 604800 } Monitor: type: object required: [id, name, type, url, method, status, interval_seconds, timeout_seconds, created_at, updated_at] properties: id: { type: string } name: { type: string } type: { type: string } url: { type: string } method: { type: string } status: { type: string, enum: [pending, up, degraded, down, paused, maintenance, awaiting_allowlist] } interval_seconds: { type: integer, minimum: 120 } timeout_seconds: { type: integer } last_check_at: { type: [string, 'null'], format: date-time } next_check_at: { type: [string, 'null'], format: date-time } created_at: { type: string, format: date-time } updated_at: { type: string, format: date-time } MonitorResponse: type: object required: [data] properties: data: { $ref: '#/components/schemas/Monitor' } MonitorCollection: type: object required: [data, meta] properties: data: type: array items: { $ref: '#/components/schemas/Monitor' } meta: { $ref: '#/components/schemas/CursorMeta' } Incident: type: object required: [id, monitor, status, severity, summary, opened_at] properties: id: { type: string } monitor: type: object required: [id, name] properties: id: { type: string } name: { type: [string, 'null'] } status: { type: string } severity: { type: string } summary: { type: string } opened_at: { type: string, format: date-time } resolved_at: { type: [string, 'null'], format: date-time } Account: type: object required: [id, name, limits, usage] properties: id: { type: string } name: { type: string } limits: { type: object, additionalProperties: { type: integer } } usage: { type: object, additionalProperties: { type: integer } } CursorMeta: type: object required: [next_cursor, per_page] properties: next_cursor: { type: [string, 'null'] } per_page: { type: integer } ApiError: type: object required: [error] properties: error: type: object required: [code, message] properties: code: { type: string } message: { type: string } responses: Unauthenticated: description: Token is missing, expired, revoked, or no longer belongs to an active workspace member. content: { application/json: { schema: { $ref: '#/components/schemas/ApiError' } } } InsufficientScope: description: Token does not include the required scope. content: { application/json: { schema: { $ref: '#/components/schemas/ApiError' } } } InvalidIdempotencyKey: description: Idempotency-Key is missing or invalid. content: { application/json: { schema: { $ref: '#/components/schemas/ApiError' } } } IdempotencyConflict: description: Key belongs to another payload or the original request is still running. content: { application/json: { schema: { $ref: '#/components/schemas/ApiError' } } } ValidationFailed: description: Request fields failed validation. content: { application/json: { schema: { type: object } } } NotFound: description: Resource does not exist in the token workspace. content: { application/json: { schema: { type: object } } } RateLimited: description: Token or source address exceeded its request limit. headers: Retry-After: { schema: { type: integer } } content: { application/json: { schema: { type: object } } }