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 every plan starts 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 bnv_): 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: use it from your server, form handler, CRM or Zapier, never in a web page (the API refuses calls from browsers). Rotate it from the same page.
  • Content-register token (starts with bni_): from the site's Content page. Only allows adding media to that site's register. It can't send call receipts or touch consent.

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

31 laws and the pending bills, as JSON and CSV, under CC BY 4.0. Version 2026-11-02. 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 bnv_…
{ "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 bnv_…
{ "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 bnv_…
{ "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=bnv_…
{ "call_id": "call_8831", "event": "started" }
{ "call_id": "call_8831", "event": "ended", "transcript": "…" }   ← read once for the disclosure, not stored

Certificates

Each conversation and call gets a certificate id when its notice is first recorded (plans with records). On a web page, read it with BotNotice.certificate() or add a hidden form field named botnotice_cert and the notice fills in the link. For calls, the link comes back from POST /api/voice/event as certificate. Anyone with the link can read a certificate. A partner you gave a key to can claim it, and the claim goes into your record. How certificates work.

GET https://botnotice.app/api/cert/bnc_…
→ { "id": "bnc_…", "notice_shown_at": "…", "notice_text": "…", "viewable": true, "rows": [ … ], "time_stamp": { … }, "signed": "<JWS>" }

POST https://botnotice.app/api/cert/bnc_…
Authorization: Bearer bnp_…
{ "reference": "lead-48213" }
→ { "ok": true, "claimed_at": "…", "first_claim": true }

Outside proof and the AI file

These need no key. GET /proof/site/<site key> returns a website's place in the latest daily hash tree with links to the time stamp tokens (how to check them). GET /d/<site key>/ai-disclosure.json is the website's hosted AI disclosure file, and GET /d/<site key>/statement.jwt is BotNotice's signed statement on its own. Signatures verify against GET /.well-known/jwks.json. GET /badge/<site key>.svg is the status badge. The badge and the statement show a website's check results only when its owner chose to publish them.

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.
API, MCP server and open data · BotNotice