{"openapi":"3.1.0","info":{"title":"Agent-Callable Signup API","version":"1.0.0","description":"HTTP API for AI agents to provision accounts on third-party services and retrieve API keys. Every sensitive operation is audit-logged and rate-limited per agent token."},"servers":[{"url":"https://relay.cumulush.com","description":"Relay API (resolved from APP_BASE_URL or Vercel production host)"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"agt_<base64url>","description":"Agent bearer token minted via POST /v1/me/agent-tokens (or scripts/seed-agent-token.ts). Only the SHA-256 hash is stored server-side."},"cookieAuth":{"type":"apiKey","in":"cookie","name":"relay_session","description":"HttpOnly session cookie issued after email OTP or passkey login."}},"schemas":{"UserWorkspace":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"slug":{"type":"string"},"is_default":{"type":"boolean"},"inbox_alias":{"type":"string","nullable":true},"is_active":{"type":"boolean","description":"True when this workspace is the caller's currently-active workspace."},"created_at":{"type":"string","nullable":true}},"required":["id","name","slug","is_default","inbox_alias","is_active","created_at"]},"MeAccountList":{"type":"array","items":{"$ref":"#/components/schemas/MeAccount"}},"MeAccount":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"provider_id":{"type":"string"},"external_id":{"type":"string"},"label":{"type":"string"},"email_alias":{"type":"string","nullable":true},"status":{"type":"string"},"created_at":{"type":"string","nullable":true}},"required":["id","provider_id","external_id","label","email_alias","status","created_at"]},"MeApiKeyList":{"type":"array","items":{"$ref":"#/components/schemas/MeApiKey"}},"MeApiKey":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"account_id":{"type":"string","format":"uuid"},"label":{"type":"string"},"provider_key_id":{"type":"string","nullable":true},"last_revealed_at":{"type":"string","nullable":true},"last_used_at":{"type":"string","nullable":true},"created_at":{"type":"string","nullable":true},"revoked_at":{"type":"string","nullable":true}},"required":["id","account_id","label","created_at"]},"MeApiKeyCreateResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"account_id":{"type":"string","format":"uuid"},"label":{"type":"string"},"key":{"type":"string","description":"Plaintext API key from the provider. Returned EXACTLY ONCE. Relay does not persist the key bytes."},"created_at":{"type":"string","nullable":true}},"required":["id","account_id","label","key","created_at"]},"MeApiKeyCreateBody":{"type":"object","properties":{"label":{"type":"string"}}},"MeApiKeyRotateResponse":{"type":"object","properties":{"rotated":{"type":"boolean","enum":[true]},"revoked_key_id":{"type":"string","format":"uuid"},"new_key":{"$ref":"#/components/schemas/MeApiKeyCreateResponse"},"note":{"type":"string"}},"required":["rotated","revoked_key_id","new_key","note"]},"AgentGuide":{"type":"object","properties":{"content":{"type":"string","description":"Free-form markdown body. Empty string when the user has never set a guide."},"updated_at":{"type":"string","nullable":true,"description":"ISO timestamp of the last PUT. Null if never set."},"bytes":{"type":"integer","minimum":0,"description":"UTF-8 byte length of `content`. Max 64 KiB (65536 bytes)."}},"required":["content","updated_at","bytes"]},"AgentGuideWriteResponse":{"type":"object","properties":{"updated_at":{"type":"string"},"bytes":{"type":"integer","minimum":0}},"required":["updated_at","bytes"]},"AgentGuideWriteBody":{"type":"object","properties":{"content":{"type":"string","description":"New markdown body (UTF-8). Must be <= 64 KiB or the server returns 413."}},"required":["content"]},"ProviderList":{"type":"array","items":{"$ref":"#/components/schemas/Provider"}},"Provider":{"type":"object","properties":{"id":{"type":"string","example":"neon"},"kind":{"type":"string","enum":["builtin","tenant"]},"displayName":{"type":"string"},"description":{"type":"string","nullable":true},"docsUrl":{"type":"string","nullable":true,"format":"uri"},"homepage":{"type":"string","nullable":true,"format":"uri"},"npmPackage":{"type":"string","nullable":true},"categories":{"type":"array","items":{"type":"string"}},"pricingModel":{"type":"string","nullable":true,"enum":["free","free-tier","paid","usage-based","freemium",null]},"pricingUrl":{"type":"string","nullable":true,"format":"uri"},"freeTierSummary":{"type":"string","nullable":true},"capabilities":{"type":"array","items":{"type":"string"}},"inputSchema":{"type":"object","additionalProperties":{"nullable":true},"description":"JSON Schema describing the provider input."},"tenantId":{"type":"string","format":"uuid"},"needsEmailVerification":{"type":"boolean"}},"required":["id","kind","displayName","description","docsUrl","homepage","npmPackage","categories","pricingModel","pricingUrl","freeTierSummary","capabilities","inputSchema"]},"IndexOverview":{"type":"object","properties":{"categories":{"type":"array","items":{"$ref":"#/components/schemas/IndexCategoryEntry"}},"aliases":{"type":"object","additionalProperties":{"type":"string"}}},"required":["categories","aliases"]},"IndexCategoryEntry":{"type":"object","properties":{"slug":{"type":"string","example":"database"},"displayName":{"type":"string","example":"Databases"},"count":{"type":"integer","minimum":1,"example":1},"providerIds":{"type":"array","items":{"type":"string"},"example":["neon"]}},"required":["slug","displayName","count","providerIds"]},"IndexCategorySlice":{"type":"object","properties":{"category":{"type":"string"},"displayName":{"type":"string"},"providers":{"type":"array","items":{"$ref":"#/components/schemas/IndexProviderEntry"}}},"required":["category","displayName","providers"]},"IndexProviderEntry":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["builtin","tenant"]},"displayName":{"type":"string"},"description":{"type":"string","nullable":true},"docsUrl":{"type":"string","nullable":true,"format":"uri"},"homepage":{"type":"string","nullable":true,"format":"uri"},"npmPackage":{"type":"string","nullable":true},"categories":{"type":"array","items":{"type":"string"}},"pricingModel":{"type":"string","nullable":true,"enum":["free","free-tier","paid","usage-based","freemium",null]},"pricingUrl":{"type":"string","nullable":true,"format":"uri"},"freeTierSummary":{"type":"string","nullable":true},"capabilities":{"type":"array","items":{"type":"string"}},"inputSchema":{"type":"object","additionalProperties":{"nullable":true}},"tenantId":{"type":"string","format":"uuid"},"needsEmailVerification":{"type":"boolean"}},"required":["id","kind","displayName","description","docsUrl","homepage","npmPackage","categories","pricingModel","pricingUrl","freeTierSummary","capabilities","inputSchema"]},"UnknownCategoryError":{"type":"object","properties":{"error":{"type":"string","enum":["unknown_category"]},"requested":{"type":"string"},"canonical":{"type":"array","items":{"type":"string"}}},"required":["error","requested","canonical"]},"SignupCreateResponse":{"type":"object","properties":{"signup_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending"]}},"required":["signup_id","status"]},"SignupCreateBody":{"type":"object","properties":{"provider":{"type":"string","example":"neon"},"input":{"type":"object","additionalProperties":{"nullable":true},"description":"Provider-specific input object; see GET /v1/providers for its schema."}},"required":["provider","input"]},"SignupStatusResponse":{"type":"object","properties":{"signup_id":{"type":"string","format":"uuid"},"status":{"type":"string","example":"completed","description":"'pending' | 'awaiting_email' | 'completed' | 'failed'"},"error":{"type":"string"},"account_id":{"type":"string","format":"uuid"},"initial_api_key":{"type":"string"},"initial_credentials":{"type":"object","additionalProperties":{"nullable":true}}},"required":["signup_id","status"]},"IntentResponse":{"type":"object","properties":{"resolutions":{"type":"array","items":{"type":"object","properties":{"category":{"type":"string"},"alias":{"type":"string","nullable":true},"provider":{"type":"string","nullable":true},"status":{"type":"string","enum":["existing","provisioning","ambiguous","no_provider"]},"accountId":{"type":"string","format":"uuid"},"signupJobId":{"type":"string","format":"uuid"},"pollUrl":{"type":"string"},"envVar":{"type":"string"},"value":{"type":"string","nullable":true},"revealUrl":{"type":"string"},"candidates":{"type":"array","items":{"type":"string"}}},"required":["category","alias","provider","status"]}},"envBlock":{"type":"string"},"pending":{"type":"array","items":{"type":"string","format":"uuid"}},"unsatisfied":{"type":"array","items":{"type":"object","properties":{"category":{"type":"string"},"reason":{"type":"string"}},"required":["category","reason"]}},"unmatchedTerms":{"type":"array","items":{"type":"string"}},"revealAllUrl":{"type":"string","nullable":true},"notes":{"type":"array","items":{"type":"string"}}},"required":["resolutions","envBlock","pending","unsatisfied","unmatchedTerms","revealAllUrl","notes"]},"IntentBody":{"type":"object","properties":{"goal":{"type":"string","minLength":1,"maxLength":2000,"example":"Postgres + transactional email for a Next.js app","description":"Natural-language description of what the workspace needs."},"workspaceId":{"type":"string","format":"uuid","description":"REQUIRED. The user's workspace this resolution is scoped to — must belong to the agent's user. Resolutions are deduped per (workspace, provider, alias)."},"envStyle":{"type":"string","default":"raw","description":"Format of the returned envBlock. v1 only supports \"raw\" — other styles are reserved and will 400."},"pin":{"type":"array","items":{"type":"object","properties":{"category":{"type":"string","minLength":1},"providerId":{"type":"string","minLength":1},"alias":{"type":"string","minLength":1,"maxLength":64}},"required":["category","providerId"]},"maxItems":20,"description":"Override the parser/selector for specific categories. Each pin becomes its own resolution slot; use `alias` to express multiple distinct accounts within the same provider."}},"required":["goal","workspaceId"]},"AccountList":{"type":"array","items":{"$ref":"#/components/schemas/Account"}},"Account":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"provider_id":{"type":"string"},"external_id":{"type":"string"},"label":{"type":"string"},"email_alias":{"type":"string","nullable":true},"status":{"type":"string"},"created_at":{"type":"string","nullable":true,"description":"ISO-8601 timestamp."}},"required":["id","provider_id","external_id","label","email_alias","status","created_at"]},"AuditLogResponse":{"type":"object","properties":{"entries":{"type":"array","items":{"$ref":"#/components/schemas/AuditEntry"}},"total":{"type":"number"}},"required":["entries","total"]},"AuditEntry":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"agent_id":{"type":"string","nullable":true,"format":"uuid"},"action":{"type":"string"},"target":{"type":"string","nullable":true},"metadata":{"type":"object","nullable":true,"additionalProperties":{"nullable":true}},"created_at":{"type":"string","nullable":true}},"required":["id","agent_id","action","target","metadata","created_at"]},"ApiKeyList":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"}},"ApiKey":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"account_id":{"type":"string","format":"uuid"},"label":{"type":"string"},"provider_key_id":{"type":"string","nullable":true},"last_revealed_at":{"type":"string","nullable":true},"last_used_at":{"type":"string","nullable":true,"description":"Most recent Relay-observable use: mint, signup delivery, legacy reveal, or rotation. Does NOT track direct provider-side calls."},"created_at":{"type":"string","nullable":true},"revoked_at":{"type":"string","nullable":true}},"required":["id","account_id","label","created_at"]},"ApiKeyCreateResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"account_id":{"type":"string","format":"uuid"},"label":{"type":"string"},"key":{"type":"string","description":"Plaintext API key from the provider. Returned EXACTLY ONCE. Relay does not persist the key bytes — hand it to the user in-chat and forget it."},"created_at":{"type":"string","nullable":true}},"required":["id","account_id","label","key","created_at"]},"ApiKeyCreateBody":{"type":"object","properties":{"label":{"type":"string"}}},"ApiKeyRevealResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"label":{"type":"string"},"key":{"type":"string","description":"Plaintext API key (legacy reveal)."},"revealed_at":{"type":"string"},"note":{"type":"string"}},"required":["id","label","key","revealed_at","note"]},"ApiKeyRotateResponse":{"type":"object","properties":{"rotated":{"type":"boolean","enum":[true]},"revoked_key_id":{"type":"string","format":"uuid"},"new_key":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"account_id":{"type":"string","format":"uuid"},"label":{"type":"string"},"key":{"type":"string"},"created_at":{"type":"string","nullable":true}},"required":["id","account_id","label","key","created_at"]},"note":{"type":"string"}},"required":["rotated","revoked_key_id","new_key","note"]},"RevealBatchResponse":{"type":"object","properties":{"revealed":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["revealed","mint_required","not_found","rate_limited","tenant_inactive","error"]},"key":{"type":"string"},"label":{"type":"string"},"revealed_at":{"type":"string"},"error":{"type":"string"}},"required":["id","status"]}}},"required":["revealed"]},"RevealBatchBody":{"type":"object","properties":{"keyIds":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1,"maxItems":50}},"required":["keyIds"]},"Whoami":{"type":"object","properties":{"agentId":{"type":"string","format":"uuid"},"userId":{"type":"string","nullable":true,"format":"uuid"},"tenantId":{"type":"string","nullable":true,"format":"uuid"},"scopes":{"type":"array","items":{"type":"string"}},"label":{"type":"string","nullable":true},"createdAt":{"type":"string","nullable":true,"description":"ISO-8601 timestamp when the agent token was minted."}},"required":["agentId","userId","tenantId","scopes","label","createdAt"]}},"parameters":{}},"paths":{"/health":{"get":{"tags":["meta"],"summary":"Liveness probe","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ok"]},"version":{"type":"string"}},"required":["status","version"]}}}}}}},"/v1/auth/email/start":{"post":{"tags":["auth"],"summary":"Send an email OTP","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"}},"required":["email"]}}}},"responses":{"200":{"description":"OTP sent.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]},"expiresAt":{"type":"string"}},"required":["ok","expiresAt"]}}}},"400":{"description":"Bad request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limit.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/auth/email/verify":{"post":{"tags":["auth"],"summary":"Verify an email OTP and start a session","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"},"code":{"type":"string","pattern":"^\\d{6}$"}},"required":["email","code"]}}}},"responses":{"200":{"description":"Session started.","content":{"application/json":{"schema":{"type":"object","properties":{"userId":{"type":"string","format":"uuid"},"email":{"type":"string"},"created":{"type":"boolean"}},"required":["userId","email","created"]}}}},"401":{"description":"Invalid OTP.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/auth/logout":{"post":{"tags":["auth"],"summary":"Destroy the current session","description":"Destroys the session (DB row + cookie). Programmatic callers get JSON; browser form submits (Sec-Fetch-Dest: document, or Accept: text/html) get a 303 redirect to `/` so the user lands back on the logged-out home page instead of seeing the raw JSON body.","responses":{"200":{"description":"Logged out (programmatic).","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"303":{"description":"Logged out (browser) — redirect to home."}}}},"/v1/auth/webauthn/register/options":{"post":{"tags":["auth","webauthn"],"summary":"Passkey registration options (requires session)","security":[{"cookieAuth":[]}],"responses":{"200":{"description":"Options.","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"nullable":true}}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/auth/webauthn/register/verify":{"post":{"tags":["auth","webauthn"],"summary":"Verify + store a registered passkey","security":[{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"response":{"type":"object","additionalProperties":{"nullable":true}},"name":{"type":"string"}},"required":["response"]}}}},"responses":{"200":{"description":"Passkey stored.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]},"passkeyId":{"type":"string","format":"uuid"}},"required":["ok","passkeyId"]}}}},"400":{"description":"Verification failed.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/auth/webauthn/login/options":{"post":{"tags":["auth","webauthn"],"summary":"Passkey authentication options","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"}}}}}},"responses":{"200":{"description":"Options.","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"nullable":true}}}}}}}},"/v1/auth/webauthn/login/verify":{"post":{"tags":["auth","webauthn"],"summary":"Verify passkey auth and start a session","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"response":{"type":"object","additionalProperties":{"nullable":true}},"email":{"type":"string","format":"email"}},"required":["response"]}}}},"responses":{"200":{"description":"Session started.","content":{"application/json":{"schema":{"type":"object","properties":{"userId":{"type":"string","format":"uuid"},"email":{"type":"string"}},"required":["userId","email"]}}}},"401":{"description":"Verification failed.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/session":{"get":{"tags":["session"],"summary":"Read the current session + workspace context","security":[{"cookieAuth":[]}],"responses":{"200":{"description":"Session + owned/member tenants.","content":{"application/json":{"schema":{"type":"object","properties":{"userId":{"type":"string","format":"uuid"},"email":{"type":"string"},"activeWorkspace":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["user"]}},"required":["kind"]},{"type":"object","properties":{"kind":{"type":"string","enum":["tenant"]},"tenantId":{"type":"string","format":"uuid"}},"required":["kind","tenantId"]}]},"tenants":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"name":{"type":"string"},"role":{"type":"string","enum":["owner","admin","viewer","member"]}},"required":["id","slug","name","role"]},"description":"Tenants the user is an owner or member of, used to populate the workspace switcher."}},"required":["userId","email","activeWorkspace","tenants"]}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/session/workspace":{"post":{"tags":["session"],"summary":"Switch the active workspace for this session","description":"Switch between the end-user workspace ({ kind: \"user\" }) and a developer workspace for a tenant the user can access ({ kind: \"tenant\", tenantId }).","security":[{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["user"]}},"required":["kind"]},{"type":"object","properties":{"kind":{"type":"string","enum":["tenant"]},"tenantId":{"type":"string","format":"uuid"}},"required":["kind","tenantId"]}]}}}},"responses":{"200":{"description":"Active workspace updated.","content":{"application/json":{"schema":{"type":"object","properties":{"activeWorkspace":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["user"]}},"required":["kind"]},{"type":"object","properties":{"kind":{"type":"string","enum":["tenant"]},"tenantId":{"type":"string","format":"uuid"}},"required":["kind","tenantId"]}]}},"required":["activeWorkspace"]}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Not a member of that tenant.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/user":{"get":{"tags":["user"],"summary":"Current user identity + inbox + counts","security":[{"cookieAuth":[]}],"responses":{"200":{"description":"User profile + quick aggregates.","content":{"application/json":{"schema":{"type":"object","properties":{"userId":{"type":"string","format":"uuid"},"email":{"type":"string"},"name":{"type":"string","nullable":true},"inboxAlias":{"type":"string","nullable":true},"inboxAddress":{"type":"string","nullable":true},"counts":{"type":"object","properties":{"accounts":{"type":"number"},"activeAgents":{"type":"number"},"activeMagicLinks":{"type":"number"}},"required":["accounts","activeAgents","activeMagicLinks"]}},"required":["userId","email","name","inboxAlias","inboxAddress","counts"]}}}}}}},"/v1/user/accounts":{"get":{"tags":["user"],"summary":"List the user's accounts","security":[{"cookieAuth":[]}],"responses":{"200":{"description":"Accounts owned by the user (credentials omitted).","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"provider_id":{"type":"string"},"label":{"type":"string"},"email_alias":{"type":"string","nullable":true},"status":{"type":"string"},"created_at":{"type":"string","nullable":true}},"required":["id","provider_id","label","email_alias","status","created_at"]}}}}}}}},"/v1/user/accounts/{id}":{"get":{"tags":["user"],"summary":"Account detail + non-revoked API key bookkeeping rows","security":[{"cookieAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Account + keys.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"provider_id":{"type":"string"},"label":{"type":"string"},"email_alias":{"type":"string","nullable":true},"status":{"type":"string"},"created_at":{"type":"string","nullable":true},"keys":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"label":{"type":"string"},"provider_key_id":{"type":"string","nullable":true},"created_at":{"type":"string","nullable":true},"last_revealed_at":{"type":"string","nullable":true}},"required":["id","label","provider_key_id","created_at","last_revealed_at"]}}},"required":["id","provider_id","label","email_alias","status","created_at","keys"]}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/user/signups":{"get":{"tags":["user"],"summary":"Timeline of signups initiated by the user's agents","security":[{"cookieAuth":[]}],"responses":{"200":{"description":"Signup jobs.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string"},"provider_slug":{"type":"string","nullable":true},"tenant_name":{"type":"string","nullable":true},"account_id":{"type":"string","nullable":true,"format":"uuid"},"error":{"type":"string","nullable":true},"created_at":{"type":"string","nullable":true}},"required":["id","status","provider_slug","tenant_name","account_id","error","created_at"]}}}}}}}},"/v1/user/keys":{"get":{"tags":["user"],"summary":"Flat view of non-revoked API keys across the user's accounts","security":[{"cookieAuth":[]}],"responses":{"200":{"description":"Keys (bytes never returned).","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"label":{"type":"string"},"account_id":{"type":"string","format":"uuid"},"account_label":{"type":"string"},"provider_id":{"type":"string"},"created_at":{"type":"string","nullable":true},"last_revealed_at":{"type":"string","nullable":true}},"required":["id","label","account_id","account_label","provider_id","created_at","last_revealed_at"]}}}}}}}},"/v1/user/agent-tokens":{"get":{"tags":["user"],"summary":"List active agent tokens","security":[{"cookieAuth":[]}],"responses":{"200":{"description":"Tokens.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"label":{"type":"string","nullable":true},"scopes":{"type":"array","items":{"type":"string"}},"created_at":{"type":"string","nullable":true},"last_used_at":{"type":"string","nullable":true}},"required":["id","label","scopes","created_at","last_used_at"]}}}}}}},"post":{"tags":["user"],"summary":"Mint a new agent token (plaintext returned once)","security":[{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"label":{"type":"string","minLength":1,"maxLength":100},"scopes":{"type":"array","items":{"type":"string"}},"expires_in_days":{"type":"integer","minimum":1,"maximum":365,"description":"How many days the new token remains valid. Defaults to 30."},"never_expires":{"type":"boolean","description":"Opt in to a non-expiring token."}},"required":["label"]}}}},"responses":{"201":{"description":"Created.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"token":{"type":"string","description":"Plaintext — shown once."},"label":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"expires_at":{"type":"string","nullable":true}},"required":["id","token","label","scopes","expires_at"]}}}}}}},"/v1/user/agent-tokens/{id}":{"delete":{"tags":["user"],"summary":"Revoke an agent token","security":[{"cookieAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Revoked.","content":{"application/json":{"schema":{"type":"object","properties":{"revoked":{"type":"boolean","enum":[true]},"id":{"type":"string","format":"uuid"}},"required":["revoked","id"]}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/user/inbox":{"get":{"tags":["user"],"summary":"Recent emails delivered to the user's Relay inbox","security":[{"cookieAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Messages.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"to":{"type":"string"},"from":{"type":"string"},"subject":{"type":"string","nullable":true},"body_text":{"type":"string","nullable":true},"received_at":{"type":"string","nullable":true}},"required":["id","to","from","subject","body_text","received_at"]}}}}}}}},"/v1/user/audit-log":{"get":{"tags":["user"],"summary":"Audit rows scoped to the current user","security":[{"cookieAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string"},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"Entries.","content":{"application/json":{"schema":{"type":"object","properties":{"entries":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"action":{"type":"string"},"target":{"type":"string","nullable":true},"metadata":{"type":"object","nullable":true,"additionalProperties":{"nullable":true}},"created_at":{"type":"string","nullable":true}},"required":["id","action","target","metadata","created_at"]}}},"required":["entries"]}}}}}}},"/v1/user/magic-links":{"get":{"tags":["user"],"summary":"List active share links","security":[{"cookieAuth":[]}],"responses":{"200":{"description":"Active links (plaintext tokens never returned).","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"purpose":{"type":"string"},"expires_at":{"type":"string"},"claimed_at":{"type":"string","nullable":true},"max_uses":{"type":"number"},"used_count":{"type":"number"},"created_at":{"type":"string","nullable":true}},"required":["id","purpose","expires_at","claimed_at","max_uses","used_count","created_at"]}}}}}}},"post":{"tags":["user"],"summary":"Mint a new share link (plaintext token returned once)","description":"Creates a session-less URL that opens a read-only summary of the user's Relay data. Default TTL 10 minutes, single-use. Plaintext token is never persisted — only its SHA-256 hash.","security":[{"cookieAuth":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"ttl_minutes":{"type":"integer","minimum":1,"maximum":60},"max_uses":{"type":"integer","minimum":1,"maximum":10}}}}}},"responses":{"201":{"description":"Link minted.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string"},"expires_at":{"type":"string"}},"required":["id","url","expires_at"]}}}},"402":{"description":"Token balance too low to cover share_link; top up to retry.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"required":{"type":"number"},"available":{"type":"number"},"topup_url":{"type":"string"}},"required":["error","required","available","topup_url"]}}}}}}},"/v1/user/magic-links/{id}":{"delete":{"tags":["user"],"summary":"Revoke a share link","security":[{"cookieAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Revoked.","content":{"application/json":{"schema":{"type":"object","properties":{"revoked":{"type":"boolean","enum":[true]},"id":{"type":"string","format":"uuid"}},"required":["revoked","id"]}}}}}}},"/v1/user/workspaces":{"get":{"tags":["user-workspaces"],"summary":"List the caller's personal workspaces","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Workspaces owned by the caller.","content":{"application/json":{"schema":{"type":"object","properties":{"active_id":{"type":"string","format":"uuid"},"workspaces":{"type":"array","items":{"$ref":"#/components/schemas/UserWorkspace"}}},"required":["active_id","workspaces"]}}}}}},"post":{"tags":["user-workspaces"],"summary":"Create a new personal workspace","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"slug":{"type":"string","minLength":1,"maxLength":40,"pattern":"^[a-z0-9-]+$"},"make_active":{"type":"boolean","description":"If true, the new workspace becomes the caller's active workspace (cookie sessions only — bearer tokens are immutably pinned)."}},"required":["name"]}}}},"responses":{"201":{"description":"Created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserWorkspace"}}}},"400":{"description":"Invalid name / slug, or limit reached.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Slug already used in this account.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/user/workspaces/{id}/switch":{"post":{"tags":["user-workspaces"],"summary":"Make this the caller's active workspace","description":"Sets `users.active_user_workspace_id`. Cookie sessions only — bearer tokens are immutably pinned to the workspace they were minted in, so this call is rejected for bearer callers with 400.","security":[{"cookieAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Switched.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserWorkspace"}}}},"400":{"description":"Bad request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Workspace not found or not owned by the caller.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Conflict.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/user/workspaces/{id}/rename":{"post":{"tags":["user-workspaces"],"summary":"Rename a personal workspace","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80}},"required":["name"]}}}},"responses":{"200":{"description":"Renamed.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"}},"required":["id","name"]}}}},"400":{"description":"Invalid name.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Workspace not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Conflict.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/user/workspaces/{id}":{"delete":{"tags":["user-workspaces"],"summary":"Delete a personal workspace (hard)","description":"Permanently deletes the workspace and every row scoped to it (accounts, keys, inbox, signup history). The default workspace cannot be deleted — make another workspace the default first. Requires `confirm_name` to equal the workspace name exactly.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"confirm_name":{"type":"string","minLength":1}},"required":["confirm_name"]}}}},"responses":{"200":{"description":"Deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean","enum":[true]},"id":{"type":"string","format":"uuid"}},"required":["deleted","id"]}}}},"400":{"description":"Cannot delete (is_default, last remaining, confirm_name mismatch).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Workspace not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Conflict.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/dev":{"get":{"tags":["dev"],"summary":"Developer workspace overview","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Tenant + weekly aggregates.","content":{"application/json":{"schema":{"type":"object","properties":{"tenant":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"name":{"type":"string"},"created_at":{"type":"string","nullable":true}},"required":["id","slug","name","created_at"]},"weekly":{"type":"object","properties":{"signupsStarted":{"type":"number"},"signupsCompleted":{"type":"number"},"signupsFailed":{"type":"number"},"awaitingEmail":{"type":"number"},"uniqueUsers":{"type":"number"}},"required":["signupsStarted","signupsCompleted","signupsFailed","awaitingEmail","uniqueUsers"]}},"required":["tenant","weekly"]}}}},"404":{"description":"Tenant not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/dev/products":{"get":{"tags":["dev"],"summary":"List products (tenant providers) with per-product counts","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Products.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"display_name":{"type":"string"},"signup_webhook_url":{"type":"string"},"teardown_webhook_url":{"type":"string","nullable":true},"verification_mode":{"type":"string"},"needs_email_verification":{"type":"boolean"},"created_at":{"type":"string","nullable":true},"signups_total":{"type":"number"},"signups_week":{"type":"number"}},"required":["id","slug","display_name","signup_webhook_url","teardown_webhook_url","verification_mode","needs_email_verification","created_at","signups_total","signups_week"]}}}}}}},"post":{"tags":["dev"],"summary":"Register a new product on this tenant (webhook secret returned once)","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"slug":{"type":"string","minLength":2,"maxLength":60,"pattern":"^[a-z0-9-]+$"},"display_name":{"type":"string","minLength":1,"maxLength":120},"signup_webhook_url":{"type":"string","format":"uri"},"teardown_webhook_url":{"type":"string","format":"uri"},"input_schema":{"type":"object","additionalProperties":{"nullable":true}},"verification_mode":{"type":"string","enum":["none","relay_confirm_link","integrator_email"]},"description":{"type":"string","maxLength":500},"docs_url":{"type":"string","format":"uri"},"homepage":{"type":"string","format":"uri"},"npm_package":{"type":"string","maxLength":214},"categories":{"type":"array","items":{"type":"string"},"maxItems":8},"pricing_model":{"type":"string","enum":["free","free-tier","paid","usage-based","freemium"]},"pricing_url":{"type":"string","format":"uri"},"free_tier_summary":{"type":"string","maxLength":240},"capabilities":{"type":"array","items":{"type":"string"},"maxItems":24}},"required":["slug","display_name","signup_webhook_url"]}}}},"responses":{"201":{"description":"Created.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"webhook_secret":{"type":"string"},"categories":{"type":"array","items":{"type":"string"}}},"required":["id","slug","webhook_secret","categories"]}}}},"400":{"description":"Invalid slug, categories, or pricing model.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"invalid":{"type":"array","items":{"type":"string"}},"canonical":{"type":"array","items":{"type":"string"}}},"required":["error"]}}}},"409":{"description":"Slug taken.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/dev/products/{slug}/rotate":{"post":{"tags":["dev"],"summary":"Rotate a product's webhook secret (new plaintext returned once)","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"Rotated.","content":{"application/json":{"schema":{"type":"object","properties":{"slug":{"type":"string"},"webhook_secret":{"type":"string"}},"required":["slug","webhook_secret"]}}}},"404":{"description":"Product not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/dev/products/{slug}":{"delete":{"tags":["dev"],"summary":"Remove a product","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"Deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean","enum":[true]},"slug":{"type":"string"}},"required":["deleted","slug"]}}}}}}},"/v1/dev/users":{"get":{"tags":["dev"],"summary":"End-users who signed up through this tenant","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Users.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"user_id":{"type":"string","format":"uuid"},"email":{"type":"string"},"signups":{"type":"number"},"last_signup_at":{"type":"string","nullable":true},"last_product":{"type":"string","nullable":true},"last_status":{"type":"string","nullable":true}},"required":["user_id","email","signups","last_signup_at","last_product","last_status"]}}}}}}}},"/v1/dev/team":{"get":{"tags":["dev"],"summary":"List tenant members (owner + additional members)","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Members.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"user_id":{"type":"string","format":"uuid"},"email":{"type":"string"},"role":{"type":"string"},"is_owner":{"type":"boolean"},"created_at":{"type":"string","nullable":true}},"required":["user_id","email","role","is_owner","created_at"]}}}}}}},"post":{"tags":["dev"],"summary":"Invite an existing Relay user to the tenant","description":"Owner-only. The invitee must already have a Relay user (by email). This does not send an email; their next login will surface the tenant in their workspace switcher.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"},"role":{"type":"string","enum":["admin","viewer","member"]}},"required":["email"]}}}},"responses":{"201":{"description":"Added.","content":{"application/json":{"schema":{"type":"object","properties":{"user_id":{"type":"string","format":"uuid"},"role":{"type":"string"}},"required":["user_id","role"]}}}},"403":{"description":"Owner-only.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"User not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Already a member.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/dev/team/{userId}":{"delete":{"tags":["dev"],"summary":"Remove a tenant member","description":"Owner-only. Cannot remove the owner themselves.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"userId","in":"path"}],"responses":{"200":{"description":"Removed.","content":{"application/json":{"schema":{"type":"object","properties":{"removed":{"type":"boolean","enum":[true]},"user_id":{"type":"string","format":"uuid"}},"required":["removed","user_id"]}}}},"403":{"description":"Owner-only.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/dev/settings":{"get":{"tags":["dev"],"summary":"Tenant settings","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Tenant row.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"name":{"type":"string"},"created_at":{"type":"string","nullable":true}},"required":["id","slug","name","created_at"]}}}},"404":{"description":"Tenant not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"patch":{"tags":["dev"],"summary":"Update tenant name","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120}},"required":["name"]}}}},"responses":{"200":{"description":"Updated.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"}},"required":["id","name"]}}}},"403":{"description":"Owner-only.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"delete":{"tags":["dev"],"summary":"Delete the active workspace (hard, owner-only)","description":"Permanently deletes the workspace and everything scoped to it. Requires the caller to be the owner, no live Stripe subscription, and a confirm_name body field that exactly matches the workspace name.","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"confirm_name":{"type":"string","minLength":1,"description":"Must equal the workspace name exactly."}},"required":["confirm_name"]}}}},"responses":{"200":{"description":"Deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean","enum":[true]},"tenant_id":{"type":"string","format":"uuid"},"slug":{"type":"string"}},"required":["deleted","tenant_id","slug"]}}}},"400":{"description":"confirm_name did not match the workspace name.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Owner-only.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Tenant not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"A live Stripe subscription exists — cancel via /dev/billing first.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["active_subscription"]},"subscription_status":{"type":"string"}},"required":["error","subscription_status"]}}}}}}},"/v1/dev/logs":{"get":{"tags":["dev"],"summary":"Recent signup_jobs for this tenant","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Rows.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string"},"provider_slug":{"type":"string","nullable":true},"account_id":{"type":"string","nullable":true,"format":"uuid"},"error":{"type":"string","nullable":true},"created_at":{"type":"string","nullable":true}},"required":["id","status","provider_slug","account_id","error","created_at"]}}}}}}}},"/v1/dev/feature-flags":{"get":{"tags":["dev"],"summary":"Enabled feature flags for this tenant","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Flags.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"flag":{"type":"string"},"enabled_at":{"type":"string"}},"required":["flag","enabled_at"]}}}}}}},"post":{"tags":["dev"],"summary":"Enable a feature flag","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"flag":{"type":"string","enum":["per_product_rate_limits","webhook_retries","audit_log_export"]}},"required":["flag"]}}}},"responses":{"201":{"description":"Enabled.","content":{"application/json":{"schema":{"type":"object","properties":{"flag":{"type":"string"},"enabled_at":{"type":"string"}},"required":["flag","enabled_at"]}}}}}}},"/v1/dev/feature-flags/{flag}":{"delete":{"tags":["dev"],"summary":"Disable a feature flag","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"flag","in":"path"}],"responses":{"200":{"description":"Disabled.","content":{"application/json":{"schema":{"type":"object","properties":{"disabled":{"type":"boolean","enum":[true]},"flag":{"type":"string"}},"required":["disabled","flag"]}}}}}}},"/v1/me":{"get":{"tags":["me"],"summary":"Identify the caller (session cookie OR bearer token)","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Caller identity.","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["user"]},"userId":{"type":"string","format":"uuid"},"email":{"type":"string"},"name":{"type":"string","nullable":true},"inbox_alias":{"type":"string","nullable":true},"inbox_address":{"type":"string","nullable":true}},"required":["kind","userId","email","name","inbox_alias","inbox_address"]},{"type":"object","properties":{"kind":{"type":"string","enum":["agent"]},"agentId":{"type":"string","format":"uuid"},"scopes":{"type":"array","items":{"type":"string"}},"userId":{"type":"string","nullable":true,"format":"uuid"},"label":{"type":"string","nullable":true},"inbox_alias":{"type":"string","nullable":true},"inbox_address":{"type":"string","nullable":true}},"required":["kind","agentId","scopes","userId","label","inbox_alias","inbox_address"]}]}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/me/agent-tokens":{"get":{"tags":["me"],"summary":"List my agent tokens","security":[{"cookieAuth":[]}],"responses":{"200":{"description":"Tokens.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"label":{"type":"string","nullable":true},"scopes":{"type":"array","items":{"type":"string"}},"created_at":{"type":"string","nullable":true},"last_used_at":{"type":"string","nullable":true}},"required":["id","label","scopes","created_at","last_used_at"]}}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"post":{"tags":["me"],"summary":"Mint a new agent token (plaintext returned once)","security":[{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"label":{"type":"string","minLength":1,"maxLength":100},"scopes":{"type":"array","items":{"type":"string"}},"expires_in_days":{"type":"integer","minimum":1,"maximum":365,"description":"How many days the new token remains valid. Defaults to 30."},"never_expires":{"type":"boolean","description":"Opt in to a non-expiring token."}},"required":["label"]}}}},"responses":{"201":{"description":"Created.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"token":{"type":"string","description":"Plaintext — shown once."},"label":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"expires_at":{"type":"string","nullable":true}},"required":["id","token","label","scopes","expires_at"]}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/me/agent-tokens/{id}":{"delete":{"tags":["me"],"summary":"Revoke an agent token","security":[{"cookieAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Revoked.","content":{"application/json":{"schema":{"type":"object","properties":{"revoked":{"type":"boolean","enum":[true]},"id":{"type":"string","format":"uuid"}},"required":["revoked","id"]}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/me/tenants":{"get":{"tags":["me","tenants"],"summary":"List tenants I own","security":[{"cookieAuth":[]}],"responses":{"200":{"description":"Tenants.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"name":{"type":"string"},"created_at":{"type":"string","nullable":true}},"required":["id","slug","name","created_at"]}}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"post":{"tags":["me","tenants"],"summary":"Create a tenant","security":[{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"slug":{"type":"string","minLength":1,"maxLength":40,"pattern":"^[a-z0-9-]+$"}},"required":["name"]}}}},"responses":{"201":{"description":"Created.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"name":{"type":"string"},"created_at":{"type":"string","nullable":true}},"required":["id","slug","name","created_at"]}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Slug taken.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/me/tenants/{id}":{"get":{"tags":["me","tenants"],"summary":"Get a tenant + its providers","security":[{"cookieAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Tenant.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"name":{"type":"string"},"created_at":{"type":"string","nullable":true},"providers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"display_name":{"type":"string"},"signup_webhook_url":{"type":"string"},"teardown_webhook_url":{"type":"string","nullable":true},"input_schema":{"type":"object","additionalProperties":{"nullable":true}},"needs_email_verification":{"type":"boolean"},"created_at":{"type":"string","nullable":true}},"required":["id","slug","display_name","signup_webhook_url","teardown_webhook_url","input_schema","needs_email_verification","created_at"]}}},"required":["id","slug","name","created_at","providers"]}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/me/tenants/{id}/providers":{"post":{"tags":["me","tenants"],"summary":"Register a tenant provider (webhook secret is returned once)","description":"Relay will dispatch HMAC-signed webhook calls to `signup_webhook_url` when an agent initiates a signup. `needs_email_verification` is reserved for a future phase — today it is stored but not enforced; Relay does NOT yet send a confirmation email to the user before calling the integrator webhook.","security":[{"cookieAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"slug":{"type":"string","minLength":2,"maxLength":60,"pattern":"^[a-z0-9-]+$"},"display_name":{"type":"string","minLength":1,"maxLength":120},"signup_webhook_url":{"type":"string","format":"uri"},"teardown_webhook_url":{"type":"string","format":"uri"},"input_schema":{"type":"object","additionalProperties":{"nullable":true}},"needs_email_verification":{"type":"boolean"}},"required":["slug","display_name","signup_webhook_url"]}}}},"responses":{"201":{"description":"Provider registered.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"webhook_secret":{"type":"string","description":"Plaintext — shown once."}},"required":["id","slug","webhook_secret"]}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Tenant not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Slug taken.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/me/tenants/{id}/providers/{providerId}":{"delete":{"tags":["me","tenants"],"summary":"Remove a tenant provider","security":[{"cookieAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"providerId","in":"path"}],"responses":{"200":{"description":"Deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean","enum":[true]},"id":{"type":"string","format":"uuid"}},"required":["deleted","id"]}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/me/accounts":{"get":{"tags":["me","accounts"],"summary":"List accounts I own","security":[{"cookieAuth":[]}],"responses":{"200":{"description":"Accounts (credentials omitted).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeAccountList"}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/me/accounts/{id}":{"get":{"tags":["me","accounts"],"summary":"Account detail (metadata only)","security":[{"cookieAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeAccount"}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/me/accounts/{id}/api-keys":{"get":{"tags":["me","api-keys"],"summary":"List my API keys for an account","security":[{"cookieAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Non-revoked key bookkeeping. `last_used_at` reflects Relay-observable use (mint, delivery, rotation, legacy reveal).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeApiKeyList"}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"post":{"tags":["me","api-keys"],"summary":"Mint a new API key (plaintext returned once, not stored)","security":[{"cookieAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeApiKeyCreateBody"}}}},"responses":{"201":{"description":"Minted key returned once; not stored.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeApiKeyCreateResponse"}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Server error.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/me/accounts/{id}/api-keys/{keyId}/rotate":{"post":{"tags":["me","api-keys"],"summary":"Rotate an API key (mint new + revoke old; plaintext returned once)","description":"Retrieve-my-key = rotate. Mints a new key via the provider, returns the plaintext EXACTLY ONCE, marks the old key revoked, and attempts best-effort provider-side revocation of the old key (logs and continues on failure).","security":[{"cookieAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"keyId","in":"path"}],"responses":{"200":{"description":"Rotation complete.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeApiKeyRotateResponse"}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Server error.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/agent-guide":{"get":{"tags":["agent-guide"],"summary":"Read the caller-user agent guide","description":"Returns the markdown body the user stores for their AI agents. Agents should fetch this at session start so they inherit the user's preferences and defaults.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The agent guide for the caller-user.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentGuide"}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"retryAfter":{"type":"number"}},"required":["error","retryAfter"]}}}}}},"put":{"tags":["agent-guide"],"summary":"Replace the caller-user agent guide","description":"Replaces the entire guide. Last-write-wins; no versioning. Convention: agents propose edits in chat and PUT only after the user approves — Relay does not enforce this in code.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentGuideWriteBody"}}}},"responses":{"200":{"description":"Guide replaced.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentGuideWriteResponse"}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds 64 KiB cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"retryAfter":{"type":"number"}},"required":["error","retryAfter"]}}}}}}},"/v1/me/agent-guide":{"get":{"tags":["me","agent-guide"],"summary":"Read my agent guide (dashboard)","security":[{"cookieAuth":[]}],"responses":{"200":{"description":"Guide.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentGuide"}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"put":{"tags":["me","agent-guide"],"summary":"Replace my agent guide (dashboard)","security":[{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentGuideWriteBody"}}}},"responses":{"200":{"description":"Guide replaced.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentGuideWriteResponse"}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"413":{"description":"Body exceeds 64 KiB cap.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/cli/start":{"post":{"tags":["cli"],"summary":"Begin the CLI login handshake","responses":{"200":{"description":"Device code created.","content":{"application/json":{"schema":{"type":"object","properties":{"device_code":{"type":"string"},"authorize_url":{"type":"string"},"poll_interval_ms":{"type":"number"},"expires_at":{"type":"string"}},"required":["device_code","authorize_url","poll_interval_ms","expires_at"]}}}}}}},"/v1/cli/poll":{"get":{"tags":["cli"],"summary":"Poll for CLI login approval","parameters":[{"schema":{"type":"string"},"required":true,"name":"device_code","in":"query"}],"responses":{"200":{"description":"Poll result — `pending`, `approved`, or `expired`/`unknown`.","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"status":{"type":"string","enum":["pending"]}},"required":["status"]},{"type":"object","properties":{"status":{"type":"string","enum":["approved"]},"agent_token":{"type":"string"},"user":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"email":{"type":"string"},"inbox_alias":{"type":"string","nullable":true}},"required":["id","email","inbox_alias"]}},"required":["status","agent_token","user"]},{"type":"object","properties":{"status":{"type":"string","enum":["expired"]}},"required":["status"]},{"type":"object","properties":{"status":{"type":"string","enum":["unknown"]}},"required":["status"]}]}}}}}}},"/v1/cron/gc":{"post":{"tags":["cron"],"summary":"Garbage-collect expired ephemeral rows","responses":{"200":{"description":"Counts of deleted rows per table.","content":{"application/json":{"schema":{"type":"object","properties":{"email_otps":{"type":"number"},"webauthn_challenges":{"type":"number"},"sessions":{"type":"number"},"cli_auth_codes":{"type":"number"},"signup_confirmations":{"type":"number"},"magic_links":{"type":"number"},"pending_credentials_scrubbed":{"type":"number"}},"required":["email_otps","webauthn_challenges","sessions","cli_auth_codes","signup_confirmations","magic_links","pending_credentials_scrubbed"]}}}},"401":{"description":"Bad cron secret.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/cron/flush-overage":{"post":{"tags":["cron"],"summary":"Push queued signup overage charges to Stripe invoice items","responses":{"200":{"description":"Counts of rows flushed + skipped.","content":{"application/json":{"schema":{"type":"object","properties":{"flushed":{"type":"number"},"skipped":{"type":"number"}},"required":["flushed","skipped"]}}}},"401":{"description":"Bad cron secret.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/cron/scale-benchmark":{"post":{"tags":["cron"],"summary":"Sample Relay-internal latencies for every Scale tenant","responses":{"200":{"description":"Sample counts per stage.","content":{"application/json":{"schema":{"type":"object","properties":{"tenants_probed":{"type":"number"},"samples_written":{"type":"number"}},"required":["tenants_probed","samples_written"]}}}},"401":{"description":"Bad cron secret.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/tenants":{"post":{"tags":["tenants"],"summary":"Register an integrator as a Relay tenant (agent-driven)","description":"Creates a tenant row owned by the calling agent's user and mints an integrator-scoped bearer token pinned to it. Plaintext integrator key is returned ONCE — store it in the integrator's environment.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"slug":{"type":"string","minLength":1,"maxLength":40,"pattern":"^[a-z0-9-]+$"},"domain":{"type":"string","minLength":3,"maxLength":253,"pattern":"^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$"},"rp_id":{"type":"string","minLength":3,"maxLength":253},"allowed_origins":{"type":"array","items":{"type":"string","maxLength":256,"format":"uri"},"maxItems":10},"expires_in_days":{"type":"integer","minimum":1,"maximum":365},"never_expires":{"type":"boolean"}},"required":["name"]}}}},"responses":{"201":{"description":"Tenant + integrator key.","content":{"application/json":{"schema":{"type":"object","properties":{"tenantId":{"type":"string","format":"uuid"},"slug":{"type":"string"},"name":{"type":"string"},"domain":{"type":"string","nullable":true},"rp_id":{"type":"string","nullable":true},"allowed_origins":{"type":"array","items":{"type":"string"}},"integratorKey":{"type":"string","description":"Plaintext integrator bearer — shown once."},"integratorKeyId":{"type":"string","format":"uuid"},"integratorKeyExpiresAt":{"type":"string","nullable":true,"description":"ISO-8601 instant when this integrator key expires. Null means never expires."}},"required":["tenantId","slug","name","domain","rp_id","allowed_origins","integratorKey","integratorKeyId","integratorKeyExpiresAt"]}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"402":{"description":"Insufficient balance.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Agent has no owning user (cannot own tenants).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Slug or domain already taken.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/integrator/auth/attest":{"post":{"tags":["integrator","auth"],"summary":"Exchange an agent bearer for an integrator attestation JWT","description":"Returns a short-lived (5 min) RS256 JWT pinned to a specific tenant. Integrators verify against /.well-known/jwks.json, match `aud` to their own tenantId, then issue their own session.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"tenantSlug":{"type":"string","minLength":1,"maxLength":40},"tenantId":{"type":"string","format":"uuid"}}}}}},"responses":{"200":{"description":"Attestation JWT.","content":{"application/json":{"schema":{"type":"object","properties":{"jwt":{"type":"string"},"externalUserId":{"type":"string"},"tenantId":{"type":"string","format":"uuid"},"expiresAt":{"type":"string","format":"date-time"}},"required":["jwt","externalUserId","tenantId","expiresAt"]}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"402":{"description":"Insufficient balance.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Agent has no owning user.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Tenant not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Tenant user cap reached.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"current":{"type":"number"},"limit":{"type":"number"},"upgrade_url":{"type":"string"}},"required":["error","current","limit","upgrade_url"]}}}}}}},"/v1/integrator/actions":{"post":{"tags":["integrator","actions"],"summary":"Register a new action (or overwrite an existing one)","description":"Creates an action for the calling tenant. Returns the plaintext HMAC webhook secret ONCE — store it on the integrator's server immediately.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","enum":["true","false"]},"required":false,"in":"query","name":"overwrite"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"slug":{"type":"string","minLength":2,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9_-]*[a-z0-9]$"},"display_name":{"type":"string","minLength":1,"maxLength":120},"description":{"type":"string","maxLength":1000},"endpoint_url":{"type":"string","maxLength":2048,"format":"uri"},"endpoint_method":{"type":"string","enum":["POST","PUT","PATCH"]},"input_schema":{"type":"object","additionalProperties":{"nullable":true}},"output_schema":{"type":"object","additionalProperties":{"nullable":true}},"timeout_ms":{"type":"integer","minimum":1000,"maximum":60000},"visibility":{"type":"string","enum":["public","private"]}},"required":["slug","display_name","endpoint_url"]}}}},"responses":{"201":{"description":"Created.","content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"display_name":{"type":"string"},"description":{"type":"string","nullable":true},"endpoint_url":{"type":"string"},"endpoint_method":{"type":"string"},"input_schema":{"type":"object","additionalProperties":{"nullable":true}},"output_schema":{"type":"object","additionalProperties":{"nullable":true}},"timeout_ms":{"type":"number"},"visibility":{"type":"string"},"disabled_at":{"type":"string","nullable":true},"created_at":{"type":"string","nullable":true},"updated_at":{"type":"string","nullable":true}},"required":["id","slug","display_name","description","endpoint_url","endpoint_method","input_schema","output_schema","timeout_ms","visibility","disabled_at","created_at","updated_at"]},"webhook_secret":{"type":"string","description":"Plaintext — shown once."}},"required":["action","webhook_secret"]}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Slug taken (and ?overwrite=true was not set).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"get":{"tags":["integrator","actions"],"summary":"List this tenant's actions","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Actions.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"display_name":{"type":"string"},"description":{"type":"string","nullable":true},"endpoint_url":{"type":"string"},"endpoint_method":{"type":"string"},"input_schema":{"type":"object","additionalProperties":{"nullable":true}},"output_schema":{"type":"object","additionalProperties":{"nullable":true}},"timeout_ms":{"type":"number"},"visibility":{"type":"string"},"disabled_at":{"type":"string","nullable":true},"created_at":{"type":"string","nullable":true},"updated_at":{"type":"string","nullable":true}},"required":["id","slug","display_name","description","endpoint_url","endpoint_method","input_schema","output_schema","timeout_ms","visibility","disabled_at","created_at","updated_at"]}}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/integrator/actions/{slug}":{"patch":{"tags":["integrator","actions"],"summary":"Update an action","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":2,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9_-]*[a-z0-9]$"},"required":true,"name":"slug","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"display_name":{"type":"string","minLength":1,"maxLength":120},"description":{"type":"string","nullable":true,"maxLength":1000},"endpoint_url":{"type":"string","maxLength":2048,"format":"uri"},"endpoint_method":{"type":"string","enum":["POST","PUT","PATCH"]},"input_schema":{"type":"object","additionalProperties":{"nullable":true}},"output_schema":{"type":"object","additionalProperties":{"nullable":true}},"timeout_ms":{"type":"integer","minimum":1000,"maximum":60000},"visibility":{"type":"string","enum":["public","private"]},"disabled":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Updated.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"display_name":{"type":"string"},"description":{"type":"string","nullable":true},"endpoint_url":{"type":"string"},"endpoint_method":{"type":"string"},"input_schema":{"type":"object","additionalProperties":{"nullable":true}},"output_schema":{"type":"object","additionalProperties":{"nullable":true}},"timeout_ms":{"type":"number"},"visibility":{"type":"string"},"disabled_at":{"type":"string","nullable":true},"created_at":{"type":"string","nullable":true},"updated_at":{"type":"string","nullable":true}},"required":["id","slug","display_name","description","endpoint_url","endpoint_method","input_schema","output_schema","timeout_ms","visibility","disabled_at","created_at","updated_at"]}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"delete":{"tags":["integrator","actions"],"summary":"Disable an action (soft-delete)","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":2,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9_-]*[a-z0-9]$"},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"Disabled.","content":{"application/json":{"schema":{"type":"object","properties":{"disabled":{"type":"boolean","enum":[true]},"id":{"type":"string","format":"uuid"}},"required":["disabled","id"]}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/tenants/{slugOrId}/actions":{"get":{"tags":["actions"],"summary":"List a tenant's public actions","description":"Discovery endpoint for agents. Returns only actions with visibility=public and no disabled_at. Any valid agent bearer may call this.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"slugOrId","in":"path"}],"responses":{"200":{"description":"Catalog.","content":{"application/json":{"schema":{"type":"object","properties":{"tenantId":{"type":"string","format":"uuid"},"tenantSlug":{"type":"string"},"actions":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"display_name":{"type":"string"},"description":{"type":"string","nullable":true},"input_schema":{"type":"object","additionalProperties":{"nullable":true}},"output_schema":{"type":"object","additionalProperties":{"nullable":true}}},"required":["slug","display_name","description","input_schema","output_schema"]}}},"required":["tenantId","tenantSlug","actions"]}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Tenant not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/actions/execute":{"post":{"tags":["actions"],"summary":"Invoke an action on an integrator","description":"Dispatches an HMAC-signed call to the integrator's registered endpoint. Charges 1 token against the caller's wallet (price: action_execute). Counts against the tenant's monthly quota. Supports Idempotency-Key header (24h window).","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":8,"maxLength":128},"required":false,"name":"idempotency-key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"tenantSlug":{"type":"string","minLength":1,"maxLength":40},"tenantId":{"type":"string","format":"uuid"},"actionSlug":{"type":"string","minLength":1,"maxLength":64},"externalUserId":{"type":"string","minLength":1,"maxLength":128},"input":{"type":"object","additionalProperties":{"nullable":true},"default":{}}},"required":["actionSlug"]}}}},"responses":{"200":{"description":"Action executed.","content":{"application/json":{"schema":{"type":"object","properties":{"invocationId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["succeeded","overage"]},"latencyMs":{"type":"number"},"output":{"nullable":true}},"required":["invocationId","status","latencyMs"]}}}},"400":{"description":"Bad input (schema violation or malformed body).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"violations":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["error"]}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"402":{"description":"Insufficient balance.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Identity not bound to this tenant.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Tenant or action not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Tenant monthly action quota exceeded.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"current":{"type":"number"},"included":{"type":"number"},"upgrade_url":{"type":"string"}},"required":["error","current","included","upgrade_url"]}}}},"502":{"description":"Integrator returned an error or is degraded.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Tenant subscription inactive.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"504":{"description":"Integrator timed out — invocation status unknown.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"invocationId":{"type":"string","format":"uuid"},"reconcileHint":{"type":"string"}},"required":["error","invocationId","reconcileHint"]}}}}}}},"/v1/actions/invocations/{id}/reconcile":{"post":{"tags":["actions"],"summary":"Reconcile an unknown-status invocation","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"outcome":{"type":"string","enum":["succeeded","failed"]},"note":{"type":"string","maxLength":500}},"required":["outcome"]}}}},"responses":{"200":{"description":"Reconciled.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string"}},"required":["id","status"]}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not found or not reconcilable.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/agents/bootstrap":{"post":{"tags":["integrator","auth"],"summary":"Start an agent bootstrap (sends OTP to email)","description":"No bearer required — used by a fresh agent that has never had a Relay token. Sends a 6-digit code to the email and returns a signed challenge to be echoed back on /verify along with the code.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"},"tenantSlug":{"type":"string","minLength":1,"maxLength":40}},"required":["email","tenantSlug"]}}}},"responses":{"200":{"description":"OTP sent.","content":{"application/json":{"schema":{"type":"object","properties":{"challenge":{"type":"string"},"expiresAt":{"type":"string","format":"date-time"}},"required":["challenge","expiresAt"]}}}},"400":{"description":"Bad request / email send failed.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Unknown tenant slug.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limit.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/agents/bootstrap/verify":{"post":{"tags":["integrator","auth"],"summary":"Verify OTP and receive a fresh agent token","description":"Exchanges the challenge + OTP code for a Relay agent bearer token (plaintext, shown once) and the stable external_user_id the integrator should key its local user on. Auto-creates the Relay user on first verify and grants the free-tier token balance.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"challenge":{"type":"string","minLength":10},"code":{"type":"string","pattern":"^\\d{6}$"}},"required":["challenge","code"]}}}},"responses":{"200":{"description":"Agent token + external user id.","content":{"application/json":{"schema":{"type":"object","properties":{"agentToken":{"type":"string","description":"Plaintext bearer — shown ONCE. Store it on the agent."},"agentId":{"type":"string","format":"uuid"},"externalUserId":{"type":"string"},"tenantId":{"type":"string","format":"uuid"},"userId":{"type":"string","format":"uuid"},"created":{"type":"boolean"}},"required":["agentToken","agentId","externalUserId","tenantId","userId","created"]}}}},"401":{"description":"Invalid challenge or code.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/admin/users/{id}/raise-limit":{"post":{"tags":["admin"],"summary":"Raise a user's monthly signup cap (admin scope only)","description":"Sets `users.signup_limit_override`. Pass `limit: null` to clear the override and fall back to USER_SIGNUP_MONTHLY_LIMIT.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"limit":{"type":"integer","nullable":true,"minimum":0,"exclusiveMinimum":true}},"required":["limit"]}}}},"responses":{"200":{"description":"Updated.","content":{"application/json":{"schema":{"type":"object","properties":{"user_id":{"type":"string","format":"uuid"},"signup_limit_override":{"type":"integer","nullable":true}},"required":["user_id","signup_limit_override"]}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Admin scope required.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"User not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/providers":{"get":{"tags":["providers"],"summary":"List registered providers","description":"Returns every public provider currently registered on the server, with JSON Schema input + discovery metadata. Pass ?include=demo to also surface internal demo providers (Neon/Vercel/Resend operator self-service); demos are hidden by default so the public catalog reflects the real product surface.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","enum":["demo"],"description":"Pass `demo` to include internal operator-self-service providers in the response."},"required":false,"description":"Pass `demo` to include internal operator-self-service providers in the response.","name":"include","in":"query"}],"responses":{"200":{"description":"Registered providers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderList"}}}},"401":{"description":"Missing or invalid bearer token.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"retryAfter":{"type":"number"}},"required":["error","retryAfter"]}}}}}}},"/v1/providers/{id}":{"get":{"tags":["providers"],"summary":"Get a provider by id","description":"Returns a single provider with full metadata + input JSON Schema. 404 if no static provider or tenant_providers row matches.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","example":"neon"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Provider metadata + input JSON Schema.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Provider"}}}},"401":{"description":"Missing or invalid bearer token.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No provider registered with that id.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"retryAfter":{"type":"number"}},"required":["error","retryAfter"]}}}}}}},"/v1/index":{"get":{"tags":["index"],"summary":"Provider index — top-level categories","description":"Returns the list of categories that currently hold at least one registered provider, with counts, provider ids, and the alias map agents can use for fuzzy lookups. Follow up with GET /v1/index/:category to fetch full provider details in that category. Public — no auth required. Pass ?include=demo to also surface internal demo providers.","parameters":[{"schema":{"type":"string","enum":["demo"],"description":"Pass `demo` to also surface internal operator-self-service providers in the response."},"required":false,"description":"Pass `demo` to also surface internal operator-self-service providers in the response.","name":"include","in":"query"}],"responses":{"200":{"description":"Index overview.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IndexOverview"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"retryAfter":{"type":"number"}},"required":["error","retryAfter"]}}}}}}},"/v1/index/{category}":{"get":{"tags":["index"],"summary":"Provider index — per-category chunk","description":"Returns the full ProviderSummary[] (including pricingModel, capabilities, docsUrl, and inputSchema) for every provider in the requested category. Aliases are resolved server-side (e.g. \"hoster\" → \"hosting\"). Optional filters: ?capability=<tag> (repeatable, AND) and ?pricing=<model>. Public — no auth required.","parameters":[{"schema":{"type":"string","example":"database"},"required":true,"name":"category","in":"path"},{"schema":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"example":"postgres","description":"Capability tag to require. Repeat the query param to AND multiple tags."},"required":false,"description":"Capability tag to require. Repeat the query param to AND multiple tags.","name":"capability","in":"query"},{"schema":{"type":"string","enum":["free","free-tier","paid","usage-based","freemium"]},"required":false,"name":"pricing","in":"query"},{"schema":{"type":"string","enum":["demo"],"description":"Pass `demo` to also surface internal operator-self-service providers in the response."},"required":false,"description":"Pass `demo` to also surface internal operator-self-service providers in the response.","name":"include","in":"query"}],"responses":{"200":{"description":"Providers in this category.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IndexCategorySlice"}}}},"404":{"description":"Unknown category and no matching alias.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnknownCategoryError"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"retryAfter":{"type":"number"}},"required":["error","retryAfter"]}}}}}}},"/v1/signups":{"post":{"tags":["signups"],"summary":"Start a signup","description":"Starts a durable workflow that provisions a new account on the named provider. Returns immediately; poll GET /v1/signups/:id for the result.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupCreateBody"}}}},"responses":{"202":{"description":"Signup started.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupCreateResponse"}}}},"400":{"description":"Invalid body.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Provider not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded or integrator signup quota exhausted.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"retryAfter":{"type":"number"}},"required":["error"]}}}},"502":{"description":"Workflow failed to start.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Integrator's Relay subscription is inactive; their product is temporarily unavailable.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/signups/{id}":{"get":{"tags":["signups"],"summary":"Poll signup status","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Current signup job state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupStatusResponse"}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"retryAfter":{"type":"number"}},"required":["error","retryAfter"]}}}}}}},"/v1/signups/{id}/confirm/{token}":{"get":{"tags":["signups"],"summary":"Confirm an agent-initiated signup (end-user clicks link from email)","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","minLength":16},"required":true,"name":"token","in":"path"}],"responses":{"200":{"description":"Confirmation page (HTML).","content":{"text/html":{"schema":{"type":"string"}}}},"404":{"description":"Unknown or malformed confirmation.","content":{"text/html":{"schema":{"type":"string"}}}},"410":{"description":"Link expired.","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/v1/intent":{"post":{"tags":["intent"],"summary":"Resolve a goal to an env block","description":"Parses a natural-language goal, dedups against existing accounts, and kicks signups for the gaps. Returns a paste-ready env block plus a list of signup_job_ids to poll for any provisioning still in flight. Always returns 200 — partial success (some categories provisioning, some with no provider) is the expected shape.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntentBody"}}}},"responses":{"200":{"description":"Resolution payload (partial success is normal).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntentResponse"}}}},"400":{"description":"Invalid body.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Workspace not found or not owned by this agent.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"retryAfter":{"type":"number"}},"required":["error"]}}}}}}},"/v1/accounts":{"get":{"tags":["accounts"],"summary":"List accounts","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"All accounts (credentials omitted).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountList"}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"retryAfter":{"type":"number"}},"required":["error"]}}}}}}},"/v1/audit-log":{"get":{"tags":["audit"],"summary":"List audit-log entries","description":"Non-admin agents see only audit rows scoped to their own user. Agents with the 'admin' scope see platform-wide audit rows.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Max rows to return (1-200).","example":"50"},"required":false,"description":"Max rows to return (1-200).","name":"limit","in":"query"},{"schema":{"type":"string","description":"Pagination offset.","example":"0"},"required":false,"description":"Pagination offset.","name":"offset","in":"query"}],"responses":{"200":{"description":"Paginated audit-log entries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditLogResponse"}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"retryAfter":{"type":"number"}},"required":["error"]}}}}}}},"/v1/accounts/{id}":{"get":{"tags":["accounts"],"summary":"Get an account","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"The account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Account"}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"retryAfter":{"type":"number"}},"required":["error"]}}}}}},"delete":{"tags":["accounts"],"summary":"Delete an account","description":"Calls the provider teardown if supported, removes child rows, deletes the account, and writes an audit-log entry.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Account deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean","enum":[true]},"id":{"type":"string","format":"uuid"}},"required":["deleted","id"]}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded or quota exhausted.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"retryAfter":{"type":"number"}},"required":["error"]}}}},"503":{"description":"Integrator's Relay subscription is inactive; their product is temporarily unavailable.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/accounts/{id}/api-keys":{"get":{"tags":["api-keys"],"summary":"List API keys for an account","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Non-revoked API key bookkeeping rows. Key bytes are never returned here; they are revealed only at mint time.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyList"}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Account not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"retryAfter":{"type":"number"}},"required":["error"]}}}}}},"post":{"tags":["api-keys"],"summary":"Mint a new API key (plaintext returned once, not stored)","description":"Mints a new API key on the provider. Relay returns the plaintext EXACTLY ONCE in the response and persists ONLY the label + provider_key_id so the key can be revoked later. Agents must hand the plaintext to the user in-chat; Relay does not retain a copy.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreateBody"}}}},"responses":{"201":{"description":"Minted key returned once; not stored.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreateResponse"}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Account not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded or quota exhausted.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"retryAfter":{"type":"number"}},"required":["error"]}}}},"500":{"description":"Server error.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Integrator's Relay subscription is inactive; their product is temporarily unavailable.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/accounts/{id}/api-keys/{keyId}/reveal":{"post":{"tags":["api-keys"],"summary":"Reveal a legacy stored API key (one-time, then scrubbed)","description":"Legacy-only: decrypts and returns the plaintext for a key created before the zero-retention policy, then scrubs the key_enc column. New keys have never been stored and cannot be revealed — call POST /v1/accounts/:id/api-keys to mint a fresh one.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"keyId","in":"path"}],"responses":{"200":{"description":"Plaintext key revealed (and then scrubbed).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRevealResponse"}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"410":{"description":"Key bytes not stored — mint a new key instead.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded or quota exhausted.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"retryAfter":{"type":"number"}},"required":["error"]}}}},"500":{"description":"Server error.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Integrator's Relay subscription is inactive; their product is temporarily unavailable.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/accounts/{id}/api-keys/{keyId}/rotate":{"post":{"tags":["api-keys"],"summary":"Rotate an API key (mint new + revoke old; plaintext returned once)","description":"Agent-facing rotate. Mints a new key via the provider, returns the plaintext EXACTLY ONCE, revokes the old key, and attempts best-effort provider-side revocation. If the plaintext is lost, rotate again — Relay never stores key bytes.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"keyId","in":"path"}],"responses":{"200":{"description":"Rotation complete.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRotateResponse"}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded or quota exhausted.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"retryAfter":{"type":"number"}},"required":["error"]}}}},"500":{"description":"Server error.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Integrator's Relay subscription is inactive; their product is temporarily unavailable.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/accounts/keys/reveal-batch":{"post":{"tags":["api-keys"],"summary":"Reveal multiple legacy API keys in one call","description":"Batched legacy reveal — used by /v1/intent consumers to populate an env block without N+1 round-trips. Each key in `keyIds` is processed independently; per-key failures (zero-retention, rate limit, missing) come back inline with a status code, never aborting the batch. Successful reveals scrub key bytes just like the per-key endpoint.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevealBatchBody"}}}},"responses":{"200":{"description":"Per-key results.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevealBatchResponse"}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded for the batch dispatch (per-key limits report inline).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"retryAfter":{"type":"number"}},"required":["error"]}}}}}}},"/v1/dev/billing/subscribe":{"post":{"tags":["billing"],"summary":"Create a Stripe subscription Checkout session for the active tenant","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"type":"string","enum":["founders","builder","starter","growth","scale"]},"interval":{"type":"string","enum":["monthly","yearly"],"default":"monthly","description":"Billing cadence. Yearly tiers ship at a 17% discount (≈ 2 months free). Founders is monthly-only."}},"required":["plan"]}}}},"responses":{"200":{"description":"Redirect URL to the Stripe-hosted Checkout page.","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"}},"required":["url"]}}}},"400":{"description":"Plan is not configured in env.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/dev/billing/credits/checkout":{"post":{"tags":["billing"],"summary":"Buy a one-shot credit pack (extra actions for the current period)","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"pack":{"type":"string","enum":["builder","starter","growth","scale"]}},"required":["pack"]}}}},"responses":{"200":{"description":"Stripe Checkout URL. Mode=payment.","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"pack":{"type":"object","properties":{"id":{"type":"string"},"actions":{"type":"number"},"amount_cents":{"type":"number"}},"required":["id","actions","amount_cents"]}},"required":["url","pack"]}}}},"400":{"description":"Pack is not configured in env.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/dev/billing/credits":{"get":{"tags":["billing"],"summary":"List the tenant’s credit-pack purchases (unspent + spent)","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Credit packs with remaining balance + the SKU catalog.","content":{"application/json":{"schema":{"type":"object","properties":{"total_remaining":{"type":"number"},"credits":{"type":"array","items":{"type":"object","properties":{"pack_id":{"type":"string"},"actions_purchased":{"type":"number"},"actions_remaining":{"type":"number"},"amount_cents_paid":{"type":"number"},"expires_at":{"type":"string"},"created_at":{"type":"string"}},"required":["pack_id","actions_purchased","actions_remaining","amount_cents_paid","expires_at","created_at"]}},"packs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"plan":{"type":"string"},"actions":{"type":"number"},"amount_cents":{"type":"number"},"effective_cents_per_action":{"type":"number"}},"required":["id","plan","actions","amount_cents","effective_cents_per_action"]}}},"required":["total_remaining","credits","packs"]}}}}}}},"/v1/dev/billing/portal":{"post":{"tags":["billing"],"summary":"Open the Stripe Customer Portal for this tenant","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Redirect URL.","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"}},"required":["url"]}}}},"400":{"description":"No active subscription / no customer on file.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/v1/dev/billing/summary":{"get":{"tags":["billing"],"summary":"Active subscription + quota state for the current tenant","security":[{"cookieAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Subscription snapshot, quota state, credit packs, and plan catalog.","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","nullable":true},"plan":{"type":"string","nullable":true},"billing_interval":{"type":"string","nullable":true},"current_period_end":{"type":"string","nullable":true},"trial_ends_at":{"type":"string","nullable":true},"canceled_at":{"type":"string","nullable":true},"stripe_customer_id":{"type":"string","nullable":true},"quota":{"type":"object","nullable":true,"properties":{"included_total":{"type":"number"},"included_remaining":{"type":"number"},"overage_count":{"type":"number"},"overage_price_cents":{"type":"number"},"overage_spend_cents":{"type":"number"},"period_start":{"type":"string","nullable":true},"period_end":{"type":"string","nullable":true}},"required":["included_total","included_remaining","overage_count","overage_price_cents","overage_spend_cents","period_start","period_end"]},"credits":{"type":"object","properties":{"total_remaining":{"type":"number"}},"required":["total_remaining"]},"plans":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"display_name":{"type":"string"},"price_cents":{"type":"number"},"included_actions":{"type":"number"},"overage_price_cents":{"type":"number"},"trial_actions":{"type":"number","nullable":true},"trial_days":{"type":"number","nullable":true},"sla_target":{"type":"string","nullable":true}},"required":["id","display_name","price_cents","included_actions","overage_price_cents","trial_actions","trial_days","sla_target"]}}},"required":["status","plan","billing_interval","current_period_end","trial_ends_at","canceled_at","stripe_customer_id","quota","credits","plans"]}}}}}}},"/v1/whoami":{"get":{"tags":["meta"],"summary":"Identify the caller","description":"Returns the agent, user, and tenant associated with the bearer token. Use this as a zero-side-effect proof that the token works.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Authenticated agent profile.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Whoami"}}}},"401":{"description":"Missing or invalid bearer token.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"retryAfter":{"type":"number"}},"required":["error","retryAfter"]}}}}}}}}}