{"openapi":"3.1.0","info":{"title":"Heartbeat Kit API","version":"0.1.0","description":"The accountability layer for autonomous systems. Create heartbeats, define escalation policies, and configure notification channels.","contact":{"name":"Heartbeat Kit","url":"https://heartbeatkit.dev"},"license":{"name":"MIT"}},"servers":[{"url":"https://api.heartbeatkit.dev/v1","description":"Production"}],"security":[{"BearerAuth":[]}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"API Key","description":"Pass your Heartbeat Kit API key as a Bearer token"}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","statusCode"],"properties":{"code":{"type":"string","example":"NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"statusCode":{"type":"integer","example":404},"details":{}}},"requestId":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}}},"PolicyStep":{"type":"object","required":["channels"],"properties":{"wait":{"type":"integer","description":"Seconds to wait before executing this step","example":300},"channels":{"type":"array","items":{"type":"string"},"description":"Channel IDs to notify at this step","example":["ch_abc123"]},"message":{"type":"string","description":"Optional message template for this step"}}},"Policy":{"type":"object","required":["id","name","steps","repeatLastStep","createdAt"],"properties":{"id":{"type":"string","example":"pol_abc123"},"name":{"type":"string","example":"Critical Alert"},"steps":{"type":"array","items":{"$ref":"#/components/schemas/PolicyStep"}},"repeatLastStep":{"type":"boolean","description":"If true, the last step repeats until resolved"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"Channel":{"type":"object","required":["id","name","type","config","createdAt"],"properties":{"id":{"type":"string","example":"ch_abc123"},"name":{"type":"string","example":"Ops Slack"},"type":{"type":"string","enum":["email","slack","webhook","pagerduty","sms"]},"config":{"type":"object","description":"Channel-specific configuration (webhook URL, Slack hook, etc.)","additionalProperties":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"Heartbeat":{"type":"object","required":["id","name","mode","status","grace","timezone","createdAt"],"properties":{"id":{"type":"string","example":"hb_abc123"},"name":{"type":"string","example":"Nightly Export"},"mode":{"type":"string","enum":["recurring","oneshot","scheduled"]},"status":{"type":"string","enum":["active","firing","paused","resolved"]},"grace":{"type":"integer","description":"Grace period in seconds after expected check-in","example":60},"timezone":{"type":"string","example":"America/New_York"},"createdAt":{"type":"string","format":"date-time"},"slug":{"type":"string","example":"nightly-export"},"interval":{"type":"integer","description":"Interval in seconds (recurring mode)","example":3600},"schedule":{"type":"string","description":"Cron expression (scheduled mode)","example":"0 2 * * *"},"policy":{"type":"string","description":"Policy ID"},"namespace":{"type":"string","description":"Namespace name"},"nextDeadline":{"type":"string","format":"date-time"},"metadata":{"type":"object","additionalProperties":true,"description":"Arbitrary metadata attached to last check-in"}}}},"responses":{"Unauthorized":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"BadRequest":{"description":"Invalid request parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InternalError":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/heartbeats":{"post":{"operationId":"createHeartbeat","summary":"Create a heartbeat","tags":["Heartbeats"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","mode"],"properties":{"name":{"type":"string"},"mode":{"type":"string","enum":["recurring","oneshot","scheduled"]},"interval":{"type":"integer","description":"Seconds between check-ins"},"grace":{"type":"integer","description":"Grace period in seconds"},"schedule":{"type":"string","description":"Cron expression"},"timezone":{"type":"string"},"slug":{"type":"string"},"policy":{"type":"string","description":"Policy ID"},"namespace":{"type":"string"},"metadata":{"type":"object","additionalProperties":true}}}}}},"responses":{"201":{"description":"Heartbeat created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Heartbeat"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}},"get":{"operationId":"listHeartbeats","summary":"List heartbeats","tags":["Heartbeats"],"parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["active","firing","paused","resolved"]}},{"name":"namespace","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"List of heartbeats","content":{"application/json":{"schema":{"type":"object","properties":{"heartbeats":{"type":"array","items":{"$ref":"#/components/schemas/Heartbeat"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/heartbeats/{idOrSlug}":{"get":{"operationId":"getHeartbeat","summary":"Get a heartbeat","tags":["Heartbeats"],"parameters":[{"name":"idOrSlug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Heartbeat","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Heartbeat"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"deleteHeartbeat","summary":"Delete a heartbeat","tags":["Heartbeats"],"parameters":[{"name":"idOrSlug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Deleted"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/heartbeats/{idOrSlug}/checkin":{"post":{"operationId":"checkin","summary":"Check in a heartbeat","description":"Signal that the monitored process is alive and healthy.","tags":["Heartbeats"],"parameters":[{"name":"idOrSlug","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"healthy":{"type":"boolean","default":true},"metadata":{"type":"object","additionalProperties":true}}}}}},"responses":{"200":{"description":"Check-in recorded","content":{"application/json":{"schema":{"type":"object","required":["status","healthy"],"properties":{"status":{"type":"string"},"healthy":{"type":"boolean"},"nextDeadline":{"type":"string","format":"date-time"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/heartbeats/{idOrSlug}/ack":{"post":{"operationId":"ackHeartbeat","summary":"Acknowledge a firing heartbeat","description":"Acknowledge and silence an active escalation.","tags":["Heartbeats"],"parameters":[{"name":"idOrSlug","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Reason for acknowledgment"}}}}}},"responses":{"200":{"description":"Acknowledged","content":{"application/json":{"schema":{"type":"object","required":["status","resolvedAt","escalationStep"],"properties":{"status":{"type":"string"},"resolvedAt":{"type":"string","format":"date-time"},"escalationStep":{"type":"integer"},"message":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/heartbeats/{idOrSlug}/pause":{"post":{"operationId":"pauseHeartbeat","summary":"Pause a heartbeat","tags":["Heartbeats"],"parameters":[{"name":"idOrSlug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Paused","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"},"pausedAt":{"type":"string","format":"date-time"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/heartbeats/{idOrSlug}/resume":{"post":{"operationId":"resumeHeartbeat","summary":"Resume a paused heartbeat","tags":["Heartbeats"],"parameters":[{"name":"idOrSlug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Resumed","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"},"nextDeadline":{"type":"string","format":"date-time"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/heartbeats/{idOrSlug}/events":{"get":{"operationId":"listHeartbeatEvents","summary":"List heartbeat event history","tags":["Heartbeats"],"parameters":[{"name":"idOrSlug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Event history","content":{"application/json":{"schema":{"type":"object","properties":{"events":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"metadata":{"type":"object","additionalProperties":true},"createdAt":{"type":"string","format":"date-time"}}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/policies":{"post":{"operationId":"createPolicy","summary":"Create an escalation policy","tags":["Policies"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","steps"],"properties":{"name":{"type":"string"},"steps":{"type":"array","items":{"$ref":"#/components/schemas/PolicyStep"}},"repeatLastStep":{"type":"boolean"}}}}}},"responses":{"201":{"description":"Policy created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Policy"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}},"get":{"operationId":"listPolicies","summary":"List escalation policies","tags":["Policies"],"responses":{"200":{"description":"List of policies","content":{"application/json":{"schema":{"type":"object","properties":{"policies":{"type":"array","items":{"$ref":"#/components/schemas/Policy"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/policies/{id}":{"get":{"operationId":"getPolicy","summary":"Get a policy","tags":["Policies"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Policy"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updatePolicy","summary":"Update a policy","tags":["Policies"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"steps":{"type":"array","items":{"$ref":"#/components/schemas/PolicyStep"}},"repeatLastStep":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Updated policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Policy"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"deletePolicy","summary":"Delete a policy","tags":["Policies"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Deleted"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/channels":{"post":{"operationId":"createChannel","summary":"Create a notification channel","tags":["Channels"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","type","config"],"properties":{"name":{"type":"string"},"type":{"type":"string","enum":["email","slack","webhook","pagerduty","sms"]},"config":{"type":"object","additionalProperties":true}}}}}},"responses":{"201":{"description":"Channel created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Channel"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}},"get":{"operationId":"listChannels","summary":"List notification channels","tags":["Channels"],"responses":{"200":{"description":"List of channels","content":{"application/json":{"schema":{"type":"object","properties":{"channels":{"type":"array","items":{"$ref":"#/components/schemas/Channel"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/channels/{id}":{"get":{"operationId":"getChannel","summary":"Get a channel","tags":["Channels"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Channel","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Channel"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateChannel","summary":"Update a channel","tags":["Channels"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string","enum":["email","slack","webhook","pagerduty","sms"]},"config":{"type":"object","additionalProperties":true}}}}}},"responses":{"200":{"description":"Updated channel","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Channel"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"deleteChannel","summary":"Delete a channel","tags":["Channels"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Deleted"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/channels/{id}/test":{"post":{"operationId":"testChannel","summary":"Send a test notification","description":"Fire a test message through the channel to verify configuration.","tags":["Channels"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Test result","content":{"application/json":{"schema":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean"},"message":{"type":"string"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/keys":{"post":{"operationId":"createApiKey","summary":"Create an API key","tags":["Keys"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"},"description":"Permission scopes for this key"},"expiresAt":{"type":"string","format":"date-time","description":"Optional expiry datetime"}}}}}},"responses":{"201":{"description":"API key created (token only shown once)","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"token":{"type":"string","description":"Full key — shown only at creation"},"scopes":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}},"get":{"operationId":"listApiKeys","summary":"List API keys","tags":["Keys"],"responses":{"200":{"description":"List of API keys","content":{"application/json":{"schema":{"type":"object","properties":{"keys":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"prefix":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time"},"lastUsedAt":{"type":"string","format":"date-time"}}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/keys/{id}":{"delete":{"operationId":"deleteApiKey","summary":"Revoke an API key","tags":["Keys"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Revoked"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}}},"tags":[{"name":"Heartbeats","description":"Create and monitor heartbeats"},{"name":"Policies","description":"Escalation policies"},{"name":"Channels","description":"Notification channels"},{"name":"Keys","description":"API key management"}]}