# auth.md

Agent authentication guide for **QuvtyQR** (https://quvtyqr.com).

## Overview

- Protected resource: https://quvtyqr.com/api/public/v1: menu, account, theme, statistics
  and subscription API.
- Authoritative metadata (always prefer these over probing):
  - Authorization server (RFC 8414): https://quvtyqr.com/.well-known/oauth-authorization-server
  - Protected resource (RFC 9728): https://quvtyqr.com/.well-known/oauth-protected-resource
  - API description (OpenAPI 3.1): https://quvtyqr.com/api/public/v1/openapi.json
- Credential type: long-lived opaque master API key (prefix qpk_live_), sent as
  Authorization: Bearer. Keys are not JWTs; there is no JWKS.

## Agent registration (service_auth)

Self-service registration for agents, approved by the business owner
(claim ceremony). Scopes available to agents: menu.read, menu.write, account.read.

1. Register: POST https://quvtyqr.com/agent/identity with JSON body:
   {"identity_type": "service_auth", "agent_name": "<your agent name>", "scope": "menu.read account.read"}
   Response (201) contains registration_id, poll_secret (shown once), claim_uri,
   poll_endpoint, interval and expires_in (30 minutes).
2. Claim: show claim_uri to the QuvtyQR business owner. They sign in, pick the
   business (tenant) and approve the requested scopes on that page.
3. Poll: POST https://quvtyqr.com/agent/identity/poll with JSON body:
   {"registration_id": "<id>", "poll_secret": "<secret>"}
   While pending the response is {"status": "pending", "interval": 5}. After
   approval the response contains the credential exactly once:
   {"status": "approved", "api_key": "qpk_live_...", "token_type": "ApiKey", "scope": "...", "tenant_id": "..."}
   Store it encrypted server-side; it is never shown again ("delivered" on later polls).
4. Use: call the API with Authorization: Bearer <api_key>. Required scope per
   endpoint is listed in the OpenAPI document. Unauthorized responses carry
   WWW-Authenticate: Bearer resource_metadata="..." per RFC 9728.
5. Revoke: the business owner can revoke the key from their panel at any time.
   Handle 401 responses by asking the owner to re-approve a new registration.

## Partner registration (OAuth 2.0, manual)

Privileged scopes (account.provision, subscription.manage) and redirect-based
integrations use OAuth 2.0 Authorization Code + PKCE (S256) with a manually
provisioned client. Endpoints are listed in the RFC 8414 metadata:
authorize at https://quvtyqr.com/oauth/authorize, token at https://quvtyqr.com/oauth/token
(client_secret_post; JSON or form-urlencoded), revoke at https://quvtyqr.com/oauth/revoke
(token parameter, RFC 7009 style). To request a client, contact
help@quvtyqr.com with your use case and redirect URIs.

## Public resources (no authentication)

- https://quvtyqr.com/ with Accept: text/markdown (markdown version of the site)
- https://quvtyqr.com/.well-known/api-catalog (RFC 9727 API catalog)
- https://quvtyqr.com/.well-known/mcp/server-card.json (MCP server card, tools at https://quvtyqr.com/mcp)
