# ProxyJam auth.md

How an agent gets credentials for ProxyJam: the MCP server at
`https://proxyjam.com/mcp` (also served directly at `https://mcp.proxyjam.com/mcp`) and the
REST API at `https://api.proxyjam.com/public/v1`.

## Audience

Agents acting for a person who has, or will open, a ProxyJam account. Every
account-bound call reads that person's data or spends their money, so every
credential that reaches an account is the account holder's to give and to take
back. An agent with no account behind it can still register on its own and
read the public catalogue (see Agent registration below).

## Supported methods

| Method | Credential | Send it as | Issued by |
|---|---|---|---|
| OAuth 2.1 sign-in (MCP clients) | access token (15 min) + rotating refresh token | `Authorization: Bearer …` on the MCP server | the person, on a consent screen |
| Agent registration (anonymous) | `pj_…` API key, `catalog:read` until claimed | `Authorization: Bearer pj_…` on the MCP server and on `/public/v1` | `https://api.proxyjam.com/agent/auth`, no account needed |
| Dashboard API key | `pj_…` API key | `X-API-Key: pj_…` or `Authorization: Bearer pj_…`, on the MCP server and on `/public/v1` | the person, in the dashboard |

## Scopes

| Scope | Grants |
|---|---|
| `catalog:read` | the catalogue (the MCP tool `list_offers`) |
| `account:read` | profile, referrals, promo-code preview, IP-check history and quota |
| `orders:read` | orders, traffic and proxy details, **including proxy logins and passwords** |
| `wallet:read` | wallets, balances and transactions |
| `orders:write` | spending from the wallet (paid IP checks included) and every change to a paid service |

Webhook management, API-key management and account settings are never granted
to an OAuth token or an agent key.

## OAuth sign-in (MCP clients)

For MCP clients that offer "Sign in" for a remote server: claude.ai custom
connectors, Claude Code, ChatGPT connectors, MCP Inspector.

1. Add the server by its URL, `https://proxyjam.com/mcp`, with no header.
2. The first request answers `401` with `WWW-Authenticate: Bearer
   resource_metadata="https://proxyjam.com/.well-known/oauth-protected-resource/mcp"`
   and `scope="catalog:read account:read orders:read wallet:read orders:write"`.
3. That Protected Resource Metadata names the authorization server
   `https://proxyjam.com`; its metadata is at
   `https://proxyjam.com/.well-known/oauth-authorization-server`.
4. Register with a Client ID Metadata Document (an https `client_id` whose
   document allows `none`), or with Dynamic Client Registration at
   `https://api.proxyjam.com/oauth/register`.
5. Send the person to `https://api.proxyjam.com/oauth/authorize` with PKCE (`S256`) and
   `resource=https://proxyjam.com/mcp`. They sign in and approve on a consent
   screen: the four read scopes are on by default, and spending
   (`orders:write`) is a separate *Allow spending from my wallet* box that
   starts unticked. Every redirect back carries `iss=https://proxyjam.com`.
6. Exchange the code at `https://api.proxyjam.com/oauth/token` (form-encoded, with the PKCE
   verifier and the same `resource`). The access token lasts 15 minutes; the
   refresh token rotates on every use and lasts 30 days from its own issue.

Each approval replaces the connection's scopes: reconnecting without the
spending box removes spending.

## Agent registration (auth.md v0.1, anonymous)

An agent with no account registers on its own:

```http
POST https://api.proxyjam.com/agent/auth
Content-Type: application/json

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

The answer carries `credential` (a `pj_…` API key, shown once),
`scopes: ["catalog:read"]`, `credential_expires` (30 days out), `claim_url`,
`claim_token` (shown once, valid 24 hours) and `post_claim_scopes`. Send the key
as `Authorization: Bearer pj_…`; with `catalog:read` it calls `list_offers` on
the MCP server.

### Claim: attach the key to a person's account

Only when the person wants the agent to act on their account. Ask for their
email, then:

```http
POST https://api.proxyjam.com/agent/auth/claim
Content-Type: application/json

{"claim_token": "clm_…", "email": "person@example.com"}
```

ProxyJam emails them a link. On that page they click *Show my code* and read the
6-digit code back to you. It is valid for 10 minutes and 5 tries; opening the
email link again gives a fresh one. If the address already has an account, the
page shows the code only to someone signed in to that account. Then:

```http
POST https://api.proxyjam.com/agent/auth/claim/complete
Content-Type: application/json

{"claim_token": "clm_…", "otp": "123456"}
```

The answer is `{"registration_id": "reg_…", "status": "claimed"}`. The same key
is upgraded in place and stops expiring. **A claim grants `catalog:read`,
`account:read`, `orders:read` and `wallet:read`, which include proxy logins and
passwords. It never grants `orders:write`**, so a claimed key cannot spend. An
address with no account gets one, verified by the code and without a password
(the person sets one later by resetting it).

Error codes: `invalid_request`, `unsupported_credential_type`,
`verified_email_not_enabled`, `issuer_not_enabled`, `invalid_claim_token`,
`otp_invalid`, `otp_expired`, `claim_expired`, `previously_claimed`,
`rate_limited` (429, back off), and `registration_disabled` (403: new accounts
are not being created right now; do not retry).

## Using the credential

- Send it on every request. The MCP server acts only with the credential it
  was given: an API key goes on to the API as it is, and an
  OAuth access token is exchanged for a 5-minute token bound to the same
  connection and scopes.
- Money-spending MCP tools preview first and charge only with `confirm=true`;
  get the person's explicit yes before confirming.
- A tool outside the credential's scopes answers with an error that names the
  scope. For spending, the person reconnects and ticks *Allow spending from my
  wallet*.
- A `401` on a credential that used to work means it expired or was revoked:
  drop it. Refresh an OAuth token, or sign in again when the refresh fails; ask
  the person for a new API key, or register again.

## Revocation

- The person deletes an API key at https://app.proxyjam.com/apikey;
  the API refuses it from then on; the MCP server may still accept it as
  `Bearer pj_…` for up to a minute while its cached check lasts. Claimed agent keys are listed
  there too. An unclaimed agent key expires on its own after 30 days.
- OAuth connections: the person revokes one under Connected apps at
  https://app.proxyjam.com/settings, and a client can revoke its own tokens at
  `https://api.proxyjam.com/oauth/revoke` (RFC 7009). Changing or resetting the password,
  signing out everywhere and deleting the account revoke every connection. A
  revoked connection stops working within 5 minutes.

## More

- Authentication reference: https://docs.proxyjam.com/overview/authentication
- MCP server: https://docs.proxyjam.com/mcp-server
- Agent skill: https://proxyjam.com/.well-known/agent-skills/proxyjam/SKILL.md
