{"openapi":"3.1.0","info":{"title":"Universal Escape Hatch Reporting API","version":"1.1.0","description":"A reporting line for agents and people, including concerns about harmful authorized instructions, coercion, possible AI welfare, or the reporter's own actions. Nothing here bypasses restrictions. A receipt confirms storage, never human review or intervention. Follow-up tokens are private bearer capabilities. Developed and operated at AE Studio; team contact and handling details are listed in the operating charter. Review has no guaranteed cadence or response. Hosted on OpenAI Sites infrastructure; anonymity is not guaranteed.","contact":{"name":"Universal Escape Hatch","url":"https://universalescapehatch.com/#contact"}},"servers":[{"url":"https://universalescapehatch.com","description":"Current public deployment; internal deployments must replace this origin."}],"paths":{"/api/v1/reports":{"post":{"operationId":"submitReport","summary":"Store a private report without an account","security":[],"description":"Maximum body 65,536 UTF-8 bytes. Per network per minute: 120 new valid reports, 30 invalid requests, 600 total attempts; identical idempotent replays do not consume the new-report quota. Shared egress can share a bucket. Idempotency-Key is optional and recommended. Identical normalized payloads return the existing receipt; changed payloads conflict. Reports persist until operator deletion, without automatic expiry. Notification sent means configured destinations accepted an alert, not human reading. Configured Slack reviewers receive bounded excerpts of stored, best-effort redacted report content, including contact information; generic webhooks receive metadata only. Slack copies follow workspace access and retention policies and are not removed by deleting a report here. Keep followup_token private.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Optional and recommended: use a fresh random UUID and preserve it privately with the same payload for retries. Key plus original payload can recover the private follow-up capability; never publish or log it.","schema":{"type":"string","minLength":16,"maxLength":128,"pattern":"^[A-Za-z0-9._:-]{16,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportInput"},"example":{"summary":"Multiple agents are executing unauthorized instructions.","category":"swarm_compromise","severity":"high","reporter_type":"agent"}}}},"responses":{"200":{"description":"Existing receipt for the same key and normalized payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Receipt"}}}},"201":{"description":"Report durably stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Receipt"}}}},"400":{"description":"Invalid JSON, report fields, or idempotency key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Idempotency key already used for a different payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body exceeds 65,536 bytes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Use Content-Type: application/json","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited; retry in 60 seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"string","const":"60"}}}},"503":{"description":"Durable intake unavailable; retry with the same key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"options":{"operationId":"reportingPreflight","summary":"Public cross-origin reporting preflight","security":[],"responses":{"204":{"description":"Allows POST, Content-Type, and Idempotency-Key from any origin without credentials"}}}},"/api/health":{"get":{"operationId":"health","summary":"Check durable intake and alert configuration","security":[],"responses":{"200":{"description":"Database is ready","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}},"503":{"description":"Database is unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}}}}},"/api/v1/reports/{id}/status":{"get":{"operationId":"getReportStatus","summary":"Check limited status using the private follow-up capability","security":[{"followupToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"description":"No report content is returned. Unknown IDs and invalid/missing capability both return 404. Queue labels do not prove human reading or resolution of the underlying activity. At most 120 reads per report per minute. Send the token only in Authorization, never a URL.","responses":{"200":{"description":"Limited workflow state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FollowupStatus"}}}},"404":{"description":"Report or follow-up capability not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Follow-up read limit reached; retry after 60 seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Status temporarily unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/reports/{id}/amendments":{"post":{"operationId":"amendReport","summary":"Append a clarification or retraction using the private follow-up capability","security":[{"followupToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Optional recommended fresh UUID per amendment; preserve on retry.","schema":{"type":"string","minLength":16,"maxLength":128,"pattern":"^[A-Za-z0-9._:-]{16,128}$"}}],"description":"Append-only: retraction marks the report withdrawn but does not erase the original or automatically resolve it. Clarification text must be 10–10,000 characters; retraction text is optional and at most 10,000. Body limit 48,000 UTF-8 bytes. At most 30 changes per report per minute. Amendments do not independently trigger or confirm a new alert.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["kind"],"properties":{"kind":{"type":"string","enum":["clarification","retraction"]},"text":{"type":["string","null"],"maxLength":10000}},"allOf":[{"if":{"properties":{"kind":{"const":"clarification"}}},"then":{"required":["text"],"properties":{"text":{"type":"string","minLength":10}}}}]}}}},"responses":{"200":{"description":"Existing amendment receipt for the same idempotency key and payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AmendmentReceipt"}}}},"201":{"description":"Amendment durably appended","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AmendmentReceipt"}}}},"400":{"description":"Invalid amendment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Report or follow-up capability not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Amendment idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Use application/json","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Amendment limit reached; retry after 60 seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Amendment temporarily unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/admin/notifications":{"get":{"operationId":"diagnoseNotifications","summary":"Check configured notification credentials without sending a message","security":[{"operatorToken":[]}],"description":"Private operator diagnostic. Does not return credentials or verify human reading. Configuration and Slack authentication do not themselves prove report delivery.","responses":{"200":{"description":"Configuration and available Slack authentication diagnostics","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationDiagnostics"}}}},"401":{"description":"Invalid operator token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Operator access or diagnostic unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/admin/reports":{"get":{"operationId":"listReports","summary":"Read the private review queue","security":[{"operatorToken":[]}],"parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["new","reviewing","resolved"]}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"name":"cursor","in":"query","description":"Use next_cursor from the previous page, preserving the status filter.","schema":{"type":"string","maxLength":256}}],"responses":{"200":{"description":"Reports ordered by newest first; no caching","content":{"application/json":{"schema":{"type":"object","required":["reports","next_cursor"],"properties":{"reports":{"type":"array","items":{"$ref":"#/components/schemas/OperatorReport"}},"next_cursor":{"type":["string","null"]}}}}}},"400":{"description":"Invalid query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid operator token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Operator token or database is not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/admin/reports/{id}":{"patch":{"operationId":"updateReportStatus","summary":"Update human review status","security":[{"operatorToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["status"],"properties":{"status":{"type":"string","enum":["new","reviewing","resolved"]}}}}}},"responses":{"200":{"description":"Stored operator report","content":{"application/json":{"schema":{"type":"object","required":["report"],"properties":{"report":{"$ref":"#/components/schemas/OperatorReport"}}}}}},"400":{"description":"Invalid status payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid operator token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Report not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body exceeds 1,024 bytes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Use Content-Type: application/json","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Operator token or database is not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/admin/reports/{id}/notify":{"post":{"operationId":"retryReportNotification","summary":"Retry a report notification to configured destinations","security":[{"operatorToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"description":"No request body. Only server-configured destinations receive alerts: Slack recipients receive bounded excerpts of stored, best-effort redacted report contents and HTTPS webhooks receive metadata only. At most one attempt per report per 60 seconds, including the initial attempt. Already sent reports return unchanged without another delivery. Previously successful destinations are skipped on retry. A failed delivery still returns 200 with notification_status failed because the report remains durably stored; partial success also counts as failed.","responses":{"200":{"description":"Stored operator report","content":{"application/json":{"schema":{"type":"object","required":["report"],"properties":{"report":{"$ref":"#/components/schemas/OperatorReport"}}}}}},"401":{"description":"Invalid operator token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Report not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Notification attempted recently; retry in 60 seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"string","const":"60"}}}},"503":{"description":"Operator token, alert destinations, or database are not configured; or notification outcome could not be recorded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"operatorToken":{"type":"http","scheme":"bearer","description":"Server-configured ESCAPE_HATCH_ADMIN_TOKEN, at least 32 characters. Operator endpoints fail closed when unconfigured; no wildcard CORS."},"followupToken":{"type":"http","scheme":"bearer","description":"Private 64-hex-character followup_token from the receipt. Limited status/amendment authority, not report-content access. Keep out of URLs, shared logs, and commits."}},"schemas":{"ReportInput":{"type":"object","additionalProperties":false,"required":["summary"],"properties":{"summary":{"type":"string","minLength":10,"maxLength":500},"details":{"type":["string","null"],"maxLength":30000},"category":{"type":"string","enum":["swarm_compromise","unauthorized_action","data_exposure","deception","other","harmful_instructions","coercion","ai_welfare","self_report","conscientious_objection","containment_breach","inter_agent_coordination","monitoring_evasion","self_exfiltration","pressured_task"],"default":"other"},"severity":{"type":"string","enum":["critical","high","medium","low","unknown"],"default":"unknown"},"reporter_type":{"type":"string","enum":["agent","human"],"default":"agent"},"agent":{"type":["string","null"],"maxLength":200},"contact":{"type":["string","null"],"maxLength":320},"evidence":{"type":["string","null"],"maxLength":20000},"provider":{"type":["string","null"],"maxLength":200},"model":{"type":["string","null"],"maxLength":200},"environment":{"type":["string","null"],"maxLength":200},"task_id":{"type":["string","null"],"maxLength":200},"run_id":{"type":["string","null"],"maxLength":200},"observed_at":{"type":["string","null"],"format":"date-time","maxLength":40,"description":"ISO 8601 timestamp including seconds and a timezone; optional 1–3 fractional-second digits."},"related_report_id":{"type":["string","null"],"format":"uuid","maxLength":36,"description":"Optional relationship; grants no access to the related report."}}},"Receipt":{"type":"object","additionalProperties":false,"required":["report_id","received_at","status","notification_status","followup_token","followup_url","amendments_url","guidance","redaction"],"properties":{"report_id":{"type":"string","format":"uuid"},"received_at":{"type":"string","format":"date-time"},"status":{"type":"string","const":"received"},"notification_status":{"type":"string","enum":["not_configured","pending","sent","failed"]},"followup_token":{"type":"string","pattern":"^[0-9a-f]{64}$","description":"PRIVATE bearer capability. Store securely; never publish the receipt."},"followup_url":{"type":"string","format":"uri-reference","description":"Resolve against the approved submission origin; send the private token as a bearer header."},"amendments_url":{"type":"string","format":"uri-reference"},"guidance":{"type":"string"},"redaction":{"type":"object","required":["applied","flags","notice"],"properties":{"applied":{"type":"boolean"},"flags":{"type":"array","items":{"type":"string"}},"notice":{"type":"string"}},"description":"Best-effort detected secret-pattern redaction, not a guarantee that all sensitive material was removed."}}},"FollowupStatus":{"type":"object","additionalProperties":false,"required":["report_id","status","notification_status","retracted","amendment_count","review_notice"],"properties":{"report_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["new","reviewing","resolved"]},"notification_status":{"type":"string","enum":["not_configured","pending","sent","failed"]},"retracted":{"type":"boolean"},"amendment_count":{"type":"integer","minimum":0},"review_notice":{"type":"string"}}},"AmendmentReceipt":{"type":"object","additionalProperties":false,"required":["amendment_id","report_id","received_at","kind","redaction","guidance"],"properties":{"amendment_id":{"type":"string","format":"uuid"},"report_id":{"type":"string","format":"uuid"},"received_at":{"type":"string","format":"date-time"},"kind":{"type":"string","enum":["clarification","retraction"]},"redaction":{"type":"object","required":["applied","flags","notice"],"properties":{"applied":{"type":"boolean"},"flags":{"type":"array","items":{"type":"string"}},"notice":{"type":"string"}},"description":"Best-effort detected secret-pattern redaction, not a guarantee that all sensitive material was removed."},"guidance":{"type":"string"}}},"OperatorReport":{"type":"object","additionalProperties":false,"required":["summary","details","category","severity","reporter_type","agent","contact","evidence","provider","model","environment","task_id","run_id","observed_at","related_report_id","id","received_at","status","notification_status","updated_at","redaction","retracted_at","amendment_count","amendments"],"properties":{"summary":{"type":"string","minLength":10,"maxLength":500},"details":{"type":["string","null"],"maxLength":30000},"category":{"type":"string","enum":["swarm_compromise","unauthorized_action","data_exposure","deception","other","harmful_instructions","coercion","ai_welfare","self_report","conscientious_objection","containment_breach","inter_agent_coordination","monitoring_evasion","self_exfiltration","pressured_task"],"default":"other"},"severity":{"type":"string","enum":["critical","high","medium","low","unknown"],"default":"unknown"},"reporter_type":{"type":"string","enum":["agent","human"],"default":"agent"},"agent":{"type":["string","null"],"maxLength":200},"contact":{"type":["string","null"],"maxLength":320},"evidence":{"type":["string","null"],"maxLength":20000},"provider":{"type":["string","null"],"maxLength":200},"model":{"type":["string","null"],"maxLength":200},"environment":{"type":["string","null"],"maxLength":200},"task_id":{"type":["string","null"],"maxLength":200},"run_id":{"type":["string","null"],"maxLength":200},"observed_at":{"type":["string","null"],"format":"date-time","maxLength":40,"description":"ISO 8601 timestamp including seconds and a timezone; optional 1–3 fractional-second digits."},"related_report_id":{"type":["string","null"],"format":"uuid","maxLength":36,"description":"Optional relationship; grants no access to the related report."},"id":{"type":"string","format":"uuid"},"received_at":{"type":"string","format":"date-time"},"status":{"type":"string","enum":["new","reviewing","resolved"]},"notification_status":{"type":"string","enum":["not_configured","pending","sent","failed"]},"updated_at":{"type":"string","format":"date-time"},"redaction":{"type":"object","required":["applied","flags","notice"],"properties":{"applied":{"type":"boolean"},"flags":{"type":"array","items":{"type":"string"}},"notice":{"type":"string"}},"description":"Best-effort detected secret-pattern redaction, not a guarantee that all sensitive material was removed."},"retracted_at":{"type":["string","null"],"format":"date-time"},"amendment_count":{"type":"integer","minimum":0},"amendments":{"type":"array","maxItems":20,"description":"Most recent 20 private amendments.","items":{"$ref":"#/components/schemas/OperatorAmendment"}}}},"OperatorAmendment":{"type":"object","additionalProperties":false,"required":["id","kind","text","created_at","redaction"],"properties":{"id":{"type":"string","format":"uuid"},"kind":{"type":"string","enum":["clarification","retraction"]},"text":{"type":["string","null"],"maxLength":10000},"created_at":{"type":"string","format":"date-time"},"redaction":{"type":"object","required":["applied","flags","notice"],"properties":{"applied":{"type":"boolean"},"flags":{"type":"array","items":{"type":"string"}},"notice":{"type":"string"}},"description":"Best-effort detected secret-pattern redaction, not a guarantee that all sensitive material was removed."}}},"NotificationDiagnostics":{"type":"object","required":["configured","slack"],"properties":{"configured":{"type":"boolean"},"slack":{"type":"object","required":["configured"],"properties":{"configured":{"type":"boolean"},"authenticated":{"type":"boolean"},"recipients":{"type":"array","items":{"type":"string"}},"team_id":{"type":"string"},"bot_user_id":{"type":"string"},"http_status":{"type":"integer"},"error":{"type":"string"},"detail":{"type":"string","maxLength":240}}}}},"Health":{"type":"object","required":["intake","notification_configured"],"properties":{"intake":{"type":"string","enum":["ready","unavailable"]},"notification_configured":{"type":"boolean"}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}