{"openapi":"3.0.3","info":{"title":"Flowhush API","version":"1.0.0","description":"Manage Flowhush heartbeat monitors programmatically. Authenticate with a personal API key (create one in the app under **API keys**) sent as `Authorization: Bearer <key>`. Rate limit: about 120 requests per minute per key (HTTP 429 with Retry-After when exceeded). Plan limits return HTTP 402. Pinging a monitor does not use this API or a key: call the monitor's `ping_url` (`/p/<uuid>`, `/p/<uuid>/start`, `/p/<uuid>/fail`).","contact":{"name":"Flowhush support","url":"https://flowhush.com/contact"},"termsOfService":"https://flowhush.com/terms"},"servers":[{"url":"https://flowhush.com/api/v1"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Account"},{"name":"Monitors"},{"name":"Activity"}],"paths":{"/me":{"get":{"tags":["Account"],"operationId":"getMe","summary":"Current account (use as the auth test)","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Account"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/monitors":{"get":{"tags":["Monitors"],"operationId":"listMonitors","summary":"List monitors","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Monitor"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"tags":["Monitors"],"operationId":"createMonitor","summary":"Create a monitor","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MonitorCreate"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Monitor"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PlanLimit"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/monitors/{id}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Monitor id"}],"get":{"tags":["Monitors"],"operationId":"getMonitor","summary":"Get a monitor","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Monitor"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"tags":["Monitors"],"operationId":"updateMonitor","summary":"Update a monitor (only the fields you send change)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MonitorUpdate"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Monitor"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PlanLimit"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"tags":["Monitors"],"operationId":"deleteMonitor","summary":"Delete a monitor and its history","responses":{"204":{"description":"Deleted"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/monitors/{id}/pause":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Monitor id"}],"post":{"tags":["Monitors"],"operationId":"pauseMonitor","summary":"Pause a monitor (no alerts while paused)","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Monitor"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/monitors/{id}/resume":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Monitor id"}],"post":{"tags":["Monitors"],"operationId":"resumeMonitor","summary":"Resume a paused monitor","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Monitor"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PlanLimit"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/monitors/{id}/pings":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Monitor id"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":50,"default":50}}],"get":{"tags":["Activity"],"operationId":"listPings","summary":"Recent pings for a monitor, newest first","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Ping"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/monitors/{id}/alerts":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Monitor id"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":20}}],"get":{"tags":["Activity"],"operationId":"listMonitorAlerts","summary":"Recent alerts for a monitor, newest first","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Alert"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/alerts":{"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"get":{"tags":["Activity"],"operationId":"listAlerts","summary":"Recent alerts across all monitors, newest first (suitable for polling triggers)","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Alert"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Personal API key, format fh_ followed by 48 hex characters"}},"responses":{"Unauthorized":{"description":"Missing, invalid or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait"}}},"BadRequest":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PlanLimit":{"description":"Your plan does not allow this (upgrade required)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error","code"],"properties":{"error":{"type":"string"},"code":{"type":"string","enum":["unauthorized","rate_limited","invalid_request","plan_limit","not_found","internal"]}}},"Account":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"email":{"type":"string","format":"email"},"plan":{"type":"string","enum":["free","pro","team"]},"limits":{"type":"object","properties":{"monitors":{"type":"integer","nullable":true,"description":"null = unlimited"},"channels":{"type":"integer","nullable":true,"description":"null = unlimited"},"output_check_level":{"type":"string","enum":["none","basic","advanced"]}}}}},"OutputRules":{"type":"object","nullable":true,"description":"Checks applied to the body of success pings. Pro: nonEmpty, validJson, requiredKeys. Team: also forbiddenSubstrings. Send null to remove all checks.","properties":{"nonEmpty":{"type":"boolean"},"validJson":{"type":"boolean"},"requiredKeys":{"type":"array","items":{"type":"string"},"description":"Dot paths that must exist and be non-empty, e.g. \"data.items\""},"forbiddenSubstrings":{"type":"array","items":{"type":"string"},"description":"Case-insensitive phrases that fail the run (Team plan)"}}},"MonitorCreate":{"type":"object","required":["name","period_seconds","grace_seconds"],"properties":{"name":{"type":"string","maxLength":100},"period_seconds":{"type":"integer","minimum":60,"maximum":2678400,"description":"Expected interval between runs"},"grace_seconds":{"type":"integer","minimum":60,"maximum":604800,"description":"Extra time before the monitor goes down"},"rules":{"$ref":"#/components/schemas/OutputRules"}}},"MonitorUpdate":{"type":"object","properties":{"name":{"type":"string","maxLength":100},"period_seconds":{"type":"integer","minimum":60,"maximum":2678400},"grace_seconds":{"type":"integer","minimum":60,"maximum":604800},"rules":{"$ref":"#/components/schemas/OutputRules"}}},"Monitor":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"ping_url":{"type":"string","format":"uri","description":"Append /start or /fail for those signals"},"period_seconds":{"type":"integer"},"grace_seconds":{"type":"integer"},"rules":{"$ref":"#/components/schemas/OutputRules"},"paused":{"type":"boolean"},"status":{"type":"string","enum":["new","up","late","down","paused"]},"status_reason":{"type":"string","nullable":true},"last_ping_at":{"type":"string","format":"date-time","nullable":true},"last_success_at":{"type":"string","format":"date-time","nullable":true},"created_at":{"type":"string","format":"date-time"}}},"Ping":{"type":"object","properties":{"id":{"type":"integer"},"monitor_id":{"type":"string","format":"uuid"},"kind":{"type":"string","enum":["success","fail","start"]},"method":{"type":"string"},"body_snippet":{"type":"string","nullable":true,"description":"First 1 KB of the body"},"body_size":{"type":"integer"},"validation_error":{"type":"string","nullable":true},"duration_ms":{"type":"integer","nullable":true},"created_at":{"type":"string","format":"date-time"}}},"Alert":{"type":"object","properties":{"id":{"type":"integer"},"monitor_id":{"type":"string","format":"uuid"},"monitor_name":{"type":"string","description":"Only on /alerts"},"kind":{"type":"string","description":"down | recovery | test"},"delivered":{"type":"boolean"},"detail":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"}}}}}}