# auth.md

> How an AI agent authenticates to whatsolarcosts.com. Short version: it does not
> have to. The public API is anonymous, key-free and needs no registration.

- Service: What Solar Costs (https://whatsolarcosts.com)
- Audience: autonomous agents, LLM tools and other programmatic clients calling the
  public Estimate API on behalf of a homeowner.
- Updated: 2026-10-07
- Human documentation: https://whatsolarcosts.com/developers/

## Authentication required: none

There is no account, no API key, no client registration, no OAuth flow and no
credential to obtain. Send the request. An agent that finds this file has already
finished the auth work for every public endpoint.

Because nothing issues credentials here, no OAuth metadata is published — there is
no `/.well-known/oauth-protected-resource` and no authorization server. Treat that
absence as "unauthenticated by design", not as an auth flow you failed to discover.
If that ever changes, this document will point at the metadata.

## The same answer, machine-readable

Every field below is an assertion. `null` and `[]` mean "there is none", not
"not documented here".

```json
{
  "service": "https://whatsolarcosts.com",
  "updated": "2026-10-07",
  "authentication": "none",
  "registration": "not_required",
  "registration_endpoint": null,
  "dynamic_client_registration": false,
  "oauth_protected_resource_metadata": null,
  "oauth_authorization_server_metadata": null,
  "authorization_servers": [],
  "token_endpoint": null,
  "identity_types": [
    "anonymous"
  ],
  "credential_types": [
    "none"
  ],
  "methods": [
    {
      "method": "anonymous",
      "audience": "agents and all other public clients",
      "credential": "none",
      "endpoints": [
        "/api/v1/estimate",
        "/api/v1/health",
        "/api/a2a/v1"
      ],
      "registration": "not_required"
    },
    {
      "method": "bearer",
      "audience": "site operator only",
      "credential": "bearer token, issued out of band",
      "endpoints": [
        "/api/admin/*"
      ],
      "registration": "unavailable"
    }
  ],
  "rate_limit": {
    "requests": 60,
    "period_seconds": 60,
    "scope": "per client, per Cloudflare location",
    "over_limit": {
      "status": 429,
      "header": "Retry-After"
    }
  },
  "web_bot_auth": {
    "directory": "https://whatsolarcosts.com/.well-known/http-message-signatures-directory",
    "signature_agent": "https://whatsolarcosts.com",
    "key_id": "8aQc9XAhaxvv-A5WMgLiZDEz7Hg74Fist5a88KC6EqM",
    "algorithm": "ed25519",
    "tag": "web-bot-auth",
    "inbound_required": false,
    "outbound_signed": true
  },
  "lead_creation": "unavailable",
  "documentation": "https://whatsolarcosts.com/developers/"
}
```

## Endpoints and what they need

| Endpoint | Method | Credential |
| --- | --- | --- |
| `/api/v1/estimate` | GET, POST | None (anonymous) |
| `/api/v1/health` | GET | None (anonymous) |
| `/api/a2a/v1` (A2A JSON-RPC) | POST | None (anonymous) |
| `/openapi.json`, `/.well-known/api-catalog`, `/.well-known/agent-card.json`, `/llms.txt`, `/auth.md` | GET | None (anonymous) |
| `/api/admin/*` | — | Operator-only bearer token, issued out of band. Not available to agents, not registrable, and not part of the public API. |

## Registration and provisioning

- **Agents: nothing to register.** There is no registration endpoint, no dynamic
  client registration, and no `POST` that mints a credential. A scanner looking for
  one should record "none required" rather than probing for it.
- **Solar installers (humans, not agents):** the one relationship this site does
  provision is an installer partnership, applied for at
  https://whatsolarcosts.com/for-installers/. It is a reviewed, human application handled over
  email. It grants lead delivery, not API credentials, and it does not change how
  the API behaves.
- **Higher volume:** ask via the contact details at https://whatsolarcosts.com/about — a raised
  limit is a conversation, not a credential. Do not rotate addresses to work around
  the limit.

## Supported methods

1. `anonymous` — the only method for every public endpoint. No credential, no
   identity, no claim step.
   This covers the A2A interface at `/api/a2a/v1` as well: its agent card publishes no
   `securitySchemes` and no `securityRequirements`, which is this same answer in the
   vocabulary an A2A client already parses.
2. `bearer` — `Authorization: Bearer <token>` on the private admin endpoints only.
   Tokens are held by the site operator and are never issued on request; an agent
   cannot obtain one and should not attempt the endpoints.

## Using the API without credentials

- Identify yourself in `User-Agent` with a name and a contact URL. It is optional
  and unauthenticated, but it is how we tell a well-behaved agent from abuse, and
  it is the only identity signal we look at.
- Rate limit: 60 requests per minute, per client, per Cloudflare location. Over it you get
  `429` with `Retry-After`; wait out the header.
- CORS is open (`access-control-allow-origin: *`), so browser-side agents work too.
- Responses are `no-store` because each call mints its own estimate id. Cache on
  your side, keyed by the inputs, if you need to.

## Web Bot Auth: our identity, not yours

This site publishes an Ed25519 public key at
`https://whatsolarcosts.com/.well-known/http-message-signatures-directory` (Web Bot Auth, IETF
WebBotAuth WG), and signs the requests it originates with the matching private key
per RFC 9421 — `Signature-Agent`, `Signature-Input` and `Signature`, tagged
`web-bot-auth`. The key id is `8aQc9XAhaxvv-A5WMgLiZDEz7Hg74Fist5a88KC6EqM`, the base64url JWK thumbprint
of that key.

Read that as a claim about outbound traffic only. **Nothing about inbound requests
changes**: calling this API needs no signature, no key and no `Signature-Agent`
header, exactly as the rest of this document says. Signed requests are not
preferred, not rate-limited differently, and not treated as more trustworthy —
there is no inbound verification at all.

## What the API cannot do, credential or not

The Estimate API is read-only. It creates no lead, sends no email, and accepts no
names, email addresses or phone numbers — do not send them. An installer
introduction needs the homeowner's own consent, by one of two routes: submitting the
form on the results page, or having an assistant ask for quotes through
`POST /api/v1/quote-requests` (or the MCP tool `request_installer_quotes`) and
then clicking the link in the confirmation email that sends. The second route
contacts no installer until that click. No credential would unlock a shortcut past
it, because there is nothing to unlock: it is a consent boundary, not a permission.

## Discovery

- A2A Agent Card: https://whatsolarcosts.com/.well-known/agent-card.json
- API catalog (RFC 9727): https://whatsolarcosts.com/.well-known/api-catalog
- OpenAPI 3.1 description: https://whatsolarcosts.com/openapi.json
- Site summary for LLMs: https://whatsolarcosts.com/llms.txt
- Web Bot Auth key directory: https://whatsolarcosts.com/.well-known/http-message-signatures-directory
- Terms of use: https://whatsolarcosts.com/terms/
