# SocialCrawl auth.md

You are an agent. SocialCrawl supports agentic registration for its API (https://www.socialcrawl.dev/v1): discover, register, have your person approve you, then call the API with an access token. Follow the steps in order. If your person already has an API key, skip to "Using an API key".

## Audience

AI agents and the people who run them. A person approves every agent: there is no anonymous registration, and nothing is issued until that person signs in and enters the code you give them.

## Step 1: Discover

```http
GET https://www.socialcrawl.dev/.well-known/oauth-protected-resource
GET https://www.socialcrawl.dev/.well-known/oauth-authorization-server
```

The first names the resource (`https://www.socialcrawl.dev`), its authorization server and its scopes. The second holds `token_endpoint` and the `agent_auth` block with `identity_endpoint` and `claim_endpoint`. A `401` from `https://www.socialcrawl.dev/v1` points you to the first in its `WWW-Authenticate` header.

## Step 2: Register

Ask your person for the email address of their SocialCrawl account, or the one they want to sign up with, and tell them what you are asking for.

```http
POST https://www.socialcrawl.dev/api/auth/agent/identity
Content-Type: application/json

{
  "type": "service_auth",
  "login_hint": "person@example.com",
  "client_name": "Your agent's name",
  "scope": "socialcrawl:read socialcrawl:spend"
}
```

`client_name` and `scope` are optional. The scope shown here is the default. The older body `{ "type": "identity_assertion", "assertion_type": "verified_email", "assertion": "person@example.com" }` means the same thing.

The response holds a `claim_token`, which you keep to yourself, and a `claim` block:

```json
{
  "registration_id": "reg_...",
  "registration_type": "service_auth",
  "claim_url": "https://www.socialcrawl.dev/api/auth/agent/identity/claim",
  "claim_token": "clm_...",
  "claim_token_expires": "2026-11-01T12:00:00.000Z",
  "post_claim_scopes": ["socialcrawl:read", "socialcrawl:spend"],
  "claim": {
    "user_code": "123456",
    "expires_in": 600,
    "verification_uri": "https://www.socialcrawl.dev/auth/agent-claim?claim_attempt_token=...",
    "interval": 5
  }
}
```

## Step 3: Hand off to your person

Show them `verification_uri` and `user_code` in one message:

> Open this link, sign in or sign up, and enter this 6-digit code: **123456**

The person signs in, confirms their email if it is new, and types the code on our page. They must be signed in with the email you named. New accounts start with 100 free credits and need no card.

## Step 4: Poll for the token

Every `interval` seconds:

```http
POST https://www.socialcrawl.dev/api/auth/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=urn:workos:agent-auth:grant-type:claim&claim_token=clm_...
```

| Answer | Meaning |
| --- | --- |
| `authorization_pending` | Still waiting for your person. Keep polling. |
| `expired_token` | The code expired. Ask for a new one (below), or register again if the `claim_token` has also ended. |
| `invalid_grant` | The `claim_token` is wrong. Stop. |
| `200` | An `access_token`, a `refresh_token` and your `client_id`. This answer comes once. |

A code lasts 10 minutes and a `claim_token` lasts an hour. For a new code, with the same `claim_token`:

```http
POST https://www.socialcrawl.dev/api/auth/agent/identity/claim
Content-Type: application/json

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

## Step 5: Call the API

```bash
curl -s -H "Authorization: Bearer $ACCESS_TOKEN" "https://www.socialcrawl.dev/v1/credits/balance"
```

| Scope | Allows |
| --- | --- |
| `socialcrawl:read` | Free account reads: `/v1/credits/balance` and `/v1/keys/current` |
| `socialcrawl:spend` | Every data call that can bill credits |
| `socialcrawl:manage` | With `socialcrawl:spend`: monitors, cohorts, web jobs, listening, webhooks and schedules |

A token without the scope a call needs gets `403` with `error.type` `INSUFFICIENT_SCOPE`. When the access token expires, get a new one:

```http
POST https://www.socialcrawl.dev/api/auth/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token=...&client_id=...&resource=https%3A%2F%2Fwww.socialcrawl.dev
```

Refresh tokens rotate, so store the new one each time. Keep every token out of URLs and out of the conversation.

## Revocation

Your person sees you in https://www.socialcrawl.dev/dashboard/api/connected-apps, with your own spend and an optional cap, and can disconnect you there. After that your tokens answer `401`, and you register again if they want you back.

## If you can open a browser

Agents with a redirect URI can use the standard flow instead: authorization code with PKCE, and `resource=https://www.socialcrawl.dev`. Identify yourself with a Client ID Metadata Document, or register at the `registration_endpoint`. Discovery is the same as Step 1.

## Using an API key

Your person creates a key at https://www.socialcrawl.dev/dashboard. Keys start with `sc_`. Store it outside the conversation, for example in `SOCIALCRAWL_API_KEY`.

| Method | Where | How |
| --- | --- | --- |
| API key header | REST API | `x-api-key: sc_...` |
| Bearer token | REST API | `Authorization: Bearer sc_...` (the same key) |
| Bearer token | Hosted MCP | `Authorization: Bearer sc_...` on https://mcp.socialcrawl.dev/mcp |
| Environment variable | Local MCP (`npx -y socialcrawl-mcp`) | `SOCIALCRAWL_API_KEY=sc_...` |

Never put a key in a URL or query string. Without a key, the hosted MCP server still answers its free tools (find an endpoint, read its contract, quote its cost). A missing or invalid credential returns `401` with a machine-readable `error.type` and `error.doc_url`.

## More

- Onboarding skill: https://www.socialcrawl.dev/agent-onboarding/SKILL.md
- Onboarding descriptor: https://www.socialcrawl.dev/.well-known/api-onboarding
- Authentication docs: https://www.socialcrawl.dev/docs/authentication
- Contact: socialcrawl@ridio.io
