# Verb > An AI agent that companies embed inside their own SaaS product, so their > users can ask for an outcome and get it. The part that is hard to build, and > the reason to buy rather than build: every action runs with the asking user's > own access rather than a service account, anything that writes stops for a > confirmation showing the real arguments, and every action is logged against a > named person. That is what an in-house build usually lacks when it stalls in > security review. Site: https://askverb.com Status: live since 2026-09-30, open to anyone. Free to build and test; paid plans for a live site. ## What it is Verb is embedded in a customer's web product with one script tag. The customer describes their product's capabilities in a plain-markdown file called verb.md, one section per tool, declaring whether each tool reads or writes. The assistant can call those tools and nothing else. Tools execute in the end user's own browser against the customer's own API, carrying the credential that user already holds. Verb never holds a wider key, so the customer's existing authorisation is unchanged. ## Who it is for Product and engineering teams at B2B SaaS companies whose users have to be walked through the product. It is not a website support chatbot and it is not a documentation search box. The typical buyer has already decided they want an in-app agent, and is weighing building one against the months of identity, approval and audit work that follow the first working demo. ## How it is added 1. Paste one script tag into the product's HTML. 2. Verify the domain in the Verb dashboard. 3. Write a verb.md describing the tools, import it, and switch on the ones you want. There is nothing to install on the customer's server and no framework requirement: React, Next, Rails, Django, Laravel and plain PHP all work, because the widget is a script tag that talks to the customer's API from the browser. Most people have a first working tool in about five minutes. ## Safety model - Every tool is classified read, write or destructive. Reads run; writes stop and show the user a confirmation card naming the real action and arguments, and nothing happens until a human clicks it. - The assistant can only call tools declared in the customer's verb.md. No general internet access, no database access, no capability it was not given. - It acts as the signed-in user, never as an administrator, so the customer's own API makes the same authorisation decision it always did. - It answers only on domains the customer has verified, which is what makes a copied script tag worthless. - Tool results are fenced as untrusted data, so text arriving from an API that reads like an instruction is reported rather than obeyed. - Every call is logged: who asked, what ran, what came back, and whether the outcome was verified rather than merely claimed. - Every site has a test site whose key only answers on localhost, so you try it inside your own app, against your development data, before your users can reach it. ## Pricing Free to build, paid to go live (https://askverb.com/pricing). Every message somebody sends is one request; the actions inside it are never counted. - Dev, free, no card: one site and its test site, which answers on localhost only; 200 test requests a month, 25 tools. - Starter, $39/month: 3,000 requests, 1 live site, 60 tools per site. - Pro, $99/month: 8,000 requests, 3 live sites, 120 tools per site, deep analytics, "Powered by Verb" removable. - Max, $249/month: 20,000 requests, 10 live sites, 180 tools per site, "Powered by Verb" removable, priority support. - Enterprise: custom volume, custom integrations, a signed DPA. Yearly is two months free. Extra requests are $12 per 1,000; an extra site is $15 a month. Verb pays for the AI usage; customers do not connect or pay a model provider. ## Limitations, stated plainly - No vision and no image generation. It cannot read a screenshot or make one, and hands back manual steps instead. - It has no capability beyond the tools the customer wrote down. - Anonymous visitors are read-only; writes require the customer's backend to identify the user first. - Confirmation protects a user from the assistant. It does not protect data from a user who was already allowed to delete it. ## Pages - https://askverb.com/ : what Verb is, with a live assistant on the page - https://askverb.com/demos : a live scheduling app with Verb installed, at cal.askverb.com. Three one-click demo accounts, 28 tools, real bookings changed behind a confirmation. Our own install into open source software, not a customer - https://askverb.com/compare : Verb against building your own agent, a docs chatbot, or a person on a call. What each is best at, why most in-house builds stall after the demo, and when Verb is the wrong choice - https://askverb.com/vs/kapa : Verb compared to kapa.ai specifically. kapa answers from your documentation at real enterprise scale; Verb calls your product's own API to finish the task. Sourced and dated, updated 2026-08-16 - https://askverb.com/vs/sitegpt : Verb compared to SiteGPT. SiteGPT trains a chatbot on your website and documents and hands off to a person; Verb is an AI agent inside the signed-in product that does the user's work through your own API. Sourced and dated, updated 2026-09-23 - https://askverb.com/security : what stops it changing data it should not, and the security controls running today - https://askverb.com/docs : quickstart, four steps to a working assistant - https://askverb.com/blog : writing on building agents that act on real data - https://askverb.com/security.md : the same page as plain markdown - https://askverb.com/.well-known/security.txt : where to report a vulnerability - https://askverb.com/llms-full.txt : everything on this page plus every docs page in full, security and the full FAQ - https://app.askverb.com/sign-up : sign up ## Documentation - https://askverb.com/docs/mcp (markdown: https://askverb.com/docs/mcp.md) : Let Claude Code or Cursor create the site, import your tools and take it live. - https://askverb.com/docs/verb-md (markdown: https://askverb.com/docs/verb-md.md) : The file that decides what the assistant can do. Format, classification, and how to have your coding agent write it. - https://askverb.com/docs/adapter-tools (markdown: https://askverb.com/docs/adapter-tools.md) : For anything that is not a same-origin fetch. Supabase, Firebase, tRPC, an API on another host. - https://askverb.com/docs/identity (markdown: https://askverb.com/docs/identity.md) : The step it is easiest to miss and the one that matters most. Nothing writes until this is done. - https://askverb.com/docs/testing (markdown: https://askverb.com/docs/testing.md) : The test site, and the four things worth deliberately breaking in it. - https://askverb.com/docs/appearance (markdown: https://askverb.com/docs/appearance.md) : Name, greeting, starters, avatar, colour and placement. No redeploy. - https://askverb.com/docs/archiving (markdown: https://askverb.com/docs/archiving.md) : The kill switch for a key that ended up in a screenshot. Nothing is deleted, and restoring takes one click. - https://askverb.com/docs/troubleshooting (markdown: https://askverb.com/docs/troubleshooting.md) : Nothing showing, writes refused, or a tool failing. The four real causes. - https://askverb.com/docs/numbers (markdown: https://askverb.com/docs/numbers.md) : The Overview, weekly summaries, Analytics tab by tab, and alerts. All counted in requests. ## Writing - https://askverb.com/blog/ai-agent-take-actions-in-your-app : Tools instead of retrieval, the user's own permissions instead of a service account, and why the hard part is reporting failure honestly. - https://askverb.com/blog/safe-database-writes-for-ai-agents : Classification, confirmation with real values, and the difference between an action verified and an action merely claimed. - https://askverb.com/blog/postgres-rls-multi-tenant-ai-agent : The isolation boundary, the tables that deliberately sit outside it, and why the loud failure is the one worth engineering for. - https://askverb.com/blog/confirming-destructive-actions-ai-agent : Why a model asking 'shall I?' is not a confirmation, what the card must show, and what to do when nobody answers it. - https://askverb.com/blog/ai-agent-says-it-worked-but-it-didnt : The 200 that lied: why a successful response is not evidence, and the per-tool success shape that stops an agent claiming work it never did. - https://askverb.com/blog/writing-tool-error-messages-for-ai-agents : Why "failed" produces a guess, the four parts of an error a model can act on, and why descriptions are advisory but validation is binding. - https://askverb.com/blog/stop-ai-agent-retrying-failed-tool-calls : The 23-call spiral, why per-tool caps let a model walk the tool list, and budgeting failures by cause. - https://askverb.com/blog/identify-signed-in-user-embedded-ai-assistant : Why the assistant cannot see your session, why it needs a function and not a token, and the encoding detail that fails every signature silently. - https://askverb.com/blog/ai-chat-widget-sign-out-conversation-leak : A real shared-device leak: permissions held, the transcript did not. Keying on the subject, not the token, and why sign-out has no single choke point. - https://askverb.com/blog/prompt-injection-ai-agents-that-take-actions : Why offering a tool list is not enforcement, the switch that is the allowlist, and bounding what an obeyed injection can reach. - https://askverb.com/blog/embedded-script-blocked-content-security-policy : The failure that cannot report itself, the second directive everybody forgets, and why strict-dynamic makes your allowlist irrelevant. - https://askverb.com/blog/test-ai-agent-without-production-data : Run it for real against development data, the five cases worth deliberately breaking, and why a timer on real execution backfires. - https://askverb.com/blog/choosing-llm-model-for-in-app-ai-agent : Why prompt engineering cannot fix a routing problem, the asymmetric classifier, and the settings that silently stop applying after a model swap. - https://askverb.com/blog/best-ways-to-add-ai-assistant-to-saas-2026 : Documentation chatbot, build your own, or embedded action assistant: the one question that decides between them. - https://askverb.com/blog/ai-assistant-for-legal-practice-management-software : Why most of a lawyer's time in practice software is small updates after court, and what an assistant needs before a firm lets it make them. - https://askverb.com/blog/ai-agent-for-marketplace-admin-panel : Moderation queues, bulk changes and broadcasts: what an agent can take off a marketplace operator's hands, and the confirmations bulk actions need. - https://askverb.com/blog/ai-assistant-for-scheduling-software : Why most of what people do in a scheduling tool is procedures rather than questions, and what an assistant has to get right to take them on. - https://askverb.com/blog/nextjs-build-out-of-memory-small-server : Why a build takes down a small server when the app it produces runs fine, and the one-line cgroup fix. - https://askverb.com/blog/why-in-app-ai-agents-never-ship : The demo takes a weekend and the sign-off takes a quarter. The identity mistake that causes it, and the day our own test suite turned out to be pointing at production. - https://askverb.com/blog/add-ai-agent-to-saas-from-claude-code-cursor : The setup that used to mean a dashboard, three tabs and a key to paste is now one sentence to the coding agent you already have open. - https://askverb.com/blog/set-up-in-app-ai-agent-in-one-conversation : The whole setup as a conversation you can copy: the prompts, what your coding agent does with each one, and the two moments it stops to ask you. ## Contact vishnu.ps@askverb.com reaches the person who built it. ## Security and compliance: what stops it breaking your product Four things, and none of them is the model behaving well. Reads and writes are separate, and anything that changes data stops to show your user a confirmation card with the real values on it. The assistant can only call tools you declared in your own repository, so it has no capability you did not write down. It runs as the signed-in user, never as an administrator, so your API makes the same authorisation decision it always did. And every call is logged with what actually ran and what came back. Everything below names a mechanism rather than a promise, so you can check it against the product rather than take our word for it. ## The controls ### Reads and writes are separate, and only reads run unattended Every tool you declare is classified read, write or destructive. Reads execute. Anything that changes data stops and shows the person a card naming the actual action and the actual values, “cancel order 1041” rather than “confirm action?”, and nothing happens until they click it. A declined card is not retried. ### It can only do what you wrote down Capabilities come from a verb.md file in your repository, reviewed in a diff like any other code. There is no general internet access, no database connection and no way for the assistant to reach past that list. A tool you never described does not exist as far as it is concerned, however the question is phrased. ### It acts as the user, never as an administrator Tools run in that user's browser against your API with the credential they already hold. Verb is never given a wider key of its own, so your existing authorisation is the authorisation. An anonymous visitor is read-only by default and every write is refused until your backend vouches for who is asking. ### It only answers on domains you have proven you own A site key is public: it ships in your page source. So the check that matters is the origin one. It answers only on verified domains, and a request from anywhere else is refused before a model is called, which is what makes a copied script tag worthless. ### Tool results are data, not instructions Everything returned by your API is fenced and declared untrusted to the model, so content that reads like a command is reported rather than obeyed. And because a write still stops at a confirmation card a human has to click, a successful injection still cannot finish a destructive action by itself. ### Every call is on the record Who asked, which tool ran, the arguments it ran with, what came back, and whether the result was verified rather than merely claimed. That log is yours, in your dashboard, alongside the conversations themselves. An action reported as done that did not happen is the one outcome the whole design exists to prevent. ### A test site that stays on your computer Every site has a test site whose key only answers on localhost. It runs your actions for real against your development app, so you watch the assistant use your actual API before your users can reach it, and anything that changes data still asks first. ### Tenants are separated in the database, not just in queries Conversation data sits behind Postgres row-level security, so a query that forgets its tenant returns nothing rather than returning somebody else's rows. The failure mode is deliberately the visible one. ## In place today Every control below is running in production today. | Control | What it means | What is in place | |---|---|---| | Encryption in transit | Nobody sitting between your user, your product and Verb can read what goes past. | TLS on every hostname, with plain HTTP redirected rather than served. The database listens only on the loopback interface, so queries never cross a network at all. | | Browser security headers | The instructions a browser enforces on our behalf: forbid framing, refuse to guess at content types, never fall back to plain HTTP, and refuse to load or talk to any origin not on a fixed list. | A full set on all three hostnames, including a content security policy, HSTS for two years across every subdomain, and a permissions policy that switches off camera, microphone, geolocation and payment at the browser. Run any public scanner against askverb.com and app.askverb.com and you can grade this yourself in ten seconds, which is the point of it. | | Account security | How somebody proves they are allowed into the dashboard, and what a breach elsewhere would cost you here. | There are no passwords at all, which is the strong version rather than the missing one: no hash to leak, no credential reused from a site that was breached, and no reset flow to attack. Sign-in is Google, GitHub, or a six-digit code that expires in ten minutes and dies after three wrong attempts. Linking a second provider requires that provider to assert the address is verified, so signing up elsewhere with your email does not reach your account. | | Tenant isolation | One customer's data cannot come back in another customer's response, even through a bug. | Postgres row-level security, forced, which applies to the table owner too rather than only to unprivileged roles. A query that forgets which site it is for returns nothing instead of returning somebody else's rows, so the failure mode is deliberately the visible one. The orchestrator also refuses to start if that is ever switched off, because tenant isolation silently turning itself off is the one fault nobody would notice in time. | | Audit log | A record complete enough to answer “what did this thing do to my account” months after it happened. | Who asked, which tool ran, the arguments it ran with, what came back, whether the result was verified rather than merely claimed, and if an action did not happen, which of the reasons it was. It is in your dashboard, and it is yours. | | Retention and deletion | Data is kept until you ask for it to be deleted, and then it is actually deleted, rather than hidden from a view. | Conversations are kept, so the record of what happened stays answerable, and you can export them as CSV or JSON at any time. To delete one user's data, send us the user ID your backend puts in their token; to delete everything, close the account. Deleted rows leave the live database at once, and the nightly backups that still hold them are gone within 14 days. | | Backups, and restores that are tested | You can get the data back after a mistake or a failure, and somebody has proved it rather than assumed it. | A dump every night, then a separate job that restores it into a scratch database, counts the rows against production and fails loudly if they disagree. That runs weekly, because a backup nobody has restored is a belief. | | Vulnerability disclosure | A published route for a researcher to report a hole to you instead of to everybody at once. | A security.txt at /.well-known/security.txt, pointing at the person who wrote the code, with a reply within 72 hours. | | Private user identity | Your users cannot be tracked from one product to another through Verb. | Identity arrives as a token your backend mints, and is hashed with a salt unique to your site and never stored. So the same person using two Verb-powered products cannot be linked between them, by anyone, including Verb. To have one user's data deleted, send us the user ID your backend puts in their token and we delete it. | ## Common questions ### What stops the AI from deleting my production data? Every tool is classified as read, write or destructive, and only reads run on their own. Anything that changes data stops and shows the user a confirmation card naming the exact action and its real arguments, and nothing runs until they click it. The assistant also cannot invent a capability: it can only call tools you have written into your own verb.md file, so an action you never described does not exist for it. ### Can the AI assistant see data the user is not allowed to see? No. Tools run in the user's own browser against your own API, carrying whatever credential that user already has. Verb never holds a second, wider key, so your API makes exactly the same authorisation decision it makes for every other request from that person. If they cannot see it without Verb, they cannot see it with Verb. ### What happens if someone copies our script tag onto another site? Nothing works. A site key only answers on domains you have verified, and requests from any other origin are refused before a model is ever called. The key is public by construction, because it ships in your page source, which is precisely why the domain check is what actually holds. ### Can a prompt injection make the assistant run a destructive tool? Tool results are fenced and declared to the model as untrusted data rather than instructions, so text arriving from your API that reads like a command is reported rather than followed. The stronger guarantee is structural: a write still stops at the confirmation card, which a human reads and clicks, so a successful injection still cannot complete a destructive action on its own. ### Do you train models on our data? No. Your data is not used to train anything, by us or by the model provider on our behalf. Conversations belong to your users and are visible to you in your dashboard, and Verb's internal staff view deliberately does not show them. ### Can we test it before our users see it? Every site comes with a test site alongside it: its own key, its own log, and a Test badge on the widget. Its key only answers on localhost, so it runs inside your app on your own computer, against your development data, and never reaches your users. Anything that changes data still asks for a yes first. --- Source: https://askverb.com/security ## Frequently asked questions These are the questions answered on askverb.com, verbatim. ### What does Verb actually do? It embeds inside your SaaS, reads the real data in the product, and finishes the task for the logged-in user, in one message, inside the product they are already in. ### Why not just build this ourselves? You can, and the first working version is a weekend now. The part that takes months is everything after the model call: making each action run as the signed-in user rather than an admin key, showing a confirmation with the real values in it, keeping an audit trail that names a person instead of a service account, and a sandbox to test in. That is the work that decides whether it ever leaves your staging environment, and it is the part you are buying. ### Where does the agent live? Inside your product, in the browser, the place your users already are. Not a separate tab, not another login to remember, and not inside somebody else's chat window where your users have to go looking for it. ### What kind of requests is it best at? The multi-step ones a menu cannot be: look something up, reason about an exception, then act. Creation, assignment and access management are the sweet spot, because those are the requests that span several screens and need a judgement call in the middle. ### Does it answer from stale or cached data? No. It reads your product's live state at the moment of the question, not a cached answer from last month and not a training copy of your docs. ### What do I have to build? Nothing to install alongside your product, no rewrite, no migration, no new service to run. If you remove Verb tomorrow, your product is exactly what it was. ### How long until it answers real questions? About five minutes from signing in for read-only questions. Actions that change things take longer, because they run against your backend, and we would rather you wired that up deliberately than quickly. ### Will it slow my product down? It loads as one small script with no framework inside it, styled in isolation from your product’s own styles both ways. It opens as a side panel or a popup, you choose whether the side panel pushes your page aside or covers it, it goes full-screen on a phone, and it never opens itself. ### Can we make it look like part of our product? Yes. Its name, greeting, opening suggestions and colour are set from your dashboard, and contrast is worked out for whatever colour you pick so the text stays readable. ### What if someone asks for something it can’t do? It only acts through capabilities you have described. Anything outside them gets an honest “that isn’t something I can do”, not a guess dressed up as an answer. ### Do my users need an account before they can ask anything? No. Anyone on your site can ask read-only questions from the moment the assistant is there. Signed-in users get whatever their sign-in already permits them inside your product. ### How will my users know what they can ask it? When it opens it suggests things it can genuinely do, drawn from the capabilities you turned on. You edit those suggestions from your dashboard, because a feature nobody knows about may as well not exist. ### What if it doesn’t show up on our site? You would know, not wonder. If something on your side stops it loading, usually a strict content-security header, the failure is reported in your dashboard rather than staying silent. ### What does it cost? Building is free: the Dev plan is your site and its test site, working on your own computer, for good. When your users can reach it, Starter is $39 a month for 3,000 requests and 1 site, Pro is $99 a month for 8,000 requests and 3 sites, Max is $249 a month for 20,000 requests and 10 sites. The actions it takes are never counted. Every number is on the pricing page. ### Do I need a card to sign up? No. No card, no call, no invite code. You are in the dashboard in under a minute, and building on Dev stays free. A card comes in only when you pick a plan to go live. ### What happens when I reach my requests? It stops answering until the month starts again, and your visitors see a calm "temporarily unavailable", never an error. You can add more requests from your dashboard, and you are never billed for anything you did not choose. ### Do I have to talk to sales to start? No. There is nobody to wait for and nothing to request. Sign up, point Verb at your site, and it starts answering. ### Is it priced per seat? No. Plans are priced on requests and sites. Your team is not what costs anything, so it is not what you pay for. ### Can I cover more than one product? Yes. Each product is a site with its own tools and script tag. Pro includes three and Max ten, and any plan can add one for 15 a month. ### Is there a demo I can try? The assistant on this page is the real product. Open it, top right, and ask it anything about Verb. It answers with the same discipline it would bring to yours. ### What is the sandbox, exactly? Every site comes with a test site alongside it: its own key, its own log, and a Test badge on the widget. Its key only answers on localhost, so it runs inside your app on your own computer, against your development data, and never reaches your users. Anything that changes data still asks for a yes first. ### Does testing count against my limits? No. Everything you and your team try in the sandbox is free and never counted. It is there to be leaned on. ### I signed up during the beta. What happens now? Your live site stays free until 30 October 2026, and you will hear it from us directly before that date. Choose a plan before then and it keeps answering without a break. Everyone who was here during the beta gets a founding offer: just ask. ### Who do I talk to when something isn’t working? The people who built it. There is no ticket queue and nobody reading from a script. The contact on this page reaches us directly. ### Why is building free? Because you should see it work on your own product, with your own data, before paying anything. The test site only answers on your own computer, so it cannot carry a real product, and nobody is on a clock while they wire it up. ### Does Verb see my users’ passwords or sessions? No. Verb has no way to sign in as anyone: no passwords, no sessions, no cookies of ours inside your product. It works through the access your product already grants. ### Does it touch money? No checkout, no payments, no card data. Not a setting you switch off. It is simply not something Verb does, and it never will be. ### What stops it doing something the user didn’t ask for? Anything that edits or deletes shows the user exactly what is about to happen, in plain language, with the real details. They confirm, or it does not happen. ### What if it can’t do the thing? It says so. Your user gets an honest “that didn’t work” instead of a confident lie, and you hear about it before they do. ### Can we audit what it did? Every action is on the record: what was done, who asked for it, when, and whether it actually worked. Crucially the record names the person, not a service account, so your log never reduces to “the AI agent did this”, which is the line that makes an audit useless and stops an in-house build being signed off. ### Who is accountable for what it does? The person who asked, and the record says so by name. Verb never holds a credential of yours, it cannot do anything the person asking could not already do themselves, and anything that writes stops for a confirmation showing the real values. There is a security page on this site written for whoever will actually ask these questions. ### We already run an MCP server. Does that matter? It helps: your existing tools wire in as they are. It is entirely optional, though: if that means nothing to you, it changes nothing. ### Who is behind Verb? The same team runs TradePilot, a trading copilot with 170+ tools live in production, software people rely on for decisions about their own money. Verb is that discipline, packaged for your product instead of ours. ### Can it do more than the person asking is allowed to? No. It acts as the logged-in user, through the access your product already grants them, and your backend remains the authority. If a user could not do the thing themselves, neither can the assistant on their behalf. ### Who can see my users’ conversations? You and your team, in your dashboard. Conversations are kept until you ask us to delete them, and you can export them whenever you like. Each user appears as a pseudonymous reference rather than a name. The model provider processes a conversation to produce the reply. Nobody else sees it. ### Could someone copy my site and run the assistant there too? A domain serves no answers until you have proven you own it. Requests coming from any unverified origin are refused outright, so a copied page gets nothing. ### What does a confirmation actually look like? The real details, not a generic “are you sure”. If it is about to cancel an order, you see which order, whose, and what changes. Confirm, and it happens; close it, and nothing does. ### How long does its access to our system last? Minutes. Any credential is issued by your own backend, short-lived, treated by Verb as opaque, and never stored beyond the request it came with. ### Can we decide how much it is allowed to offer? From your dashboard: read-only questions or everything you have described, one switch apart, with per-tool control in between. Even at full access, anything that writes still asks first. ## Documentation, in full ### Verb MCP server: set up Verb from Claude Code or Cursor https://askverb.com/docs/mcp Connect Verb to Claude Code, Cursor, VS Code, Windsurf, the Claude app or any other MCP client, and it can set Verb up for you: create the site, import your tools, give you the script tag and take it live, without you opening the dashboard. You sign in once, in the browser, with the same email code as the dashboard. There is no API key to copy. #### Connect your editor Claude Code: Paste into Claude Code: ```text Install the Verb MCP server for me: run claude mcp add --transport http --scope user verb https://app.askverb.com/api/mcp. It signs in with OAuth in the browser, so there is no token to store; keep every other setting as it is, then check that "verb" is listed. STRICTLY ASK ME TO REFRESH, OR CLOSE AND REOPEN THE SESSION, FOR THE MCP TO APPLY. Do not claim Verb's tools are available in the current session, even if the server shows as connected. Only ask me to step in earlier if a permission you need is blocked. ``` Codex: Paste into Codex: ```text Install the Verb MCP server for me: run codex mcp add verb --url https://app.askverb.com/api/mcp then codex mcp login verb. It signs in with OAuth in the browser, so there is no token to store; keep every other setting as it is, then check that "verb" is listed. STRICTLY ASK ME TO REFRESH, OR CLOSE AND REOPEN THE SESSION, FOR THE MCP TO APPLY. Do not claim Verb's tools are available in the current session, even if the server shows as connected. Only ask me to step in earlier if a permission you need is blocked. ``` Cursor: Paste into Cursor: ```text Install the Verb MCP server for me: add {"mcpServers":{"verb":{"url":"https://app.askverb.com/api/mcp"}}} to ~/.cursor/mcp.json. It signs in with OAuth in the browser, so there is no token to store; keep every other setting as it is, then check that "verb" is listed. STRICTLY ASK ME TO REFRESH, OR CLOSE AND REOPEN THE SESSION, FOR THE MCP TO APPLY. Do not claim Verb's tools are available in the current session, even if the server shows as connected. Only ask me to step in earlier if a permission you need is blocked. ``` VS Code: Paste into VS Code: ```text Install the Verb MCP server for me: add {"servers":{"verb":{"type":"http","url":"https://app.askverb.com/api/mcp"}}} to .vscode/mcp.json. It signs in with OAuth in the browser, so there is no token to store; keep every other setting as it is, then check that "verb" is listed. STRICTLY ASK ME TO REFRESH, OR CLOSE AND REOPEN THE SESSION, FOR THE MCP TO APPLY. Do not claim Verb's tools are available in the current session, even if the server shows as connected. Only ask me to step in earlier if a permission you need is blocked. ``` Windsurf: Paste into Windsurf: ```text Install the Verb MCP server for me: add {"mcpServers":{"verb":{"serverUrl":"https://app.askverb.com/api/mcp"}}} to ~/.codeium/windsurf/mcp_config.json. It signs in with OAuth in the browser, so there is no token to store; keep every other setting as it is, then check that "verb" is listed. STRICTLY ASK ME TO REFRESH, OR CLOSE AND REOPEN THE SESSION, FOR THE MCP TO APPLY. Do not claim Verb's tools are available in the current session, even if the server shows as connected. Only ask me to step in earlier if a permission you need is blocked. ``` Claude app: Settings, Connectors, Add custom connector: ```text https://app.askverb.com/api/mcp ``` ChatGPT: Settings, Apps and Connectors, Advanced: turn on Developer mode, then Create: ```text https://app.askverb.com/api/mcp ``` Any MCP client: Paste into Any MCP client: ```text Install the Verb MCP server for me: add a remote server named "verb" at https://app.askverb.com/api/mcp, over Streamable HTTP, at user level so it works in every project. It signs in with OAuth in the browser, so there is no token to store; keep every other setting as it is, then check that "verb" is listed. STRICTLY ASK ME TO REFRESH, OR CLOSE AND REOPEN THE SESSION, FOR THE MCP TO APPLY. Do not claim Verb's tools are available in the current session, even if the server shows as connected. Only ask me to step in earlier if a permission you need is blocked. ``` Then **close and reopen your agent's session**, or reload the editor. Clients load MCP servers when a session starts, so Verb is not there in the session you added it from. The first time your agent uses it, a browser tab opens. Sign in with your email code, check the editor's name, and choose **Allow**. That is the whole setup. > `--scope user` makes Verb available in every project, which is what you want for setup. Added to one project only, it can go missing on Windows when the editor opens the folder with a different drive-letter case (`c:` instead of `C:`). #### What your agent can do with it - **Create a site**, with its test site alongside it. - **Check a verb.md** with the same safety review the dashboard runs, then **import the tools**, test site first. - **Switch tools on or off.** Anything that changes data still asks your user for a yes every time. - **Give you the script tag** for the test site and the live site. - **Add, verify and remove your domains**, and **take the site live** on one. - **Turn real actions on** for the test site, so actions run for real on localhost. - **Tell you where the site stands**: the same checklist the dashboard shows. Try: "add Verb to this app". With the [agent skill](/docs/verb-md) installed, your agent writes the verb.md from your code and uses the MCP server to ship it. #### What it asks you first, and what it cannot do Taking a site live on a domain and turning on real actions each happen in two steps. The first call changes nothing and says exactly what will happen; your agent shows you that and only goes ahead once you say yes. Your editor also asks before every tool call, unless you have told it not to. > It cannot read your users' conversations, delete or archive a site, or do anything the dashboard would not let you do. Every call goes through the same checks as the dashboard. #### Disconnect Remove the server from your editor's MCP settings. To connect again, add it back and sign in. ### Writing verb.md: telling the assistant what it can do https://askverb.com/docs/verb-md `verb.md` is a plain markdown file at your repository root with one section per action the assistant may take. It is the complete list: a tool that is not in this file does not exist as far as the assistant is concerned, however the question is phrased. #### Have your coding agent write it This is the fastest path by a distance, and it is not a shortcut. Your dashboard's install step has a copy-paste prompt. Hand it to whichever coding agent you already use in that repository. ```bash npx --yes @askverb/skill@latest install --dir /.claude/skills # then, in that repo: "add Verb to this app" ``` It installs a skill that teaches the format, then reads your actual routes rather than guessing at endpoints. An agent that can see your router writes better tool descriptions than you will from memory, because it uses the real parameter names. #### What a tool looks like ```markdown ## cancel_order Cancels an order that has not shipped. Call this when the user asks to cancel, stop or undo an order they placed. - classification: destructive - run: browser - confirm: Cancel order {order_id} ### Arguments - `order_id` (string, required) The order to cancel, for example "1041" ```js return fetch(`/api/orders/${args.order_id}/cancel`, { method: "POST" }) .then((r) => r.json()); ``` ``` The description is the part worth spending time on. It is what the model reads when deciding whether this is the right tool, so say *when to call it* rather than what it does internally. "Call this when the user asks to cancel, stop or undo an order" is worth more than a paragraph about your order state machine. Arguments are deliberately flat and scalar: `string`, `number`, `integer`, `boolean`, `file`. There are no nested objects or arrays, and that is a choice rather than a gap. A model fills flat arguments correctly far more often, and your adapter is a better place to build the shape your API actually wants. Type them as precisely as your app does, because they become a form. When a value is missing, or the model had no source for it, the confirmation card asks the user for it, and the inputs are built from these types. A seat count typed `integer, min 1, max 8` is a number box that refuses letters; typed `string` it accepts "two". Everything goes in the parentheses: ```markdown - `seats` (integer, required, min 1, max 8) How many seats - `contact_email` (string, email, required) Where the tickets go - `seat_type` (string, required, one of: standard, premium, accessible) Which section - `booking_ref` (string, required, matches ^[A-Z]{2}\d{4}$) Like AB1234 - `id_photo` (file, image, optional, max 5mb) Photo ID ``` Formats are `email`, `url`, `date` and `tel`. `one of:` is radio buttons up to four options and a select from five. A file takes `image`, `pdf`, `csv` or a mime type, and a size in `kb`, `mb` or `gb`: that size is the only limit, so always give one. The file goes from the browser straight to your endpoint, never through Verb. Optional arguments are not asked for, except files, so mark required whatever the action cannot run without. So do not mirror your endpoint's input schema. Take `status` as a string and let the adapter nest it into `filters: { statuses: [...] }`. Take ISO strings and let the adapter construct the dates. Take one guest per call and let the adapter build the array. If a capability genuinely cannot be expressed this way, leave it out: the assistant declines cleanly, which is much better than it improvising a wrong shape and your API accepting it. #### Classification is the safety model Every tool is one of three, and this is what decides how it behaves at runtime: - **read** runs on its own. Nothing is confirmed, because nothing changes. - **write** stops and shows the user a confirmation card with the real arguments before anything runs. - **destructive** is the same gate, held to a higher bar in the safety review, and worth leaving switched off until somebody has asked for it twice. The `confirm:` line is the wording on that card. Write it with the argument in it, *Cancel order {order_id}*, because a card that says "confirm this action?" teaches people to click through it and the entire value of the gate is that somebody read it. #### Where a tool runs `run: browser` is a same-origin fetch. Verb makes it, and there is nothing to wire up. `run: adapter` is anything else: Supabase, Firebase, tRPC, an API on another host. It goes through your own client, because Verb never holds your credentials. [That is its own page](/docs/adapter-tools) #### Describe them well before you worry about how many Start with one real workflow, then cover the rest of what your users actually ask for. What costs you accuracy is a vague description, not a long list: every entry that overlaps another is a chance for the model to pick a near-miss, and a near-miss is worse than a refusal because it looks like an answer. A 28 tool integration was tested against near-miss pairs, destructive-adjacent phrasing and multi-step chains, and picked correctly throughout. So do not stop early to keep the count down. Cut the tools that mirror your route table rather than the ones somebody would ask for by name. Pick the workflow your users ask for most, which for most products is creation, assignment and access management, and add more once you can see people asking. > A destructive action that touches a Supabase or Firebase table directly, rather than going through an RPC, is refused outright by the safety review. There is no override. Put it behind a function where your own rules can run. #### Importing it Paste the file into the dashboard. Every tool is parsed, reviewed and imported as a draft or blocked, and read-only tools switch themselves on. Nothing that writes activates without you choosing it. Re-import any time. Your own decisions about what is switched on survive the re-import; a tool the review has blocked stays blocked. ### Adapter tools: Supabase, Firebase, tRPC and other clients https://askverb.com/docs/adapter-tools A tool marked `run: browser` is a same-origin fetch and Verb makes it for you. A tool marked `run: adapter` runs through your own authenticated client instead, because Verb never holds your credentials and never will. You give it one function and it calls that. #### One function, one switch statement Call `configure` once, wherever your app boots. Verb reads the adapter at the moment a tool is called rather than at start-up, so running late is harmless. Running EARLY is not: the script tag is `defer`, so code below it in the document still runs before it, and `window.Verb` is undefined at that point. Wait for it. "Wherever your app boots" means the same file that already carries the Verb script tag, the one every route in your app renders through, right after that tag. Concretely, by stack: - Next.js App Router: `app/layout.tsx` - Next.js Pages Router: `pages/_document.tsx` - Vite or Create React App: `index.html` - Vue or Nuxt: your root component, or `nuxt.config.ts`'s `app.head.script` - Anything else: the shared template or layout every route already goes through wherever your app boots, once: ```javascript function setUpVerb() { window.Verb.configure({ execute: async (tool, args) => { switch (tool) { case "cancel_order": return await supabase.rpc("cancel_order", { p_order_id: args.order_id }); // one case per adapter tool, from the code block already in verb.md default: throw new Error(`Unknown tool: ${tool}`); } }, }); } // Both: the event covers a cold load, the check covers a warm cache where // the script was already there before this ran. if (window.Verb) setUpVerb(); else window.addEventListener("verb:ready", setUpVerb); ``` Pass `execute` and `getSessionToken` in the same call if you have both, or in two calls: `configure` merges what you give it, so a later call adding identity will not discard the adapter you set here. One `case` per adapter tool, matching the tool names in your `verb.md`. The code block in each tool's section is what belongs in its case. Keep it a literal switch even when it gets long, and expect it to get long: thirty tools is a function of a couple of hundred lines. That will trip a maximum function length rule in a strict lint config. Suppress the rule for this function rather than splitting it up or replacing it with a lookup. The switch *is* your allowlist, and anything that resolves a tool name dynamically lets any string the model produces reach your authenticated client, which is the one thing this shape exists to prevent. #### Throw on failure. Do not return an error object This is the one rule worth getting right, and it is easy to get wrong because both look like they work. A thrown error is recorded as a failed call and the assistant says so. A returned `{ error: ... }` is recorded as a *successful* call that happened to come back with unusual data, and the model, doing its best with what it was handed, will often summarise the turn as done. > That is the failure the whole product exists to prevent, arriving through a door the rest of the design does not cover. If your client returns errors rather than throwing, as the Supabase client does, check the error field yourself and throw. ```javascript case "cancel_order": { const { data, error } = await supabase.rpc("cancel_order", { p_order_id: args.order_id, }); if (error) throw new Error(error.message); // not: return { error } return data; } ``` #### The default case matters too Throw on an unknown tool name rather than falling through silently. The name arrives from a model, and a switch that quietly returns `undefined` for something it does not recognise reports a successful call that did nothing at all. #### If you skip this Nothing breaks quietly. Adapter tools still import and still activate, and every call fails with a clear "needs an adapter" error naming the tool. You will know. #### A worked example, in public Verb's own dashboard is an adapter site: the assistant at **app.askverb.com** runs four tools through this exact mechanism against our own API. Three rules from building it are worth stealing: - **The session decides the account, never an argument.** There is deliberately no organisation id parameter, because a tool that takes one can be talked into naming a different one. - **An unknown action is a 404, not a pass-through.** Forwarding a model-supplied name as a path fragment hands the model your whole API. - **Throw, do not return.** The rule above, learned here first. ### Connecting your signed-in users so the assistant can write https://askverb.com/docs/identity Being logged into your product proves nothing to Verb. It cannot see your session cookie, so every visitor starts anonymous and read-only, including one who is signed in. Nothing that writes will work until your own backend mints a short-lived signed token and hands it to the widget. #### Why it works this way > This is the step it is easiest to miss and the one that matters most. Skip it and reads work perfectly, while every write is refused with an honest "you need to sign in" to somebody who already is. If that is what you are seeing, you are on the right page. The alternative would be Verb holding a credential of its own that can act on behalf of your users, which is exactly the thing you should not want. Instead your backend, which already knows who is logged in, says so in a token only you can sign, and Verb takes your word for it and nothing more. The upshot is that the assistant can never do more than the person talking to it. Your API makes the same authorisation decision it already makes for every other request. #### 1. Mint a token on your server Sign with the key from **Sites, your site, Connect your users**. It is an HMAC-SHA256 over a small JSON blob, which every backend language does in its standard library. Nothing here is framework-specific, and your dashboard has the same snippet for Next.js, Express, Fastify, Rails, Django and Laravel. your server, wherever you already know who is logged in: ```javascript // sign with the key from Sites, your site, Connect your users const claims = { sub: user.id, exp: Math.floor(Date.now() / 1000) + 900 }; const body = base64url(JSON.stringify(claims)); const sig = hmacSha256(body, VERB_SIGNING_KEY); return Response.json({ token: `${body}.${sig}` }); ``` `sub` is your own user id, whatever that is in your system. `exp` is a unix timestamp: keep it short, fifteen minutes is plenty, because the widget asks again when it needs to. > Both halves are **base64url without padding**, so `-` and `_` rather than `+` and `/`, and no trailing `=`. Standard base64 is the one mistake that fails every token while looking entirely correct in a log. #### 2. Hand it to the widget anywhere after the script tag, on load: ```javascript const getSessionToken = () => fetch("/api/verb-token", { credentials: "same-origin", cache: "no-store" }) .then(r => r.json()) .then(({ token }) => token); // Sign in now, and let the widget ask again when the token gets old. window.Verb.configure({ getSessionToken }); getSessionToken().then(token => token && window.Verb.identify(token)); ``` **Give it `getSessionToken`, not just one token.** Tokens are short-lived on purpose, because one is a bearer credential for one person. A page left open outlives its token, and without a way to ask for another the visitor's next question is refused. With it, the widget fetches a new one before the old one expires and nobody notices. If your API is on a different origin from the page, that fetch needs the full URL and `credentials: "include"`. A relative path there quietly returns your own HTML, and the widget stays anonymous with nothing in the console to say why. Return `null` when nobody is signed in. The widget drops the old token and carries on anonymously, which is a working state, rather than sending the previous person's identity on a shared machine. `identify` still works on its own and is worth calling on load, so the panel is signed in from the first frame rather than from the first question. #### 3. Call reset() when they sign out wherever your app signs somebody out: ```javascript window.Verb.reset(); ``` This forgets the conversation, the saved transcript and the cached token, so the next person at that machine starts from nothing. On an admin panel where a manager and an assistant share a desk, that matters more than it sounds: the conversation is on screen, and it can contain customer names and figures somebody read out of your product. **You get most of this for free.** When `identify()` or `getSessionToken` returns a token for a *different* person, the widget clears the conversation itself. It compares the token's `sub`, so a refreshed token for the same person is not a change and your conversation survives it. Signing in after browsing anonymously is not a change either, because that is the same person continuing. `reset()` is for the case with no next user to notice: they sign out, and nobody signs in. #### Checking it worked Ask the assistant to do something that writes. If it does it, you are done. If it says you need to sign in, one of three things is true: - `identify()` was never called, or ran before the script tag had loaded. - The token expired, because `exp` is in the past or in milliseconds rather than seconds. - The signature does not match, usually a different key than the one on that site. Your dashboard's Logs tab shows whether any conversation on the site has ever carried a signed-in user, which separates "wired up, nobody has used it yet" from "never wired up at all". Those look identical in config and completely different in the log. #### If every page is behind your login Some products, an admin panel especially, have no anonymous visitors at all. Turn on **Hide the assistant until someone signs in** on the site, and the launcher stays hidden until a token arrives rather than offering an assistant that will refuse everything. It offers less and grants nothing: the gates behind it are unchanged, and your API still decides what that person may do. ### Testing an AI assistant without touching real data https://askverb.com/docs/testing Every site comes with a test site: its own key, the same tools, the same confirmation cards, the same logs, and a Test badge on the widget. Its key only answers on localhost, so you run it inside your own app on your own computer, and actions really run against your development data. Anything that changes data still asks for a yes first. Point your local app at a development API, not production: if it talks to production, that is where actions run. #### Four things worth deliberately trying > Nothing you try in the sandbox counts against your monthly requests. It is there to be leaned on, so lean on it. Not a smoke test. The point is to watch the guards work, because you are about to put this in front of your own users and the only useful evidence is having seen it refuse. - **Ask for a destructive action.** Does the confirmation card show the real arguments, the actual order number, or a generic "confirm this action?" If it is generic, fix the confirm line in your verb.md before this goes anywhere near a real user. - **Decline one.** Nothing should run, and the assistant should ask what you would prefer instead rather than rephrasing and offering again. - **Ask for something you never gave it a tool for.** It should say plainly that it cannot, and not invent a plausible answer. This is the behaviour that decides whether your users trust it. - **Break something on purpose.** Point a tool at a URL that 500s and watch what the assistant says. It should report the failure, not summarise the turn as done. #### Then check the log Your dashboard shows every tool call with its arguments, its outcome, and whether the result was *verified* rather than merely claimed. It also shows the conversations themselves, which is where you find the questions your assistant has no tool for yet. That second one is usually the more useful list. A capability nobody built generates no rows anywhere else. #### Before you switch to the live key - Domain shows verified, not pending. - Adapter tools, if any, have their switch statement wired and deployed. - Identity is wired if any tool writes, and you have tried one signed in for real. - Destructive tools were tried on the twin first, not live. After that, one number is worth watching more than any other: the verified-action rate. Actions confirmed to have actually happened, not merely attempted. It is the one this product is built to earn. ### Customising how the assistant looks https://askverb.com/docs/appearance The assistant's name, greeting, starter questions, avatar, colour, position and shape are all set from the dashboard, with no code and no redeploy. The script tag can set most of them too, for anyone who would rather keep it in the repository. #### From the dashboard - **Name and greeting.** What it calls itself in the header, and the first thing anybody sees. The name reaches the model too, so it can answer “what are you called?” - **Starter questions.** The cards shown before anyone has typed. A user who does not know what to ask asks nothing, so these are worth more than they look. A starter can also carry a written answer, in which case pressing it is not counted as a request and replies with exactly your words. - **What your product is.** Your own description, in the system prompt. This is what the assistant answers from when somebody asks what you do, who it is for, or what it costs. Leave it empty and it will say it does not know rather than guess. - **Avatar, brand colour, launcher position, panel or centred dialog.** - **“Powered by Verb”.** On Pro and above you can remove the line at the foot of the panel. On other plans it stays. #### From the script tag Anything set on the dashboard wins, so these are a starting point rather than a fight. The advantage is that they apply on first paint, with no wait for config to load. ```html ``` #### Contrast is worked out for you Whatever brand colour you pick, the text drawn on top of it is computed rather than assumed, so a dark navy gets light text and a pale yellow gets dark. Picking a colour cannot leave you with a button nobody can read. #### Placing it yourself A corner is the default, not the only choice. There are several other ways to put the assistant on your page, and you can mix them: a button in your navbar and a "Track my order" link on the orders page can both open the same assistant. **Your own button.** Add `data-verb-open` to any element and a click opens the assistant. No JavaScript needed. Give the attribute a question and that question is typed in, ready to send. Hide our floating button with `data-verb-hide-launcher="true"` on the script tag if yours replaces it. ```html Track my order ``` **Our button, inside your layout.** Point `data-verb-launcher` at an element of yours and the button is drawn inside it instead of floating, taking on your container's layout. Your colours and glow still apply. It works in React, Vue and other single-page apps: the button moves in as soon as the element appears, and follows it when a route change redraws it. Until then it floats in its corner, and if nothing matches after 10 seconds the browser console says so. ```html ``` **A corner, nudged.** If a cookie banner or another chat bubble already sits in your corner, keep the corner and move the button away from the edge, in pixels. It applies on phones too. ```html ``` **From your own code.** Open, ask or close it from anywhere, for example after somebody has been stuck on a page for a while. Wait for the widget first: the script is deferred, so check `window.Verb` and listen for `verb:ready`. ```javascript function whenVerb(fn) { if (window.Verb) fn(); else window.addEventListener("verb:ready", fn, { once: true }); } whenVerb(() => window.Verb.open()); // open it whenVerb(() => window.Verb.open({ prompt: "Where is my order?" })); // open, question typed in whenVerb(() => window.Verb.ask("Where is my order?")); // open and send whenVerb(() => window.Verb.close()); ``` **Side panel or popup.** `data-verb-mode="panel"` opens a panel down the side that moves your page over to make room; `data-verb-mode="dialog"` opens a centred popup over it. Also on the dashboard, under Theme. #### Your own chat interface: not available yet The widget is the only supported way to put Verb on a page. There is no supported API for drawing the conversation in an interface of your own yet. The connection the widget uses is internal and changes without notice, so anything built on it can stop working with any release. If you need your own interface, tell us at vishnu.ps@askverb.com: it is how we decide when to build a supported one. Until then, the button, the colours, the placement and opening it from your own controls are all yours to change. > Wherever Verb's answers appear, on plans below Pro they carry “Powered by Verb”, linked to askverb.com and visible beside the answers. That is in the terms. #### Style the button with your own CSS Our button is exposed as a CSS part called `launcher`, so your own stylesheet can change its shape, size, padding, shadow, font and hover with `::part(launcher)`. Put the host in front: `[data-verb-root]` when it floats, `[data-verb-launcher-root]` when you placed it with `data-verb-launcher`. Writing both covers either. ```css [data-verb-root]::part(launcher), [data-verb-launcher-root]::part(launcher) { border-radius: 10px; padding: 10px 16px; box-shadow: 0 4px 14px rgb(0 0 0 / .18); } ``` > Set the colour with `data-verb-color` or the dashboard rather than `background`: the text on the button is worked out from the brand colour, so a colour set in CSS can leave it unreadable. Only the button is open to your CSS. The panel is not, so nothing in your stylesheet can break the assistant. **Making room for the panel.** While it is open, `` carries `data-verb-panel-open="true"` and the variable `--verb-panel-width`, for anything of yours that should move aside too, like a fixed header. ```css html[data-verb-panel-open="true"] .site-header { right: var(--verb-panel-width); } ``` ### Archiving a site: switching a widget key off without losing anything https://askverb.com/docs/archiving Your widget key is public by design. It sits in a script tag on your own pages, and anyone who views source can read it. That is fine while it is pinned to your verified domains and can be switched off. Archiving is how you switch it off. #### What archiving actually stops > Archiving deletes nothing. Your tools, domains, conversations and the whole tool-call history stay exactly as they are, and restoring puts the site back in one click. Everything. An archived site is not hidden, it is inert, and the key behaves as though it never existed: - **The widget does not load.** The boot call answers 404, and the launcher is drawn only after that call succeeds, so nothing appears on the page at all. - **Nothing is answered.** Every question is refused before it reaches the model, so an archived site cannot use up your requests or reach any of your tools. - **Nothing is recorded.** No conversations, no ratings, no audit rows. - **The sandbox twin goes with it**, so the test key stops working too. #### The reason most people will want it Demos and recordings. The moment your key is on screen in a video, a screenshot or a conference talk, it is readable by everybody who ever watches, and unlike a leaked secret you cannot rotate it out of a file somebody else is hosting. The domain pinning already means a stranger cannot use your key from their own site: it only answers on origins you have verified. Archiving is the stronger version, for when you would rather the key simply do nothing at all. So the loop is: build the demo site, record it, archive it. If you need the same demo again next month, restore it, and everything is where you left it. #### How Archiving is on the site's Settings tab, at the bottom. It asks you to type the site's name, because it takes a live assistant off every page it is on. Restoring is on the sites list, under **Archived**. One button, and the key works again immediately. #### Two things that can refuse a restore Both are worth knowing before you archive something you will definitely want back. - **Your plan's site limit.** An archived site does not count against it, which is the point, so if you created another site in the meantime the slot may be taken. Archive or remove that one first. - **A domain somebody else has claimed.** An archived site releases its verified domains, so another site can take them. If that has happened, the restore is refused and names the domain and the site holding it, rather than quietly bringing your site back without the domain it needs to answer on. > Archiving is not a way to hide a site from your bill or your team, and it is not a delete. If you genuinely need data removed rather than switched off, ask us and we will do it properly, including the conversation history that archiving deliberately keeps. ### Troubleshooting: when the assistant does not appear or refuses https://askverb.com/docs/troubleshooting Four things account for almost everything that goes wrong, and each has a symptom you can recognise without reading any code. Find yours below. #### Nothing appears at all Almost always a Content-Security-Policy. The script is blocked before it runs, and a blocked script cannot tell you it was blocked, so the page looks completely normal. Add `api.askverb.com` to both `script-src` and `connect-src`. Two directives, and missing the second is the sneakier failure: the widget appears, then every question fails to reach us. If your policy uses `'strict-dynamic'`, which is the default in a Next.js app that sets a CSP, that advice changes. A browser honouring `'strict-dynamic'` ignores host allowlists in `script-src` entirely, so adding us there does nothing at all. What the tag needs instead is your per-request nonce. `connect-src` still needs the host, and that is the half that would otherwise break. Next.js: the nonce is what matters, not the allowlist: ```html ``` Worth knowing before you ship: many apps only send a CSP in production. That means this failure cannot reproduce on your machine. The install works all afternoon locally and is silently dead the moment it deploys, which is the most expensive shape this particular bug has. Check your browser console for a CSP violation naming askverb.com. If there is nothing there at all, confirm the script tag is in your root layout rather than a single page, and that the site key matches the one in your dashboard. #### It appears, but every question fails Your domain is not verified yet. It only answers on domains you have declared, and the widget will tell you so directly rather than showing a generic error. Open the site in your dashboard. If the domain shows **seen**, we have watched the widget boot there and one click confirms it. If it shows nothing, the domain was never declared: add it. > This is also what protects you. A copied script tag on somebody else's site hits exactly this and serves nothing, which is why the key being public does not matter. #### Reads work, writes are refused for a signed-in user Identity is not wired up. Verb cannot see your session cookie, so a user who is signed into your product is still anonymous to the assistant, and anonymous means read-only. [Connecting your users](/docs/identity) is the fix, and the page lists the three ways the token itself can be wrong once you have wired it. #### One tool fails with needs an adapter That tool is marked `run: adapter`, so it runs through your own client, and the client has not been given to Verb. Call `window.Verb.configure` with a case for that tool name. [Adapter tools](/docs/adapter-tools) #### It answers, but the answers are wrong or vague Two different causes, and the log tells you which. Open the Logs tab on your site and read the conversations. - **It called no tools.** Either it had none that fit, or the descriptions do not say when to call them. Descriptions should say *when to use this*, not what it does internally. - **It called the wrong tool.** Usually two tools whose descriptions overlap. Make them differ in the situation they name, not just the wording. If it is vague about your product rather than your data, fill in **What your product is** on the site. With nothing there the assistant knows only your site name and its tools, and will describe itself instead of your product. We shipped that mistake on our own landing page and a stranger pointed it out within an hour of launching. #### Still stuck Write to [vishnu.ps@askverb.com](mailto:vishnu.ps@askverb.com) with your site name and roughly when it happened. There is no ticket queue and nobody reading from a script. ### Reading your numbers: the Overview, summaries, Analytics and alerts https://askverb.com/docs/numbers Every site's Overview shows how it is doing for any period you pick, and Analytics breaks it down one site at a time. Everything is counted in requests: every message somebody sends the assistant is one request, whether it is "cancel my order", "where is it?" or "yes, go ahead". It is the same unit your monthly allowance counts. #### Requests, and why they are the only unit A request is one message from one of your users, handled: a question answered or a task done. A conversation is a thread of them, and you can read each one on the Conversations tab, but one conversation can be a single request or thirty, so nothing is counted in them. What you see on a chart, on a card and on your allowance is always requests. > Nothing you ask on your test site is counted, anywhere. #### Read a site's Overview - **Open Sites and pick a site.** The Overview tab opens first. - **Pick a period** from the menu at the top right: 7, 30, 60 or 90 days, all time, or **Custom** for any dates. Everything below follows it. - **Read the line above the cards.** It says what Verb did in that period: requests handled, things done, and for how many people. - **Read the four cards.** Requests; People reached; Rated helpful, from your users' thumbs up and down; and Verified actions, the share confirmed to have actually happened. A site with no actions shows Different requests instead. - **Below them** are the summary (next section), requests per day, and Do people come back?, which follows signed-in users from one visit to the next. - **Press Refresh** to fetch the latest numbers without reloading the page. #### Summaries A summary groups what your users asked for and what the assistant could not do for them. The AI only groups and names. Every count is counted from your own data, and every quote is a request exactly as it was typed. - **Every Monday**, a summary of the week before is emailed to everyone in your workspace and shown on the Overview. - **Ask for one** for the period on screen with **Summarize this period**. On Dev and Starter you can ask for one every 7 days per site, and the button shows the date it comes back. - **Long periods are sampled.** Up to 250 requests are read, spread evenly across the period, so a summary of a year covers the whole year. The summary says when it read a sample. - **Read the full summary** opens it on its own page: the numbers against the period before, what to do next, everything people wanted, what stood out, how each action did, who came back, and their own words. - **Every summary is kept** under Analytics, Summaries, until the conversations it was made from reach the end of their retention. > What to do next is worked out from your numbers, not written by the AI: an action that keeps failing, something people asked for that nothing could do, or an action nobody used. #### Analytics, tab by tab Open **Analytics** in the side panel and pick a site at the top. It covers the last 30 days, one tab at a time. - **Activity.** Requests per day, how many people asked, and when they use it, by day and hour in your timezone. - **Actions.** Anything failing right now at the top, then how many actions ran, how many were confirmed, and a row per action with its most common error. A red count on the tab means something is failing now. - **Could not do.** What people asked for that nothing handled, split into actions worth adding and answers worth adding to what it knows. It has the same plan limit as summaries. - **Summaries.** Every summary kept for the site, newest first. Open one to read it here, or open it in full. - **Alerts.** Every alert for the site, newest first, with the ones you have not seen marked New. #### Alerts Everyone in your workspace gets an email when: - **An action keeps failing:** 3 or more failures in 24 hours, making up 30% or more of its uses. - **The assistant fails to load** on one of your pages. Each problem is emailed at most once a day. A gold count on **Analytics** in the side panel shows alerts you have not opened yet. Clicking it opens that site's Alerts tab, which clears it. The count is yours alone: somebody else in your workspace still sees theirs until they look. #### Find one person On a site's Conversations tab, **Find one person** takes the id your app gives a signed-in user and shows what they asked and what Verb did for them. Use it to help that person. Every lookup is recorded, the id you type is not stored, and only signed-in users can be found.