{"openapi":"3.1.0","info":{"title":"BotNotice API","version":"1.0.0","summary":"AI disclosure notices, consent records and call receipts for websites and AI phone agents, plus an open dataset of AI disclosure laws.","description":"BotNotice shows a \"you're talking to AI\" notice beside a website's chat, keeps a tamper-evident record of every notice, checks consent before AI phone calls and keeps a receipt per call. Plans: Chatbot Disclosure $12.99/month per website; Disclosure + Records $29.99/month per website; Managed Compliance $199/month per website; 14-day free trial on plans with records; no sales call, sign up at https://botnotice.app. The scan and the law dataset need no key. The consent, voice and content endpoints take a per-site token from the dashboard (Voice page for the voice token, Content page for the content-register token). Nothing here is legal advice.","termsOfService":"https://botnotice.app/legal/terms","contact":{"name":"BotNotice support","email":"support@botnotice.app","url":"https://botnotice.app/support"},"license":{"name":"Law dataset: CC BY 4.0","url":"https://creativecommons.org/licenses/by/4.0/"}},"externalDocs":{"description":"Human documentation","url":"https://botnotice.app/docs/api"},"servers":[{"url":"https://botnotice.app"}],"tags":[{"name":"Free tools","description":"No key needed."},{"name":"Law dataset","description":"Open data, CC BY 4.0. https://botnotice.app/datasets/ai-disclosure-laws"},{"name":"Consent","description":"Consent to AI phone calls: record, check before dialing, revoke. Token: the site's voice token (bni_…)."},{"name":"Voice","description":"What an AI call must open with, and the receipt per call. Token: the site's voice token (bni_…)."},{"name":"Content register","description":"Register AI-generated media as it is created or uploaded. Token: the site's content-register token."}],"components":{"securitySchemes":{"voiceToken":{"type":"http","scheme":"bearer","description":"The site's voice token (starts with bni_), from the dashboard's Voice page. Also accepted as ?token= on voice endpoints, and as x-vapi-secret on /api/voice/event."},"registerToken":{"type":"http","scheme":"bearer","description":"The site's content-register token, from the dashboard's Content page."}},"schemas":{"LawRecord":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string"},"url":{"type":"string"},"jurisdiction":{"type":"string","description":"US-FED, EU, UK or a state code"},"jurisdiction_name":{"type":"string"},"name":{"type":"string"},"citation":{"type":"string"},"status":{"type":"string","enum":["in_force","enacted_future","pending","enjoined"]},"effective_date":{"type":"string","description":"ISO date"},"applicability_note":{"type":["string","null"]},"summary":{"type":"string"},"who_must_comply":{"type":"string"},"applies_to":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"label":{"type":"string"}}}},"disclosure":{"type":"object","properties":{"required":{"type":"string"},"timing":{"type":"string"},"re_disclose_hours":{"type":["integer","null"]},"re_disclose_hours_minors":{"type":["integer","null"]},"when_asked":{"type":"boolean"},"minor_notice":{"type":"boolean"}}},"other_duties":{"type":"array","items":{"type":"string"}},"exemptions":{"type":"array","items":{"type":"string"}},"penalties":{"type":"string"},"enforcement":{"type":"string"},"private_right_of_action":{"type":"boolean"},"sources":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"url":{"type":"string"}}}},"risk_weight":{"type":"integer"},"last_verified":{"type":"string","description":"ISO date a person last checked the record"}}},"ScanResult":{"type":"object","properties":{"ok":{"type":"boolean"},"url":{"type":"string"},"finalUrl":{"type":"string"},"error":{"type":"string"},"blocked":{"type":"boolean","description":"The site's bot protection answered instead of the page"},"chatWidgets":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"evidence":{"type":"string"},"weak":{"type":"boolean","description":"Mentioned in code, not seen running"}}}},"voiceAgents":{"type":"array","items":{"type":"object","properties":{"platform":{"type":"string"},"name":{"type":["string","null"]},"evidence":{"type":"string"}}}},"aiSignals":{"type":"array","items":{"type":"string"}},"disclosureFound":{"type":"boolean","description":"An AI notice is already on the page"},"disclosureSnippets":{"type":"array","items":{"type":"string"}},"botnoticeInstalled":{"type":"boolean"},"pagesChecked":{"type":"integer"},"cached":{"type":"boolean"},"cachedAt":{"type":"string"}}},"ConsentCheck":{"type":"object","properties":{"allowed":{"type":"boolean","description":"Dial only when true"},"reason":{"type":"string","enum":["consent_on_file","no_consent","revoked","wrong_scope","invalid_number"]},"message":{"type":"string"},"consent":{"type":["object","null"],"properties":{"id":{"type":"string"},"scope":{"type":"string"},"method":{"type":"string"},"consented_at":{"type":"string"}}},"disclosure":{"type":"string","description":"The line the call must open with"},"policyVersion":{"type":["integer","null"]}}},"VoicePolicy":{"type":"object","properties":{"business":{"type":"string"},"language":{"type":"string"},"disclosure":{"type":"string","description":"The line to speak at the start of the call"},"timing":{"type":"string","enum":["start"]},"repeatEveryHours":{"type":["integer","null"]},"askForHuman":{"type":"boolean"},"minorsNotice":{"type":"boolean"},"inProfile":{"type":"boolean"},"laws":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"slug":{"type":"string"},"jurisdiction":{"type":"string"},"timing":{"type":"string"},"effective":{"type":["string","null"]}}}},"policyVersion":{"type":["integer","null"]}}}}},"paths":{"/api/scan":{"post":{"tags":["Free tools"],"operationId":"scanWebsite","security":[],"summary":"Check a website for chat tools, voice agents and an existing AI notice","description":"The HTML pass of the free scan: which chat and voice-AI tools the site runs and whether an AI disclosure is already shown. Results are cached for 12 hours per host; send fresh=true to re-read. Rate limited to 10 per minute per address. For the browser pass (opens the chat, reads its opening line) use the page at /scan.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","description":"https://example.com"},"fresh":{"type":"boolean"}},"required":["url"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScanResult"}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"429":{"description":"Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/datasets/ai-disclosure-laws.json":{"get":{"tags":["Law dataset"],"operationId":"lawDataset","security":[],"summary":"Every tracked AI disclosure law, with pending bills","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"version":{"type":"string","description":"Newest last-verified date"},"license":{"type":"string"},"count":{"type":"integer"},"laws":{"type":"array","items":{"$ref":"#/components/schemas/LawRecord"}},"watchlist":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"jurisdiction_name":{"type":"string"},"name":{"type":"string"},"status":{"type":"string"},"note":{"type":"string"},"last_verified":{"type":"string"}}}}}}}}}}}},"/datasets/ai-disclosure-laws.csv":{"get":{"tags":["Law dataset"],"operationId":"lawDatasetCsv","security":[],"summary":"The same set as CSV","responses":{"200":{"description":"CSV, one row per law","content":{"text/csv":{"schema":{"type":"string"}}}}}}},"/datasets/ai-disclosure-laws/{slug}.json":{"get":{"tags":["Law dataset"],"operationId":"lawBySlug","security":[],"summary":"One law in full","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"},"example":"california-sb-243"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LawRecord"}}}},"404":{"description":"Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/consent":{"post":{"tags":["Consent"],"operationId":"recordConsent","security":[{"voiceToken":[]}],"summary":"Record that a person agreed to AI calls","description":"Send it the moment consent is given (form submit, CRM field). The number is stored only as a fingerprint plus its last four digits; the wording is kept so the record shows what the person agreed to.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"phone":{"type":"string","description":"E.164 or a plain U.S. number"},"scope":{"type":"string","enum":["marketing","informational"],"default":"marketing"},"method":{"type":"string","description":"web_form, verbal, sms…"},"wording":{"type":"string","description":"The exact consent text shown"},"source":{"type":"string","description":"Form name or page address"},"external_id":{"type":"string","description":"Your lead or contact id"},"consented_at":{"type":"string","description":"ISO time; defaults to now"}},"required":["phone","wording"]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"consent_id":{"type":"string"},"last4":{"type":"string"},"scope":{"type":"string"},"method":{"type":"string"},"consented_at":{"type":"string"}}}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/consent/check":{"post":{"tags":["Consent"],"operationId":"checkConsent","security":[{"voiceToken":[]}],"summary":"The pre-dial check: may an AI call be placed to this number?","description":"Call it right before dialing and dial only when allowed is true. The answer is written to the record, so it shows the check happened before the call, and the response carries the opening line the call must start with. POST only, so no phone number lands in a URL.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"phone":{"type":"string"},"scope":{"type":"string","enum":["marketing","informational"],"default":"marketing"},"call_ref":{"type":"string","description":"Your call id, written to the record"}},"required":["phone"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsentCheck"}}}},"401":{"description":"Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/consent/revoke":{"post":{"tags":["Consent"],"operationId":"revokeConsent","security":[{"voiceToken":[]}],"summary":"Someone said stop: revoke consent for a number","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"phone":{"type":"string"},"consent_id":{"type":"string","description":"Instead of phone: one consent by id"},"method":{"type":"string","description":"How they said stop, e.g. texted STOP"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"revoked":{"type":"integer"}}}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}},"/api/voice/policy":{"get":{"tags":["Voice"],"operationId":"voicePolicy","security":[{"voiceToken":[]},{}],"summary":"What a call for this site must open with","description":"The disclosure line, in the site's language, from its law determination. The site key (?k=) is enough to read it, since the line is spoken to every caller. Cache for up to an hour; policyVersion changes when the settings or the profile change.","parameters":[{"name":"k","in":"query","required":false,"schema":{"type":"string"},"description":"The site key, instead of the token"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoicePolicy"}}}},"401":{"description":"Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"402":{"description":"No active plan"}}}},"/api/voice/event":{"post":{"tags":["Voice"],"operationId":"voiceEvent","security":[{"voiceToken":[]}],"summary":"The receipt for a call","description":"Point the voice platform's webhook here. Understands BotNotice's own shape, Retell's webhooks (call_started, call_ended, call_analyzed) and Vapi's server messages (status-update, end-of-call-report). A transcript in an end-of-call message is read once for the disclosure and where it fell, then dropped; only the platform's call id is kept.","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"call_id":{"type":"string"},"event":{"type":"string","enum":["started","disclosed","handoff_human","ended"]},"at":{"type":"string","description":"ISO time"},"agent":{"type":"string"},"transcript":{"type":"string","description":"Only with ended; read once, not stored"}},"required":["call_id","event"]},{"type":"object","description":"A Retell or Vapi webhook body, as sent"}]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"platform":{"type":"string"},"recorded":{"type":"array","items":{"type":"object","properties":{"call_id":{"type":"string"},"written":{"type":"integer"}}}},"kept":{"type":"string"}}}}}},"401":{"description":"Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"402":{"description":"No active plan"}}}},"/api/ingest/content":{"post":{"tags":["Content register"],"operationId":"ingestContent","security":[{"registerToken":[]}],"summary":"Register media as it is created or uploaded","description":"url may be a media file (fetched, its provenance read: Content Credentials, IPTC, generator metadata) or a page (every media file on it is inspected). force=true registers the file on the sender's word even when the file declares nothing, for files straight from a creation tool. Up to 20 urls per call.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"},"urls":{"type":"array","items":{"type":"string"},"description":"Up to 20"},"page":{"type":"string","description":"The page the file appears on"},"title":{"type":"string"},"media_type":{"type":"string","enum":["image","video","audio"]},"force":{"type":"boolean"},"tool":{"type":"string","description":"Midjourney, Sora, ElevenLabs…"},"depicts_real":{"type":"boolean","description":"Shows people, places or events that could be taken as real"},"is_ad":{"type":"boolean"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"results":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string"},"kind":{"type":"string","enum":["media","page","error"]},"verdict":{"type":"string","enum":["declared","likely","none"]},"registered":{"type":"integer"},"assets":{"type":"integer"},"error":{"type":"string"},"item":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"labeled":{"type":"boolean"},"label_text":{"type":"string"}}}}}}}}}}},"401":{"description":"Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"The plan has no content register"}}}}}}