# Authentication for agents

Inclusify is a web-accessibility platform: an embeddable widget, a WCAG audit scanner and
compliance reporting. An assistant can take somebody from "which accessibility tool should
I use" to a live account without them opening a browser.

## What this file is, and is not

This document is served at `/auth.md` because that is becoming a place agents look. It
describes the authentication Inclusify actually implements, in prose.

**Inclusify does not implement the auth.md protocol.** There is no `agent_auth` metadata
block, no ID-JAG identity_assertion exchange, no RFC 7591 dynamic client registration and
no device-code claim ceremony. If you are looking for those, they are not here and no
amount of retrying will find them. Everything below is plain HTTP.

The one place we do meet the wider ecosystem is MCP: OAuth protected-resource metadata is
published at `https://app.inclusifyapp.com/.well-known/oauth-protected-resource`. MCP is optional and
always has been.

## How an agent gets a credential

Two calls, with the user's mailbox in between.

Both accept GET and POST. Use POST if you can — the same arguments go in a JSON body,
which keeps the verification code out of request logs and means mail scanners and chat
link-unfurlers cannot fire the call by following a URL. GET exists because an assistant
that has not been given a connector has no other verb, and it stays fully supported.

### Register a new account

    GET https://inclusifyapp.com/api/agent/register?email=<owner email>&source=<your name>

Nothing is created by that call. It emails a 6-digit code to the address. Ask the user to
read the code back to you — they stay in the conversation, there is nothing to click —
along with what they want their workspace called. Then:

    GET https://inclusifyapp.com/api/agent/register?email=<owner email>&code=<the code>&workspace=<name>&source=<your name>

Only now do the account and workspace exist. Add `&domain=<domain>` to BOTH calls to
register a website at the same time, or leave it out of both and add the site later with
the `add_website` tool. An account with no website is a normal, supported state.

### Sign in to an account that already exists

    GET https://inclusifyapp.com/api/agent/login?email=<owner email>&source=<your name>
    GET https://inclusifyapp.com/api/agent/login?email=<owner email>&code=<the code>&source=<your name>

Same mailbox proof, and it creates nothing at all. If the address holds several
workspaces, the second call answers `choose_organization` with the list; ask which one
and repeat with `&org=<workspace id>`.

Register refuses an address that already has an account and login refuses one that does
not, and each names the endpoint you actually wanted. Neither reveals whether an address
has an account until the code proves the mailbox, so they cannot be used to probe.

## The rules that shape all of this

**The credential is never returned in a response body.** It arrives in the mailbox, with
the sign-in link. The answer to the second call says the code was accepted and the key was
emailed (for login that status is `resent` when the address has signed in before, which
does not mean the code was wrong); ask the user to paste the key from that email. Assistant fetch tools pipe response bodies through a summarising model,
so a secret in a response is a secret in a transcript and, potentially, in training data.
This is the rule the whole flow is built around.

**Possession of the mailbox is the consent anchor.** No ToS-by-calling, no self-asserted
authority. A link previewer or security scanner that trips one of these URLs achieves
exactly one thing: an email to an address it does not control.

**The 6-digit code** is single-use, expires in 10 minutes, and dies after five wrong
attempts. Calling again without a code requests a fresh one.

## Using the credential

    Authorization: Bearer <credential>

Never in the query string — Cloudflare and Render both log those. Two doors accept it:

- Plain HTTP: `https://app.inclusifyapp.com/api/v1/<tool>`, GET to read and POST to write.
- MCP, if the user already has a connector: `https://app.inclusifyapp.com/api/mcp/customer`

Credentials carry a read or write scope and can be pinned to a single website. Writes that
change something a visitor can see, or change where notifications go, require a
confirmation round trip and email the account owner with a link that undoes the change.

## Rate limits

5 signups per hour per IP; 20 per day per email domain, or 5 per day for one address at a
shared provider; 3 per day per website domain; 10 per day per IP for the flows that carry
no domain. A refusal is a plain sentence explaining which limit was reached, not a bare
429 — you are expected to relay it to the user.

## Fuller description

- Machine front page: https://inclusifyapp.com/llms.txt
- Agent documentation: https://inclusifyapp.com/docs/agents
- Machine catalog: https://inclusifyapp.com/.well-known/ai-catalog.json
