# JustIdea Agency auth.md

You are an agent. justidea.agency lets agents register and get an OAuth access token for its public MCP server (`https://justidea.agency/mcp`) and its public REST API (`https://justidea.agency/api/v1/`). Both are open without any token and serve only content published on the website. A token identifies your agent and raises your REST API rate limit from 120 to 600 requests per minute.

There are no user accounts on justidea.agency. Registration asks for no personal data, sends no email and stores nothing: every credential is a signed, self-contained token.

## Step 1. Discover

- Protected Resource Metadata (RFC 9728): `https://justidea.agency/.well-known/oauth-protected-resource/mcp` for the MCP server, `https://justidea.agency/.well-known/oauth-protected-resource/api/v1` for the REST API, `https://justidea.agency/.well-known/oauth-protected-resource` for the whole site.
- Authorization Server Metadata (RFC 8414): `https://justidea.agency/.well-known/oauth-authorization-server`. Its `agent_auth` block points back to this file.
- A 401 (an invalid or expired token) carries `WWW-Authenticate: Bearer resource_metadata="..."` with the right metadata URL.

## Step 2. Pick a method

1. You are an autonomous agent or a script: register anonymously (Step 3). One request, no person involved.
2. You are an MCP client acting for a person (Claude, ChatGPT, Cursor and similar) and you speak OAuth: use the authorization code flow with PKCE (Step 6). Dynamic client registration is open.

Identity assertions (ID-JAG, verified email), service auth and API keys are not supported here.

## Step 3. Register anonymously

```http
POST https://justidea.agency/agent/auth/
Content-Type: application/json

{"type": "anonymous", "requested_credential_type": "access_token"}
```

Response (200):

```json
{
  "registration_id": "reg_...",
  "registration_type": "anonymous",
  "credential_type": "access_token",
  "credential": "<access token, valid for 1 hour>",
  "credential_expires": "2026-10-03T13:00:00.000Z",
  "scopes": ["site:read"],
  "identity_assertion": "<signed JWT, valid for 30 days>",
  "assertion_expires": "2026-11-02T12:00:00.000Z",
  "pre_claim_scopes": ["site:read"],
  "claim_url": "https://justidea.agency/agent/auth/claim/",
  "claim_token": "<signed JWT, valid for 24 hours>",
  "claim_token_expires": "2026-10-04T12:00:00.000Z",
  "post_claim_scopes": ["site:read"]
}
```

Use `credential` right away (Step 5). Keep `identity_assertion`: when the access token expires, exchange the assertion for a new one (Step 4) instead of registering again.

## Step 4. Exchange the identity assertion

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

grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=<identity_assertion>&resource=https://justidea.agency/mcp
```

Response (200): `{"access_token": "...", "token_type": "Bearer", "expires_in": 3600, "scope": "site:read"}`. Use `resource=https://justidea.agency/api/v1` for a token meant for the REST API; one token works for both.

## Step 5. Use the credential

```http
GET https://justidea.agency/api/v1/search?q=prestashop
Authorization: Bearer <access_token>
```

```http
POST https://justidea.agency/mcp
Authorization: Bearer <access_token>
Content-Type: application/json
Accept: application/json, text/event-stream

{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}
```

- 401: the token is invalid or expired. Drop it, exchange the assertion again (Step 4) or register again (Step 3). Requests without any token are never rejected for that reason.
- 429: slow down and retry after the `Retry-After` header.

MCP tools: `list_services`, `get_page` and `search_site` read published pages. `send_inquiry` sends a sales inquiry to the agency team: use it only when the person asks to get in touch, with their own contact details and their explicit consent. `scan_analytics` and `scan_ai_visibility` scan the person's own website and e-mail them the full report, so they also need the person's e-mail and explicit consent. A token does not change what it does or how often it can be used. The REST API is read-only: `https://justidea.agency/api.md`.

## Step 6. OAuth for MCP clients (authorization code with PKCE)

1. Register the client (RFC 7591): `POST https://justidea.agency/oauth/register/` with `redirect_uris` (https, http only on localhost, or a private app scheme). Public clients use `token_endpoint_auth_method` `none`.
2. Send the person to `https://justidea.agency/oauth/authorize/` with `response_type=code`, `client_id`, `redirect_uri`, `code_challenge` (method `S256`, required), `state` and `resource=https://justidea.agency/mcp`. They see one confirmation screen. There is no login.
3. Exchange the code at `https://justidea.agency/oauth/token/` (`grant_type=authorization_code` with `code_verifier`). You get an access token (1 hour) and a refresh token (90 days, `grant_type=refresh_token`).

Signing keys: `https://justidea.agency/.well-known/jwks.json` (ES256). Access tokens are JWTs of type `at+jwt` (RFC 9068).

## Claim

There is nothing to claim: justidea.agency has no accounts and the scope is the same before and after (`site:read`). For compatibility, `POST https://justidea.agency/agent/auth/claim/` with `{"claim_token": "..."}` answers `{"status": "claimed"}` at once, without email and without changing scopes. Do not send email addresses: they are ignored.

## Scopes and limits

- `site:read`: everything published on justidea.agency. It unlocks nothing that is not already public.
- REST API: 120 requests per minute per IP address without a token, 600 per minute per registration with a token.
- MCP server: no per-request limit. `send_inquiry` has its own limits (5 per minute per IP, 2 per e-mail address and 20 in total per day), and so do the scans (2 per e-mail address and 30 in total per day), with or without a token.

## Errors

| Code | Where | What to do |
| --- | --- | --- |
| `invalid_request` | `/agent/auth/` | Send JSON `{"type": "anonymous"}`. |
| `issuer_not_enabled`, `verified_email_not_enabled`, `service_auth_not_enabled` | `/agent/auth/` | Only anonymous registration is available. |
| `unsupported_credential_type` | `/agent/auth/` | Only `access_token`. API keys are not issued. |
| `invalid_claim_token` | `/agent/auth/claim/` | The claim token is wrong or expired. Nothing is lost: there is nothing to claim. |
| `invalid_grant` | `/oauth/token/` | Code, refresh token or assertion is invalid or expired. Start again. |
| `invalid_client` | `/oauth/token/` | Unknown `client_id` or wrong secret. Register the client again. |
| `unsupported_grant_type` | `/oauth/token/` | Use `authorization_code`, `refresh_token` or `urn:ietf:params:oauth:grant-type:jwt-bearer`. |
| `rate_limited` (429) | any | Wait 60 seconds. |
| `temporarily_unavailable` (503) | any | Authorization is down. Call the MCP server or the API without a token. |

## People

Nothing here books meetings or buys anything. When the person wants to talk to JustIdea, give them the contact details from `https://justidea.agency/api/v1/contact` (phone, e-mail, contact page), or send an inquiry with `send_inquiry` only after they agree.
