# AgentGate admin API contract (OpenAPI 3.1). Market-readiness D1/D2. # # Written in YAML's JSON-compatible flow style so the Go contract test # (admin_openapi_test.go) and the dashboard's type generator # (dashboard/scripts/gen-types.mjs) read it without a YAML dependency: # strip these leading comment lines and it is JSON. # # Private API: nginx answers 404 for /v1/admin/ on the public host. Every # route needs an operator session cookie (ag_admin, from POST /v1/admin/login) # plus X-CSRF-Token on mutations, or the X-Admin-Key header. { "openapi": "3.1.0", "info": { "title": "AgentGate admin API", "version": "1.0.0", "description": "Operator API behind the dashboard: sites, credentials, rules, traffic read models and decision labels. Every read and lookup is scoped to one site (siteKey); an ID from another site answers 404 like an unknown ID. Time ranges and page sizes are bounded; cursors are opaque." }, "servers": [{ "url": "http://127.0.0.1:18080", "description": "Local or SSH tunnel" }], "security": [{ "session": [], "csrf": [] }, { "adminKey": [] }], "tags": [ { "name": "session" }, { "name": "sites" }, { "name": "rules" }, { "name": "traffic" }, { "name": "labels" }, { "name": "models" }, { "name": "accounts" }, { "name": "docs" } ], "paths": { "/v1/admin/login": { "post": { "tags": ["session"], "operationId": "login", "security": [], "summary": "Exchange the operator credential (ADMIN_KEY) for a session cookie", "description": "Sets ag_admin (HttpOnly, SameSite=Strict, Path=/v1/admin, Secure over TLS). Rate-limited per source and globally.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LoginRequest" } } } }, "responses": { "200": { "description": "Logged in", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SessionInfo" } } } }, "400": { "$ref": "#/components/responses/Error" }, "401": { "$ref": "#/components/responses/Error" }, "429": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/logout": { "post": { "tags": ["session"], "operationId": "logout", "summary": "End the session", "responses": { "204": { "description": "Logged out" }, "401": { "$ref": "#/components/responses/Error" }, "403": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/session": { "get": { "tags": ["session"], "operationId": "getSession", "summary": "The current operator and the CSRF token for mutations", "responses": { "200": { "description": "Session", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SessionInfo" } } } }, "401": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/sites": { "get": { "tags": ["sites"], "operationId": "listSites", "summary": "Every site with today's traffic and integration health", "responses": { "200": { "description": "Sites", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SiteList" } } } } } }, "post": { "tags": ["sites"], "operationId": "createSite", "summary": "Create a site (monitor mode unless mode is given)", "description": "With no customerId a customer named customerName (default: the site name) is created. Records site.create.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SiteInput" } } } }, "responses": { "201": { "description": "Created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SiteDetail" } } } }, "400": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/sites/{siteKey}": { "parameters": [{ "$ref": "#/components/parameters/SiteKeyPath" }], "get": { "tags": ["sites"], "operationId": "getSite", "summary": "Site detail with credentials, recent changes and rules summary", "responses": { "200": { "description": "Site", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SiteDetail" } } } }, "404": { "$ref": "#/components/responses/Error" } } }, "patch": { "tags": ["sites"], "operationId": "updateSite", "summary": "Change name, origins, actions, routes or mode", "description": "Absent fields are kept. Records site.mode (mode only) or site.update with a {field: {from, to}} diff.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SiteInput" } } } }, "responses": { "200": { "description": "Updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SiteDetail" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/sites/{siteKey}/changes": { "parameters": [{ "$ref": "#/components/parameters/SiteKeyPath" }], "get": { "tags": ["sites"], "operationId": "listSiteChanges", "summary": "The site's configuration audit trail, newest first", "parameters": [{ "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 500, "default": 50 } }], "responses": { "200": { "description": "Changes", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChangeList" } } } }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/sites/{siteKey}/credentials": { "parameters": [{ "$ref": "#/components/parameters/SiteKeyPath" }], "post": { "tags": ["sites"], "operationId": "createCredential", "summary": "Create a backend credential; the secret is returned once", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CredentialInput" } } } }, "responses": { "201": { "description": "Created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CredentialSecret" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/sites/{siteKey}/credentials/{credId}/rotate": { "parameters": [{ "$ref": "#/components/parameters/SiteKeyPath" }, { "$ref": "#/components/parameters/CredId" }], "post": { "tags": ["sites"], "operationId": "rotateCredential", "summary": "Create a replacement; the old credential works for overlapSeconds (default 86400, at most 7 days)", "requestBody": { "required": false, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RotateInput" } } } }, "responses": { "201": { "description": "Replacement", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CredentialSecret" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/sites/{siteKey}/credentials/{credId}/revoke": { "parameters": [{ "$ref": "#/components/parameters/SiteKeyPath" }, { "$ref": "#/components/parameters/CredId" }], "post": { "tags": ["sites"], "operationId": "revokeCredential", "summary": "Revoke immediately", "responses": { "200": { "description": "Revoked", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Credential" } } } }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/sites/{siteKey}/rules": { "parameters": [{ "$ref": "#/components/parameters/SiteKeyPath" }], "get": { "tags": ["rules"], "operationId": "getRules", "summary": "The site's base rule set, per-rule overrides and effective rule set", "responses": { "200": { "description": "Rules", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RulesView" } } } }, "404": { "$ref": "#/components/responses/Error" } } }, "put": { "tags": ["rules"], "operationId": "putRules", "summary": "Replace the site's rule configuration", "description": "ruleSet/ruleSetText set a site rule set; neither follows the server's rules. overrides maps rule names to actions (count to observe only). revision is the revision the operator edited; a stale one answers 409. Records rules.update with a diff.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RulesRequest" } } } }, "responses": { "200": { "description": "Stored", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RulesView" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" }, "422": { "description": "Invalid rules", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateResult" } } } } } } }, "/v1/admin/sites/{siteKey}/rules/validate": { "parameters": [{ "$ref": "#/components/parameters/SiteKeyPath" }], "post": { "tags": ["rules"], "operationId": "validateRules", "summary": "Validate without storing; errors carry line and column for ruleSetText", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RulesRequest" } } } }, "responses": { "200": { "description": "Result", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateResult" } } } }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/managed-groups": { "get": { "tags": ["rules"], "operationId": "listManagedGroups", "summary": "Every registered managed rule group version and its member rules", "description": "Read-only. A rule set references a group as {group, version, overrides}; this lists what each reference expands to, so per-member overrides can be chosen by name. Versions are frozen: digest is the first 16 hex characters of SHA-256 over the members' JSON.", "responses": { "200": { "description": "Groups", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ManagedGroupList" } } } } } } }, "/v1/admin/metrics": { "get": { "tags": ["traffic"], "operationId": "getMetrics", "summary": "Overview counts and a bucketed series for a time range", "parameters": [{ "$ref": "#/components/parameters/SiteKeyQuery" }, { "$ref": "#/components/parameters/From" }, { "$ref": "#/components/parameters/To" }], "description": "Default range: the last 7 days; at most 92 days. Challenge requests (kind challenge) are counted separately from protected requests. When the range starts inside event retention, totals and series count exactly [from, to) from events (hourly buckets up to 48 hours, else UTC days clipped to the range); otherwise they come from daily aggregates and rangeFrom/rangeTo name the whole UTC days counted. daily always lists the UTC days' aggregates.", "responses": { "200": { "description": "Metrics", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Metrics" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/visitors": { "get": { "tags": ["traffic"], "operationId": "listVisitors", "summary": "Pseudonymous request sources, most recently seen first", "description": "A source is a site-scoped HMAC of the client address, not a person. Default range: 24 hours; at most 31 days.", "parameters": [{ "$ref": "#/components/parameters/SiteKeyQuery" }, { "$ref": "#/components/parameters/From" }, { "$ref": "#/components/parameters/To" }, { "$ref": "#/components/parameters/Limit" }, { "$ref": "#/components/parameters/Cursor" }], "responses": { "200": { "description": "Page", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/VisitorPage" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/visitors/{visitorId}": { "get": { "tags": ["traffic"], "operationId": "getVisitor", "summary": "One source's summary and its 50 newest decisions (default range: 31 days)", "parameters": [{ "name": "visitorId", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[0-9a-f]{16}$" } }, { "$ref": "#/components/parameters/SiteKeyQuery" }, { "$ref": "#/components/parameters/From" }, { "$ref": "#/components/parameters/To" }], "responses": { "200": { "description": "Source", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/VisitorDetail" } } } }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/decisions": { "get": { "tags": ["traffic"], "operationId": "listDecisions", "summary": "Decisions, newest first, with filters", "description": "Default range: 24 hours; at most 31 days. queue=labelling selects challenged or would-challenge requests; labelled=no hides decisions an operator already labelled.", "parameters": [ { "$ref": "#/components/parameters/SiteKeyQuery" }, { "$ref": "#/components/parameters/From" }, { "$ref": "#/components/parameters/To" }, { "$ref": "#/components/parameters/Limit" }, { "$ref": "#/components/parameters/Cursor" }, { "name": "kind", "in": "query", "schema": { "type": "string", "enum": ["request", "challenge"] } }, { "name": "outcome", "in": "query", "schema": { "$ref": "#/components/schemas/Outcome" } }, { "name": "wouldDecision", "in": "query", "schema": { "$ref": "#/components/schemas/Outcome" } }, { "name": "rule", "in": "query", "schema": { "type": "string" } }, { "name": "label", "in": "query", "description": "An exact signal label, e.g. agentgate:proof:missing", "schema": { "type": "string" } }, { "name": "visitor", "in": "query", "schema": { "type": "string" } }, { "name": "action", "in": "query", "schema": { "type": "string" } }, { "name": "queue", "in": "query", "schema": { "type": "string", "enum": ["labelling"] } }, { "name": "labelled", "in": "query", "schema": { "type": "string", "enum": ["any", "no"] } } ], "responses": { "200": { "description": "Page", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DecisionPage" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/decisions/{decisionId}": { "parameters": [{ "$ref": "#/components/parameters/DecisionId" }, { "$ref": "#/components/parameters/SiteKeyQuery" }], "get": { "tags": ["traffic"], "operationId": "getDecision", "summary": "Why: labels grouped by source, matched rule, scores, versions, would-decision, trace", "responses": { "200": { "description": "Decision", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DecisionDetail" } } } }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/decisions/{decisionId}/label": { "parameters": [{ "$ref": "#/components/parameters/DecisionId" }, { "$ref": "#/components/parameters/SiteKeyQuery" }], "put": { "tags": ["labels"], "operationId": "labelDecision", "summary": "Label a decision human, bot, agent or unsure for the model loop", "description": "The decision's evidence is copied with the first label, so labels outlive event retention.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LabelInput" } } } }, "responses": { "200": { "description": "Label", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DecisionLabel" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/labels": { "get": { "tags": ["labels"], "operationId": "listLabels", "summary": "Export operator labels in update order (oldest first) for training", "parameters": [{ "$ref": "#/components/parameters/SiteKeyQuery" }, { "$ref": "#/components/parameters/Cursor" }, { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 1000, "default": 100 } }], "responses": { "200": { "description": "Labels", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LabelPage" } } } }, "404": { "$ref": "#/components/responses/Error" } } }, "post": { "tags": ["labels"], "operationId": "addLabel", "summary": "Label a decision (by decision or request ID) with a source, for probes and scripts", "description": "Relabelling a decision replaces its label (latest wins). Source is operator, probe or synthetic; unsure is exported but never trained on.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LabelRequest" } } } }, "responses": { "201": { "description": "Label", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DecisionLabel" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/sites/{site}/feature-capture": { "parameters": [{ "name": "site", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Site ID or site key" }], "get": { "tags": ["models"], "operationId": "getFeatureCapture", "summary": "Whether the site stores model feature vectors (never content) and for how long", "responses": { "200": { "description": "Setting", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FeatureCapture" } } } }, "404": { "$ref": "#/components/responses/Error" } } }, "put": { "tags": ["models"], "operationId": "setFeatureCapture", "summary": "Opt in or out of feature capture; opting out deletes the site's vectors", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "additionalProperties": false, "properties": { "enabled": { "type": "boolean" }, "retentionDays": { "type": "integer", "minimum": 1 } } } } } }, "responses": { "200": { "description": "Setting", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FeatureCapture" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/models": { "get": { "tags": ["models"], "operationId": "listModels", "summary": "Active, candidate and previous model per kind (agent, bot, content)", "responses": { "200": { "description": "Models", "content": { "application/json": { "schema": { "type": "object", "required": ["models"], "properties": { "models": { "type": "object", "additionalProperties": { "type": "object", "properties": { "active": { "$ref": "#/components/schemas/ModelSummary" }, "candidate": { "$ref": "#/components/schemas/ModelSummary" }, "previous": { "$ref": "#/components/schemas/ModelSummary" } } } } } } } } } } } }, "/v1/admin/models/{kind}/{role}": { "parameters": [{ "$ref": "#/components/parameters/ModelKind" }, { "name": "role", "in": "path", "required": true, "schema": { "type": "string", "enum": ["active", "candidate", "previous"] } }], "get": { "tags": ["models"], "operationId": "getModel", "summary": "A model file (weights and metadata)", "responses": { "200": { "description": "Model", "content": { "application/json": { "schema": { "type": "object" } } } }, "404": { "$ref": "#/components/responses/Error" } } }, "put": { "tags": ["models"], "operationId": "putCandidateModel", "summary": "Load a candidate model; it scores traffic in shadow without affecting decisions", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object" } } } }, "responses": { "200": { "description": "Candidate", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ModelSummary" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" } } }, "delete": { "tags": ["models"], "operationId": "clearCandidateModel", "summary": "Stop shadow scoring", "responses": { "200": { "description": "Cleared", "content": { "application/json": { "schema": { "type": "object", "properties": { "cleared": { "type": "string" } } } } } }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/models/{kind}/compare": { "parameters": [{ "$ref": "#/components/parameters/ModelKind" }], "get": { "tags": ["models"], "operationId": "compareModels", "summary": "Candidate vs active on recent traffic: agreement, would-change counts, per-label rates", "responses": { "200": { "description": "Report", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CompareReport" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/models/{kind}/promote": { "parameters": [{ "$ref": "#/components/parameters/ModelKind" }], "post": { "tags": ["models"], "operationId": "promoteModel", "summary": "Make the candidate active (recorded in config_changes; survives restarts)", "responses": { "200": { "description": "Swap", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ModelSwap" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/models/{kind}/rollback": { "parameters": [{ "$ref": "#/components/parameters/ModelKind" }], "post": { "tags": ["models"], "operationId": "rollbackModel", "summary": "Restore the previous active model", "responses": { "200": { "description": "Swap", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ModelSwap" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/sites/{siteKey}/install": { "parameters": [{ "$ref": "#/components/parameters/SiteKeyPath" }], "get": { "tags": ["sites"], "operationId": "getInstallStatus", "summary": "Whether AgentGate has seen traffic for the site (the add-site wizard's install check)", "description": "Also mounted for customers at /v1/console/sites/{siteKey}/install (docs/openapi-console.yaml).", "responses": { "200": { "description": "Install status", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InstallStatus" } } } }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/sites/{siteKey}/health": { "parameters": [{ "$ref": "#/components/parameters/SiteKeyPath" }], "get": { "tags": ["sites"], "operationId": "getSiteHealth", "summary": "Integration health: typed checks over the last 24 hours", "description": "Checks, worst first: siteverify_missing (critical when tokens older than the receipt lifetime were issued and neither siteverify, the gateway nor the site's credentials saw any server check; warning when server checks are under half of at least 10 such tokens), no_credential, not_installed (info), no_traffic, origin_not_allowed (page origins the browser API refused), siteverify_errors (at least 5 failed siteverify calls and at least 20% of them). Only failed checks are listed; status is the worst severity, or ok. Also mounted for customers at /v1/console/sites/{siteKey}/health.", "responses": { "200": { "description": "Health", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SiteHealth" } } } }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/sites/{siteKey}/analytics": { "parameters": [{ "$ref": "#/components/parameters/SiteKeyPath" }], "get": { "tags": ["sites"], "operationId": "getSiteAnalytics", "summary": "Challenge outcomes, solve types, token validation and top lists for a range", "description": "Default range: the last 24 hours; at most 92 days. Hourly buckets up to 48 hours, UTC days (clipped to the range) beyond. Browser-verification and gateway numbers come from decision events, so they start at eventsFrom when the range begins before event retention; siteverify and origin counts are hourly counters (range edges round out to whole hours) that start at countersSince. Also mounted for customers at /v1/console/sites/{siteKey}/analytics.", "parameters": [{ "name": "from", "in": "query", "schema": { "type": "string" } }, { "name": "to", "in": "query", "schema": { "type": "string" } }], "responses": { "200": { "description": "Analytics", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SiteAnalytics" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/sites/{siteKey}/agent-prompt": { "parameters": [{ "$ref": "#/components/parameters/SiteKeyPath" }], "get": { "tags": ["sites"], "operationId": "getAgentPrompt", "summary": "A self-contained setup prompt for a coding agent (Markdown)", "description": "Names the site key, action, allowed origins, widget mode, this AgentGate's public URL (PUBLIC_BASE_URL or the request's origin), the siteverify contract and a checklist. It never contains a secret: the agent is told to read AGENTGATE_SECRET from the environment. Also mounted for customers at /v1/console/sites/{siteKey}/agent-prompt.", "responses": { "200": { "description": "Prompt", "content": { "text/markdown": { "schema": { "type": "string" } } } }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/access-requests": { "get": { "tags": ["accounts"], "operationId": "listAccessRequests", "summary": "Access requests from /signup, newest first", "parameters": [ { "name": "status", "in": "query", "schema": { "type": "string", "enum": ["pending", "approved", "declined", "all"] }, "description": "Default: all" }, { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 500, "default": 100 } } ], "responses": { "200": { "description": "Requests", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessRequestList" } } } }, "400": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/access-requests/{id}/approve": { "parameters": [{ "$ref": "#/components/parameters/AccessRequestId" }], "post": { "tags": ["accounts"], "operationId": "approveAccessRequest", "summary": "Create the customer (named by company, else name), the user and a 7-day invite link; email the link", "description": "The link uses PUBLIC_BASE_URL (else this request's host). It is also in the outbox.", "responses": { "200": { "description": "Invited", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InviteResult" } } } }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/access-requests/{id}/decline": { "parameters": [{ "$ref": "#/components/parameters/AccessRequestId" }], "post": { "tags": ["accounts"], "operationId": "declineAccessRequest", "summary": "Decline a pending request (no email is sent)", "responses": { "200": { "description": "Declined", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccessRequest" } } } }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/accounts": { "get": { "tags": ["accounts"], "operationId": "listAccounts", "summary": "Customer users with their customer", "responses": { "200": { "description": "Users", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccountList" } } } } } }, "post": { "tags": ["accounts"], "operationId": "inviteAccount", "summary": "Invite a user directly (a new customer, or customerId to add a teammate)", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InviteInput" } } } }, "responses": { "201": { "description": "Invited", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InviteResult" } } } }, "400": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/customers": { "get": { "tags": ["accounts"], "operationId": "listCustomers", "summary": "Accounts (customers), newest first, with owner email, member and site counts, status, limits and last sign-in", "parameters": [ { "name": "status", "in": "query", "schema": { "type": "string", "enum": ["active", "suspended", "deleted", "all"] }, "description": "Default: all" }, { "name": "q", "in": "query", "schema": { "type": "string", "maxLength": 200 }, "description": "Matches the account ID, its name or a member's email (case-insensitive substring)" }, { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 1000, "default": 200 } } ], "responses": { "200": { "description": "Accounts", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CustomerList" } } } }, "400": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/customers/{id}": { "parameters": [{ "$ref": "#/components/parameters/CustomerId" }], "get": { "tags": ["accounts"], "operationId": "getCustomer", "summary": "One account: members (roles, status, 2FA), sites (mode, widget mode, health), API tokens, limits, usage and the 20 newest audit events", "responses": { "200": { "description": "Account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CustomerDetail" } } } }, "404": { "$ref": "#/components/responses/Error" } } }, "patch": { "tags": ["accounts"], "operationId": "patchCustomer", "summary": "Rename the account or change its limits (absent fields keep their value); audit account.rename / account.limits", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CustomerPatch" } } } }, "responses": { "200": { "description": "Account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CustomerDetail" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" } } }, "delete": { "tags": ["accounts"], "operationId": "deleteCustomer", "summary": "Delete the account with the owner's rules: confirm must be the account name; refused (409 sites_enforcing) while any site enforces. Sites stop issuing challenges, credentials, sessions and API tokens are revoked; data is purged 30 days later (reactivate undoes it before then). The service's own accounts answer 409 protected_account.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConfirmInput" } } } }, "responses": { "200": { "description": "Deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DeletedAccount" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/customers/{id}/suspend": { "parameters": [{ "$ref": "#/components/parameters/CustomerId" }], "post": { "tags": ["accounts"], "operationId": "suspendCustomer", "summary": "Suspend an active account: its console and API answer 403 account_suspended; its sites keep protecting", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["reason"], "additionalProperties": false, "properties": { "reason": { "type": "string", "minLength": 1, "maxLength": 500 } } } } } }, "responses": { "200": { "description": "Account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CustomerDetail" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/customers/{id}/reactivate": { "parameters": [{ "$ref": "#/components/parameters/CustomerId" }], "post": { "tags": ["accounts"], "operationId": "reactivateCustomer", "summary": "Make a suspended account active again; also restores a deleted account within its 30 days (revoked credentials stay revoked)", "responses": { "200": { "description": "Account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CustomerDetail" } } } }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/customers/{id}/sites": { "parameters": [{ "$ref": "#/components/parameters/CustomerId" }], "post": { "tags": ["accounts"], "operationId": "createCustomerSite", "summary": "Create a site in this account (the site create body without customerId/customerName; the account's limits do not bind the operator)", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SiteInput" } } } }, "responses": { "201": { "description": "Created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SiteDetail" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/customers/{id}/members": { "parameters": [{ "$ref": "#/components/parameters/CustomerId" }], "post": { "tags": ["accounts"], "operationId": "inviteCustomerMember", "summary": "Add someone to the account with a role (the operator may add owners). An existing user gets a membership and a notice; a new address gets an invite link (returned, 7 days). 409 member_limit / already_member.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MemberInput" } } } }, "responses": { "201": { "description": "Invited", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MemberInviteResult" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/customers/{id}/members/{userId}": { "parameters": [{ "$ref": "#/components/parameters/CustomerId" }, { "$ref": "#/components/parameters/UserId" }], "patch": { "tags": ["accounts"], "operationId": "patchCustomerMember", "summary": "Change a member's role; the last owner cannot be demoted (409 last_owner)", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RoleInput" } } } }, "responses": { "200": { "description": "Member", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Member" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } }, "delete": { "tags": ["accounts"], "operationId": "removeCustomerMember", "summary": "Remove a member (not the last owner); their sessions fall back to another account", "responses": { "204": { "description": "Removed" }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/customers/{id}/members/{userId}/resend-invite": { "parameters": [{ "$ref": "#/components/parameters/CustomerId" }, { "$ref": "#/components/parameters/UserId" }], "post": { "tags": ["accounts"], "operationId": "resendCustomerInvite", "summary": "A fresh invite link for a member who has not set a password (also emailed); 409 already_active otherwise", "responses": { "200": { "description": "Link", "content": { "application/json": { "schema": { "type": "object", "required": ["inviteUrl"], "properties": { "inviteUrl": { "type": "string" } } } } } }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/customers/{id}/audit": { "parameters": [{ "$ref": "#/components/parameters/CustomerId" }], "get": { "tags": ["accounts"], "operationId": "customerAudit", "summary": "The account's audit log, newest first", "parameters": [{ "$ref": "#/components/parameters/Limit" }, { "$ref": "#/components/parameters/Cursor" }, { "$ref": "#/components/parameters/AuditAction" }], "responses": { "200": { "description": "Events", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuditPage" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/customers/{id}/tokens/{tokenId}": { "parameters": [{ "$ref": "#/components/parameters/CustomerId" }, { "name": "tokenId", "in": "path", "required": true, "schema": { "type": "string" } }], "delete": { "tags": ["accounts"], "operationId": "revokeCustomerToken", "summary": "Revoke one of the account's API tokens (audit token.revoke); 404 for a token of another account", "responses": { "204": { "description": "Revoked" }, "404": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/users/{userId}/mfa/reset": { "parameters": [{ "$ref": "#/components/parameters/UserId" }], "post": { "tags": ["accounts"], "operationId": "userMfaReset", "summary": "Turn a user's two-factor authentication off (a lost authenticator); they sign in with the password and can set it up again. 409 mfa_not_enabled", "responses": { "200": { "description": "User", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Account" } } } }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/sites/{siteKey}/move": { "parameters": [{ "$ref": "#/components/parameters/SiteKeyPath" }], "post": { "tags": ["accounts"], "operationId": "moveSite", "summary": "Move a site to another account (not deleted); console scoping follows the new owner at once. Audit: site.move_out on the old account, site.move on the new one. The service's own sites cannot move (409).", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["customerId"], "additionalProperties": false, "properties": { "customerId": { "type": "string" } } } } } }, "responses": { "200": { "description": "Moved", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SiteDetail" } } } }, "400": { "$ref": "#/components/responses/Error" }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/users/{userId}/suspend": { "parameters": [{ "$ref": "#/components/parameters/UserId" }], "post": { "tags": ["accounts"], "operationId": "suspendUser", "summary": "Suspend a user everywhere: sign-in and console answer 403 user_suspended, sessions end", "requestBody": { "required": false, "content": { "application/json": { "schema": { "type": "object", "additionalProperties": false, "properties": { "reason": { "type": "string", "maxLength": 500 } } } } } }, "responses": { "200": { "description": "User", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Account" } } } }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/users/{userId}/reactivate": { "parameters": [{ "$ref": "#/components/parameters/UserId" }], "post": { "tags": ["accounts"], "operationId": "reactivateUser", "summary": "Lift a user's suspension", "responses": { "200": { "description": "User", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Account" } } } }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/users/{userId}/reset-link": { "parameters": [{ "$ref": "#/components/parameters/UserId" }], "post": { "tags": ["accounts"], "operationId": "userResetLink", "summary": "A password reset link (1 hour, single use), also emailed to the user; 409 for a suspended user", "responses": { "200": { "description": "Link", "content": { "application/json": { "schema": { "type": "object", "required": ["resetUrl"], "properties": { "resetUrl": { "type": "string" } } } } } }, "404": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" } } } }, "/v1/admin/outbox": { "get": { "tags": ["accounts"], "operationId": "listOutbox", "summary": "Messages the service sent or will send, newest first; bodies hold one-time links", "parameters": [{ "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 500, "default": 100 } }], "responses": { "200": { "description": "Messages", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OutboxList" } } } } } } }, "/v1/admin/docs-feedback": { "get": { "tags": ["docs"], "operationId": "docsFeedback", "summary": "\"Was this helpful?\" answers on the public docs, counted per page (most answered first)", "parameters": [{ "name": "days", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 3650, "default": 30 } }], "responses": { "200": { "description": "Counts", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DocsFeedbackReport" } } } }, "400": { "$ref": "#/components/responses/Error" } } } } }, "components": { "securitySchemes": { "session": { "type": "apiKey", "in": "cookie", "name": "ag_admin" }, "csrf": { "type": "apiKey", "in": "header", "name": "X-CSRF-Token", "description": "Required on session mutations" }, "adminKey": { "type": "apiKey", "in": "header", "name": "X-Admin-Key", "description": "For scripts; no CSRF token needed" } }, "parameters": { "ModelKind": { "name": "kind", "in": "path", "required": true, "schema": { "type": "string", "enum": ["agent", "bot", "content"] } }, "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}$" } }, "AccessRequestId": { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }, "CustomerId": { "name": "id", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Account (customer) ID" }, "UserId": { "name": "userId", "in": "path", "required": true, "schema": { "type": "string" } }, "AuditAction": { "name": "action", "in": "query", "schema": { "type": "string" }, "description": "An action and its dotted children: member matches member.invite, member.role, …; member.invite only that (a trailing dot is allowed)" }, "CredId": { "name": "credId", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^cred_[0-9a-f]{16}$" } }, "DecisionId": { "name": "decisionId", "in": "path", "required": true, "schema": { "type": "string" } }, "From": { "name": "from", "in": "query", "description": "RFC 3339, YYYY-MM-DD (UTC) or Unix milliseconds; inclusive", "schema": { "type": "string" } }, "To": { "name": "to", "in": "query", "description": "Exclusive; default now", "schema": { "type": "string" } }, "Limit": { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 200, "default": 50 } }, "Cursor": { "name": "cursor", "in": "query", "description": "nextCursor from the previous page", "schema": { "type": "string" } } }, "responses": { "Error": { "description": "Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } } }, "schemas": { "ApiError": { "type": "object", "required": ["error"], "properties": { "error": { "type": "string" }, "message": { "type": "string" } } }, "LoginRequest": { "type": "object", "required": ["key"], "properties": { "key": { "type": "string" }, "operator": { "type": "string", "maxLength": 64, "pattern": "^[A-Za-z0-9._@-]+( [A-Za-z0-9._@-]+)*$", "description": "Name for the audit trail: letters, digits, single spaces and . _ @ -" } } }, "SessionInfo": { "type": "object", "required": ["operator", "via"], "properties": { "operator": { "type": "string" }, "via": { "type": "string", "enum": ["session", "key"] }, "csrfToken": { "type": "string" }, "expiresAt": { "type": "string", "format": "date-time" } } }, "Outcome": { "type": "string", "enum": ["allow", "challenge", "block", "drop"] }, "Mode": { "type": "string", "enum": ["monitor", "enforce"] }, "WidgetMode": { "type": "string", "enum": ["invisible", "managed", "interactive"], "description": "The browser widget's data-mode in the site's snippets: invisible shows nothing unless AgentGate asks for the check; managed shows a small status chip; interactive always shows the check" }, "SiteAction": { "type": "object", "required": ["browserRequired"], "properties": { "browserRequired": { "type": "boolean" }, "clearance": { "type": "boolean" }, "requireContentDigest": { "type": "boolean" } } }, "SiteRoute": { "type": "object", "required": ["method", "action"], "properties": { "method": { "type": "string" }, "path": { "type": "string" }, "pathPrefix": { "type": "string" }, "action": { "type": "string" } } }, "Site": { "type": "object", "required": ["id", "customerId", "siteKey", "name", "allowedOrigins", "mode", "widgetMode", "actions", "routes", "createdAt", "updatedAt"], "properties": { "id": { "type": "string" }, "customerId": { "type": "string" }, "siteKey": { "type": "string" }, "name": { "type": "string" }, "allowedOrigins": { "type": "array", "items": { "type": "string" } }, "mode": { "$ref": "#/components/schemas/Mode" }, "widgetMode": { "$ref": "#/components/schemas/WidgetMode" }, "actions": { "type": "object", "additionalProperties": { "$ref": "#/components/schemas/SiteAction" } }, "routes": { "type": "array", "items": { "$ref": "#/components/schemas/SiteRoute" } }, "ruleSet": { "type": "string" }, "agentPolicy": { "type": "object", "additionalProperties": true }, "createdAt": { "type": "string", "format": "date-time" }, "updatedAt": { "type": "string", "format": "date-time" } } }, "SiteSummary": { "allOf": [{ "$ref": "#/components/schemas/Site" }, { "type": "object", "required": ["customerName", "credentials", "today", "health"], "properties": { "customerName": { "type": "string" }, "credentials": { "type": "object", "required": ["active", "total"], "properties": { "active": { "type": "integer" }, "total": { "type": "integer" } } }, "today": { "type": "object", "description": "Protected-request outcomes today (UTC) plus monitored", "additionalProperties": { "type": "integer" } }, "health": { "type": "object", "required": ["status"], "properties": { "status": { "type": "string", "enum": ["ok", "no_credentials", "no_traffic", "no_routes"] }, "lastEventAt": { "type": "string", "format": "date-time" } } } } }] }, "SiteList": { "type": "object", "required": ["items"], "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/SiteSummary" } } } }, "SiteDetail": { "allOf": [{ "$ref": "#/components/schemas/SiteSummary" }, { "type": "object", "required": ["credentialList", "changes", "rules"], "properties": { "credentialList": { "type": "array", "items": { "$ref": "#/components/schemas/Credential" } }, "changes": { "type": "array", "items": { "$ref": "#/components/schemas/ConfigChange" } }, "rules": { "type": "object", "required": ["source", "effectiveVersion", "overrides", "revision"], "properties": { "source": { "type": "string", "enum": ["server", "site"] }, "effectiveVersion": { "type": "string" }, "overrides": { "type": "integer" }, "revision": { "type": "integer" } } } } }] }, "SiteInput": { "type": "object", "additionalProperties": false, "properties": { "name": { "type": "string" }, "allowedOrigins": { "type": "array", "items": { "type": "string" } }, "actions": { "type": "object", "additionalProperties": { "$ref": "#/components/schemas/SiteAction" } }, "routes": { "type": "array", "items": { "$ref": "#/components/schemas/SiteRoute" } }, "mode": { "$ref": "#/components/schemas/Mode" }, "widgetMode": { "$ref": "#/components/schemas/WidgetMode" }, "siteKey": { "type": "string", "description": "Create only; generated when absent" }, "customerId": { "type": "string", "description": "Create only" }, "customerName": { "type": "string", "description": "Create only" } } }, "Credential": { "type": "object", "required": ["id", "siteId", "name", "createdAt"], "properties": { "id": { "type": "string" }, "siteId": { "type": "string" }, "name": { "type": "string" }, "createdAt": { "type": "string", "format": "date-time" }, "expiresAt": { "type": "string", "format": "date-time" }, "revokedAt": { "type": "string", "format": "date-time" }, "lastUsedAt": { "type": "string", "format": "date-time" } } }, "CredentialInput": { "type": "object", "additionalProperties": false, "properties": { "name": { "type": "string", "maxLength": 100 } } }, "RotateInput": { "type": "object", "additionalProperties": false, "properties": { "overlapSeconds": { "type": "integer", "minimum": 0, "maximum": 604800 } } }, "CredentialSecret": { "type": "object", "required": ["credential", "secret"], "properties": { "credential": { "$ref": "#/components/schemas/Credential" }, "secret": { "type": "string", "description": "Shown once; only an HMAC is stored" } } }, "ConfigChange": { "type": "object", "required": ["id", "time", "actor", "kind"], "properties": { "id": { "type": "integer" }, "siteId": { "type": "string" }, "time": { "type": "string", "format": "date-time" }, "actor": { "type": "string" }, "kind": { "type": "string" }, "detail": { "type": "object", "additionalProperties": true } } }, "ChangeList": { "type": "object", "required": ["items"], "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/ConfigChange" } } } }, "RuleMatch": { "type": "object", "additionalProperties": true, "properties": { "all_labels": { "type": "array", "items": { "type": "string" } }, "any_labels": { "type": "array", "items": { "type": "string" } }, "none_labels": { "type": "array", "items": { "type": "string" } }, "any_prefix": { "type": "array", "items": { "type": "string" } }, "score": { "type": "string" }, "min_score": { "type": "number" } } }, "RuleResponse": { "type": "object", "additionalProperties": true, "properties": { "status": { "type": "integer" }, "message": { "type": "string" }, "retry_after": { "type": "integer" } } }, "Rule": { "type": "object", "required": ["name", "match", "action"], "additionalProperties": true, "properties": { "name": { "type": "string" }, "routes": { "type": "array", "items": { "type": "string" } }, "match": { "$ref": "#/components/schemas/RuleMatch" }, "action": { "type": "string", "enum": ["allow", "challenge", "block", "drop", "count"] }, "response": { "$ref": "#/components/schemas/RuleResponse" } } }, "ManagedGroupRef": { "type": "object", "required": ["group", "version"], "additionalProperties": true, "description": "A managed rule group reference in a rule set's rules, in place of a rule. It carries no name or action of its own (the server may send them empty); its members keep their own names.", "properties": { "group": { "type": "string" }, "version": { "type": "integer", "minimum": 1 }, "overrides": { "type": "object", "description": "Member rule name to action", "additionalProperties": { "type": "string", "enum": ["allow", "challenge", "block", "drop", "count"] } }, "scope_down": { "$ref": "#/components/schemas/RuleMatch" } } }, "ManagedGroupMember": { "type": "object", "required": ["name", "action", "routes", "match"], "properties": { "name": { "type": "string" }, "action": { "type": "string", "description": "Before any override" }, "routes": { "type": "array", "items": { "type": "string" } }, "match": { "$ref": "#/components/schemas/RuleMatch" } } }, "ManagedGroup": { "type": "object", "required": ["name", "version", "description", "digest", "rules"], "properties": { "name": { "type": "string" }, "version": { "type": "integer" }, "description": { "type": "string" }, "digest": { "type": "string" }, "rules": { "type": "array", "items": { "$ref": "#/components/schemas/ManagedGroupMember" } } } }, "ManagedGroupList": { "type": "object", "required": ["items"], "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/ManagedGroup" } } } }, "RuleSet": { "type": "object", "required": ["version", "rules", "default_action"], "additionalProperties": true, "properties": { "version": { "type": "string" }, "rules": { "type": "array", "items": { "oneOf": [{ "$ref": "#/components/schemas/Rule" }, { "$ref": "#/components/schemas/ManagedGroupRef" }] } }, "default_action": { "type": "string" } } }, "RulesView": { "type": "object", "required": ["siteKey", "source", "revision", "serverVersion", "ruleSet", "overrides", "effective"], "properties": { "siteKey": { "type": "string" }, "source": { "type": "string", "enum": ["server", "site"] }, "revision": { "type": "integer" }, "updatedAt": { "type": "string", "format": "date-time" }, "updatedBy": { "type": "string" }, "serverVersion": { "type": "string" }, "ruleSet": { "$ref": "#/components/schemas/RuleSet" }, "overrides": { "type": "object", "additionalProperties": { "type": "string" } }, "effective": { "$ref": "#/components/schemas/RuleSet" } } }, "RulesRequest": { "type": "object", "properties": { "ruleSet": { "oneOf": [{ "$ref": "#/components/schemas/RuleSet" }, { "type": "null" }] }, "ruleSetText": { "type": "string", "description": "The rule set as editor text; errors carry line and column" }, "overrides": { "type": "object", "additionalProperties": { "type": "string" } }, "revision": { "type": "integer" } } }, "RuleProblem": { "type": "object", "required": ["message"], "properties": { "message": { "type": "string" }, "line": { "type": "integer" }, "column": { "type": "integer" }, "rule": { "type": "string" } } }, "ValidateResult": { "type": "object", "required": ["valid", "errors"], "properties": { "valid": { "type": "boolean" }, "errors": { "type": "array", "items": { "$ref": "#/components/schemas/RuleProblem" } }, "effective": { "$ref": "#/components/schemas/RuleSet" } } }, "OutcomeCounts": { "type": "object", "required": ["requests", "allowed", "challenged", "blocked", "dropped", "wouldChallenge", "wouldBlock", "wouldDrop"], "properties": { "requests": { "type": "integer" }, "allowed": { "type": "integer" }, "challenged": { "type": "integer" }, "blocked": { "type": "integer" }, "dropped": { "type": "integer" }, "wouldChallenge": { "type": "integer" }, "wouldBlock": { "type": "integer" }, "wouldDrop": { "type": "integer" } } }, "DailyPoint": { "allOf": [{ "$ref": "#/components/schemas/OutcomeCounts" }, { "type": "object", "required": ["day", "challengesPassed", "challengesFailed"], "properties": { "day": { "type": "string" }, "challengesPassed": { "type": "integer" }, "challengesFailed": { "type": "integer" } } }] }, "SeriesPoint": { "allOf": [{ "$ref": "#/components/schemas/OutcomeCounts" }, { "type": "object", "required": ["start", "end", "challengesPassed", "challengesFailed"], "properties": { "start": { "type": "string", "format": "date-time", "description": "Inclusive, clipped to the range" }, "end": { "type": "string", "format": "date-time", "description": "Exclusive, clipped to the range" }, "challengesPassed": { "type": "integer" }, "challengesFailed": { "type": "integer" } } }] }, "Metrics": { "type": "object", "required": ["siteKey", "mode", "from", "to", "totals", "challenges", "latency", "errors", "uniqueSources", "retention", "daily", "rangeFrom", "rangeTo", "source", "bucket", "series"], "properties": { "rangeFrom": { "type": "string", "format": "date-time", "description": "Start of what totals and series count (inclusive)" }, "rangeTo": { "type": "string", "format": "date-time", "description": "End of what totals and series count (exclusive)" }, "source": { "type": "string", "enum": ["events", "aggregates"], "description": "events: exactly the requested range; aggregates: whole UTC days (the range starts before event retention)" }, "bucket": { "type": "string", "enum": ["hour", "day"], "description": "Series bucket width: hourly up to 48 hours from events, otherwise UTC days" }, "series": { "type": "array", "items": { "$ref": "#/components/schemas/SeriesPoint" } }, "siteKey": { "type": "string" }, "mode": { "$ref": "#/components/schemas/Mode" }, "from": { "type": "string", "description": "First UTC day" }, "to": { "type": "string", "description": "Last UTC day" }, "totals": { "$ref": "#/components/schemas/OutcomeCounts" }, "challenges": { "type": "object", "required": ["issued", "passed", "failed", "completionRate"], "properties": { "issued": { "type": "integer" }, "passed": { "type": "integer" }, "failed": { "type": "integer" }, "completionRate": { "type": ["number", "null"] } } }, "latency": { "type": "object", "required": ["meanMs", "p50Ms", "p95Ms", "samples"], "properties": { "meanMs": { "type": ["number", "null"] }, "p50Ms": { "type": ["number", "null"] }, "p95Ms": { "type": ["number", "null"] }, "samples": { "type": "integer", "description": "Newest request events in range used for percentiles (at most 5000)" } } }, "errors": { "type": "object", "required": ["serviceErrors", "eventWriteFailures"], "properties": { "serviceErrors": { "type": "integer" }, "eventWriteFailures": { "type": "integer", "description": "Since process start" } } }, "uniqueSources": { "type": "integer" }, "retention": { "type": "object", "required": ["eventDays", "metricDays"], "properties": { "eventDays": { "type": "integer" }, "metricDays": { "type": "integer" } } }, "daily": { "type": "array", "items": { "$ref": "#/components/schemas/DailyPoint" } } } }, "VisitorSummary": { "type": "object", "required": ["id", "firstSeen", "lastSeen", "requests", "outcomes", "monitored", "challenges", "challengesPassed", "actions", "agents"], "properties": { "id": { "type": "string" }, "firstSeen": { "type": "string", "format": "date-time" }, "lastSeen": { "type": "string", "format": "date-time" }, "requests": { "type": "integer" }, "outcomes": { "type": "object", "additionalProperties": { "type": "integer" } }, "monitored": { "type": "integer" }, "challenges": { "type": "integer" }, "challengesPassed": { "type": "integer" }, "actions": { "type": "array", "items": { "type": "string" } }, "agents": { "type": "array", "items": { "type": "string" } } } }, "VisitorPage": { "type": "object", "required": ["items", "from", "to"], "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/VisitorSummary" } }, "nextCursor": { "type": "string" }, "from": { "type": "string" }, "to": { "type": "string" } } }, "VisitorDetail": { "allOf": [{ "$ref": "#/components/schemas/VisitorSummary" }, { "type": "object", "required": ["recent"], "properties": { "recent": { "type": "array", "items": { "$ref": "#/components/schemas/Decision" } } } }] }, "Decision": { "type": "object", "required": ["id", "siteId", "time", "kind", "route", "method", "outcome", "mode", "reasons", "labels", "status", "durationUs"], "properties": { "id": { "type": "string" }, "siteId": { "type": "string" }, "time": { "type": "string", "format": "date-time" }, "kind": { "type": "string", "enum": ["request", "challenge"] }, "requestId": { "type": "string" }, "traceId": { "type": "string" }, "visitorId": { "type": "string" }, "action": { "type": "string" }, "route": { "type": "string" }, "method": { "type": "string" }, "outcome": { "$ref": "#/components/schemas/Outcome" }, "wouldDecision": { "$ref": "#/components/schemas/Outcome" }, "mode": { "type": "string" }, "reasons": { "type": "array", "items": { "type": "string" } }, "labels": { "type": "array", "items": { "type": "string" } }, "matchedRule": { "type": "string" }, "ruleSetVersion": { "type": "string" }, "modelVersions": { "type": "object", "additionalProperties": { "type": "string" } }, "scores": { "type": "object", "additionalProperties": { "type": "number" } }, "agent": { "type": "string" }, "status": { "type": "integer" }, "durationUs": { "type": "integer" }, "operatorLabel": { "$ref": "#/components/schemas/DecisionLabel" } } }, "DecisionPage": { "type": "object", "required": ["items", "from", "to"], "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/Decision" } }, "nextCursor": { "type": "string" }, "from": { "type": "string" }, "to": { "type": "string" } } }, "DecisionDetail": { "type": "object", "required": ["decision", "labelsBySource", "ruleCurrent", "monitored"], "properties": { "decision": { "$ref": "#/components/schemas/Decision" }, "labelsBySource": { "type": "object", "description": "Signal labels grouped by source (agentgate::...)", "additionalProperties": { "type": "array", "items": { "type": "string" } } }, "rule": { "$ref": "#/components/schemas/Rule" }, "ruleCurrent": { "type": "boolean", "description": "The site's effective rule set still has the version that decided" }, "monitored": { "type": "boolean", "description": "Monitor mode allowed what enforce mode would not" }, "traceUrl": { "type": "string", "description": "From AGENTGATE_TRACE_URL with {traceId} substituted" } } }, "LabelValue": { "type": "string", "enum": ["human", "bot", "agent", "unsure"] }, "LabelInput": { "type": "object", "required": ["label"], "additionalProperties": false, "properties": { "label": { "$ref": "#/components/schemas/LabelValue" }, "note": { "type": "string", "maxLength": 500 } } }, "LabelRequest": { "type": "object", "required": ["site", "label"], "additionalProperties": false, "properties": { "site": { "type": "string", "description": "Site ID or key" }, "decisionId": { "type": "string" }, "requestId": { "type": "string" }, "label": { "type": "string", "enum": ["human", "bot", "agent", "unsure"] }, "source": { "type": "string", "enum": ["operator", "probe", "synthetic"] }, "note": { "type": "string", "maxLength": 500 } } }, "FeatureCapture": { "type": "object", "required": ["siteId", "enabled", "retentionDays"], "properties": { "siteId": { "type": "string" }, "enabled": { "type": "boolean" }, "retentionDays": { "type": "integer" }, "updatedAt": { "type": "string", "format": "date-time" }, "deletedVectors": { "type": "integer" } } }, "ModelSummary": { "type": "object", "required": ["version"], "properties": { "version": { "type": "string" }, "source": { "type": "string" }, "metadata": { "type": "object" } } }, "ModelSwap": { "type": "object", "required": ["kind", "from", "to"], "properties": { "kind": { "type": "string" }, "from": { "type": "string" }, "to": { "type": "string" } } }, "CompareReport": { "type": "object", "required": ["kind", "active", "candidate", "source", "n", "agreement"], "properties": { "kind": { "type": "string" }, "active": { "type": "string" }, "candidate": { "type": "string" }, "source": { "type": "string", "enum": ["events", "features"] }, "since": { "type": "string", "format": "date-time" }, "sites": { "type": "array", "items": { "type": "string" } }, "n": { "type": "integer" }, "skipped": { "type": "integer" }, "agreement": { "type": "number" }, "flagged": { "type": "object", "additionalProperties": { "type": "integer" } }, "transitions": { "type": "object", "additionalProperties": { "type": "integer" } }, "wouldChange": { "type": "object" }, "labelled": { "type": "object" } } }, "DecisionLabel": { "type": "object", "required": ["decisionId", "label", "operator", "source", "createdAt", "updatedAt", "decisionTime", "signalLabels"], "properties": { "source": { "type": "string", "enum": ["operator", "probe", "synthetic"] }, "decisionId": { "type": "string" }, "label": { "$ref": "#/components/schemas/LabelValue" }, "operator": { "type": "string" }, "note": { "type": "string" }, "createdAt": { "type": "string", "format": "date-time" }, "updatedAt": { "type": "string", "format": "date-time" }, "decisionTime": { "type": "string", "format": "date-time" }, "kind": { "type": "string" }, "action": { "type": "string" }, "route": { "type": "string" }, "outcome": { "type": "string" }, "wouldDecision": { "type": "string" }, "matchedRule": { "type": "string" }, "ruleSetVersion": { "type": "string" }, "modelVersions": { "type": "object", "additionalProperties": { "type": "string" } }, "signalLabels": { "type": "array", "items": { "type": "string" } }, "scores": { "type": "object", "additionalProperties": { "type": "number" } } } }, "LabelPage": { "type": "object", "required": ["items"], "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/DecisionLabel" } }, "nextCursor": { "type": "string" } } }, "InstallDecision": { "type": "object", "required": ["id", "time", "route", "outcome"], "properties": { "id": { "type": "string" }, "time": { "type": "string", "format": "date-time" }, "route": { "type": "string" }, "outcome": { "$ref": "#/components/schemas/Outcome" } } }, "InstallStatus": { "type": "object", "required": ["status", "firstSeenAt", "lastSeenAt", "originsSeen", "recent"], "properties": { "status": { "type": "string", "enum": ["waiting", "live"] }, "firstSeenAt": { "type": ["string", "null"], "format": "date-time" }, "lastSeenAt": { "type": ["string", "null"], "format": "date-time" }, "originsSeen": { "type": "array", "items": { "type": "string" }, "description": "Browser origins of recent SDK challenges" }, "recent": { "type": "array", "items": { "$ref": "#/components/schemas/InstallDecision" }, "description": "Newest 5 decisions" } } }, "HealthCheck": { "type": "object", "required": ["id", "severity", "title", "detail"], "properties": { "id": { "type": "string", "enum": ["siteverify_missing", "no_credential", "not_installed", "no_traffic", "origin_not_allowed", "siteverify_errors"] }, "severity": { "type": "string", "enum": ["critical", "warning", "info"] }, "title": { "type": "string" }, "detail": { "type": "string" }, "fix": { "type": "object", "required": ["kind", "label"], "properties": { "kind": { "type": "string", "enum": ["server_verification", "snippet", "origins", "credential"] }, "label": { "type": "string" } } }, "evidence": { "type": "object", "additionalProperties": { "type": "integer" }, "description": "The counts behind the check (per origin or error code where it names them)" } } }, "SiteHealth": { "type": "object", "required": ["siteKey", "name", "status", "from", "to", "checks", "summary", "checkedAt"], "properties": { "siteKey": { "type": "string" }, "name": { "type": "string" }, "status": { "type": "string", "enum": ["ok", "info", "warning", "critical"], "description": "The worst check's severity" }, "from": { "type": "string", "format": "date-time" }, "to": { "type": "string", "format": "date-time" }, "checks": { "type": "array", "items": { "$ref": "#/components/schemas/HealthCheck" } }, "summary": { "type": "object", "required": ["verifications", "tokensIssued", "tokensSettled", "serverChecks", "siteverifyValid", "likelyHuman", "likelyHumanOf", "lastRequestAt"], "properties": { "verifications": { "type": "integer", "description": "Browser verifications started (see FlowCounts.issued)" }, "tokensIssued": { "type": "integer" }, "tokensSettled": { "type": "integer", "description": "Tokens issued more than a receipt lifetime ago: past verifying" }, "serverChecks": { "type": "integer", "description": "siteverify answers (valid or not) plus gateway receipt and clearance checks" }, "siteverifyValid": { "type": "integer" }, "likelyHuman": { "type": ["number", "null"], "description": "Estimate, 0-1: of the first verdicts on browser verifications (passed without a visible check, device-attested, shown the visible check, or declined), the share that passed without a visible check and that enforce mode would also have passed. Interactive widgets always show the check, so it reads low for them. Null with no verifications." }, "likelyHumanOf": { "type": "integer", "description": "The number of first verdicts likelyHuman is a share of" }, "lastRequestAt": { "type": ["string", "null"], "format": "date-time" } } }, "checkedAt": { "type": "string", "format": "date-time" } } }, "FlowCounts": { "type": "object", "required": ["issued", "solved", "unsolved", "solvedInvisible", "solvedAttested", "solvedInteractive", "shown", "failedStep", "declined", "errors", "abandoned", "solveRate", "clearanceReuse", "siteverifyValid", "siteverifyInvalid", "wouldChallenge", "wouldBlock"], "properties": { "issued": { "type": "integer", "description": "Browser verifications started: solvedInvisible + solvedAttested + shown + declined + errors" }, "solved": { "type": "integer", "description": "Tokens issued: solvedInvisible + solvedAttested + solvedInteractive" }, "unsolved": { "type": "integer", "description": "issued - solved" }, "solvedInvisible": { "type": "integer", "description": "Passed without a visible check (non-interactive)" }, "solvedAttested": { "type": "integer", "description": "Passed with a device attestation (Private Access Token)" }, "solvedInteractive": { "type": "integer", "description": "Passed after the visible check (press-and-hold or wait)" }, "shown": { "type": "integer", "description": "Answered with the visible check" }, "failedStep": { "type": "integer", "description": "The visible check's answer was refused" }, "declined": { "type": "integer", "description": "Refused by the rules or rate limited" }, "errors": { "type": "integer", "description": "Unknown, expired, reused or misbound challenges; service errors" }, "abandoned": { "type": "integer", "description": "Estimate: shown - solvedInteractive - failedStep, at least 0" }, "solveRate": { "type": ["number", "null"], "description": "solved / issued" }, "clearanceReuse": { "type": "integer", "description": "Gateway requests admitted by a clearance instead of a new check" }, "siteverifyValid": { "type": "integer" }, "siteverifyInvalid": { "type": "integer", "description": "Failures AgentGate could attribute to the site (after the credential matched)" }, "wouldChallenge": { "type": "integer", "description": "Monitor mode: tokens enforce mode would have challenged" }, "wouldBlock": { "type": "integer", "description": "Monitor mode: tokens enforce mode would have blocked or dropped" } } }, "AnalyticsPoint": { "allOf": [{ "$ref": "#/components/schemas/FlowCounts" }, { "type": "object", "required": ["start", "end"], "properties": { "start": { "type": "string", "format": "date-time" }, "end": { "type": "string", "format": "date-time" } } }] }, "KeyCount": { "type": "object", "required": ["key", "count"], "properties": { "key": { "type": "string" }, "count": { "type": "integer" } } }, "SiteAnalytics": { "type": "object", "required": ["siteKey", "mode", "widgetMode", "rangeFrom", "rangeTo", "eventsFrom", "countersSince", "bucket", "totals", "series", "top"], "properties": { "siteKey": { "type": "string" }, "mode": { "$ref": "#/components/schemas/Mode" }, "widgetMode": { "$ref": "#/components/schemas/WidgetMode" }, "rangeFrom": { "type": "string", "format": "date-time" }, "rangeTo": { "type": "string", "format": "date-time" }, "eventsFrom": { "type": "string", "format": "date-time", "description": "Event-derived numbers start here (event retention)" }, "countersSince": { "type": ["string", "null"], "format": "date-time", "description": "When siteverify and origin counting started on this server" }, "bucket": { "type": "string", "enum": ["hour", "day"] }, "totals": { "allOf": [{ "$ref": "#/components/schemas/FlowCounts" }, { "type": "object", "required": ["likelyHuman", "likelyHumanOf", "gatewayReceipts", "siteverifyByCode"], "properties": { "likelyHuman": { "type": ["number", "null"], "description": "Estimate; defined as in SiteHealth.summary.likelyHuman" }, "likelyHumanOf": { "type": "integer" }, "gatewayReceipts": { "type": "integer", "description": "Gateway receipt checks, valid or not" }, "siteverifyByCode": { "type": "object", "additionalProperties": { "type": "integer" }, "description": "success and each error code" } } }] }, "series": { "type": "array", "items": { "$ref": "#/components/schemas/AnalyticsPoint" } }, "top": { "type": "object", "required": ["origins", "engines", "countries", "widgetModes"], "properties": { "origins": { "type": "array", "items": { "$ref": "#/components/schemas/KeyCount" }, "description": "Page origins of issued tokens" }, "engines": { "type": "array", "items": { "$ref": "#/components/schemas/KeyCount" }, "description": "Browser engine from the User-Agent (verifications)" }, "countries": { "type": "array", "items": { "$ref": "#/components/schemas/KeyCount" }, "description": "Country of the client address's AS registration (verifications); empty without the IP intelligence feed" }, "widgetModes": { "type": "array", "items": { "$ref": "#/components/schemas/KeyCount" }, "description": "Widget modes the pages used (verifications)" } } } } }, "AccessRequest": { "type": "object", "required": ["id", "email", "name", "company", "website", "message", "status", "createdAt"], "properties": { "id": { "type": "string" }, "email": { "type": "string" }, "name": { "type": "string" }, "company": { "type": "string" }, "website": { "type": "string" }, "message": { "type": "string" }, "status": { "type": "string", "enum": ["pending", "approved", "declined"] }, "createdAt": { "type": "string", "format": "date-time" }, "decidedAt": { "type": "string", "format": "date-time" }, "decidedBy": { "type": "string" }, "customerId": { "type": "string" } } }, "AccessRequestList": { "type": "object", "required": ["items"], "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/AccessRequest" } } } }, "InviteInput": { "type": "object", "required": ["email", "name"], "additionalProperties": false, "properties": { "email": { "type": "string" }, "name": { "type": "string", "maxLength": 200 }, "company": { "type": "string", "maxLength": 200 }, "customerId": { "type": "string", "description": "Add the user to this existing customer instead of creating one" } } }, "InviteResult": { "type": "object", "required": ["inviteUrl", "customerId", "userId"], "properties": { "inviteUrl": { "type": "string", "description": "Single use, 7 days" }, "customerId": { "type": "string" }, "userId": { "type": "string" } } }, "Account": { "type": "object", "required": ["id", "email", "name", "customerId", "customerName", "activated", "createdAt", "accounts", "status", "mfa"], "properties": { "id": { "type": "string" }, "email": { "type": "string" }, "name": { "type": "string" }, "customerId": { "type": "string" }, "customerName": { "type": "string" }, "activated": { "type": "boolean", "description": "A password is set" }, "createdAt": { "type": "string", "format": "date-time" }, "lastLoginAt": { "type": "string", "format": "date-time" }, "disabledAt": { "type": "string", "format": "date-time", "description": "Suspended since" }, "status": { "type": "string", "enum": ["active", "invited", "suspended"], "description": "invited: no password yet" }, "mfa": { "type": "boolean", "description": "2FA is on" }, "accounts": { "type": "array", "items": { "$ref": "#/components/schemas/MembershipRef" }, "description": "The user's memberships; customerId is the default account" } } }, "AccountList": { "type": "object", "required": ["items"], "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/Account" } } } }, "Role": { "type": "string", "enum": ["owner", "admin", "member", "readonly"] }, "AccountStatus": { "type": "string", "enum": ["active", "suspended", "deleted"] }, "Permission": { "type": "string", "enum": ["sites:read", "sites:write", "analytics:read", "members:read", "members:write", "tokens:write", "account:write", "account:delete", "audit:read"] }, "AccountLimits": { "type": "object", "required": ["maxSites", "maxCredentialsPerSite", "maxMembers"], "properties": { "maxSites": { "type": "integer" }, "maxCredentialsPerSite": { "type": "integer", "description": "Live (unrevoked, unexpired) credentials per site" }, "maxMembers": { "type": "integer", "description": "Memberships, invited or active" } } }, "AccountUsage": { "type": "object", "required": ["sites", "members", "tokens"], "properties": { "sites": { "type": "integer" }, "members": { "type": "integer" }, "tokens": { "type": "integer", "description": "Live API tokens" } } }, "MembershipRef": { "type": "object", "required": ["id", "customerId", "name", "role", "status"], "properties": { "id": { "type": "string", "description": "The account's ID (the same as customerId)" }, "customerId": { "type": "string" }, "name": { "type": "string", "description": "The account's name" }, "role": { "$ref": "#/components/schemas/Role" }, "status": { "$ref": "#/components/schemas/AccountStatus" } } }, "AccountSummary": { "type": "object", "required": ["id", "name", "role", "status"], "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "role": { "$ref": "#/components/schemas/Role" }, "status": { "$ref": "#/components/schemas/AccountStatus" } } }, "ActiveAccount": { "type": "object", "required": ["id", "name", "role", "status", "limits", "permissions"], "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "role": { "$ref": "#/components/schemas/Role" }, "status": { "$ref": "#/components/schemas/AccountStatus" }, "limits": { "$ref": "#/components/schemas/AccountLimits" }, "permissions": { "type": "array", "items": { "$ref": "#/components/schemas/Permission" }, "description": "What the role allows, for role-aware UI" } } }, "MfaInfo": { "type": "object", "required": ["enabled", "required"], "properties": { "enabled": { "type": "boolean", "description": "The user has 2FA on" }, "required": { "type": "boolean", "description": "The active account requires 2FA" } } }, "ConsoleAccount": { "type": "object", "required": ["id", "name", "status", "createdAt", "limits", "usage", "requireMfa"], "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "status": { "$ref": "#/components/schemas/AccountStatus" }, "createdAt": { "type": "string", "format": "date-time" }, "limits": { "$ref": "#/components/schemas/AccountLimits" }, "usage": { "$ref": "#/components/schemas/AccountUsage" }, "requireMfa": { "type": "boolean" } } }, "AccountPatch": { "type": "object", "additionalProperties": false, "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 200 }, "requireMfa": { "type": "boolean", "description": "Require 2FA for every member" } } }, "ConfirmInput": { "type": "object", "required": ["confirm"], "additionalProperties": false, "properties": { "confirm": { "type": "string", "description": "The account name, exactly" } } }, "DeletedAccount": { "type": "object", "required": ["id", "status", "deletedAt", "purgeAt"], "properties": { "id": { "type": "string" }, "status": { "$ref": "#/components/schemas/AccountStatus" }, "deletedAt": { "type": "string", "format": "date-time" }, "purgeAt": { "type": "string", "format": "date-time", "description": "The data is removed after this" } } }, "Member": { "type": "object", "required": ["userId", "email", "name", "role", "status", "mfa", "invitedAt", "activated"], "properties": { "userId": { "type": "string" }, "email": { "type": "string" }, "name": { "type": "string" }, "role": { "$ref": "#/components/schemas/Role" }, "status": { "type": "string", "enum": ["active", "invited", "suspended"], "description": "invited: no password yet" }, "lastLoginAt": { "type": "string", "format": "date-time" }, "mfa": { "type": "boolean" }, "invitedAt": { "type": "string", "format": "date-time", "description": "When the membership was created" }, "invitedBy": { "type": "string", "description": "Audit actor that added the member" }, "activated": { "type": "boolean" } } }, "MemberList": { "type": "object", "required": ["items"], "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/Member" } } } }, "MemberInput": { "type": "object", "required": ["email", "role"], "additionalProperties": false, "properties": { "email": { "type": "string" }, "name": { "type": "string", "maxLength": 200, "description": "For a new user; default the email's local part" }, "role": { "$ref": "#/components/schemas/Role" } } }, "RoleInput": { "type": "object", "required": ["role"], "additionalProperties": false, "properties": { "role": { "$ref": "#/components/schemas/Role" } } }, "MemberInviteResult": { "type": "object", "required": ["userId", "member"], "properties": { "userId": { "type": "string" }, "member": { "$ref": "#/components/schemas/Member" }, "inviteUrl": { "type": "string", "description": "Present when a link was issued (a user without a password)" } } }, "AuditEvent": { "type": "object", "required": ["id", "customerId", "time", "actor", "action", "target", "detail"], "properties": { "id": { "type": "integer" }, "customerId": { "type": "string" }, "time": { "type": "string", "format": "date-time" }, "actor": { "type": "string", "description": "user:, token:, operator: or system" }, "actorEmail": { "type": "string", "description": "For user actors" }, "action": { "type": "string", "description": "e.g. member.invite, member.role, member.remove, member.resend_invite, account.create, account.rename, account.limits, account.suspend, account.reactivate, account.delete, account.transfer_ownership, user.sign_in, user.suspend, user.reactivate, user.reset_link, site.create, site.update, site.mode, site.move, site.move_out, credential.create, credential.rotate, credential.revoke, rules.*" }, "target": { "type": "string", "description": "A user ID, site key or account ID" }, "detail": { "type": "object", "additionalProperties": true } } }, "AuditPage": { "type": "object", "required": ["items"], "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/AuditEvent" } }, "nextCursor": { "type": "string" } } }, "Customer": { "type": "object", "required": ["id", "name", "status", "createdAt", "limits", "ownerEmail", "memberCount", "siteCount"], "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "status": { "$ref": "#/components/schemas/AccountStatus" }, "createdAt": { "type": "string", "format": "date-time" }, "suspendedAt": { "type": "string", "format": "date-time" }, "suspendedReason": { "type": "string" }, "deletedAt": { "type": "string", "format": "date-time" }, "purgeAt": { "type": "string", "format": "date-time", "description": "Deleted accounts: when the data goes" }, "limits": { "$ref": "#/components/schemas/AccountLimits" }, "ownerEmail": { "type": "string", "description": "The first owner's; empty if none" }, "memberCount": { "type": "integer" }, "siteCount": { "type": "integer" }, "lastActivityAt": { "type": "string", "format": "date-time", "description": "The newest sign-in of a member" } } }, "CustomerList": { "type": "object", "required": ["items"], "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/Customer" } } } }, "CustomerSite": { "type": "object", "required": ["siteKey", "name", "mode", "widgetMode", "createdAt", "health"], "properties": { "siteKey": { "type": "string" }, "name": { "type": "string" }, "mode": { "type": "string", "enum": ["monitor", "enforce"] }, "widgetMode": { "type": "string", "enum": ["invisible", "managed", "interactive"] }, "createdAt": { "type": "string", "format": "date-time" }, "health": { "type": "object", "required": ["status"], "properties": { "status": { "type": "string", "enum": ["ok", "no_credentials", "no_traffic", "no_routes"] }, "lastEventAt": { "type": "string", "format": "date-time" } } } } }, "CustomerDetail": { "allOf": [{ "$ref": "#/components/schemas/Customer" }, { "type": "object", "required": ["usage", "requireMfa", "members", "sites", "tokens", "audit"], "properties": { "usage": { "$ref": "#/components/schemas/AccountUsage" }, "requireMfa": { "type": "boolean" }, "members": { "type": "array", "items": { "$ref": "#/components/schemas/Member" } }, "sites": { "type": "array", "items": { "$ref": "#/components/schemas/CustomerSite" } }, "tokens": { "type": "array", "items": { "$ref": "#/components/schemas/CustomerToken" }, "description": "API tokens, revoked ones included; never secrets" }, "audit": { "type": "array", "items": { "$ref": "#/components/schemas/AuditEvent" }, "description": "The 20 newest audit events" } } }] }, "CustomerToken": { "type": "object", "required": ["id", "name", "scopes", "createdBy", "createdAt", "status"], "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "scopes": { "type": "array", "items": { "type": "string", "enum": ["sites:read", "sites:write", "analytics:read", "members:read"] } }, "createdBy": { "type": "string" }, "createdByEmail": { "type": "string" }, "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"] } } }, "CustomerPatch": { "type": "object", "additionalProperties": false, "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 200 }, "limits": { "type": "object", "additionalProperties": false, "properties": { "maxSites": { "type": "integer", "minimum": 0, "maximum": 1000 }, "maxCredentialsPerSite": { "type": "integer", "minimum": 1, "maximum": 20 }, "maxMembers": { "type": "integer", "minimum": 1, "maximum": 1000 } } } } }, "OutboxMessage": { "type": "object", "required": ["id", "to", "subject", "body", "createdAt", "attempts"], "properties": { "id": { "type": "string" }, "to": { "type": "string" }, "subject": { "type": "string" }, "body": { "type": "string" }, "createdAt": { "type": "string", "format": "date-time" }, "sentAt": { "type": "string", "format": "date-time" }, "attempts": { "type": "integer" }, "error": { "type": "string" } } }, "OutboxList": { "type": "object", "required": ["items"], "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/OutboxMessage" } } } }, "DocsFeedbackCount": { "type": "object", "required": ["page", "title", "helpful", "notHelpful"], "properties": { "page": { "type": "string" }, "title": { "type": "string", "description": "Empty when the page no longer exists" }, "helpful": { "type": "integer" }, "notHelpful": { "type": "integer" } } }, "DocsFeedbackReport": { "type": "object", "required": ["days", "since", "helpful", "notHelpful", "pages"], "properties": { "days": { "type": "integer" }, "since": { "type": "string", "format": "date-time" }, "helpful": { "type": "integer" }, "notHelpful": { "type": "integer" }, "pages": { "type": "array", "items": { "$ref": "#/components/schemas/DocsFeedbackCount" } } } } } } }