Wire up the button.
Everything connects to a switch through one public status URL. First create a switch to get its ID.
Embed on a website
Add this before the closing </body> tag of any page:
<script src="https://ai-off.netlify.app/embed.js" data-switch="YOUR_SWITCH_ID" async></script>While AI is off, the script:
- hides every element marked as AI content;
- blocks
fetch, XHR, WebSocket and EventSource calls from the page to major AI providers (OpenAI, Anthropic, Google Gemini, Mistral, Cohere, Groq, OpenRouter, Hugging Face and others); - adds a
noai, noimageairobots tag; - sets
window.AI_OFF = trueanddata-ai-off="true"on<html>.
Visitors can press the floating button to switch AI off for themselves. When you switch it off from your control panel, it's off for everyone and the button shows LOCKED. The script re-checks every 10 seconds.
| Attribute | Meaning |
|---|---|
| data-switch | Your switch ID. Optional — without it, only the visitor toggle works. |
| data-hide | Extra CSS selectors to hide, comma separated. e.g. "#chat-widget, .ai-summary" |
| data-block | Extra hostnames to block, comma separated. e.g. "llm.mycompany.com" |
| data-button | "false" hides the floating button. |
| data-position | bottom-right (default), bottom-left, top-right, top-left |
Mark AI content
Anything with one of these attributes or classes disappears while AI is off:
<div data-ai>AI chat widget</div>
<p data-ai-generated>This summary was written by AI.</p>
<img class="ai-generated" src="/generated.png" alt="…">Style your own fallbacks with the root attribute:
html[data-ai-off="true"] .search-box { /* show classic search */ }JavaScript API
window.AIOff.isOff() // true | false
window.AIOff.turnOff() // visitor-level off
window.AIOff.turnOn()
window.AIOff.onChange(({ off, global, local }) => { … })
document.addEventListener('aioff:change', (e) => {
if (e.detail.off) myChatbot.destroy()
})Agents & bots
Have your agent read the switch before each action and stop when it says off. Ready-made Node and Python guards are on every switch's control panel.
loop:
status = GET https://ai-off.netlify.app/api/switch/YOUR_SWITCH_ID
if status.ai == "off": stop
do next stepTreat an unreachable status URL as a reason to pause, not to carry on — that way the switch fails safe.
REST API
| Endpoint | What it does |
|---|---|
| GET /api/switch/:id | Public. Returns { id, name, ai: "on"|"off", aiEnabled, flipCount, updatedAt }. Also sets an X-AI-Status header. Add ?events=1 for the last 20 flips. |
| POST /api/switch/:id | Owner only. Header Authorization: Bearer <admin key>. Body { "ai": "off" | "on" | "toggle", "source"?: "pager" }. |
| POST /api/switches | Creates a switch. Body { "name" }. Returns the switch and its admin key (shown once). |
curl -X POST https://ai-off.netlify.app/api/switch/YOUR_SWITCH_ID \
-H "Authorization: Bearer $AIOFF_KEY" \
-H "Content-Type: application/json" \
-d '{"ai":"off","source":"on-call"}'