# Agent API

Status: Live
Updated: 2026-09-30
Verified against: ecosystem cecd4003

API version `2026-09-01`. Discovery is public and grants nothing. Execution needs a Paintaire-issued bearer credential with the scope for each step.

## Discovery (public)

- `GET https://paintaire.com/.well-known/paintaire-agent.json` — versioned manifest (also `/api/agent/manifest`)
- `GET https://paintaire.com/api/agent/content` — resource index; `?resource=<id>` returns one Markdown resource
- `GET https://paintaire.com/llms.txt` and every page’s `.md` mirror

## Actions

| Action | Audience | Purpose | Required | Optional |
|---|---|---|---|---|
| `homeowner_quote` | homeowner | Request a quote from one vetted painting contractor. | `name`, `email`, `postal_code` | `phone`, `project_type`, `timeline`, `message` |
| `contractor_signup` | contractor | Request a Paintaire platform walkthrough. | `name`, `email`, `company` | `phone`, `company_size`, `message` |
| `contact` | either | Send a message to Paintaire. | `name`, `email`, `subject`, `message` | `phone` |
| `newsletter` | either | Subscribe to Paintaire updates. | `email` | — |

Values are strings up to 2,000 characters; unknown fields are ignored; file uploads are not accepted. The contractor who receives a lead is chosen by Paintaire on the server — an agent cannot select one.

## Flow

1. `POST /api/agent/actions/{action}/preview` with scope `lead:preview`. Returns a signed `preview_id` valid for 10 minutes, the normalized `fields`, the `destination`, and the exact `confirmation_statement`.
2. Show the user the fields, destination and statement. Proceed only on their explicit agreement.
3. `POST /api/agent/actions/{action}/execute` with scope `lead:create`, header `Idempotency-Key` (up to 200 characters, unique per user request), and body `{ "preview_id", "confirmation": true, "confirmation_text" }`. The text must be exactly: “I confirm that Paintaire may use these details for the selected action.”
4. Store the returned `receipt`. Retrying with the same key and preview replays it (`replay: true`) without a second submission. A preview executes once: an executed preview is consumed even if the execution failed (a relayed 4xx or a 502). To try again after a failure, create a new preview and confirm with the user again; do not retry under a new key.

## Responses

| Status | When |
|---|---|
| 200 | Preview created; execution submitted or replayed |
| 400 | Invalid JSON, field, preview token (including a malformed token — anything other than exactly `<payload>.<signature>`), expired preview, missing `Idempotency-Key`, or action mismatch |
| 401 | Missing or invalid bearer credential |
| 403 | Credential lacks the scope |
| 404 | Unknown action |
| 409 | Confirmation missing or not exact; execution already in progress (`retryable: true`); `preview_already_used` — this preview was executed under another key; `idempotency_conflict` — this key was used with a different preview |
| 429 | Preview over 60 per minute or execute over 20 per minute per client address, or on execute an upstream rate limit relayed with its `Retry-After`; honor `Retry-After` |
| Other 4xx | Execution only: the upstream rejected the submission; its status passed through and the receipt is `failed` |
| 502 | Upstream failed or could not confirm the submission; also returned when replaying a failed execution |
| 503 | Execution not configured or receipt storage unavailable |

Errors are `{ "error": "<message or code>" }` with extra fields where listed.

## Credentials

Issued by Paintaire on request for a named client, scoped to `lead:preview` and/or `lead:create`. Rotation overlaps old and new credentials; revocation is immediate on the next deployment. There is no self-service issuance.

Human page: https://paintaire.com/developers/agent-api
