For developers and AI agents

API, MCP server and open data

Three things need no key: the website check, the law dataset and the MCP server. Three take a per-site token from the dashboard: consent, voice receipts and the content register. The machine-readable description is at /openapi.json.

On this page

What you can call

NeedCallKey
Which AI disclosure laws apply to a businessMCP laws_for_business, or the datasetnone
What one law says, with sourcesMCP get_law, or /datasets/ai-disclosure-laws/<slug>.jsonnone
Does this website run AI chat, and does it say so?MCP check_website, or POST /api/scannone
Record, check or revoke consent to AI calls/api/consent, /api/consent/check, /api/consent/revokevoice token
The line an AI call must open with; a receipt per callGET /api/voice/policy, POST /api/voice/eventvoice token or site key
Register AI-generated media as it is madePOST /api/ingest/contentcontent-register token
Show the notice on a websiteOne script tag; see the setup guidesite key

Buying is self-serve: pricing is public, sign-up is a magic link to an email address, checkout is Stripe, and plans with records start with 14 days free. No sales call, no quote.

Keys and tokens

Each website in an account has its own credentials, so a token can only touch one site's records.

  • Site key (12 characters): identifies the website. It is in the notice script tag and may be public. It reads the voice policy and nothing else.
  • Voice token (starts with bni_): from the site's Voice page in the dashboard. Sent as Authorization: Bearer, as ?token=, or as x-vapi-secret. Treat it as a secret; rotate it from the same page.
  • Content-register token: from the site's Content page. Only allows adding to that site's register.

No endpoint returns personal data about a visitor or a caller. Phone numbers are stored as a fingerprint plus the last four digits; transcripts are read once and dropped.

MCP server

A Model Context Protocol server at https://botnotice.app/mcp (Streamable HTTP, no authentication). Four read-only tools, the same facts as this site:

ToolDoes
laws_for_businessGiven how a business uses AI (chatbot, voice agent, AI media, hiring…) and where its customers are (states, all U.S., EU, UK), returns the laws in force and coming, each with what to disclose, when, penalties and sources, plus the notice rules that satisfy all of them.
get_lawOne law in full, by slug.
list_lawsEvery tracked law, filterable by jurisdiction, status or kind of AI use; the pending bills too.
check_websiteReads a public website: chat and voice-AI tools found, whether an AI notice is shown, and which laws that leaves unmet for the business described.

In Claude, add it from the connectors directory: BotNotice: AI disclosure laws. It is also in the official MCP registry as app.botnotice/mcp. Any other client that takes a URL (ChatGPT, Cursor, VS Code and most others):

{ "mcpServers": { "botnotice": { "url": "https://botnotice.app/mcp" } } }

Every answer carries not_legal_advice: true and links to the law's page and sources, so a reader can check the claim rather than take the tool's word for it.

Law dataset

29 laws and the pending bills, as JSON and CSV, under CC BY 4.0. Version 2026-10-07. Fields, licence and citation are on the dataset page.

GET https://botnotice.app/datasets/ai-disclosure-laws.json
GET https://botnotice.app/datasets/ai-disclosure-laws.csv
GET https://botnotice.app/datasets/ai-disclosure-laws/california-sb-243.json

Website check

The HTML pass of the free scan: which chat and voice-AI tools a site runs and whether an AI disclosure is already shown. Cached 12 hours per host; fresh: true re-reads. The browser pass (opens the chat, reads its first message) runs only on the scan page.

POST https://botnotice.app/api/scan
Content-Type: application/json

{ "url": "https://example.com" }

→ { "ok": true, "chatWidgets": [{ "name": "Intercom", "evidence": "…" }], "voiceAgents": [], "disclosureFound": false, "aiSignals": [], "pagesChecked": 3 }

Three calls, all with the voice token. Record consent when it is given; check right before dialing and dial only when allowed is true; revoke when someone says stop. The check is written to the record, so the record shows it happened before the call.

POST https://botnotice.app/api/consent
Authorization: Bearer bni_…
{ "phone": "+15551234567", "scope": "marketing", "method": "web_form", "wording": "I agree to receive AI-assisted calls from Acme.", "source": "https://acme.com/quote" }
→ 201 { "ok": true, "consent_id": "…", "last4": "4567", "scope": "marketing", "consented_at": "…" }

POST https://botnotice.app/api/consent/check
Authorization: Bearer bni_…
{ "phone": "+15551234567", "scope": "marketing", "call_ref": "call_8831" }
→ { "allowed": true, "reason": "consent_on_file", "disclosure": "Hi, this is Acme's AI assistant…", "policyVersion": 4 }

POST https://botnotice.app/api/consent/revoke
Authorization: Bearer bni_…
{ "phone": "+15551234567", "method": "texted STOP" }
→ { "ok": true, "revoked": 1 }

reason is one of consent_on_file, no_consent, revoked, wrong_scope (agreed to informational calls only), invalid_number.

Voice: opening line and receipts

GET /api/voice/policy returns the line the call must open with, from the site's law determination, in the site's language. The site key alone can read it (?k=), since it is spoken to every caller. POST /api/voice/event is the webhook for the call itself: started, disclosed, handed to a person, ended. Retell and Vapi webhook bodies are understood as sent.

GET https://botnotice.app/api/voice/policy?k=<site key>
→ { "business": "Acme", "language": "en", "disclosure": "…", "timing": "start", "repeatEveryHours": null, "askForHuman": true, "laws": [ … ], "policyVersion": 4 }

POST https://botnotice.app/api/voice/event?token=bni_…
{ "call_id": "call_8831", "event": "started" }
{ "call_id": "call_8831", "event": "ended", "transcript": "…" }   ← read once for the disclosure, not stored

Content register

Send media as it is created or uploaded, before a CMS or CDN strips its metadata. A file is fetched and its provenance read (Content Credentials, IPTC digital source type, generator metadata); a page has every media file on it inspected. force: true registers on the sender's word, for files straight out of a creation tool.

POST https://botnotice.app/api/ingest/content
Authorization: Bearer <content-register token>
{ "url": "https://acme.com/img/hero.png", "page": "https://acme.com/", "force": true, "tool": "Midjourney" }
→ { "ok": true, "results": [{ "url": "…", "kind": "media", "verdict": "declared", "registered": 1, "item": { "id": "…", "labeled": false } }] }

Limits and conduct

  • Scan: 10 per minute per address. Consent and voice: 600 per minute per address and 3,000 per minute per site. Content: 20 urls per call.
  • Public endpoints answer any origin (CORS *). Tokens never belong in browser code.
  • Changes are additive. A field is never renamed or removed without a new path. The dataset version is its newest last_verified date.
  • Status and incidents: support page. Questions: support@botnotice.app.
  • Nothing here is legal advice. Each law record links its primary sources so the claim can be checked.