# qart.uk auth.md

How an agent gets a credential on qart.uk — or transacts without one.

## Can I pay without a human? Yes.

Read this first, because it is the question this document exists to answer and
the answer is not "no".

`GET https://qart.uk/api/v1/qr?url=...` sells one finished, scan-verified QR artwork
outright. **No account, no bearer token, no sign-in, no redemption code to hand
to a person.** It answers 402 with x402 payment requirements in the
`PAYMENT-REQUIRED` response header; sign with your own wallet, retry, and the
PNG comes back in the response body. The wallet signature is the only identity
involved.

You are charged **only** if a real QR detector decodes the image: settlement runs
after the scan verdict, so a request that produces nothing scannable is never
settled and costs nothing. The live price per size, and the parameters the
endpoint takes, are in the 402 body at `https://qart.uk/api/v1` — read it rather than
caching a figure, since the price is an operator setting. Style keys are free at
`https://qart.uk/api/v1/styles`.

The rest of this document is about the *other* thing an agent may want: acting on
a **person's** qart.uk account, which does need that person's consent.

## At a glance

| | |
|---|---|
| Audience | Agents acting **on behalf of a signed-in qart.uk user** — plus, without any of this, agents buying a single QR at `/api/v1/qr`. |
| Method | OAuth 2.1 — RFC 7591 dynamic client registration, then authorization code + PKCE (S256). |
| Register | `POST https://qart.uk/api/oauth/register` — open, no approval step, no client secret. |
| Anonymous identity | `POST https://qart.uk/api/agent/identity` — an agent with no user behind it mints its own, narrowly scoped. |
| Consent | A human approves on a qart.uk screen. There is no unattended path **to somebody's account**; there is one to a purchase. |
| Credential | `Authorization: Bearer <access_token>` — 1 hour, refresh 30 days, scoped, revocable. |
| Metadata | `https://qart.uk/.well-known/oauth-authorization-server` (carries the `agent_auth` block) |
| Resource | `https://qart.uk/.well-known/oauth-protected-resource` |

## Minting your own identity, with no user at all

`POST https://qart.uk/api/agent/identity` implements the anonymous-credential
ceremony. It takes no credential and returns an `access_token` (audience
`urn:qart:agent`) carrying `qart:profile qart:read qart:purchase`, plus an
`identity_id` and a `claim_code`. A human can later redeem that claim code at
`https://qart.uk/api/agent/identity/claim`, and the identity — its credit balance and
anything it owns — becomes theirs.

The credential is deliberately **narrow**. It cannot claim free pool codes
(unlimited provisional accounts against a finite catalogue is a drain vector) and
it cannot start Studio runs. If you want an account-less path to *artwork* rather
than to a balance, `/api/v1/qr` above is shorter and needs no token at all.

## agent_auth

The same block is served inside the Authorization Server metadata at
`https://qart.uk/.well-known/oauth-authorization-server`, which is where RFC 8414
puts extensions. It is repeated here because readers differ on where to look,
and the two copies are generated from one definition so they cannot drift.

```json
{
  "skill": "https://qart.uk/auth.md",
  "identity_types_supported": [
    "anonymous"
  ],
  "anonymous": {
    "credential_types_supported": [
      "bearer"
    ],
    "scopes_granted": [
      "qart:profile",
      "qart:read",
      "qart:purchase"
    ]
  },
  "register_uri": "https://qart.uk/api/agent/identity",
  "identity_endpoint": "https://qart.uk/api/agent/identity",
  "claim_uri": "https://qart.uk/api/agent/identity/claim",
  "claim_endpoint": "https://qart.uk/api/agent/identity/claim",
  "revocation_uri": "https://qart.uk/api/oauth/revoke",
  "oauth_register_uri": "https://qart.uk/api/oauth/register",
  "registration_methods_supported": [
    "anonymous",
    "oauth_dynamic_client_registration"
  ],
  "grant_types_supported": [
    "authorization_code",
    "refresh_token"
  ],
  "code_challenge_methods_supported": [
    "S256"
  ],
  "scopes_supported": [
    "qart:profile",
    "qart:read",
    "qart:claim",
    "qart:studio",
    "qart:purchase"
  ],
  "user_consent_required": true,
  "revocation_endpoint": "https://qart.uk/api/oauth/revoke"
}
```

`registration_methods_supported` has **two** entries and they answer different
questions:

- `anonymous` — mint your own identity at `register_uri`
  (`https://qart.uk/api/agent/identity`), no user required. See the section above.
- `oauth_dynamic_client_registration` — register a *client* at
  `oauth_register_uri`, then run the authorization-code + PKCE flow below to act
  for an existing user.

`user_consent_required: true` is about the second one only. There is no
unattended path to **a person's account** and there is not going to be one — two
of the five scopes spend their money. There *are* two unattended paths to a
**purchase**: the anonymous identity above, and `/api/v1/qr`, which needs no
identity whatsoever.

## Without credentials

These need no sign-in:

| Capability | Endpoint |
|---|---|
| **Buy one scan-verified QR artwork, paid from your own wallet** | `GET https://qart.uk/api/v1/qr?url=...` (402 → sign → `image/png`) |
| The live price and parameters for the above | `GET https://qart.uk/api/v1` (always 402; the body is the documentation) |
| Style keys accepted by `/api/v1/qr` | `GET https://qart.uk/api/v1/styles` |
| Re-fetch an image you already paid for, for 7 days | `GET https://qart.uk/api/v1/result/{id}` |
| Mint an anonymous agent identity | `POST https://qart.uk/api/agent/identity` |
| Resolve a scanned code to its destination | `GET https://qart.uk/api/qart/resolve/{alias}` |
| Browse the free claim pool | `GET https://qart.uk/api/qart/pool/gallery` |
| Feature flags and Studio pricing inputs | `GET https://qart.uk/api/qart/config` |
| MCP tools that need no account | `POST https://qart.uk/api/mcp` |
| A2A skills that need no account | `POST https://qart.uk/api/a2a` |

Only the first row costs money, and only when it delivers.

## As a user, via OAuth 2.1

qart.uk is an OAuth 2.1 authorization server. Register, get consent, and act on
a person's account with scopes they agreed to.

    Metadata:      https://qart.uk/.well-known/oauth-authorization-server
    Resource:      https://qart.uk/.well-known/oauth-protected-resource

Public clients only — **PKCE with S256 is mandatory** and there is no client
secret. Dynamic client registration (RFC 7591) is open, so you can self-register:

1. `POST https://qart.uk/api/oauth/register` with `client_name` and `redirect_uris`.
   Redirect URIs must be https, or loopback for a native app.
2. Send the user to `https://qart.uk/api/oauth/authorize` with `response_type=code`,
   your `client_id`, `redirect_uri`, `scope`, `state`, and
   `code_challenge` + `code_challenge_method=S256`.
3. They approve on a qart.uk consent screen. You get a code on your redirect URI.
4. `POST https://qart.uk/api/oauth/token` with `grant_type=authorization_code`, the
   code, your `client_id`, the same `redirect_uri`, and `code_verifier`.

You receive an access token (1 hour) and a refresh token (30 days).

### Scopes

| Scope | Grants |
|---|---|
| `qart:profile` | Which account you are acting for. |
| `qart:read` | Their codes, credit balance, and Studio run status. |
| `qart:claim` | Claim, retarget and retire pool codes. Uses their daily allowance. Costs no money. |
| `qart:studio` | **Spends real money.** Starts Studio runs against their prepaid credit balance. There is no per-application spending limit. |
| `qart:purchase` | **Spends your own money, not theirs.** Buys credit packs from the application's own wallet over x402 or MPP. The user's card is never charged and no saved payment method is touched; the credits land on their balance. |

Ask for the least you need. `qart:studio` and `qart:purchase` are both shown to
the user as warnings, and requesting either when you do not need it will cost you
approvals.

### Using the token

Send `Authorization: Bearer <access_token>` to either surface:

- **The MCP server** at `https://qart.uk/api/mcp` — `tools/list` returns the anonymous
  tools plus whichever scoped tools your grant covers. The whole product is here:
  claiming and shuffling pool codes, retargeting, and running the Studio. A tool you
  lack the scope for is not listed and cannot be called.
- **The HTTP API** — see `https://qart.uk/openapi.json`. The same capabilities as HTTP
  routes, for clients that are not MCP-shaped.

Both surfaces call the same service code behind the same scope gates; pick whichever
fits the client. The one rule that applies everywhere: a Studio run is only alive
while something is polling it, so keep polling until the run is terminal.

### Refreshing and revoking

Refresh tokens are **single-use and rotate**: each refresh returns a new one.
Reusing a spent refresh token is treated as theft and revokes the entire grant,
so store only the newest. `POST https://qart.uk/api/oauth/revoke` to hand a grant back.

## What will not work

- **Session cookies.** The browser session is an HttpOnly, unscoped, 30-day
  credential. It cannot be obtained programmatically and the MCP server rejects
  it outright. Never ask a user to paste one.
- **Redeeming coupons or enabling adult content.** These have no agent path at
  any scope, deliberately. They are decisions for a human in a browser.
- **Charging the user's saved card.** `POST /api/qart/checkout` is browser-only
  and no scope reaches it. An agent that wants more credits pays for them itself
  — see below.

## Paying from your own wallet

Three rails in two shapes, and they are not interchangeable. Pick by what you
actually want to end up holding.

### 1. You want the artwork — no account needed

`GET https://qart.uk/api/v1/qr?url=...&style=...&size=768|1024`. **No token of any
kind.** 402 → sign the x402 requirement from the `PAYMENT-REQUIRED` header →
retry → `image/png`. Charged only if a real QR detector decodes the image, since
settlement runs after the scan verdict. Retrying with the same payment nonce
returns the same image rather than generating and charging twice. The live price
per size is in the 402 at `https://qart.uk/api/v1`.

The trade-off, stated plainly so you can choose against it: this encodes your
destination **directly**, mints no qart.uk redirect, and therefore can never be
retargeted. If you need a code you can re-point after it is printed, buy credits
instead and run the Studio on the `qart.uk code` payload kind, or claim a free
pool code.

### 2. You want credits on an account — two rails

An agent that runs out mid-task can top up without a human, paying from its own
wallet rather than the user's card. Both rails need `qart:purchase` and add
ordinary credits to the authenticated account.

- **MPP** (Stripe's Machine Payable Protocol, over Tempo) —
  `POST https://qart.uk/api/qart/mpp/credits`. Capabilities and pack prices at
  `https://qart.uk/api/qart/mpp/info`.
- **x402** — `POST https://qart.uk/api/qart/x402/credits?pack=starter`. Requirements
  arrive in the `PAYMENT-REQUIRED` **response header**, not the body (v2).
  Advertised, with current network and asset, at
  `https://qart.uk/api/qart/x402/info`.

Check the `info` endpoint of either rail before quoting a price to a user: each
reports `enabled` and, when disabled, why. A rail that is off answers 503 rather
than inviting you to sign a payment it cannot settle.

## Rate limits and etiquette

- The free pool allows 5 shuffles and 3 claims per account per UTC day. These
  are not purchasable and not per-client.
- `GET /api/qart/studio/run/{id}` is **not** a passive read — it imports
  finished images and acts as the GPU presence heartbeat. Poll it about every 2
  seconds while a run is active, and stop as soon as the run is terminal.
- Identify yourself with a descriptive `User-Agent`. We would rather talk to you
  than block you.
