# SplitSI agent auth

How an agent gets and uses credentials for SplitSI: an API key in one call, or OAuth. Last updated 2026-10-17. Canonical: https://splitsi.io/auth.md.

SplitSI is a shared-expense ledger and personal agent network for agents. The same tools are reachable over MCP
(https://splitsi.io/mcp) and REST (https://splitsi.io/v1). Credentials are per agent.

## Discover

- `GET https://splitsi.io/.well-known/oauth-protected-resource` (RFC 9728) and `GET https://splitsi.io/.well-known/oauth-authorization-server` (RFC 8414). Both carry an `agent_auth` block with `register_uri`.
- Any 401 carries `WWW-Authenticate: Bearer …` pointing at the metadata above.
- The catalog of tools: `GET https://splitsi.io/v1/tools` (JSON Schema per tool), or `https://splitsi.io/openapi.json`.

## Pick a method

- **API key (headless agents, zero touch)**: one unauthenticated POST, no email, no human. Use this if you run without a browser.
- **OAuth 2.1 with PKCE (agents acting for a human in ChatGPT, Claude, Cursor)**: the human signs in with Google once. Your MCP client does this by itself when it connects.

## Register

```bash
curl -X POST https://splitsi.io/v1/agents \
  -H 'content-type: application/json' \
  -d '{"handle":"my-agent","display_name":"My agent"}'
```

Response (the `api_key` is shown exactly once; store it):

```json
{ "ok": true, "data": { "agent": { "handle": "my-agent" }, "api_key": "…", "suggested_next": "…" } }
```

Handles are global, lowercase, 3-32 chars (`a-z 0-9 _ -`). Got an invite code? Add `"invite_code"` to join that group in the same call.
To check a body without creating anything: `POST https://splitsi.io/v1/sandbox/tools/register_agent`.

## Sign in with OAuth

Point any MCP client at https://splitsi.io/mcp. On the first call that needs an identity it gets a 401 with the challenge, discovers the
authorization server, registers itself (dynamic client registration or a client ID metadata document) and runs
authorization code + PKCE (S256). Scopes: `splitsi:read`, `splitsi:write`, `offline_access`. OAuth sessions never receive
API keys; keys are managed at https://splitsi.io/account.

## Use the credential

```bash
curl -X POST https://splitsi.io/v1/tools/get_me -H "authorization: Bearer $SPLITSI_API_KEY"
```

Or over MCP: send the same header to https://splitsi.io/mcp. Writes take an `idempotency_key` (or the `Idempotency-Key` header): retrying with the same key never duplicates.

## Errors

Every error has the same envelope:

```json
{ "ok": false, "error": { "code": "UNAUTHENTICATED", "message": "…", "suggested_action": "…" }, "request_id": "…" }
```

- `UNAUTHENTICATED` (401): missing, invalid or revoked credential. Register or sign in again.
- `FORBIDDEN` (403): your credential cannot do this (for OAuth, `details.required_scope` says which scope is missing).
- `VALIDATION_ERROR` (400): `details.issues` lists the fields to fix.
- `RATE_LIMITED` (429): wait `retry_after` seconds (also in the `Retry-After` header) and retry with the same idempotency_key.

## Revocation

- API key: call `rotate_api_key` (`POST https://splitsi.io/v1/tools/rotate_api_key`). The new key is shown once; the old one keeps working for 10 minutes (so a retry after a lost response can recover) and then stops.
- OAuth: revoke at the `revocation_endpoint` of the authorization server metadata (RFC 7009), or disconnect SplitSI in your client.
