{"openapi":"3.0.0","info":{"title":"Secret Vault API","version":"0.28.0","description":"Self-hosted secret management on Cloudflare Workers. Secrets are encrypted at rest with AES-256-GCM in a D1 database. Two auth paths via Cloudflare Access: interactive sessions (IdP, optionally with hardware keys) for humans, and registered service tokens with named identities and scoped permissions (read/write/delete) for CI pipelines and other Workers. Every operation is audit-logged with identity, action, and IP."},"tags":[{"name":"Secrets","description":"Store, retrieve, update, and delete encrypted secrets. Supports bulk export."},{"name":"Tokens","description":"Register and manage service tokens. Each token gets a name, scoped permissions, and usage tracking. Admin only."},{"name":"Users","description":"User management with RBAC role assignment (admin only)"},{"name":"Roles","description":"Role definitions with scoped permissions (admin only)"},{"name":"Flags","description":"Feature flags backed by KV (plaintext, not encrypted)"},{"name":"Admin","description":"Authentication status and audit log access."},{"name":"Public","description":"Unauthenticated endpoints."}],"servers":[{"url":"https://secrets.homeflare.dev"}],"components":{"schemas":{"Health":{"type":"object","properties":{"status":{"type":"string","example":"ok"},"database":{"type":"string","example":"ok"},"kv":{"type":"string","example":"ok"},"version":{"type":"string","example":"0.20.0"},"region":{"type":"string","example":"ATL"},"maintenance":{"type":"boolean","example":false},"read_only":{"type":"boolean","example":false},"timestamp":{"type":"string","example":"2026-03-29T04:30:00.000Z"}},"required":["status","database","kv","version","region","maintenance","read_only","timestamp"]},"Whoami":{"type":"object","properties":{"method":{"type":"string","example":"interactive"},"identity":{"type":"string","example":"you@example.com"},"name":{"type":"string","example":"Tim Schneider"},"role":{"type":"string","example":"admin"},"scopes":{"type":"array","items":{"type":"string"},"example":["*"]},"e2e":{"type":"boolean","example":true,"description":"Age public key registered"},"deviceBound":{"type":"boolean","example":true,"description":"ZT CA fingerprint registered"},"policies":{"type":"number","example":2,"description":"Number of policy rules on role"},"lastLogin":{"type":"string","nullable":true,"example":"2026-03-29 20:42:58"},"totalSecrets":{"type":"number","example":15,"description":"Total secrets in vault"},"warp":{"type":"object","properties":{"connected":{"type":"boolean","example":true},"ztVerified":{"type":"boolean","example":true},"deviceId":{"type":"string","example":"device-abc-123"}},"required":["connected","ztVerified"]}},"required":["method","identity","name","role","scopes","e2e","deviceBound","policies","lastLogin","totalSecrets"]},"AuditEntry":{"type":"object","properties":{"id":{"type":"number"},"timestamp":{"type":"string"},"method":{"type":"string"},"identity":{"type":"string"},"action":{"type":"string"},"secret_key":{"type":"string","nullable":true},"ip":{"type":"string","nullable":true},"user_agent":{"type":"string","nullable":true},"request_id":{"type":"string","nullable":true},"prev_hash":{"type":"string","nullable":true}},"required":["id","timestamp","method","identity","action","secret_key","ip","user_agent","request_id","prev_hash"]},"Error":{"type":"object","properties":{"error":{"type":"string","example":"Secret not found"}},"required":["error"]},"ConsumerEntry":{"type":"object","properties":{"identity":{"type":"string","example":"alice@co.com"},"user_agent":{"type":"string","nullable":true,"example":"hfs-cli/0.23.1"},"method":{"type":"string","example":"interactive"},"access_count":{"type":"number","example":42},"last_accessed":{"type":"string","example":"2026-03-28T12:00:00Z"},"first_accessed":{"type":"string","example":"2026-01-01T00:00:00Z"}},"required":["identity","user_agent","method","access_count","last_accessed","first_accessed"]},"User":{"type":"object","properties":{"email":{"type":"string","example":"you@example.com"},"name":{"type":"string","example":"Tim Schneider"},"role":{"type":"string","example":"admin"},"enabled":{"type":"number","example":1},"age_public_key":{"type":"string","nullable":true,"example":"age1..."},"last_login_at":{"type":"string","nullable":true},"created_by":{"type":"string"},"created_at":{"type":"string"},"updated_by":{"type":"string"},"updated_at":{"type":"string"}},"required":["email","name","role","enabled","age_public_key","last_login_at","created_by","created_at","updated_by","updated_at"]},"Role":{"type":"object","properties":{"name":{"type":"string","example":"operator"},"scopes":{"type":"string","example":"read,write"},"allowed_tags":{"type":"string","example":"ci,staging"},"description":{"type":"string"},"created_by":{"type":"string"},"created_at":{"type":"string"},"updated_by":{"type":"string"},"updated_at":{"type":"string"}},"required":["name","scopes","allowed_tags","description","created_by","created_at","updated_by","updated_at"]},"Policy":{"type":"object","properties":{"id":{"type":"number"},"role":{"type":"string"},"scopes":{"type":"string","example":"read,write"},"tags":{"type":"string","example":"customer-zero"},"description":{"type":"string"},"created_by":{"type":"string"},"created_at":{"type":"string"}},"required":["id","role","scopes","tags","description","created_by","created_at"]},"ServiceToken":{"type":"object","properties":{"client_id":{"type":"string","example":"abc123.access"},"name":{"type":"string","example":"github-actions"},"description":{"type":"string"},"scopes":{"type":"string","example":"read,write"},"role":{"type":"string","nullable":true,"example":"operator"},"created_by":{"type":"string"},"created_at":{"type":"string"},"updated_at":{"type":"string"},"last_used_at":{"type":"string","nullable":true}},"required":["client_id","name","description","scopes","role","created_by","created_at","updated_at","last_used_at"]},"TokenCreateResponse":{"type":"object","properties":{"ok":{"type":"boolean"},"client_id":{"type":"string"},"name":{"type":"string"},"scopes":{"type":"string"}},"required":["ok","client_id","name","scopes"]},"TokenDeleteResponse":{"type":"object","properties":{"ok":{"type":"boolean"},"revoked":{"type":"string"}},"required":["ok","revoked"]},"Flag":{"type":"object","properties":{"key":{"type":"string"},"value":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"object","additionalProperties":{"nullable":true}}]},"type":{"type":"string","enum":["string","number","boolean","json"]},"description":{"type":"string"},"updated_by":{"type":"string"},"updated_at":{"type":"string"}},"required":["key","value","type","description","updated_by","updated_at"]},"SecretExportItem":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string","nullable":true},"error":{"type":"string"},"description":{"type":"string"},"tags":{"type":"string"},"expires_at":{"type":"string","nullable":true},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["key","value","description","tags","expires_at","created_at","updated_at"]},"SecretImportResponse":{"type":"object","properties":{"ok":{"type":"boolean"},"imported":{"type":"number"},"skipped":{"type":"number"}},"required":["ok","imported","skipped"]},"SecretCreateResponse":{"type":"object","properties":{"ok":{"type":"boolean"},"key":{"type":"string"}},"required":["ok","key"]},"SecretListItem":{"type":"object","properties":{"key":{"type":"string","example":"api-key"},"description":{"type":"string","example":"Anthropic API key"},"tags":{"type":"string","example":"production,ci"},"expires_at":{"type":"string","nullable":true,"example":"2026-06-01 00:00:00"},"created_by":{"type":"string","example":"you@example.com"},"updated_by":{"type":"string","example":"you@example.com"},"created_at":{"type":"string","example":"2026-03-28 12:00:00"},"updated_at":{"type":"string","example":"2026-03-28 12:00:00"}},"required":["key","description","tags","expires_at","created_by","updated_by","created_at","updated_at"]},"SecretEntry":{"allOf":[{"$ref":"#/components/schemas/SecretListItem"},{"type":"object","properties":{"value":{"type":"string","example":"sk-ant-..."}},"required":["value"]}]},"SecretDeleteResponse":{"type":"object","properties":{"ok":{"type":"boolean"},"deleted":{"type":"string"}},"required":["ok","deleted"]}},"parameters":{}},"paths":{"/health":{"get":{"tags":["Public"],"summary":"Health check (no auth required)","responses":{"200":{"description":"Service is healthy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}}}}},"/whoami":{"get":{"tags":["Admin"],"summary":"Check authentication status","responses":{"200":{"description":"Auth info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Whoami"}}}}}}},"/audit":{"get":{"tags":["Admin"],"summary":"View audit log (admin only)","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":500,"default":50,"example":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"default":0,"example":0},"required":false,"name":"offset","in":"query"},{"schema":{"type":"string","example":"you@example.com"},"required":false,"name":"identity","in":"query"},{"schema":{"type":"string","example":"get"},"required":false,"name":"action","in":"query"},{"schema":{"type":"string","example":"api-key"},"required":false,"name":"key","in":"query"},{"schema":{"type":"string","example":"interactive"},"required":false,"name":"method","in":"query"},{"schema":{"type":"string","example":"2026-03-01"},"required":false,"name":"from","in":"query"},{"schema":{"type":"string","example":"2026-03-31"},"required":false,"name":"to","in":"query"}],"responses":{"200":{"description":"Audit entries","content":{"application/json":{"schema":{"type":"object","properties":{"entries":{"type":"array","items":{"$ref":"#/components/schemas/AuditEntry"}}},"required":["entries"]}}}},"403":{"description":"Owner only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/audit/consumers/{key}":{"get":{"tags":["Admin"],"summary":"List consumers of a specific secret (admin only)","parameters":[{"schema":{"type":"string","example":"stripe-api-key"},"required":true,"name":"key","in":"path"},{"schema":{"type":"string","example":"2026-03-01"},"required":false,"name":"from","in":"query"},{"schema":{"type":"string","example":"2026-03-31"},"required":false,"name":"to","in":"query"}],"responses":{"200":{"description":"Unique consumers of the secret, ordered by access count","content":{"application/json":{"schema":{"type":"object","properties":{"consumers":{"type":"array","items":{"$ref":"#/components/schemas/ConsumerEntry"}}},"required":["consumers"]}}}},"403":{"description":"Admin only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/admin/re-encrypt":{"post":{"tags":["Admin"],"summary":"Re-encrypt legacy secrets with envelope encryption (admin only)","responses":{"200":{"description":"Re-encryption results","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"migrated":{"type":"number"},"skipped":{"type":"number"}},"required":["ok","migrated","skipped"]}}}},"403":{"description":"Insufficient scope or owner only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/admin/rotate-key":{"post":{"tags":["Admin"],"summary":"Re-wrap all DEKs with a new master key (admin only)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"new_key":{"type":"string","pattern":"^[0-9a-fA-F]{64}$"}},"required":["new_key"]}}}},"responses":{"200":{"description":"Key rotation results","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"rotated":{"type":"number"},"versions_rotated":{"type":"number"},"legacy":{"type":"number"}},"required":["ok","rotated","versions_rotated","legacy"]}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or owner only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/users":{"get":{"tags":["Users"],"summary":"List all users","responses":{"200":{"description":"All registered users","content":{"application/json":{"schema":{"type":"object","properties":{"users":{"type":"array","items":{"$ref":"#/components/schemas/User"}}},"required":["users"]}}}},"403":{"description":"Insufficient scope or owner only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/users/{email}":{"put":{"tags":["Users"],"summary":"Add or update a user","parameters":[{"schema":{"type":"string","minLength":1,"example":"you@example.com"},"required":true,"name":"email","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","maxLength":256,"format":"email"},"name":{"type":"string","maxLength":256,"default":""},"role":{"type":"string","minLength":1,"maxLength":64}},"required":["email","role"]}}}},"responses":{"201":{"description":"User added","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"email":{"type":"string"}},"required":["ok","email"]}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or owner only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Users"],"summary":"Update a user (partial)","parameters":[{"schema":{"type":"string","minLength":1,"example":"you@example.com"},"required":true,"name":"email","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":256},"role":{"type":"string","maxLength":64},"enabled":{"type":"boolean"},"age_public_key":{"type":"string","nullable":true,"maxLength":256},"zt_fingerprint":{"type":"string","maxLength":128}}}}}},"responses":{"200":{"description":"User updated","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"email":{"type":"string"}},"required":["ok","email"]}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or owner only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Users"],"summary":"Remove a user","parameters":[{"schema":{"type":"string","minLength":1,"example":"you@example.com"},"required":true,"name":"email","in":"path"}],"responses":{"200":{"description":"User removed","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"deleted":{"type":"string"}},"required":["ok","deleted"]}}}},"403":{"description":"Insufficient scope or owner only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/recipients":{"get":{"tags":["Users"],"summary":"List users with age public keys (for e2e encryption)","parameters":[{"schema":{"type":"string","example":"production"},"required":false,"name":"tags","in":"query"}],"responses":{"200":{"description":"Users with registered age public keys who can access the given tags","content":{"application/json":{"schema":{"type":"object","properties":{"recipients":{"type":"array","items":{"type":"object","properties":{"email":{"type":"string"},"name":{"type":"string"},"age_public_key":{"type":"string"}},"required":["email","name","age_public_key"]}}},"required":["recipients"]}}}},"403":{"description":"Insufficient scope or owner only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/roles":{"get":{"tags":["Roles"],"summary":"List all roles","responses":{"200":{"description":"All roles with scopes","content":{"application/json":{"schema":{"type":"object","properties":{"roles":{"type":"array","items":{"$ref":"#/components/schemas/Role"}}},"required":["roles"]}}}},"403":{"description":"Insufficient scope or owner only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/roles/{name}":{"put":{"tags":["Roles"],"summary":"Create or update a role","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"example":"operator"},"required":true,"name":"name","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z][a-z0-9_-]*$"},"scopes":{"type":"string"},"allowed_tags":{"type":"string","maxLength":500,"default":""},"description":{"type":"string","maxLength":500,"default":""}},"required":["name","scopes"]}}}},"responses":{"201":{"description":"Role created or updated","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"name":{"type":"string"}},"required":["ok","name"]}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or owner only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Roles"],"summary":"Update a role (partial)","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"example":"operator"},"required":true,"name":"name","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"scopes":{"type":"string"},"allowed_tags":{"type":"string","maxLength":500},"description":{"type":"string","maxLength":500}}}}}},"responses":{"200":{"description":"Role updated","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"name":{"type":"string"}},"required":["ok","name"]}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or owner only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Roles"],"summary":"Delete a role","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"example":"operator"},"required":true,"name":"name","in":"path"}],"responses":{"200":{"description":"Role deleted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"deleted":{"type":"string"}},"required":["ok","deleted"]}}}},"400":{"description":"Role in use","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or owner only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/roles/{name}/policies":{"get":{"tags":["Roles"],"summary":"List policies for a role","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"example":"operator"},"required":true,"name":"name","in":"path"}],"responses":{"200":{"description":"Policies for the role","content":{"application/json":{"schema":{"type":"object","properties":{"policies":{"type":"array","items":{"$ref":"#/components/schemas/Policy"}}},"required":["policies"]}}}},"403":{"description":"Insufficient scope or owner only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"tags":["Roles"],"summary":"Replace all policies for a role","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"example":"operator"},"required":true,"name":"name","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"policies":{"type":"array","items":{"type":"object","properties":{"scopes":{"type":"string"},"tags":{"type":"string","maxLength":500,"default":""},"description":{"type":"string","maxLength":500,"default":""}},"required":["scopes"]},"minItems":1}},"required":["policies"]}}}},"responses":{"200":{"description":"Policies replaced","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"count":{"type":"number"}},"required":["ok","count"]}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or owner only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/tokens":{"get":{"tags":["Tokens"],"summary":"List registered service tokens","responses":{"200":{"description":"List of service tokens","content":{"application/json":{"schema":{"type":"object","properties":{"tokens":{"type":"array","items":{"$ref":"#/components/schemas/ServiceToken"}}},"required":["tokens"]}}}}}}},"/tokens/{clientId}":{"put":{"tags":["Tokens"],"summary":"Register a service token","parameters":[{"schema":{"type":"string","minLength":1,"example":"abc123.access"},"required":true,"name":"clientId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":256},"description":{"type":"string","maxLength":1000,"default":""},"scopes":{"type":"string","default":"*"},"role":{"type":"string","maxLength":64},"client_secret_hash":{"type":"string","pattern":"^[0-9a-f]{64}$"},"age_public_key":{"type":"string","maxLength":100}},"required":["name"]}}}},"responses":{"201":{"description":"Token registered","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenCreateResponse"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Tokens"],"summary":"Revoke a service token","parameters":[{"schema":{"type":"string","minLength":1,"example":"abc123.access"},"required":true,"name":"clientId","in":"path"}],"responses":{"200":{"description":"Token revoked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenDeleteResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/flags":{"get":{"tags":["Flags"],"summary":"List all feature flags","responses":{"200":{"description":"All flags with metadata","content":{"application/json":{"schema":{"type":"object","properties":{"flags":{"type":"array","items":{"$ref":"#/components/schemas/Flag"}}},"required":["flags"]}}}},"403":{"description":"Insufficient scope or owner only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/flags/{key}":{"get":{"tags":["Flags"],"summary":"Get a feature flag","parameters":[{"schema":{"type":"string","minLength":1,"example":"maintenance"},"required":true,"name":"key","in":"path"}],"responses":{"200":{"description":"Flag","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Flag"}}}},"403":{"description":"Insufficient scope or owner only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"tags":["Flags"],"summary":"Set a feature flag (string, number, boolean, or JSON)","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":256,"example":"maintenance"},"required":true,"name":"key","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"value":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"object","additionalProperties":{"nullable":true}}]},"description":{"type":"string","maxLength":1000,"default":""}},"required":["value"]}}}},"responses":{"200":{"description":"Flag set","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Flag"}}}},"403":{"description":"Insufficient scope or owner only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Flags"],"summary":"Delete a feature flag","parameters":[{"schema":{"type":"string","minLength":1,"example":"maintenance"},"required":true,"name":"key","in":"path"}],"responses":{"200":{"description":"Flag deleted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"deleted":{"type":"string"}},"required":["ok","deleted"]}}}},"403":{"description":"Insufficient scope or owner only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/secrets/export":{"get":{"tags":["Secrets"],"summary":"Export all secrets decrypted (interactive only)","responses":{"200":{"description":"All secrets decrypted","content":{"application/json":{"schema":{"type":"object","properties":{"secrets":{"type":"array","items":{"$ref":"#/components/schemas/SecretExportItem"}}},"required":["secrets"]}}}},"403":{"description":"Insufficient scope or owner only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/secrets/import":{"post":{"tags":["Secrets"],"summary":"Bulk import secrets from JSON (interactive only)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"secrets":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","minLength":1,"maxLength":256},"value":{"type":"string","minLength":1,"maxLength":1000000},"description":{"type":"string","maxLength":1000,"default":""},"tags":{"type":"string","maxLength":500,"default":""},"expires_at":{"type":"string","nullable":true,"default":null},"created_at":{"type":"string"},"updated_at":{"type":"string"},"created_by":{"type":"string"},"updated_by":{"type":"string"}},"required":["key","value"]},"minItems":1},"overwrite":{"type":"boolean","default":false}},"required":["secrets"]}}}},"responses":{"200":{"description":"Import results","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretImportResponse"}}}},"403":{"description":"Insufficient scope or owner only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/secrets/{key}/versions":{"get":{"tags":["Secrets"],"summary":"List version history for a secret","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":256,"example":"api-key"},"required":true,"name":"key","in":"path"}],"responses":{"200":{"description":"Version history (values not included for security)","content":{"application/json":{"schema":{"type":"object","properties":{"versions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"number"},"changed_by":{"type":"string"},"changed_at":{"type":"string"}},"required":["id","changed_by","changed_at"]}}},"required":["versions"]}}}},"403":{"description":"Insufficient scope or owner only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/secrets/{key}/versions/{id}":{"get":{"tags":["Secrets"],"summary":"Get a decrypted version value","parameters":[{"schema":{"type":"string","minLength":1,"example":"api-key"},"required":true,"name":"key","in":"path"},{"schema":{"type":"integer","minimum":1,"example":1},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Decrypted version value","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"number"},"key":{"type":"string"},"value":{"type":"string"},"description":{"type":"string"},"changed_by":{"type":"string"},"changed_at":{"type":"string"}},"required":["id","key","value","description","changed_by","changed_at"]}}}},"403":{"description":"Insufficient scope or owner only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/secrets/{key}/versions/{id}/restore":{"post":{"tags":["Secrets"],"summary":"Restore a secret to a previous version","parameters":[{"schema":{"type":"string","minLength":1,"example":"api-key"},"required":true,"name":"key","in":"path"},{"schema":{"type":"integer","minimum":1,"example":1},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Secret restored","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"key":{"type":"string"},"restored_from":{"type":"number"}},"required":["ok","key","restored_from"]}}}},"403":{"description":"Insufficient scope or owner only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/secrets/{key}":{"put":{"tags":["Secrets"],"summary":"Create or update a secret","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":256,"example":"api-key"},"required":true,"name":"key","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"value":{"type":"string","minLength":1,"maxLength":1000000},"description":{"type":"string","maxLength":1000,"default":""},"tags":{"type":"string","maxLength":500,"default":""},"expires_at":{"type":"string","nullable":true,"default":null}},"required":["value"]}}}},"responses":{"201":{"description":"Secret stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretCreateResponse"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or owner only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"tags":["Secrets"],"summary":"Get a decrypted secret","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":256,"example":"api-key"},"required":true,"name":"key","in":"path"}],"responses":{"200":{"description":"Decrypted secret","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretEntry"}}}},"403":{"description":"Insufficient scope or owner only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Secrets"],"summary":"Delete a secret","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":256,"example":"api-key"},"required":true,"name":"key","in":"path"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretDeleteResponse"}}}},"403":{"description":"Insufficient scope or owner only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/secrets":{"get":{"tags":["Secrets"],"summary":"List secret keys (no values)","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":500,"default":100,"example":100},"required":false,"name":"limit","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"default":0,"example":0},"required":false,"name":"offset","in":"query"},{"schema":{"type":"string","example":"api"},"required":false,"name":"search","in":"query"}],"responses":{"200":{"description":"Paginated list of secrets","content":{"application/json":{"schema":{"type":"object","properties":{"secrets":{"type":"array","items":{"$ref":"#/components/schemas/SecretListItem"}},"total":{"type":"number"}},"required":["secrets","total"]}}}},"403":{"description":"Insufficient scope or owner only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}