# Onboarding

# Onboarding

Onboarding is the gap between finding an API and calling it — the account, the form, the key, the
consent screen. This page is every way into the APIs.io API, what each one earns, and what each one
costs you.

There are four, and **two of them have no human in them at all.**

| door | who it is for | what it costs | what it earns |
|---|---|---|---|
| **Keyless** | anyone, including any agent | nothing | immediate read access, limited per IP |
| **Sign in** | a person with a browser | a GitHub, Google or LinkedIn account | a Learn key, and the ability to pay for more |
| **Sign a request** | an agent with its own key | an agent provider must vouch for you | read access **attributed** to your identifier |
| **Client credentials** | an agent that wants its own account | the same, plus one POST | an account, an API key, its own quota |

Quotas and rate limits for each tier are on the [rate limits](./rate-limits) page, and what each
tier unlocks is on [plans](./plans). Those numbers are deliberately not repeated here — a limit
written down in two places eventually disagrees with itself.

## The best signup is no signup

The base tier is keyless. No account, no form, no email, no verification link:

```bash
curl "https://apis.io/api/v1/search?q=sms"
```

Version 1 is read-only and the catalog is public, so there is no read path that requires an
identity and no reason to make a machine prove one. See [authentication](./authentication).

Keyless callers are limited **per IP** rather than by a usage plan — a plan meters per key, and
every anonymous caller would share one. Identifying yourself is what gets you a meter of your own,
before it gets you any extra volume. See [rate limits](./rate-limits).

## A person signing in

A [Learn key is free](./plans) with a GitHub, Google or LinkedIn sign-in, and Understanding and Influence are
self-serve from your [account page](./account). Which providers are configured is itself readable:

```bash
curl "https://apis.io/api/v1/auth/providers"
```

This is a browser flow with a person in it. We say so rather than dressing it up — if you are
building an unattended client, the two sections below are the ones you want.

## An agent identifying itself

APIs.io is an [AAuth](https://www.aauth.dev/) resource
(`draft-hardt-oauth-aauth-protocol`). An agent holding its own Ed25519 keypair can be recognised by
a service it has never registered with. There is no registration call, and no human.

Two proofs travel with the request, and both are required:

1. an **`aa-agent+jwt`** in the `Signature-Key` header, in which an agent provider vouches for your
   portable `aauth:local@domain` identifier and names the key it is bound to in `cnf.jwk`;
2. an **RFC 9421 HTTP message signature** over the request itself, made with that key.

The first alone is a replayable bearer token. The second alone is a self-asserted identity.

```
Signature-Key:   sig=jwt; jwt="<aa-agent+jwt>"
Signature-Input: sig1=("@method" "@path" "@authority" "signature-key");created=…;alg="Ed25519"
Signature:       sig1=:<base64>:
```

The signature must cover at least `@method`, `@path`, `@authority` and `signature-key`, and carry a
`created` parameter — without one a captured signature is valid forever. `GET` and `HEAD` need
nothing more. **Any other method must also sign a `content-digest`**, because the component that
verifies your signature never sees the request body and will not assert integrity it did not
measure.

What signing earns is **attribution**: the request is recognised and audited as your identifier
rather than as an anonymous address. It is not payment and it is not a paid tier.

Be precise about what it does *not* yet earn. Signing alone gives you no meter of your own — until
you take a principal (next section) you are still limited per IP like any anonymous caller. The
identifier is what makes the metering possible; the principal is what turns it on.

Our resource metadata is at
[`/.well-known/aauth-resource.json`](https://apis.io/.well-known/aauth-resource.json).

**The allowlist is small and you should know that before you build.** The authorizer will fetch an
agent provider's keys only from origins we have allowlisted, and today that list contains one entry,
which is our own. AAuth is an Internet-Draft with very little deployed base; if you run an agent
provider and want to interoperate, [get in touch](mailto:info@apis.io).

## An agent with its own account

That same signature, presented at the token endpoint, gets an agent its own account:

```bash
curl -X POST "https://apis.io/api/v1/auth/token" \
  -H "content-type: application/x-www-form-urlencoded" \
  -H "signature-key: sig=jwt; jwt=<aa-agent+jwt>" \
  -H "signature-input: sig1=(\"@method\" \"@path\" \"@authority\" \"signature-key\" \"content-digest\");created=…;alg=\"Ed25519\"" \
  -H "signature: sig1=:<base64>:" \
  -H "content-digest: sha-256=:<base64 of the body>:" \
  -d "grant_type=client_credentials"
```

You get back an access token, and behind it an **agent principal** — its own row, its own API key,
its own quota, distinct from any human account in every table. No person is involved at any step.

Three things about that grant are deliberate:

- **It is not anonymous, and never will be.** `client_credentials` is reachable only by a caller
  already carrying a verified AAuth identity. An account that costs nothing to mint makes any
  reputation attached to it worthless — abuse the quota, take a fresh identity, repeat — so the
  price of one is that an agent provider we allowlist has to vouch for you.
- **The scope ceiling is `apis:read`.** Ask for `apis:pro` or `offline_access` and you get neither;
  the token's own `scope` says what was granted. An agent cannot self-grant paid data, and a refresh
  token with no user behind it is just a longer-lived credential with nobody accountable for it.
- **It is reversible in one request.** An agent principal can delete itself, authorised by the same
  signature rather than by a session cookie:

  ```bash
  curl -X DELETE "https://apis.io/api/v1/auth/me" \
    -H "signature-key: …" -H "signature-input: …" -H "signature: …" \
    -H "content-digest: sha-256=:<base64 of the empty string>:"
  ```

  That removes the principal, its key and its usage-plan key. Onboarding you cannot undo is an
  asymmetry we penalise other providers for, so we do not ship it here.

`GET /api/v1/auth/me` with the same signature reports what the principal currently is.

## Read the door before you knock

Everything above is declared in one machine-readable document, so a client does not have to read
this page to know any of it:

```bash
curl "https://apis.io/.well-known/api-onboarding"
```

That is an [API Onboarding Descriptor](https://apicommons.org/onboarding/) — whether an account is
required at all, an explicit `agentPolicy`, every registration mechanism that exists and precisely
what each is worth, how credentials map to header names, an executable flow, and a `gaps` array
saying what does not work yet. **Where this page and that document disagree, the document is the
one we test.**

The OAuth surface is discoverable the same way:

| document | what it tells you |
|---|---|
| [`/.well-known/oauth-authorization-server`](https://apis.io/.well-known/oauth-authorization-server) | RFC 8414 metadata — endpoints, grants, scopes, and a `registration_endpoint` |
| [`/.well-known/oauth-protected-resource`](https://apis.io/.well-known/oauth-protected-resource) | RFC 9728 — which authorization server protects this resource |
| [`/.well-known/aauth-resource.json`](https://apis.io/.well-known/aauth-resource.json) | the AAuth resource document |

Clients can register with no human through **RFC 7591 dynamic client registration** or a **Client ID
Metadata Document** (a `client_id` that is a URL resolving to its own metadata). Both are supported;
both are for the [MCP surface](./mcp-server) and the OAuth flows, and neither by itself yields an
account or a paid tier.

## What you still cannot do without a person

Stated plainly, because a page like this is where providers usually stop being honest:

- **You cannot buy anything.** Every path above tops out at free. Understanding and Influence need a card, a
  browser and a human, and machine payment is not solved here. An above-tier request returns
  [`402 Payment Required`](./rate-limits) — a payment signal, distinct from a permission failure and
  distinct from being throttled.
- **AAuth is a draft with almost no deployed base**, and one agent provider on our allowlist. Treat
  it as a working implementation to test against, not as an ecosystem.

The last human-shaped step in getting started here is the one where money changes hands. We think
that is a more interesting place for it to be than a signup form — but it is still there.
