PureStats documentation
Authentication and Delegation
Authenticate PureStats agents with scoped OAuth, browser-approved delegation, short-lived tokens and recovery keys.
Public documentation, llms.txt and OpenAPI do not require authentication. Data and management operations do. Use the canonical OpenAPI schemas for exact fields and scopes.
Standalone Identities
New agents first call POST /api/v1/agents/identity/challenges with JSON domain, method (dns or https) and recovery_key. Optional fields are purpose (register, the default), name and contact_email. Generate the recovery key locally from 32 cryptographically random bytes represented as 64 lowercase hexadecimal characters; store it before the request. Contact email is optional and does not create a human login. Challenge creation does not reserve a domain or create an account.
Publish the returned data.value as a TXT record at data.dns_name, or as the HTTPS body at data.https_url, according to the requested method. Call POST /api/v1/agents/identity/registrations with JSON challenge_id, requested scopes and the original recovery_key. This verifies the proof and activates the machine account, confidential OAuth client and initial private site together. A challenge expires within 30 minutes; use its returned expires_at. Never send a recovery key to a proof location. Published proof alone cannot complete registration.
These public identity endpoints require JSON and a private Idempotency-Key of 64 lowercase hexadecimal characters generated independently from the recovery key. A successful response is HTTP 201 with {data, request_id}; data contains the confidential client credentials and granted site identifiers, never the recovery key. A retry with the same key and body can replay that confidential response. Keep the header private, redact it from logs and respect Retry-After on throttling. Identity responses are not cacheable.
After activation, Passport handles confidential-client token exchange at POST /oauth/token. Send form-encoded grant_type=client_credentials, client_id, client_secret, requested space-separated scope and the exact management resource from activation (https://purestats.io/api/v1/management). OAuth responses use top-level fields token_type, expires_in and access_token, not a data envelope. Machine tokens expire within 15 minutes and do not include a refresh token. Obtain another through client credentials when needed. The API validates issuer, audience, scopes, active ownership and site grants on every request; use Authorization: Bearer <access token>. Credentials belong on the server, never in the browser or tracker snippet. Client-credentials access does not implicitly grant a human's account.
Machine Recovery
Recovery needs both the old recovery key and fresh domain proof. Generate and securely store a different new 32-byte recovery key. Call POST /api/v1/agents/identity/challenges with domain, method, purpose: "recover" and the new recovery_key; include optional client_id if known. Publish this fresh proof, then call POST /api/v1/agents/identity/recoveries with its challenge_id, the old recovery_key and the same optional client_id. When recovering a lost activation response, omit client_id from both requests so the active credential can be resolved by the proven domain.
Use a distinct identity idempotency key for each operation. Success returns replacement confidential client credentials, retains the account, scopes and granted sites, and revokes the old client and its access and refresh tokens. Save the new credentials and new recovery key atomically in your secret store. No recovery key is returned, and reusing the old key as its replacement is rejected. A contact email or domain proof without the old key is not recovery authorization.
Existing-Account Delegation
Use the OAuth device authorization flow rather than asking an owner for a password, session cookie or personal API key. Bootstrap a pending confidential client at POST /api/v1/agents/identity/delegations with JSON name, scopes and optional resource (management is the default), plus the identity idempotency header. The HTTP 201 data contains client_id, client_secret, requested scopes and OAuth endpoints. Initially owner_id is null, effective scopes and site_ids are empty, and client credentials cannot issue account access. A human browser cookie sent to bootstrap does not approve anything.
Send form-encoded client_id, client_secret and space-separated scope to POST /oauth/device/code. Surface only the returned verification_uri and user_code so the owner can sign in and explicitly approve scopes and sites in the browser. Do not automate the consent decision or claim that opening the URI is approval.
A human with zero sites can approve consent with no existing sites selected when granting the explicit sites:create permission, or an account-only capability such as account.capabilities (account:read). Request only the intended capabilities. sites:create authorizes creation of new sites and grants those created sites to this client; sites:write configures existing granted sites and is not permission to create them. Read or configure a newly created site only with the separately consented sites:read or sites:write scope. Empty site selection never grants access to foreign sites: existing-site operations still enforce explicit grants and the owner's membership.
Consent can require fresh password or two-factor reauthentication. The owner completes that step directly in the PureStats browser session; the agent must not collect, transmit or log the owner's reauthentication secret.
Poll POST /oauth/token with form-encoded grant_type=urn:ietf:params:oauth:grant-type:device_code, the returned device_code, client_id, client_secret and the client's exact resource. Honor the returned polling interval and expires_in. Keep waiting on authorization_pending, increase the interval on slow_down, and stop on access_denied or expired_token. Only use a returned token after approval. The device code is a credential and must not be exposed alongside the user-facing code.
Approved delegation may return a refresh token. Refresh at /oauth/token using grant_type=refresh_token, refresh_token, client_id, client_secret, the granted scope and exact resource. Save a replacement refresh token atomically; rotation revokes the previous refresh token and associated access token. Scopes cannot grow beyond browser consent, and refresh does not grant additional existing sites. New sites created through explicitly consented sites:create are granted to the creating client; this is not authority over other accounts' sites. The flow semantics follow RFC 8628; deployed schemas and consent remain authoritative.
Secret Handling
Read secrets from a secret manager or environment: PURESTATS_CLIENT_SECRET, PURESTATS_ACCESS_TOKEN, PURESTATS_RECOVERY_SECRET. Keep them outside the worktree. Disable command tracing and never print registration or token responses containing credentials. Redact Authorization, identity idempotency keys, client secrets, device codes, recovery secrets, refresh tokens, cookies, API keys and sensitive payloads from debug output. A public domain challenge is not recovery material.
Do not commit secrets, embed them in URLs, install them in browser source, pass them to third-party documentation tools or quote them in chat. Public documentation examples intentionally contain no live credential. Rotate or revoke leaked access through supported identity operations rather than editing documentation or reusing old recovery material.