---
title: "Notapex Agent Authentication & OAuth 2.0 Guide (auth.md)"
description: "Step-by-step walkthrough for AI agents to discover, register, claim, exchange, and use OAuth 2.0 scoped credentials on Notapex."
canonical: "https://notapex.com/auth.md"
last-updated: "2026-09-29"
---

# Notapex Agent Authentication & Scoped Permissions Guide

Notapex supports standards-based **OAuth 2.0 (RFC 6749 / RFC 8414)**, **PKCE with S256 (RFC 7636)**, **Dynamic Client Registration (RFC 7591)**, **Protected Resource Metadata (RFC 9728)**, and the **WorkOS `agent_auth`** specification so AI agents can autonomously obtain least-privilege credentials without human intervention.

---

## 1. Discover

Start by inspecting Notapex's machine-readable discovery endpoints or triggering a `WWW-Authenticate` challenge:

- **Protected Resource Metadata (RFC 9728)**: `GET https://notapex.com/.well-known/oauth-protected-resource`
- **OAuth 2.0 Authorization Server Metadata (RFC 8414)**: `GET https://notapex.com/.well-known/oauth-authorization-server`
- **OpenID Connect Discovery**: `GET https://notapex.com/.well-known/openid-configuration`
- **OpenAPI 3.1 Specification**: `GET https://notapex.com/openapi.json` (or `/api/openapi.yaml`)

When an unauthenticated request is sent to a protected endpoint (such as `GET https://notapex.com/agent/auth` or `POST https://notapex.com/api/v1/conversions`), Notapex returns HTTP `401 Unauthorized` with a `WWW-Authenticate` header pointing directly to the protected resource metadata:

```http
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://notapex.com/.well-known/oauth-protected-resource", scope="notes:read notes:convert"
```

The `agent_auth` block inside `/.well-known/oauth-authorization-server` advertises:
- `identity_endpoint`: `https://notapex.com/agent/identity`
- `claim_endpoint`: `https://notapex.com/agent/claim`
- `events_endpoint`: `https://notapex.com/agent/events`
- `identity_types_supported`: `["anonymous", "identity_assertion", "service_auth"]`
- `identity_assertion.assertion_types_supported`: `["urn:ietf:params:oauth:token-type:id-jag", "urn:ietf:params:oauth:token-type:jwt"]`

---

## 2. Pick a Method

Choose the authentication method that matches your task's required privilege level:

1. **`anonymous` (Public Read & Sandbox)**:
   - Public catalog queries (`GET /api/v1/notes`, `GET /api/v1/categories`, `GET /api/v1/pricing`) and sandbox tests (`X-Sandbox-Mode: true` or `https://notapex.com/api/sandbox`) work immediately without a token.
2. **`service_auth` (Machine-to-Machine via Client Credentials)**:
   - Dynamically register a client at `POST https://notapex.com/api/oauth/register` or `POST https://notapex.com/agent/identity` and exchange credentials via `grant_type=client_credentials`.
3. **`identity_assertion` (Delegated User Access / ID-JAG)**:
   - Present a signed identity assertion (`urn:ietf:params:oauth:token-type:id-jag`) or complete PKCE `S256` Authorization Code flow via `GET https://notapex.com/api/oauth/authorize`.

### Named OAuth Scopes (`scopes_supported`)

Always request only the specific scopes required for your task:
- `notes:read` — Read published study notes, transcripts, metadata, and categories.
- `notes:write` — Upload and update study notes and attachments.
- `notes:convert` — Submit handwritten note images/PDFs for AI OCR & PDF/PPTX conversion.
- `channels:read` — Browse creator channels and curated study bundles.
- `account:read` — Read authenticated user profile and AI credit balance.
- `webhooks:manage` — Register and manage async conversion webhook endpoints.

---

## 3. Register

Self-register your agent via RFC 7591 Dynamic Client Registration (`POST https://notapex.com/api/oauth/register`) or the `identity_endpoint` (`POST https://notapex.com/agent/identity`):

```bash
curl -X POST https://notapex.com/api/oauth/register \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "StudyResearchAgent",
    "grant_types": ["client_credentials", "authorization_code", "refresh_token"],
    "redirect_uris": ["http://localhost:8080/callback"],
    "scope": "notes:read notes:convert channels:read"
  }'
```

Response (`201 Created`):
```json
{
  "client_id": "<YOUR_CLIENT_ID>",
  "client_secret": "<YOUR_CLIENT_SECRET>",
  "client_id_issued_at": 1759145800,
  "client_secret_expires_at": 0,
  "scope": "notes:read notes:convert channels:read"
}
```

---

## 4. Claim

If acting on behalf of a specific user (for example, when you know the user's email address), bind the registered agent identity at `POST https://notapex.com/agent/claim`:

```bash
curl -X POST https://notapex.com/agent/claim \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "<YOUR_CLIENT_ID>",
    "email": "student@example.com",
    "scope": "notes:read notes:convert"
  }'
```

---

## 5. Exchange

Exchange your client credentials, authorization code (with PKCE `code_verifier`), or `id-jag` assertion for an `access_token` at `POST https://notapex.com/api/oauth/token`:

```bash
curl -X POST https://notapex.com/api/oauth/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "client_credentials",
    "client_id": "<YOUR_CLIENT_ID>",
    "client_secret": "<YOUR_CLIENT_SECRET>",
    "scope": "notes:read notes:convert"
  }'
```

Response (`200 OK`):
```json
{
  "access_token": "<YOUR_ACCESS_TOKEN>",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "<YOUR_REFRESH_TOKEN>",
  "scope": "notes:read notes:convert"
}
```

---

## 6. Use the access_token

Pass the token in the `Authorization: Bearer <access_token>` header on REST API calls or MCP Streamable HTTP requests (`https://notapex.com/mcp`):

```bash
curl -X POST https://notapex.com/api/v1/conversions \
  -H "Authorization: Bearer <YOUR_ACCESS_TOKEN>" \
  -H "Idempotency-Key: idem_12345678-abcd" \
  -H "Content-Type: application/json" \
  -d '{
    "source_url": "https://notapex.com/sample-handwritten-notes.pdf",
    "mode": "faithful_exact_copy",
    "sandbox": true
  }'
```

---

## 7. Errors

All authentication and authorization failures return structured JSON (`ErrorResponse`) alongside a `WWW-Authenticate` header:

- `401 UNAUTHORIZED`: Token is missing or expired. Re-run step 5 (`POST /api/oauth/token`) using your `refresh_token` or `client_credentials`.
- `403 INSUFFICIENT_SCOPE`: Token lacks the required scope (listed in the `WWW-Authenticate` `scope="..."` parameter). Re-request a token with the required scope.
- `429 RATE_LIMITED`: Respect the `Retry-After` and `RateLimit-Reset` headers before retrying.

---

## 8. Revocation

When a task or session completes, revoke active tokens via RFC 7009 Token Revocation at `POST https://notapex.com/api/oauth/revoke`:

```bash
curl -X POST https://notapex.com/api/oauth/revoke \
  -H "Content-Type: application/json" \
  -d '{"token": "<YOUR_ACCESS_TOKEN>"}'
```
