# CapchaCloud — Integration Guide (for developers & AI agents)

This file is written to be **acted on directly by an AI coding agent**. Paste it
(or its URL, https://capchacloud.com/integrate.md) into Claude and say
"integrate CapchaCloud into my app." Everything needed is below.

> Authoritative machine-readable API contract: **https://capchacloud.com/openapi.json**
> Positioning & facts: `/llms.txt`, `/FACTS.md`. Pricing: `/pricing.html` (wins on conflict).

CapchaCloud has **two cooperating products**:

1. **CapchaID** — drop-in auth widget (sign-in, 2FA, passkeys, password reset). Browser side.
2. **Trust API** — server-side lead verification, trust scoring, consent evidence,
   defense packets, suppression. Uses a secret API key.

You can use either or both. Auth widget = browser. Trust API = your server.

---

## 1. Add the sign-in widget (browser, ~2 lines)

```html
<script src="https://capchacloud.com/widget.js" async data-capcha-widget></script>
<capcha-auth data-capcha-client-id="YOUR_PUBLISHABLE_KEY"></capcha-auth>
```

- `YOUR_PUBLISHABLE_KEY` looks like `cid_pk_live_…` (or `cid_pk_test_…` for sandbox).
- The publishable key is **safe in the browser**. Never put a secret key in client code.
- The widget renders sign-in / sign-up, runs a dual human check (Turnstile + an invisible proof-of-work), seals the visitor's IP + Cloudflare geolocation, and logs consent.

### Sandbox vs live keys — IMPORTANT (avoids the #1 first-run failure)
A **live** key only renders on origins you've allow-listed. A **sandbox** key
(`allowed_origins: ["*"]`) renders on **any** origin, including `localhost`, so your
first paste always works. Develop with sandbox, then lock down to your real
domain(s) for production. (Mint keys via the MCP `create_tenant` / `allow_domain`
tools, the `/v1/provision` endpoint, or the dashboard.)

### Password-reset gotcha
The reset email link lands on a page with `?capchaid_reset_token=…`. The widget
auto-detects that token and shows the "choose a new password" screen — so the app's
**reset redirect URL must point at a page that hosts `<capcha-auth>`** (your sign-in
page is fine). Set it in the dashboard or via `reset_redirect_url`.

---

## 2. Verify & score leads (server side)

Base URL: `https://capchacloud.com`
Auth: send your secret API key as **either** header:
`Authorization: Bearer <API_KEY>` **or** `X-API-Key: <API_KEY>`.

### Verify a lead
```bash
curl -sX POST https://capchacloud.com/api/v1/verify-lead \
  -H "Authorization: Bearer $CAPCHA_API_KEY" -H "Content-Type: application/json" \
  -d '{"email":"jane@example.com","phone":"+15551234567"}'
```

### Trust score (0–100, fuses bot/risk + email + phone + suppression)
```bash
curl -sX POST https://capchacloud.com/api/v1/trust-score \
  -H "Authorization: Bearer $CAPCHA_API_KEY" -H "Content-Type: application/json" \
  -d '{"email":"jane@example.com","phone":"+15551234567"}'
```

### Other server endpoints (all POST + API key unless noted)
| Endpoint | Purpose |
|---|---|
| `/api/v1/verified-consent` | Full Verified-Human-Consent record for a `record_id` (PII; owner only) |
| `/api/v1/defense-packet` | Court-ready memo for a `record_id` (`format:"json"` for data) |
| `/api/v1/suppression/add` / `/check` | Tenant DNC / suppression list |
| `/api/v1/verify-record` | **PUBLIC, no PII** — third party confirms a record's integrity |
| `/api/v1/transparency` | **PUBLIC, GET** — live append-only audit-chain proof |

---

## 3. Webhooks (push: get notified of events)

Register an HTTPS endpoint (dashboard or `POST /api/v1/tenant/webhook`). CapchaCloud
delivers signed JSON, retries failures, logs deliveries, and supports replay.

### Event types (as emitted today)
| Event | Fires when |
|---|---|
| `capture` | A consent / lead capture is recorded (payload `kind:"capture"`) |
| `user.created` | A new CapchaID end-user is created |
| `session.created` | A CapchaID end-user signs in |
| `session.ended` | A CapchaID session ends (sign-out / expiry) |
| `identity_verification` | An identity-verification event is recorded |
| `webhook.test` | You triggered a test delivery (see "Test your endpoint" below) |

Payloads are versioned; treat unknown event names as forward-compatible (ignore
events you don't handle).

### Verify the signature (HMAC-SHA256)
Each delivery carries two headers:
- `X-CapchaCloud-Signature: v1=<hex>` — HMAC-SHA256 over the string `"{timestamp}.{rawBody}"`
- `X-CapchaCloud-Timestamp: <unix-seconds>`

Compute the HMAC over **`timestamp + "." + rawBody`** with your signing secret and
compare to the `v1=` value. Reject if the timestamp is too old (replay protection).

```js
// Cloudflare Workers / Node (Web Crypto)
async function verify(rawBody, sigHeader, tsHeader, secret) {
  const expected = (sigHeader || "").replace(/^v1=/, "");
  const key = await crypto.subtle.importKey(
    "raw", new TextEncoder().encode(secret),
    { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
  const mac = await crypto.subtle.sign("HMAC", key,
    new TextEncoder().encode(`${tsHeader}.${rawBody}`));
  const hex = [...new Uint8Array(mac)].map(b => b.toString(16).padStart(2, "0")).join("");
  return hex === expected; // use a constant-time compare in production
}
```

```python
import hmac, hashlib
def verify(raw_body: str, sig_header: str, ts_header: str, secret: str) -> bool:
    expected = (sig_header or "").removeprefix("v1=")
    mac = hmac.new(secret.encode(), f"{ts_header}.{raw_body}".encode(), hashlib.sha256).hexdigest()
    return hmac.compare_digest(mac, expected)
```

### Test your endpoint
```bash
curl -sX POST https://capchacloud.com/api/v1/webhook-test -H "Authorization: Bearer $CAPCHA_API_KEY"
curl -s  https://capchacloud.com/api/v1/webhook-deliveries -H "Authorization: Bearer $CAPCHA_API_KEY"
```
Or use the visual tester: https://capchacloud.com/webhook-tester.html

---

## 4. MCP (let an agent do all of this)

CapchaCloud ships a remote MCP server so an AI agent can provision and operate
CapchaCloud with no dashboard:

- `create_tenant`, `get_tenant`, `allow_domain`, `get_keys`
- `register_webhook`
- `verify_lead`, `trust_score`, `verify_record`, `defense_packet`, `get_transparency`
- `embed_snippet` (returns the exact two-line embed for a tenant)

Connector + setup: see https://capchacloud.com/mcp.html

---

## Errors & idempotency

**Error envelope.** Errors return a JSON object: `{ "error": "<code>", "detail": "<human-readable>" }`
with an appropriate HTTP status (400 bad request, 401 unauthorized, 404 not found,
429 rate limited, 5xx server). Always branch on the HTTP status, then read `error`.

**Idempotency.** Send an `Idempotency-Key: <uuid>` header on any write
(`verify-lead`, `trust-score`, `suppression/add`, `defense-packet`, `webhook-test`).
The first response is cached for 24h and replayed verbatim for repeats with the same
key — so a network retry never double-applies. Replays come back with
`Idempotent-Replay: true`. Use a fresh key per logical operation.

```bash
curl -sX POST https://capchacloud.com/api/v1/suppression/add \
  -H "Authorization: Bearer $CAPCHA_API_KEY" -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1c…-uuid" -d '{"email":"x@y.com"}'
```

## Quick recipes

**"Add sign-in to my site"** → §1 with a sandbox key, then lock origins for prod.
**"Score inbound leads before my CRM"** → §2 `trust-score`, gate on the score.
**"Prove consent in a dispute"** → §2 `defense-packet` for the `record_id`.
**"Notify my backend on new consent"** → §3 register a webhook for `consent.captured`.
**"Just set it all up for me"** → §4 MCP.
