# auth.md — how an agent gets credentials for Viralmaxing

You are an agent. This document is the prose walkthrough referenced from
`agent_auth.skill` in our authorization-server metadata. It describes the
credential flow this service actually implements, in the order you should
follow it: **Discover → Pick a method → Register → Claim → Use the credential →
Errors → Revocation**.

**Scope note, so you do not waste a round trip:** Viralmaxing implements the
discovery half of the auth.md profile (protected-resource metadata,
authorization-server metadata, `WWW-Authenticate` challenges) on top of standard
OAuth 2.1. It does **not** implement the profile's `/agent/identity`
registration shapes, ID-JAG (`urn:ietf:params:oauth:token-type:id-jag`)
`identity_assertion` exchange, or the claim-token ceremony. If your client
insists on those, fall back to the standard OAuth flow below — it is the whole
of what we accept.

- API host (resource server): `https://api.viralmaxing.com`
- Product host: `https://viralmaxing.com`
- The only protected resource an agent normally wants: the MCP endpoint
  `POST https://api.viralmaxing.com/api/mcp`

## Step 1 — Discover

Any unauthenticated request to the API answers with a challenge that names the
metadata document:

```http
GET https://api.viralmaxing.com/api

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

Fetch the two discovery documents:

```http
GET https://api.viralmaxing.com/.well-known/oauth-protected-resource
GET https://api.viralmaxing.com/.well-known/oauth-authorization-server
```

The protected-resource metadata (RFC 9728) gives you `resource`,
`authorization_servers`, `scopes_supported` (`mcp`, `offline_access`) and
`bearer_methods_supported` (`header`). The authorization-server metadata
(RFC 8414) gives you the endpoints below plus the `agent_auth` block, whose
`skill` points back at this document.

## Step 2 — Pick a method

There are two, and the choice is about who is present:

1. **A human is in the loop right now** (chat client, IDE, anything that can
   open a browser tab) → **OAuth 2.1 authorization code + PKCE**. The user
   approves the connection on our consent screen and you get a token bound to
   their workspace. Use this by default.
2. **No human is present** (cron job, server-side automation, CI) → **API key**.
   The user creates a `vmx_` key at <https://viralmaxing.com/settings/api> and
   hands it to you out of band. There is no programmatic key-issuance endpoint:
   minting long-lived credentials without a human present is exactly the thing
   we do not want an agent to be able to do.

## Step 3 — Register

Dynamic client registration is open (RFC 7591) — no pre-shared secret, no
manual onboarding:

```http
POST https://api.viralmaxing.com/api/oauth/register
Content-Type: application/json

{
  "client_name": "Your Agent",
  "redirect_uris": ["https://your-client.example.com/callback"],
  "token_endpoint_auth_method": "none",
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"]
}
```

The response carries `client_id`. This server registers public clients only
(`token_endpoint_auth_method: "none"`), so there is no `client_secret` to
store — PKCE is what proves the exchange came from you.

Skip this step entirely if you are using an API key.

## Step 4 — Claim

Registration alone grants nothing: a fresh `client_id` is attached to no
account and can read no data. The user claims it by approving the connection.

```
https://viralmaxing.com/oauth/authorize
  ?response_type=code
  &client_id=<client_id>
  &redirect_uri=<your registered redirect_uri>
  &scope=mcp offline_access
  &code_challenge=<S256 of your verifier>
  &code_challenge_method=S256
  &state=<your CSRF value>
  &resource=https://api.viralmaxing.com/api/mcp
```

Open that URL for the user. They sign in, see which workspace they are
connecting and what the connection can do, and approve. You get `code` on your
redirect URI; exchange it:

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

grant_type=authorization_code&code=<code>&client_id=<client_id>&code_verifier=<verifier>&redirect_uri=<redirect_uri>
```

You receive an `access_token`, a `refresh_token` (when you asked for
`offline_access`) and `expires_in`. Refresh with
`grant_type=refresh_token&refresh_token=…&client_id=…` at the same endpoint.
Note the endpoints differ in encoding: registration is JSON, the token endpoint
is form-encoded.

## Step 5 — Use the credential

Both credential types reach the same MCP endpoint, and both are scoped to one
workspace:

```http
POST https://api.viralmaxing.com/api/mcp
Authorization: Bearer <access_token>
Content-Type: application/json

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

```http
POST https://api.viralmaxing.com/api/mcp
X-API-Key: vmx_…
```

Start with `list_my_accounts` or `get_energy_balance`: both are free reads that
confirm the credential works and show you what the workspace holds. Read tools
never spend anything. Tools that produce something new (search, competitor
discovery, transcription, account reports, importing an account) spend the
user's energy and **require a `confirm_cost` argument holding the exact price** —
state the cost to the user and get their agreement before calling. The current
tool list and price table live in `/llms.txt` and on
<https://viralmaxing.com/developers>.

## Step 6 — Errors

| Status | Meaning | What to do |
| --- | --- | --- |
| `401` + `WWW-Authenticate` | No credential, expired token, or revoked connection | Refresh; if the refresh fails, the user revoked you — start again at Step 3 |
| `400` | Business or validation error, including a missing or wrong `confirm_cost` | Read `error_description`; do not retry unchanged |
| `403` | The credential is valid but the workspace role cannot do this | Ask the user for an owner-level connection |
| `429` | Rate limited | Back off; the discovery and registration endpoints are per-IP limited |

Business errors deliberately come back as `400` rather than as a family of
specific codes. Branch on the message, not on inventing status semantics.

## Step 7 — Revocation

Either side can end the connection, and both take effect immediately:

- **The user**, from <https://viralmaxing.com/settings/api> — the same screen
  lists active agent connections and API keys.
- **Programmatically**, per client:
  `DELETE https://api.viralmaxing.com/api/oauth/connections/{client_id}`
  authenticated as the account owner. Only the (owner, client) pair is revoked;
  the registered client itself survives for other accounts.

After revocation your access and refresh tokens stop working — the next call
returns `401` with the challenge from Step 1, which puts you back at the top of
this document.

## Related documents

- `/llms.txt` — when to use Viralmaxing, the full tool list and the cost model
- `/agents.md` — the same guidance as a standalone agent file
- `/developers` — human-facing quickstart
- `/.well-known/mcp/server-card.json` — machine-readable card for this MCP server
