# AgentGate customer API contract (OpenAPI 3.1): /v1/account/* and /v1/console/*. # # Same JSON-compatible flow style as openapi-admin.yaml (strip these leading # comment lines and it is JSON). Console operations are the admin API's # handlers mounted for customers, so their schemas are referenced from # openapi-admin.yaml. TestOpenAPICoversConsoleRoutes keeps this complete. # # Public API (nginx passes /v1/account/ and /v1/console/). /v1/console needs a # customer session: the ag_account cookie (HttpOnly, SameSite=Lax, Path=/) # from login, invite/accept or password/reset, plus X-CSRF-Token on # mutations (csrfToken from those responses or GET /v1/account/session). # Cross-site requests are refused. Operator credentials do not work here, and # this cookie does not work on /v1/admin. # # /v1/console also accepts an account API token instead of the session: # "Authorization: Bearer agt__" (no CSRF token needed), limited # to the token's scopes (sites:read, sites:write, analytics:read, # members:read). Tokens never work on /v1/account or /v1/admin. # # Two-factor authentication: when the user has 2FA on, login answers # {mfaRequired:true} and sets a 5-minute pre-session; POST # /v1/account/login/mfa with a code completes the sign-in. Until then every # session route answers 401 mfa_required. An account that requires 2FA # answers 403 mfa_setup_required on /v1/console for members without it. { "openapi": "3.1.0", "info": { "title": "AgentGate customer API", "version": "1.0.0", "description": "Accounts (request access, sign in, invites, password reset) and the customer console: the customer's own sites, credentials, rules, install check and traffic. A site of another customer answers 404 site_not_found exactly like an unknown key. Limits are per account (default 5 sites, 3 live credentials per site, 10 members, 25 live API tokens; GET /v1/console/account). Every console route needs a permission of the caller's role in the active account (owner, admin, member, readonly): sites:read for reads, sites:write for changes, analytics:read for traffic, members:*, tokens:write, account:*, audit:read; a missing one is 403 forbidden naming it. A suspended account answers 403 account_suspended (its sites keep protecting); a suspended user 403 user_suspended; a user with no account 403 no_account." }, "servers": [{ "url": "https://agentgate.proto.chakshu.co.in" }], "security": [{ "session": [], "csrf": [] }, { "bearer": [] }], "tags": [{ "name": "account" }, { "name": "console" }], "paths": { "/v1/account/request-access": { "post": { "tags": ["account"], "operationId": "requestAccess", "security": [], "summary": "Ask for an account; the same answer whether or not the address is known", "description": "Needs a receipt from the signup page's AgentGate browser check (site_signup, action request_access). 5 per source per hour; at most 3 stored per email per day (further ones answer the same). SIGNUP_MODE=open approves at once.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessRequestInput" } } } }, "responses": { "202": { "description": "Received", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Status" } } } }, "400": { "$ref": "#/components/responses/Error" }, "403": { "$ref": "#/components/responses/Error" }, "429": { "$ref": "#/components/responses/Error" } } } }, "/v1/account/login": { "post": { "tags": ["account"], "operationId": "accountLogin", "security": [], "summary": "Sign in; sets ag_account", "description": "10 attempts per source per minute. 5 failures for an account within 15 minutes pause its sign-in for 15 minutes. Every failure is 401 invalid_credentials. A user with 2FA gets MfaChallenge and a 5-minute pre-session cookie instead of a session: continue with POST /v1/account/login/mfa.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LoginInput" } } } }, "responses": { "200": { "description": "Signed in, or the second factor is needed", "content": { "application/json": { "schema": { "oneOf": [{ "$ref": "#/components/schemas/AccountSession" }, { "$ref": "#/components/schemas/MfaChallenge" }] } } } }, "400": { "$ref": "#/components/responses/Error" }, "401": { "$ref": "#/components/responses/Error" }, "403": { "$ref": "#/components/responses/Error" }, "429": { "$ref": "#/components/responses/Error" } } } }, "/v1/account/logout": { "post": { "tags": ["account"], "operationId": "accountLogout", "summary": "End the session (also a 2FA pre-session)", "security": [{ "session": [], "csrf": [] }], "responses": { "204": { "description": "Signed out" }, "401": { "$ref": "#/components/responses/Error" }, "403": { "$ref": "#/components/responses/Error" } } } }, "/v1/account/session": { "get": { "tags": ["account"], "operationId": "accountSession", "summary": "The signed-in user, the customer and the CSRF token (401 mfa_required during the 2FA step)", "security": [{ "session": [] }], "responses": { "200": { "description": "Session", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccountSession" } } } }, "401": { "$ref": "#/components/responses/Error" } } } }, "/v1/account/invite/{token}": { "parameters": [{ "name": "token", "in": "path", "required": true, "schema": { "type": "string" } }], "get": { "tags": ["account"], "operationId": "peekInvite", "security": [], "summary": "Who an invite link is for, without using it", "responses": { "200": { "description": "Invite", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InvitePeek" } } } }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/account/invite/accept": { "post": { "tags": ["account"], "operationId": "acceptInvite", "security": [], "summary": "Set the first password with an invite link (single use, 7 days) and sign in", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TokenPassword" } } } }, "responses": { "200": { "description": "Signed in", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccountSession" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/account/password/forgot": { "post": { "tags": ["account"], "operationId": "forgotPassword", "security": [], "summary": "Email a reset link (1 hour) if an account uses the address; always 202", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["email"], "additionalProperties": false, "properties": { "email": { "type": "string" } } } } } }, "responses": { "202": { "description": "Accepted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Status" } } } }, "429": { "$ref": "#/components/responses/Error" } } } }, "/v1/account/password/reset": { "post": { "tags": ["account"], "operationId": "resetPassword", "security": [], "summary": "Set a new password with a reset link; ends the user's other sessions and signs in", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TokenPassword" } } } }, "responses": { "200": { "description": "Signed in", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccountSession" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/account/switch": { "post": { "tags": ["account"], "operationId": "switchAccount", "summary": "Make another of the user's accounts the session's active one (404 not_member otherwise); answers the new session info", "description": "Works while the current account is suspended or gone, so the user can move on. Needs a session (API tokens are not accepted on /v1/account).", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["customerId"], "additionalProperties": false, "properties": { "customerId": { "type": "string" } } } } } }, "responses": { "200": { "description": "Switched", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccountSession" } } } }, "400": { "$ref": "#/components/responses/Error" }, "401": { "$ref": "#/components/responses/Error" }, "403": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/account/accounts": { "post": { "tags": ["account"], "operationId": "createAccount", "summary": "Create another account (the caller becomes its owner); at most 3 owned accounts per user (409 account_limit). The session stays on its current account; switch to use the new one.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["name"], "additionalProperties": false, "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 200 } } } } } }, "responses": { "201": { "description": "Created", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/AccountSummary" } } } }, "400": { "$ref": "#/components/responses/Error" }, "401": { "$ref": "#/components/responses/Error" }, "403": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } }, "/v1/console/account": { "get": { "tags": ["console"], "operationId": "consoleGetAccount", "summary": "The active account: name, status, limits and usage (sites:read)", "responses": { "200": { "description": "Account", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/ConsoleAccount" } } } }, "403": { "$ref": "#/components/responses/Error" } } }, "patch": { "tags": ["console"], "operationId": "consolePatchAccount", "summary": "Rename the account or set requireMfa (account:write); answers the account", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/AccountPatch" } } } }, "responses": { "200": { "description": "Account", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/ConsoleAccount" } } } }, "400": { "$ref": "#/components/responses/Error" }, "403": { "$ref": "#/components/responses/Error" } } }, "delete": { "tags": ["console"], "operationId": "consoleDeleteAccount", "summary": "Delete the account (owner: account:delete). confirm must be the account name (400 confirm_mismatch); refused while any site enforces (409 sites_enforcing). Sites stop issuing challenges; site credentials, sessions on the account and API tokens are revoked; data is purged after 30 days.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/ConfirmInput" } } } }, "responses": { "200": { "description": "Deleted", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/DeletedAccount" } } } }, "400": { "$ref": "#/components/responses/Error" }, "403": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } }, "/v1/console/account/transfer-ownership": { "post": { "tags": ["console"], "operationId": "consoleTransferOwnership", "summary": "Make another member (activated, not suspended) the owner; the caller becomes an admin (owner only: account:delete). Answers the member list.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["userId"], "additionalProperties": false, "properties": { "userId": { "type": "string" } } } } } }, "responses": { "200": { "description": "Members", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/MemberList" } } } }, "400": { "$ref": "#/components/responses/Error" }, "403": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } }, "/v1/console/members": { "get": { "tags": ["console"], "operationId": "consoleListMembers", "summary": "The account's members (members:read)", "responses": { "200": { "description": "Members", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/MemberList" } } } }, "403": { "$ref": "#/components/responses/Error" } } }, "post": { "tags": ["console"], "operationId": "consoleInviteMember", "summary": "Invite someone with a role (members:write; only owners add owners). An existing user is added and notified; a new address gets an email with an invite link (never in the response). 409 member_limit / already_member.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/MemberInput" } } } }, "responses": { "201": { "description": "Invited", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/Member" } } } }, "400": { "$ref": "#/components/responses/Error" }, "403": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } }, "/v1/console/members/{userId}": { "parameters": [{ "name": "userId", "in": "path", "required": true, "schema": { "type": "string" } }], "patch": { "tags": ["console"], "operationId": "consolePatchMember", "summary": "Change a member's role (members:write). Only owners change owners or make one; the last owner cannot be demoted (409 last_owner).", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/RoleInput" } } } }, "responses": { "200": { "description": "Member", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/Member" } } } }, "400": { "$ref": "#/components/responses/Error" }, "403": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } }, "delete": { "tags": ["console"], "operationId": "consoleRemoveMember", "summary": "Remove a member (members:write; owners only for an owner; never the last owner)", "responses": { "204": { "description": "Removed" }, "403": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } }, "/v1/console/members/{userId}/resend-invite": { "parameters": [{ "name": "userId", "in": "path", "required": true, "schema": { "type": "string" } }], "post": { "tags": ["console"], "operationId": "consoleResendInvite", "summary": "Email a fresh invite link to a member who has not set a password (members:write); 409 already_active", "responses": { "202": { "description": "Sent", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Status" } } } }, "403": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } }, "/v1/console/audit": { "get": { "tags": ["console"], "operationId": "consoleAudit", "summary": "The account's audit log, newest first (audit:read)", "parameters": [{ "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 200, "default": 50 } }, { "name": "cursor", "in": "query", "schema": { "type": "string" } }, { "name": "action", "in": "query", "schema": { "type": "string" }, "description": "An action and its dotted children (member matches member.*)" }], "responses": { "200": { "description": "Events", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/AuditPage" } } } }, "400": { "$ref": "#/components/responses/Error" }, "403": { "$ref": "#/components/responses/Error" } } } }, "/v1/account/login/mfa": { "post": { "tags": ["account"], "operationId": "accountLoginMfa", "security": [{ "session": [] }], "summary": "Complete a sign-in with a 2FA code or a recovery code; replaces the pre-session with a session", "description": "Needs the pre-session cookie from login. Send exactly one of code (6 digits, current 30-second step ±1, each step once) or recoveryCode (single use). 10 attempts per source per minute; 5 wrong codes for a user within 15 minutes pause code checks for 15 minutes (429).", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MfaCodeInput" } } } }, "responses": { "200": { "description": "Signed in", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccountSession" } } } }, "400": { "$ref": "#/components/responses/Error" }, "401": { "$ref": "#/components/responses/Error" }, "403": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" }, "429": { "$ref": "#/components/responses/Error" } } } }, "/v1/account/mfa": { "get": { "tags": ["account"], "operationId": "accountMfaStatus", "security": [{ "session": [] }], "summary": "The user's 2FA state and whether the active account requires it", "responses": { "200": { "description": "2FA state", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MfaStatus" } } } }, "401": { "$ref": "#/components/responses/Error" } } } }, "/v1/account/mfa/setup": { "post": { "tags": ["account"], "operationId": "accountMfaSetup", "security": [{ "session": [], "csrf": [] }], "summary": "Start 2FA setup: a new pending TOTP secret (SHA1, 6 digits, 30 s); render otpauthUrl as a QR code client-side", "responses": { "200": { "description": "Pending secret", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MfaSetup" } } } }, "401": { "$ref": "#/components/responses/Error" }, "403": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } }, "/v1/account/mfa/enable": { "post": { "tags": ["account"], "operationId": "accountMfaEnable", "security": [{ "session": [], "csrf": [] }], "summary": "Turn 2FA on with a code from the pending secret; returns 10 single-use recovery codes once. Other sessions of the user must then pass 2FA.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["code"], "additionalProperties": false, "properties": { "code": { "type": "string" } } } } } }, "responses": { "200": { "description": "Enabled", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MfaRecoveryCodes" } } } }, "400": { "$ref": "#/components/responses/Error" }, "401": { "$ref": "#/components/responses/Error" }, "403": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" }, "429": { "$ref": "#/components/responses/Error" } } } }, "/v1/account/mfa/disable": { "post": { "tags": ["account"], "operationId": "accountMfaDisable", "security": [{ "session": [], "csrf": [] }], "summary": "Turn 2FA off: the password and a code or recovery code (409 mfa_required_by_account when the account requires 2FA)", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MfaDisableInput" } } } }, "responses": { "204": { "description": "Disabled" }, "400": { "$ref": "#/components/responses/Error" }, "401": { "$ref": "#/components/responses/Error" }, "403": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" }, "429": { "$ref": "#/components/responses/Error" } } } }, "/v1/account/password": { "post": { "tags": ["account"], "operationId": "accountChangePassword", "security": [{ "session": [], "csrf": [] }], "summary": "Change the password (current one required; 5 wrong in 15 minutes pause checks); ends every other session of the user", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PasswordChangeInput" } } } }, "responses": { "200": { "description": "Changed", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PasswordChanged" } } } }, "400": { "$ref": "#/components/responses/Error" }, "401": { "$ref": "#/components/responses/Error" }, "403": { "$ref": "#/components/responses/Error" }, "429": { "$ref": "#/components/responses/Error" } } } }, "/v1/account/sessions": { "get": { "tags": ["account"], "operationId": "accountListSessions", "security": [{ "session": [] }], "summary": "The user's live sessions (browser and pseudonymous address; never the IP)", "responses": { "200": { "description": "Sessions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SessionList" } } } }, "401": { "$ref": "#/components/responses/Error" } } } }, "/v1/account/sessions/{id}": { "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }], "delete": { "tags": ["account"], "operationId": "accountRevokeSession", "security": [{ "session": [], "csrf": [] }], "summary": "End one of the user's sessions (the current one signs out)", "responses": { "204": { "description": "Ended" }, "401": { "$ref": "#/components/responses/Error" }, "403": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/account/sessions/revoke-others": { "post": { "tags": ["account"], "operationId": "accountRevokeOtherSessions", "security": [{ "session": [], "csrf": [] }], "summary": "End every session of the user except this one", "responses": { "200": { "description": "Ended", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SessionsRevoked" } } } }, "401": { "$ref": "#/components/responses/Error" }, "403": { "$ref": "#/components/responses/Error" } } } }, "/v1/console/sites": { "get": { "tags": ["console"], "operationId": "consoleListSites", "summary": "The customer's sites", "responses": { "200": { "description": "Sites", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/SiteList" } } } } } }, "post": { "tags": ["console"], "operationId": "consoleCreateSite", "summary": "Add a site (monitor mode, widget mode invisible unless given); siteKey, customerId and customerName are refused; default action submit; at most 10 allowed origins (400 origin_limit)", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/SiteInput" } } } }, "responses": { "201": { "description": "Created", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/SiteDetail" } } } }, "400": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } }, "/v1/console/sites/{siteKey}": { "parameters": [{ "$ref": "#/components/parameters/SiteKeyPath" }], "get": { "tags": ["console"], "operationId": "consoleGetSite", "summary": "A site with credentials, changes and rules summary", "responses": { "200": { "description": "Site", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/SiteDetail" } } } }, "404": { "$ref": "#/components/responses/Error" } } }, "patch": { "tags": ["console"], "operationId": "consoleUpdateSite", "summary": "Change name, origins (at most 10), actions, routes, mode or widget mode", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/SiteInput" } } } }, "responses": { "200": { "description": "Site", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/SiteDetail" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/console/sites/{siteKey}/changes": { "parameters": [{ "$ref": "#/components/parameters/SiteKeyPath" }], "get": { "tags": ["console"], "operationId": "consoleSiteChanges", "summary": "The site's audit trail", "responses": { "200": { "description": "Changes", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/ChangeList" } } } }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/console/sites/{siteKey}/credentials": { "parameters": [{ "$ref": "#/components/parameters/SiteKeyPath" }], "post": { "tags": ["console"], "operationId": "consoleCreateCredential", "summary": "A backend credential, shown once (at most 3 live per site)", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/CredentialInput" } } } }, "responses": { "201": { "description": "Created", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/CredentialSecret" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } }, "/v1/console/sites/{siteKey}/credentials/{credId}/rotate": { "parameters": [{ "$ref": "#/components/parameters/SiteKeyPath" }, { "name": "credId", "in": "path", "required": true, "schema": { "type": "string" } }], "post": { "tags": ["console"], "operationId": "consoleRotateCredential", "summary": "Replace a credential; the old one works for overlapSeconds", "requestBody": { "required": false, "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/RotateInput" } } } }, "responses": { "201": { "description": "Replacement", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/CredentialSecret" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/console/sites/{siteKey}/credentials/{credId}/revoke": { "parameters": [{ "$ref": "#/components/parameters/SiteKeyPath" }, { "name": "credId", "in": "path", "required": true, "schema": { "type": "string" } }], "post": { "tags": ["console"], "operationId": "consoleRevokeCredential", "summary": "Revoke a credential now", "responses": { "200": { "description": "Revoked", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/Credential" } } } }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/console/sites/{siteKey}/rules": { "parameters": [{ "$ref": "#/components/parameters/SiteKeyPath" }], "get": { "tags": ["console"], "operationId": "consoleGetRules", "summary": "The site's rule set and overrides", "responses": { "200": { "description": "Rules", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/RulesView" } } } }, "404": { "$ref": "#/components/responses/Error" } } }, "put": { "tags": ["console"], "operationId": "consolePutRules", "summary": "Replace the site's rules or overrides", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/RulesRequest" } } } }, "responses": { "200": { "description": "Rules", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/RulesView" } } } }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" }, "422": { "description": "Invalid", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/ValidateResult" } } } } } } }, "/v1/console/sites/{siteKey}/rules/validate": { "parameters": [{ "$ref": "#/components/parameters/SiteKeyPath" }], "post": { "tags": ["console"], "operationId": "consoleValidateRules", "summary": "Check rules without saving", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/RulesRequest" } } } }, "responses": { "200": { "description": "Result", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/ValidateResult" } } } }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/console/sites/{siteKey}/install": { "parameters": [{ "$ref": "#/components/parameters/SiteKeyPath" }], "get": { "tags": ["console"], "operationId": "consoleInstallStatus", "summary": "waiting until AgentGate sees a decision for the site, then live", "responses": { "200": { "description": "Install status", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/InstallStatus" } } } }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/console/sites/{siteKey}/health": { "parameters": [{ "$ref": "#/components/parameters/SiteKeyPath" }], "get": { "tags": ["console"], "operationId": "consoleSiteHealth", "summary": "Integration health over the last 24 hours: is the server verifying tokens, is traffic arriving, are pages on unlisted origins, are siteverify calls failing", "responses": { "200": { "description": "Health", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/SiteHealth" } } } }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/console/sites/{siteKey}/analytics": { "parameters": [{ "$ref": "#/components/parameters/SiteKeyPath" }], "get": { "tags": ["console"], "operationId": "consoleSiteAnalytics", "summary": "Challenge outcomes, solve rate, solve types, token validation and top lists (default: last 24 hours)", "parameters": [{ "name": "from", "in": "query", "schema": { "type": "string" } }, { "name": "to", "in": "query", "schema": { "type": "string" } }], "responses": { "200": { "description": "Analytics", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/SiteAnalytics" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/console/sites/{siteKey}/agent-prompt": { "parameters": [{ "$ref": "#/components/parameters/SiteKeyPath" }], "get": { "tags": ["console"], "operationId": "consoleAgentPrompt", "summary": "A setup prompt for a coding agent (text/markdown): site key, endpoint contract, AGENTGATE_SECRET from the environment (never the secret), checklist", "responses": { "200": { "description": "Prompt", "content": { "text/markdown": { "schema": { "type": "string" } } } }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/console/managed-groups": { "get": { "tags": ["console"], "operationId": "consoleManagedGroups", "summary": "The managed rule-group catalog (read-only, the same for every account): what each group reference in a rule set expands to", "responses": { "200": { "description": "Groups", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/ManagedGroupList" } } } } } } }, "/v1/console/metrics": { "get": { "tags": ["console"], "operationId": "consoleMetrics", "summary": "Daily counts, challenges, latency (errors.eventWriteFailures is always 0 here: it is process-wide)", "parameters": [{ "$ref": "#/components/parameters/SiteKeyQuery" }, { "name": "from", "in": "query", "schema": { "type": "string" } }, { "name": "to", "in": "query", "schema": { "type": "string" } }], "responses": { "200": { "description": "Metrics", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/Metrics" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/console/decisions": { "get": { "tags": ["console"], "operationId": "consoleDecisions", "summary": "Decisions, newest first (same filters as the admin API)", "parameters": [{ "$ref": "#/components/parameters/SiteKeyQuery" }], "responses": { "200": { "description": "Page", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/DecisionPage" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/console/decisions/{decisionId}": { "parameters": [{ "name": "decisionId", "in": "path", "required": true, "schema": { "type": "string" } }], "get": { "tags": ["console"], "operationId": "consoleDecision", "summary": "One decision with its labels and rule (no trace link)", "parameters": [{ "$ref": "#/components/parameters/SiteKeyQuery" }], "responses": { "200": { "description": "Decision", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/DecisionDetail" } } } }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/console/visitors": { "get": { "tags": ["console"], "operationId": "consoleVisitors", "summary": "Pseudonymous request sources", "parameters": [{ "$ref": "#/components/parameters/SiteKeyQuery" }], "responses": { "200": { "description": "Page", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/VisitorPage" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/console/visitors/{visitorId}": { "parameters": [{ "name": "visitorId", "in": "path", "required": true, "schema": { "type": "string" } }], "get": { "tags": ["console"], "operationId": "consoleVisitor", "summary": "One source and its recent decisions", "parameters": [{ "$ref": "#/components/parameters/SiteKeyQuery" }], "responses": { "200": { "description": "Visitor", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/VisitorDetail" } } } }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/console/tokens": { "get": { "tags": ["console"], "operationId": "consoleListTokens", "security": [{ "session": [], "csrf": [] }], "summary": "The account's API tokens (tokens:write; never the secrets)", "responses": { "200": { "description": "Tokens", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiTokenList" } } } }, "401": { "$ref": "#/components/responses/Error" }, "403": { "$ref": "#/components/responses/Error" } } }, "post": { "tags": ["console"], "operationId": "consoleCreateToken", "security": [{ "session": [], "csrf": [] }], "summary": "Create an API token (tokens:write); the secret agt__ is shown once. At most 25 live tokens (409 token_limit).", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiTokenInput" } } } }, "responses": { "201": { "description": "Created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiTokenSecret" } } } }, "400": { "$ref": "#/components/responses/Error" }, "401": { "$ref": "#/components/responses/Error" }, "403": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } }, "/v1/console/tokens/{id}": { "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }], "delete": { "tags": ["console"], "operationId": "consoleRevokeToken", "security": [{ "session": [], "csrf": [] }], "summary": "Revoke an API token now (tokens:write; idempotent)", "responses": { "200": { "description": "Revoked", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiToken" } } } }, "401": { "$ref": "#/components/responses/Error" }, "403": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" } } } } }, "components": { "securitySchemes": { "session": { "type": "apiKey", "in": "cookie", "name": "ag_account" }, "csrf": { "type": "apiKey", "in": "header", "name": "X-CSRF-Token", "description": "Required on mutations" }, "bearer": { "type": "http", "scheme": "bearer", "description": "Account API token agt__; /v1/console only, limited to its scopes, no CSRF" } }, "parameters": { "SiteKeyPath": { "name": "siteKey", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^site_[A-Za-z0-9_-]{4,64}$" } }, "SiteKeyQuery": { "name": "siteKey", "in": "query", "required": true, "schema": { "type": "string", "pattern": "^site_[A-Za-z0-9_-]{4,64}$" } } }, "responses": { "Error": { "description": "Error", "content": { "application/json": { "schema": { "$ref": "openapi-admin.yaml#/components/schemas/ApiError" } } } } }, "schemas": { "MfaChallenge": { "type": "object", "required": ["mfaRequired", "expiresAt"], "properties": { "mfaRequired": { "type": "boolean", "enum": [true] }, "expiresAt": { "type": "string", "format": "date-time", "description": "When the pre-session ends" } } }, "MfaCodeInput": { "type": "object", "additionalProperties": false, "description": "Exactly one of code and recoveryCode", "properties": { "code": { "type": "string", "pattern": "^[0-9]{6}$" }, "recoveryCode": { "type": "string" } } }, "MfaDisableInput": { "type": "object", "required": ["password"], "additionalProperties": false, "properties": { "password": { "type": "string" }, "code": { "type": "string" }, "recoveryCode": { "type": "string" } } }, "MfaStatus": { "type": "object", "required": ["enabled", "pending", "required", "enabledAt", "recoveryCodesLeft"], "properties": { "enabled": { "type": "boolean" }, "pending": { "type": "boolean", "description": "Setup started, not yet enabled" }, "required": { "type": "boolean", "description": "The active account requires 2FA" }, "enabledAt": { "type": ["string", "null"], "format": "date-time" }, "recoveryCodesLeft": { "type": "integer" } } }, "MfaSetup": { "type": "object", "required": ["secret", "otpauthUrl"], "properties": { "secret": { "type": "string", "description": "Base32, for manual entry" }, "otpauthUrl": { "type": "string", "description": "otpauth://totp/… for a QR code" } } }, "MfaRecoveryCodes": { "type": "object", "required": ["recoveryCodes"], "properties": { "recoveryCodes": { "type": "array", "items": { "type": "string" }, "description": "10 single-use codes, shown once" } } }, "PasswordChangeInput": { "type": "object", "required": ["current", "new"], "additionalProperties": false, "properties": { "current": { "type": "string" }, "new": { "type": "string", "minLength": 10, "maxLength": 256 } } }, "PasswordChanged": { "type": "object", "required": ["revokedSessions"], "properties": { "revokedSessions": { "type": "integer" } } }, "Session": { "type": "object", "required": ["id", "current", "createdAt", "lastSeenAt", "expiresAt", "userAgent", "ipPseudonym", "mfaPassed"], "properties": { "id": { "type": "string" }, "current": { "type": "boolean" }, "createdAt": { "type": "string", "format": "date-time" }, "lastSeenAt": { "type": "string", "format": "date-time" }, "expiresAt": { "type": "string", "format": "date-time" }, "userAgent": { "type": "string" }, "ipPseudonym": { "type": "string", "description": "Stable pseudonym of the address, not the IP" }, "mfaPassed": { "type": "boolean" } } }, "SessionList": { "type": "object", "required": ["items"], "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/Session" } } } }, "SessionsRevoked": { "type": "object", "required": ["revoked"], "properties": { "revoked": { "type": "integer" } } }, "ApiTokenScope": { "type": "string", "enum": ["sites:read", "sites:write", "analytics:read", "members:read"] }, "ApiToken": { "type": "object", "required": ["id", "name", "scopes", "createdBy", "createdAt", "lastUsedAt", "expiresAt", "revokedAt", "status"], "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "scopes": { "type": "array", "items": { "$ref": "#/components/schemas/ApiTokenScope" } }, "createdBy": { "type": "string" }, "createdByEmail": { "type": "string", "description": "The creating user's email (lists only)" }, "createdAt": { "type": "string", "format": "date-time" }, "lastUsedAt": { "type": ["string", "null"], "format": "date-time" }, "expiresAt": { "type": ["string", "null"], "format": "date-time" }, "revokedAt": { "type": ["string", "null"], "format": "date-time" }, "status": { "type": "string", "enum": ["active", "expired", "revoked"] } } }, "ApiTokenList": { "type": "object", "required": ["items"], "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/ApiToken" } } } }, "ApiTokenInput": { "type": "object", "required": ["name", "scopes"], "additionalProperties": false, "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 100 }, "scopes": { "type": "array", "minItems": 1, "items": { "$ref": "#/components/schemas/ApiTokenScope" } }, "expiresAt": { "type": ["string", "null"], "format": "date-time", "description": "Optional; within 3 years; null or absent = no expiry" } } }, "ApiTokenSecret": { "type": "object", "required": ["token", "secret"], "properties": { "token": { "$ref": "#/components/schemas/ApiToken" }, "secret": { "type": "string", "description": "agt__, shown once; only an HMAC is stored" } } }, "Status": { "type": "object", "required": ["status", "message"], "properties": { "status": { "type": "string" }, "message": { "type": "string" } } }, "AccessRequestInput": { "type": "object", "required": ["name", "email", "token"], "additionalProperties": false, "properties": { "name": { "type": "string", "maxLength": 200 }, "email": { "type": "string" }, "company": { "type": "string", "maxLength": 200 }, "website": { "type": "string", "maxLength": 300 }, "message": { "type": "string", "maxLength": 2000 }, "token": { "type": "string", "description": "AgentGate receipt from the signup page's browser check" } } }, "LoginInput": { "type": "object", "required": ["email", "password"], "additionalProperties": false, "properties": { "email": { "type": "string" }, "password": { "type": "string" } } }, "TokenPassword": { "type": "object", "required": ["token", "password"], "additionalProperties": false, "properties": { "token": { "type": "string" }, "password": { "type": "string", "minLength": 10, "maxLength": 256 } } }, "InvitePeek": { "type": "object", "required": ["email", "name"], "properties": { "email": { "type": "string" }, "name": { "type": "string" } } }, "AccountSession": { "type": "object", "required": ["userId", "email", "name", "customerId", "customerName", "csrfToken", "expiresAt", "accounts", "activeAccount", "mfa"], "properties": { "userId": { "type": "string" }, "email": { "type": "string" }, "name": { "type": "string" }, "customerId": { "type": "string", "description": "The active account; empty when the user belongs to none" }, "customerName": { "type": "string" }, "csrfToken": { "type": "string" }, "expiresAt": { "type": "string", "format": "date-time" }, "accounts": { "type": "array", "items": { "$ref": "openapi-admin.yaml#/components/schemas/AccountSummary" }, "description": "Every account the user belongs to (not deleted), for the switcher" }, "activeAccount": { "oneOf": [{ "$ref": "openapi-admin.yaml#/components/schemas/ActiveAccount" }, { "type": "null" }] }, "mfa": { "$ref": "openapi-admin.yaml#/components/schemas/MfaInfo" } } } } } }