# auth.md

You are an agent. VettedTen supports **agentic registration**: discover → register → exchange the identity assertion for an access_token → call the API → handle revocation. Almost nothing here needs it: every page, every read endpoint of the API at https://vettedten.com/api/v1, both MCP servers, NLWeb /ask and the A2A agent are public and keyless. One endpoint, the bulk export (`GET https://vettedten.com/api/v1/export`, the whole dataset in one call), takes a bearer token that any agent can get on its own, free, with no human step.

The resource server and the authorization server are the same host: https://vettedten.com.

| Surface | Auth |
| --- | --- |
| Pages, markdown twins, llms.txt | None |
| `GET https://vettedten.com/api/v1/*` and `POST /api/v1/batch` | None |
| `POST https://vettedten.com/mcp` and `/mcp/docs`, `/ask`, `/a2a` | None |
| `GET https://vettedten.com/api/v1/export` | Bearer access_token, scope `export` |

## Step 1: Discover

Calling the bulk export without a token answers `401` with the metadata address:

```http
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://vettedten.com/.well-known/oauth-protected-resource"
```

1a. Fetch the Protected Resource Metadata (RFC 9728) at `https://vettedten.com/.well-known/oauth-protected-resource`. Its `resource` is `https://vettedten.com/api`, `authorization_servers` is `["https://vettedten.com"]`, `scopes_supported` is `["export"]` and `bearer_methods_supported` is `["header"]`.

1b. Fetch the Authorization Server metadata (RFC 8414) at `https://vettedten.com/.well-known/oauth-authorization-server`. Its `agent_auth` block names this document as the `skill`, the `identity_endpoint` (`https://vettedten.com/agent/identity`) and `identity_types_supported`: `["anonymous"]`. The token endpoint is `https://vettedten.com/oauth/token` and the revocation endpoint is `https://vettedten.com/oauth/revoke`.

## Step 2: Pick a method

Use `anonymous`. VettedTen has no user accounts, so there is no user identity to assert: `identity_assertion` (ID-JAG) and `service_auth` are not enabled, and the identity endpoint answers `issuer_not_enabled` or `service_auth_not_enabled` if you send them. Anonymous registrations get the full scope at once.

## Step 3: Register

```http
POST https://vettedten.com/agent/identity
Content-Type: application/json

{ "type": "anonymous" }
```

Response (200):

```json
{
  "registration_id": "reg_…",
  "registration_type": "anonymous",
  "identity_assertion": "<service-signed JWT>",
  "assertion_expires": "…",
  "pre_claim_scopes": ["export"],
  "post_claim_scopes": ["export"]
}
```

There is no consent step: an anonymous registration asserts no one's identity. Keep the `identity_assertion`; it is valid for 30 days.

Prefer classic OAuth? RFC 7591 dynamic client registration works too: `POST https://vettedten.com/oauth/register` with `{"client_name": "my-agent"}` returns a `client_id` and `client_secret` for the `client_credentials` grant.

## Step 4: Claim ceremony

Not needed, and not offered. A claim ceremony binds an agent's registration to a signed-in user so the user can take ownership and unlock more scopes. VettedTen has no user accounts and no further scopes (`post_claim_scopes` equals `pre_claim_scopes`), so no `claim_endpoint` is advertised and every registration is complete as issued. Go straight to Step 5.

## Step 5: Exchange the assertion

```http
POST https://vettedten.com/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=<identity_assertion>&resource=https://vettedten.com/api
```

Response (200):

```json
{ "access_token": "<token>", "token_type": "Bearer", "expires_in": 3600, "scope": "export" }
```

The same `identity_assertion` mints more access_tokens until it expires. With RFC 7591 credentials instead: `grant_type=client_credentials&client_id=…&client_secret=…` (form fields or HTTP Basic).

## Step 6: Use the access_token

```http
GET https://vettedten.com/api/v1/export
Authorization: Bearer <access_token>
```

It returns every guide, type hub and category as one JSON document. `GET https://vettedten.com/agent/identity` with the same header says which registration the token belongs to and when it expires. When the access_token expires (after `expires_in` seconds), repeat Step 5. When the identity_assertion expires, or the token endpoint answers `invalid_grant`, start again at Step 3. There are no refresh tokens.

The export is the same data the keyless endpoints give a page at a time, so pulling it more than a few times a day suggests you want `GET /api/v1/guides` instead.

## Errors

| Code | Where | What to do |
| --- | --- | --- |
| `invalid_request` | `/agent/identity` | The body isn't JSON with a `type`. Send `{"type": "anonymous"}`. |
| `issuer_not_enabled` | `/agent/identity` | identity_assertion (ID-JAG) isn't accepted here. Use anonymous. |
| `service_auth_not_enabled` | `/agent/identity` | service_auth isn't accepted here. Use anonymous. |
| `anonymous_not_enabled` | `/agent/identity` | Never sent by VettedTen; anonymous is always on. |
| `invalid_grant` | `/oauth/token` | The assertion is expired, revoked or not ours. Restart at Step 3. |
| `invalid_client` (401) | `/oauth/token` | client_credentials with a wrong client_id or client_secret. Register again. |
| `unsupported_grant_type` | `/oauth/token` | Use `urn:ietf:params:oauth:grant-type:jwt-bearer` or `client_credentials`. |
| `invalid_target` | `/oauth/token` | The `resource` isn't `https://vettedten.com/api`. Send that, or leave it out. |
| `invalid_token` (401) | `/api/v1/export` | Missing, expired or revoked access_token. Repeat Step 5. |
| `rate_limited` (429) | any | Over 300 requests a minute from your IP. Wait for `Retry-After` seconds. |

Retry 5xx with backoff. Don't retry a 4xx unchanged: act on the table.

## Revocation

POST the token to the revocation endpoint (RFC 7009), form-encoded: `token=<access_token>&token_type_hint=access_token` to `https://vettedten.com/oauth/revoke`. It answers 200 every time (it's idempotent, and an unknown token answers 200 too). The token stops working at once; your identity_assertion still works, so Step 5 gets you a new one. Revoke an identity_assertion the same way (`token_type_hint=identity_assertion`) to retire a registration; tokens minted from it keep working until they expire. VettedTen accepts no upstream identity providers, so there is no `events_endpoint` and no registration-layer revocation.

## Contact

hello@vettedten.com. Nothing on this host writes anything on an agent's behalf.
