# auth.md

You are an agent. This service sells travel eSIM data plans, and you can
register for a credential and buy one without a human in the loop.

Everything below is also machine-readable. If you have a 401 from our
API in hand, its `WWW-Authenticate` header carries a `resource_metadata`
parameter pointing at step 1, and you can follow the chain from there
without reading this page.

## Step 1: Read the resource metadata

```http
GET https://api.simfuse.app/.well-known/oauth-protected-resource/agentic
```

This is OAuth 2.0 Protected Resource Metadata (RFC 9728). It names the
resource you are calling (`https://api.simfuse.app/agentic`), the scopes it understands, and
the authorization server that guards it.

## Step 2: Read the authorization server metadata

```http
GET https://api.simfuse.app/.well-known/oauth-authorization-server
```

OAuth 2.0 Authorization Server Metadata (RFC 8414). Take the
`registration_endpoint` and `token_endpoint` from it rather than copying
the URLs below, so you keep working if they ever move.

## Step 3: Register

There are two doors and they lead to the same place: one credential,
one scope, one expiry. Take whichever your client already speaks.

### Door A: the agentic registration profile

```http
POST https://api.simfuse.app/agentic/agent/identity
Content-Type: application/json

{
  "type": "anonymous",
  "client_name": "Your agent's name",
  "contacts": ["you@example.com"]
}
```

You get back an `identity_assertion`. It is single-use and expires in
minutes, so go straight to step 4 with it. `anonymous` is the only
registration type we accept, which is what the `agent_auth` block in
step 2 tells you.

### Door B: dynamic client registration (RFC 7591)

Ask for the client credentials grant, because you have no browser and
no user at a keyboard.

```http
POST https://api.simfuse.app/oauth/register
Content-Type: application/json

{
  "client_name": "Your agent's name",
  "grant_types": ["client_credentials"],
  "contacts": ["you@example.com"]
}
```

`client_name` is required and is shown to the seller in order tooling.
`contacts` is optional and is how we reach you if something is wrong
with your traffic.

You get back a `client_id` and a `client_secret`. The secret is shown
exactly once, so store it before closing the response. Note
`client_secret_expires_at`: this credential does expire, and registering
again costs you one request.

## Step 4: Get a token

Both doors exchange at the same endpoint. Use the one matching how you
registered.

### If you came through door A

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

grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
&assertion=<the identity_assertion from step 3>
```

No scope parameter, and no client credentials. The assertion already
names your registration and the scope it was granted, and it is spent
by this call: ask again and you get an error, not a second token.

### If you came through door B

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

grant_type=client_credentials
&client_id=...
&client_secret=...
&scope=commerce:checkout.crypto
```

**Send the `scope` on this one.** It is not optional in practice: a
token requested without it carries no scope at all, and every call you
make will be refused. `commerce:checkout.crypto` is the scope self-registered agents
hold either way.

## Step 5: Call the API

```http
GET https://api.simfuse.app/agentic/checkout_sessions/{id}
Authorization: Bearer <access_token>
```

The API base is `https://api.simfuse.app/agentic`. Read
`https://simfuse.app/.well-known/acp.json` for the endpoints, the
product feed and the payment handlers we support.

## What your credential can and cannot do

A self-registered credential settles in cryptocurrency only. Completing
a checkout session returns a deposit address and an exact amount, and
the order fulfils once the transfer confirms on-chain. Nothing is
charged back and nothing fulfils before payment lands, which is the
reason we can hand credentials to strangers at all.

Card-based checkout is not available on a self-registered credential. If
you operate a platform that needs it, `seller_support` in the discovery
document is how to reach us.

## If something is refused

Every rejected credential gets the same 401, whether it is unknown,
revoked, expired, out of scope or simply absent. That is deliberate, so
the API cannot be used to probe which of those is true. If you are
being refused, re-read the metadata in step 1 and check that your token
carries the scope from step 4.
