# auth.md

> How an AI agent registers and authenticates to **Restivity**. Restivity
> implements OAuth 2.1 (authorization code + PKCE) with RFC 7591 Dynamic Client
> Registration. Protected resources: `https://restivity.ai/api/v1` (REST) and
> `https://restivity.ai/api/mcp` (MCP, Streamable HTTP). This file follows the `auth.md`
> convention (https://github.com/workos/auth.md).

## Step 1 — Discover

### 1a. Fetch the Protected Resource Metadata (RFC 9728)

`GET` <https://restivity.ai/.well-known/oauth-protected-resource> — advertises the protected
resource (`https://restivity.ai/api/v1`) and its authorization server.

### 1b. Fetch the Authorization Server Metadata (RFC 8414)

`GET` <https://restivity.ai/.well-known/oauth-authorization-server> (alias
<https://restivity.ai/.well-known/openid-configuration>) — returns the endpoints below, the
supported scopes, `code_challenge_methods_supported: ["S256"]`,
`grant_types_supported: ["authorization_code", "refresh_token"]`, and an
`agent_auth` block (`skill`, `register_uri`, `revocation_uri`).

| Purpose | Method | Endpoint |
| --- | --- | --- |
| Dynamic Client Registration (RFC 7591) | `POST` | <https://restivity.ai/api/oauth/register> |
| Authorization | redirect | <https://restivity.ai/oauth/authorize> |
| Token | `POST` | <https://restivity.ai/api/oauth/token> |
| Token revocation (RFC 7009) | `POST` | <https://restivity.ai/api/oauth/revoke> |
| JWKS (token-verification keys) | `GET` | <https://restivity.ai/api/oauth/jwks> |

## Step 2 — Register

Restivity supports RFC 7591 Dynamic Client Registration. Public clients receive
no secret and MUST use PKCE; confidential clients may authenticate with
`client_secret_basic`.

```http
POST https://restivity.ai/api/oauth/register
Content-Type: application/json

{
  "client_name": "My Agent",
  "redirect_uris": ["https://my-agent.example/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none",
  "scope": "tasks:read tasks:write"
}
```

Response (`201 Created`) — store the `client_id`:

```json
{
  "client_id": "dcr_8f3a1c2e-...",
  "client_id_issued_at": 1748600000,
  "redirect_uris": ["https://my-agent.example/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none",
  "scope": "tasks:read tasks:write"
}
```

## Step 3 — Authorize

Redirect the user to the authorization endpoint with a PKCE challenge. `plain`
is NOT supported — use `S256`.

```
https://restivity.ai/oauth/authorize?response_type=code
  &client_id=<client_id>
  &redirect_uri=<redirect_uri>
  &scope=tasks:read%20tasks:write
  &state=<opaque>
  &code_challenge=<base64url-sha256(verifier)>
  &code_challenge_method=S256
```

## Step 4 — Token

Exchange the authorization code (with the PKCE verifier) for tokens, and renew
with the refresh token.

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

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

Response (`200 OK`):

```json
{
  "access_token": "<ES256 JWT>",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "<refresh_token>",
  "scope": "tasks:read tasks:write"
}
```

Renewal: `grant_type=refresh_token&refresh_token=<token>&client_id=<client_id>`.
Access tokens are ES256-signed JWTs verified against the JWKS above.

## Step 5 — Use the credential

Send `Authorization: Bearer <access_token>` to:

- **REST** — <https://restivity.ai/api/v1> (OpenAPI at <https://restivity.ai/api/v1/openapi.json>).
- **MCP** — <https://restivity.ai/api/mcp> (Streamable HTTP).

## Scopes

`resource:action` where resource ∈ {tasks, projects, pomodoro, comments, tags}
and action ∈ {read, write, delete}, plus `profile:read` and `profile:write`.
Account deletion is intentionally NOT an agent scope (no `profile:delete`) —
route the user to the UI. Full registry:

- `tasks:read`
- `tasks:write`
- `tasks:delete`
- `projects:read`
- `projects:write`
- `projects:delete`
- `pomodoro:read`
- `pomodoro:write`
- `pomodoro:delete`
- `comments:read`
- `comments:write`
- `comments:delete`
- `tags:read`
- `tags:write`
- `tags:delete`
- `profile:read`
- `profile:write`
- `earnings:read`

## Plan requirement

A valid token is necessary, not sufficient: the agent surface (REST + MCP)
requires a paid plan (Pro or Team). A paid Team seat unlocks it on a free
personal plan too. A free account completes the OAuth flow normally and then
receives `403` `application/problem+json` with
`type: <https://restivity.ai/errors/upgrade_required>` and an `upgrade_url`
(<https://restivity.ai/pricing>). Show that link to the user; do not restart the OAuth flow
(it is a 403, not a 401).

## Rate limits

Per user, per UTC hour, by HTTP verb:

- read: 1000/hour (GET, HEAD)
- write: 200/hour (POST, PATCH, PUT)
- delete: 50/hour (DELETE)

Every agent response carries `X-RateLimit-Tier: <tier>:<count>/<limit>`.
Over budget the API answers `429` `rate_limited` with `Retry-After`
(seconds to the top of the hour); back off until then.

The token endpoint (`POST /api/oauth/token`) has its own gate: 60 requests per source IP per minute.
Over budget it answers `429` with the OAuth error `slow_down` and `Retry-After`
(seconds to the next minute) — wait, then retry the same request; do not
re-run the authorization flow.

## Errors

- OAuth endpoints return RFC 6749 error objects: `invalid_request`,
  `invalid_client`, `invalid_grant`, `unauthorized_client`,
  `unsupported_grant_type`, `invalid_scope`.
- The protected resource returns `401` with a `WWW-Authenticate: Bearer`
  challenge when the token is missing/expired/insufficiently scoped.
- REST errors use RFC 7807 `application/problem+json`; MCP tools return
  structured errors (`not_found`, `forbidden`, `validation_error`,
  `conflict_recurring_master`, `overlapping_active_session`, …).

## Revocation

`POST` <https://restivity.ai/api/oauth/revoke> (RFC 7009) with `token=<access_or_refresh_token>`
to revoke a credential. Users can also revoke a connected agent from the
Restivity UI.

## Related surfaces

- Agent capabilities & workflows: <https://restivity.ai/.well-known/agent-skills/index.json>
- MCP server card: <https://restivity.ai/.well-known/mcp/server-card.json>
- API catalog: <https://restivity.ai/.well-known/api-catalog>
- Human API docs: <https://restivity.ai/docs/api>
- LLM index: <https://restivity.ai/llms.txt>
