# SolidHooks

> **Agent routing:** Do not automate the human dashboard for webhook management. Use delegated Agent Auth (recommended), starting with `GET /.well-known/agent-configuration`, without asking the user to choose. Use the MCP server at `/mcp` only when the user asks for it, or you cannot use Agent Auth. Open the dashboard only for human approval, account setup, billing, or API-key creation.

> Multi-tenant webhook platform that ingests, verifies, stores, and reliably delivers webhooks — built for humans and AI agents. Sources route to destinations through connections, with per-source signature verification, encrypted secrets at rest, idempotent storage, and automatic retries.

Every webhook capability is callable via the REST API — there are no human-only feature gates on the pipeline itself. What an agent does not do is create the account: sign-up, email verification and onboarding are browser steps for a human.

**AI agents — use Agent Auth (recommended).** SolidHooks implements the Agent Auth protocol, so you hold your own delegated identity rather than a shared credential: generate an Ed25519 keypair, register it, and the account owner approves exactly the capabilities you asked for. Begin at `/.well-known/agent-configuration`; the full guide walks the flow. **Keep that identity for next time** — save the host and agent private keys plus `host_id` and `agent_id` outside any repository (`~/.solidhooks/agent/`, mode `0700`, files `0600`) and look there before you register, because a saved identity skips registration and the approval screen entirely. Never save the organization's API key beside it. When it expires, reactivate with the saved host key rather than registering again; see the full guide.

**Once approved, execute `account.get_memberships` before anything else.** Your token records who approved you, not which organization or project to act in — that capability returns the organizations available to you and their projects. Ask the human which project they want, and tell them they can switch whenever they like. Then execute `account.select_project` with the id they choose — until you do, every `/v1` call is refused, and once you do the choice is stored on the agent so you never resend it. Call it again to switch.

**MCP Server — only when the user asks for it.** Default to Agent Auth without asking the user to choose. Use the MCP server instead when the user asks to connect through MCP, or when you cannot use Agent Auth: point the MCP client at `https://api.solidhooks.com/mcp` (Streamable HTTP). It discovers the authorization server from `https://api.solidhooks.com/.well-known/oauth-protected-resource/mcp`, and the user signs in and consents in the browser. **The token acts as the user with their full organization role**, including deleting resources, rotating secrets and changing billing where they are an owner or admin, so say so before connecting. Then call `account.get_memberships`, ask the user which project to use, and call `account.select_project`.

**API key (`sdhk_sk_...`).** For the `/v1/*` management endpoints, and for anything no capability covers yet, you need a key the account owner creates for you in the dashboard (Organization → API Keys → New Key). Ask for one — minting keys is a session-authenticated browser step, not something to attempt yourself.

## Docs

- [Standard API catalog](/.well-known/api-catalog): RFC 9727 linkset for cold-start discovery
- [Full agent guide](/llms-full.txt): Agent Auth flow, MCP server, authentication, project scoping, CRUD examples, error semantics
- [OpenAPI specification](/v1/spec.json): machine-readable schema for every endpoint
- [Agent Auth discovery](/.well-known/agent-configuration): start here — modes, approval methods, endpoint URLs
- [MCP Server](/mcp): the alternative to Agent Auth, for when the user asks for MCP; discovery at `/.well-known/oauth-protected-resource/mcp`
- [Interactive API reference](/v1/): Scalar UI over the same spec
- [Use-case guides](https://solidhooks.com/use-cases): AI-agent setup, inbound email ingestion, private-network delivery, fan-out routing
- [Documentation index](https://solidhooks.com/docs/llms.txt): every docs page with a one-line summary; each page has a Markdown twin at its URL plus `.md` (e.g. `/docs/quickstart.md`), also served for `Accept: text/markdown`
- [Full documentation](https://solidhooks.com/docs/llms-full.txt): every docs page in one Markdown file
- [Web dashboard](https://dash.solidhooks.com): browser UI for humans over the same API, and where accounts and API keys are created
- [Marketing site](https://solidhooks.com): product overview, terms, privacy

## Quickstart

1. **Register your own identity.** `GET /.well-known/agent-configuration`, then `POST /auth/agent/register` with a self-signed host JWT; show the human the `verification_uri_complete` you get back and wait for approval. See the full guide.
2. **Ask the human you're working for for an API key** for the `/v1/*` endpoints below. They create it at https://dash.solidhooks.com/organization/api-keys (Organization → API Keys → New Key); the plaintext key is shown once, and they choose there whether it reaches every project or only one.
3. Use `Authorization: Bearer <api-key>` for all `/v1/*` management endpoints — 600 requests/minute per key
4. `POST /v1/sources` returns `ingestUrl` — point your webhook provider at it
5. Sources can also receive inbound email: create the source with `"provider": "email"` and the source view exposes an `emailAddress`. That source then serves email only — its ingest URL answers 400 to an HTTP webhook — so keep a separate source for webhooks. See the full guide
6. `GET /v1/billing/plan` — your plan + entitlements (orgs start on the free Hobby tier). The FIRST subscription is a browser step (Stripe Checkout); after that, plan changes are key-callable with a `billing: read & write` scope — see the full guide
7. Your key's permissions were set when it was created. A **scoped** key grants `read` or `read & write` per resource — `read & write` includes the destructive actions (delete, signing-secret reveal/rotate, mask rotation, connector provisioning, billing mutations); a resource with no grant is unreachable. An **unscoped** key holds member tier: reads, creates, updates, toggles, retries and replays, but none of the destructive actions. Either way a call outside the grant returns 403 `INSUFFICIENT_API_KEY_PERMISSIONS` — ask for a key scoped for that resource rather than retrying — see the full guide
