# Omazy CX documentation The complete documentation for Omazy CX, concatenated in reading order. Canonical HTML lives at https://docs.omazy.ai --- # Get started ## Get started Source: https://docs.omazy.ai/start/ What Omazy CX is, how a workspace, an app and an agent fit together, and what to do in your first hour. Omazy CX answers for your business. It reads what you already publish, replies to customers on your site and in your channels, and hands over to a human when it should. ## How the pieces fit Three objects, nested. Almost everything in this documentation sits at one of these levels, and knowing which one saves a lot of confusion later. | Object | What it is | Scope | |---|---|---| | **Workspace** | Your company. Holds members, billing and plan. | One per business | | **App** | One brand or property inside the workspace. | Many per workspace | | **Agent** | The configured AI that answers for one app. | One per app | Settings live at the level they belong to. Team and billing are workspace-wide. The agent brief, knowledge and widget belong to a single app, so two brands in one workspace never share an answer. ## Your first hour 1. Create your workspace and your first app. 2. Point the agent at your website so it has something to answer from. 3. Write the brief: who the agent is, what it may say, when it hands over. 4. Test it against real questions before anyone else sees it. 5. Publish the widget on your site. ## In this section - [Your first agent](/start/first-agent/) from empty workspace to a real answer - [Put it on your site](/start/first-widget/) install the widget and go live Still to come: inviting your team, and connecting a second brand. --- ## Your first agent Source: https://docs.omazy.ai/start/first-agent/ From an empty workspace to an agent that answers, in about half an hour. The goal here is not a perfect agent. It is an agent that answers one real question correctly, because that is the point where everything else becomes tuning rather than guessing. ## 1. Create the workspace and app Your **workspace** is your company. Your **app** is one brand inside it. Most businesses need one of each to start, and the second app only when there is genuinely a second brand with different answers. Signup creates both, so you can usually skip ahead. ## 2. Give it something to read An agent with no knowledge is a very confident stranger. Point it at your website and let the crawler take what is already published. Crawling is the fastest start, not the best finish. It gets you to a working agent today. What makes an agent good is usually a handful of authored answers covering the things your site never says out loud, like your actual return window or whether you deliver on Sundays. ## 3. Write the brief The brief is who the agent is, what it may say, and when it hands over. It composes and publishes into the agent's system prompt, so edit the brief and leave the prompt alone. Three things earn their place immediately: - **Who you are.** One or two lines. "You answer for Acme, a bakery in Leeds." - **What is out of bounds.** Anything you do not want guessed at: prices that change, medical or legal advice, delivery promises. - **When to hand over.** Be specific. "Hand over for refunds, complaints, and anything about an existing order" beats "hand over when unsure", because an agent is rarely unsure and that is precisely the problem. ## 4. Test before anyone else does Open Testing and ask it the five questions you already know customers ask. Not the questions you wish they asked. The real ones, including the awkward one about pricing. You are looking for three failure shapes: | What you see | What it means | |---|---| | Confidently wrong | Knowledge is missing, so it filled the gap | | Correct but evasive | The brief is over-restricted | | Correct but endless | Ask it to be brief; nobody reads paragraph four in a chat window | ## 5. Publish Publishing is deliberate. Until you press it, your edits are not live, which means you can leave a half-written brief overnight without any customer meeting it. ## Then what Put it on your site. [Install the widget](/widget/) is one script tag, and the agent you just published is the one that answers. --- ## Put it on your site Source: https://docs.omazy.ai/start/first-widget/ Install the widget, decide what it shows before anyone types, and go live. The agent is only useful where your customers already are. For most businesses that is the website, and the widget is one script tag away. ## Install Copy the snippet from the console under Connect, Widget, and paste it before ``. Full detail and the host API are in [Widget](/widget/). ```html ``` ## Decide what it says before anyone speaks This is the part people skip, and it does more for resolution rate than most agent tuning. A widget that opens to an empty box asks the visitor to do the hard part: work out what this thing is for. A widget that opens with three things people actually ask has already answered a third of them. Pick starter prompts from your real questions. If you do not know them yet, launch with your best guess and replace them after a week with what Insights tells you. Guessing once is fine. Guessing twice is a choice. ## Check it on a phone first Most visitors are on a phone, and most widgets are reviewed on a laptop. The panel goes full screen below the mobile breakpoint, which is correct and still worth seeing before your customers do. ## Go live Publish the widget. Then ask it something as a stranger would, in a private window, from your actual site rather than from the preview. If the launcher does not appear, [Troubleshooting](/widget/troubleshooting/) covers the four usual causes in the order they usually happen. --- # Agent ## Agent Source: https://docs.omazy.ai/agent/ The brief, knowledge, help center, catalog, prompts, saved responses, testing and publishing. The agent is the configured AI that answers for one app. Everything that shapes what it says lives here. ## What shapes an answer | Input | What it does | |---|---| | **Brief** | Who the agent is, what it may say, when it hands over. The brief composes and publishes into the agent's system prompt, so edit the brief, not the prompt. | | **Knowledge** | What the agent can answer from: crawled pages, uploaded documents, authored articles. | | **Help center** | Structured articles the agent can quote and link. | | **Catalog** | Structured products or services, with prices and media, so answers carry real detail. | | **Saved responses** | Verbatim replies for the answers that must be word for word. | | **Suggestions** | The prompts offered to a visitor who has not asked anything yet. | | **LLM** | Which model answers, and the limits it runs under. | ## Before you publish Test against real questions. The **Testing** surface runs your agent headlessly so you can see what it would say without a customer on the other end. Publishing is a deliberate step, and until you take it your edits are not live. ## In this section - [Writing the brief](/agent/brief/) the agent's job description - [Adding knowledge](/agent/knowledge/) what it is allowed to know Still to come: building a catalog, saved responses, testing, and publishing. --- ## Writing the brief Source: https://docs.omazy.ai/agent/brief/ The brief is the agent's job description. It composes into the system prompt, so this is the file that matters. The brief is where you say who the agent is and what it may do. It composes and publishes into the system prompt, which means two things worth remembering: 1. Edit the brief, never the prompt. The next publish overwrites the prompt. 2. Nothing you write here is live until you publish. ## What a good brief contains **Identity.** One or two sentences. Who does this agent answer for, and in what tone. Resist the temptation to write a personality essay. The model is already good at sounding human, and every extra adjective competes for attention with your actual rules. **Scope.** What it may answer, and what it must not. Be concrete. "Do not give medical advice" is a rule. "Be careful with sensitive topics" is a mood. **Handover rules.** The single highest-value section, and the most commonly vague. Name the situations: > Hand over to a human for refunds, complaints, anything about an existing > order, and any question about a price that is not in the catalog. "Hand over when unsure" fails because a model is rarely unsure. That is the whole difficulty. **Format.** Chat is not email. Ask for short answers, and say so explicitly. ## What to leave out Facts. The brief is instructions, not knowledge. Prices, hours and policies belong in knowledge or the catalog, where you can change them without republishing the agent and where the answer can cite a source. A brief that contains your opening hours will be wrong in six months, confidently and in the agent's own voice. ## Test the brief, not the prose A brief reads well and still behaves badly. The only check that counts is running your real questions through Testing and seeing what comes out. Change one thing at a time. If you rewrite the identity, the scope and the handover rules together and the agent gets worse, you have learned nothing except that it got worse. --- ## Adding knowledge Source: https://docs.omazy.ai/agent/knowledge/ What the agent can answer from: crawled pages, uploaded documents, and the authored answers that do the real work. Knowledge is what the agent is allowed to know. Everything it says should trace back to something here, and an answer that cannot be traced is an answer waiting to be wrong. ## Three ways in, in ascending order of value **Crawl your site.** Fastest. Gets a working agent today. It captures what you have already published, which is a lot, and it captures it in marketing voice, which is not always what a question needs. **Upload documents.** Good for the things that exist as files: policies, price lists, manuals. **Author answers.** Slowest, and the one that actually makes an agent good. Every business has questions its website has never answered plainly. Delivery on public holidays. What happens if the size is wrong. Whether you take that one payment method everyone asks about. A dozen authored answers usually outperform a thousand crawled pages, because they are the dozen people ask. ## Keeping it honest Knowledge goes stale quietly. Nobody files a ticket saying "your agent is citing last year's returns policy", they just get a wrong answer and leave. Two habits prevent most of it: - Re-crawl when the site changes, not on a calendar. - When a human corrects the agent in the inbox, write that correction into knowledge. Otherwise you will correct it again next week. The second one is the whole game. An inbox is a list of things your agent did not know, delivered free, sorted by how often people ask. ## Sources An answer can carry its sources, and the widget will show them. Citations cost a line and buy trust, and they let a customer check the thing themselves rather than escalating to ask whether the agent was right. ## When an answer must be exact Some answers cannot be paraphrased: a legal line, a refund policy, a regulated disclosure. Those belong in saved responses, which return the text you wrote, word for word, rather than the model's summary of it. --- # Widget ## Widget Source: https://docs.omazy.ai/widget/ The chat widget on your site: a 6KB loader, a hosted chat app, and a host API for talking to it from your own code. The widget is the part of Omazy your customers actually touch. Everything else is backstage. It ships in two pieces, and the split is the whole trick: | Piece | What it is | Where it runs | |---|---|---| | **Loader** | About 6KB of dependency-free vanilla JavaScript. Draws the launcher, opens an iframe, relays messages. | Your page | | **Chat app** | The actual conversation UI. | An iframe on `chat.omazy.ai` | Your page carries the weight of a small image. The chat app, which is the heavy part, does not load at all until someone opens it. And because it lives in an iframe on our origin, its CSS cannot leak into your site and your CSS cannot leak into it. Good fences, quiet neighbours. ## Install One script tag, placed before ``: ```html ``` That middle stanza is a command queue, the same pattern analytics snippets use. Calls made before the loader finishes are not dropped, they queue up and run on arrival. You can call `ouWidget('open')` on the very next line and it will be honoured a moment later. No load-order archaeology required. Your widget key comes from the console under Connect, Widget. ## What to read next | Page | For | |---|---| | [Host API](/widget/host-api/) | Driving the widget from your own JavaScript | | [Message blocks](/widget/message-blocks/) | What the agent can put on screen beyond plain text | | [Troubleshooting](/widget/troubleshooting/) | When the launcher does not appear | ## Mobile apps The loader also speaks to native shells. If it finds `window.OuchatNative` or `window.webkit.messageHandlers.ouchat`, it forwards the same events over that bridge, so a WebView-hosted widget can tell its host app about unread counts without you writing a second protocol. --- ## Host API Source: https://docs.omazy.ai/widget/host-api/ Drive the widget from your own JavaScript. Commands, events, and identity. Everything the widget can be told to do goes through one function. If you have ever used an analytics snippet, the shape will feel familiar: a single global that takes a command name and some arguments. ```js ouWidget('open') ouWidget('identify', { id: 'cus_123', name: 'Ada', email: 'ada@example.com' }) ``` ## Commands | Command | Arguments | What it does | |---|---|---| | `init` | config object | Mounts the widget. Called once by the install snippet. | | `open` | none | Opens the panel. | | `close` | none | Closes it. | | `toggle` | none | Opens if closed, closes if open. | | `show` | none | Shows the launcher. | | `hide` | none | Hides the launcher without unmounting. | | `identify` | identity object | Tells the widget who this visitor is. `login` is an alias. | | `logout` | none | Forgets them. Call this on sign-out. | | `setContext` | context object | Attaches page or account context to the next conversation. | | `startChat` | optional message | Opens and begins a conversation. | | `sendMessage` | message | Sends on the visitor's behalf. | | `on` | event, handler | Subscribes to an event. | | `off` | event, handler | Unsubscribes. | | `getState` | callback | Calls back with a state snapshot. | | `getUnread` | callback | Calls back with the unread count. | | `destroy` | none | Unmounts and removes listeners. | `hide` and `destroy` are not the same thing, and the difference matters. `hide` keeps the widget alive with the lights off, so unread counts keep arriving. `destroy` packs up and leaves. Reach for `hide` on a checkout page and `destroy` on sign-out. ## Events Subscribe with `on`, or listen for a DOM `CustomEvent` named `ouchat:widget:` if you would rather not hold a reference. | Event | Fires when | |---|---| | `ready` | The widget has mounted and is usable. | | `open` | The panel opened. | | `close` | The panel closed. | | `message` | A message arrived. | | `unread` | The unread count changed. | | `conversation-started` | A new conversation began. | | `identity-changed` | `identify` or `logout` took effect. | ```js ouWidget('on', 'unread', function (count) { document.title = count ? '(' + count + ') Support' : 'Support' }) ``` :::note[Two event prefixes, one meaning] Events are dispatched under both `ouchat:` and the older `omazy:` prefix while existing installs migrate. Listen for `ouchat:`. The `omazy:` twin is there so nothing that already works stops working, which is the only good reason to keep two names for one thing. ::: ## Identity `identify` is what turns an anonymous visitor into someone your agent recognises. Until you call it, the widget treats a person as a stranger, which is correct but not especially useful once they have signed in to your app. ```js ouWidget('identify', { id: 'cus_123', name: 'Ada Lovelace', email: 'ada@example.com', }) ``` Call `logout` when they sign out. A shared machine with a remembered identity is a support ticket waiting to be written, and it will be written by the wrong person. --- ## Message blocks Source: https://docs.omazy.ai/widget/message-blocks/ The building blocks an agent can put on screen: text, quick replies, buttons, cards, carousels, forms and more. A message is not a string. It is an envelope containing an ordered list of blocks, each of which knows how to draw itself. Think of it less as a letter and more as a tray: several things arrive together, and each one has its own shape. This matters because it is why an agent can answer "do you have this in blue" with a picture, a price and a button, rather than a paragraph describing all three. ## The blocks | Block | What it renders | |---|---| | `text` | Markdown. The workhorse. | | `quick_replies` | Tappable suggested answers. | | `buttons` | Explicit actions. See below. | | `card` | One item: image, title, body, actions. | | `carousel` | Several cards, swipeable. | | `media` | An image, video or file on its own. | | `form` | Several fields collected in one go. | | `input_request` | A single value, asked for inline. | | `live_tracker` | Live status for an order, shipment or ticket. | | `divider` | A labelled break in the thread. | | `typing` | The three dots. | ## Button kinds Buttons are typed rather than free-form, so the widget knows what a tap means without guessing. | Kind | Does | |---|---| | `url` | Opens a link, in a new tab or the same one. | | `postback` | Sends a payload back to the agent as if the visitor had said it. | | `escalate` | Asks for a human. | | `auth` | Starts sign-in. | | `phone` | Dials a number. | | `open_product` | Opens a catalog item in the widget. | The distinction between `url` and `postback` is worth internalising. A `url` button ends the conversation and starts a page load. A `postback` keeps the person exactly where they are and moves the conversation forward. When in doubt, prefer the one that does not throw away the context you just built. ## Quick replies Quick replies are the widget's way of asking a closed question without closing the door. They render as tappable chips, and the visitor can still ignore them and type whatever they like. They are suggestions, not a menu, and an agent that treats them as a menu will frustrate the one person who wanted something else. ## Sources An assistant message can carry `sources`, each with a title and a URL. If the answer came from your knowledge base, this is what lets the widget show its working. Citations are cheap to display and expensive to omit: an answer nobody can verify is an answer somebody will escalate. --- ## Troubleshooting Source: https://docs.omazy.ai/widget/troubleshooting/ When the launcher does not appear, opens blank, or behaves differently on mobile. Most widget problems are one of four things, and they are quick to tell apart. ## The launcher never appears Work down this list in order. It is roughly ordered by how often each one is the culprit. 1. **The script did not load.** Check the network tab for `widget.js`. A content security policy that omits `chat.omazy.ai` will block it silently, which is the most polite way software has ever ruined an afternoon. 2. **`init` was never called**, or was called without a key. 3. **The key is wrong or the widget is not published.** An unpublished widget is configured but not live. 4. **The launcher is hidden on this viewport.** Desktop-only launchers do not render below the mobile breakpoint. That is a setting, not a bug, though it has been mistaken for one many times. ## It opens blank Almost always a configuration problem rather than a code one. A landing screen with a malformed footer link block will render an empty card, and an empty card looks exactly like a broken widget. Check the widget's landing configuration in the console. If you changed the landing links recently, change them back and reload before investigating anything else. ## It looks fine on desktop and wrong on a phone The panel becomes full-screen below the mobile breakpoint, by design. If your page sets `overflow: hidden` on `body` while a menu is open, or traps focus, the widget inherits that behaviour because it lives in your document. ## Events fire twice You are probably listening for both `ouchat:` and `omazy:` prefixed events. They are the same event dispatched under two names during migration. Pick the `ouchat:` one. ## Getting help Reproduce it with the console open and capture: - the widget key, - what `ouWidget('getState', console.log)` prints, - any errors from `chat.omazy.ai`. Those three turn a two-day thread into a two-message one. --- # Engage ## Engage Source: https://docs.omazy.ai/engage/ Inbox, conversations, customers, journeys and broadcast. Where your team and your agent meet the customer. | Surface | What it is for | |---|---| | **Inbox** | Live conversations your team works, including anything the agent handed over. | | **Conversations** | The full history, searchable, including everything the agent handled alone. | | **Customers** | The contact record built from conversations: who they are, what they asked, what they bought. | | **Journeys** | Guided flows that collect what you need, such as a booking, an order or a callback request. | | **Broadcast** | Sending a message out rather than waiting to be asked. | ## Handover An agent that never hands over is a liability, and one that hands over constantly is not saving anyone time. Handover rules live in the agent brief, and the conversation lands in the inbox with its full history attached, so whoever picks it up is not asking the customer to repeat themselves. ## In this section - [Working the inbox](/engage/inbox/) picking up a handover and closing the loop Still to come: the customer record, building a journey, and broadcasts. --- ## Working the inbox Source: https://docs.omazy.ai/engage/inbox/ Picking up a handover, what the agent already tried, and closing the loop so it does not happen twice. The inbox is where conversations arrive that the agent could not or should not finish. Everything in it is, by definition, the interesting half. ## A handover arrives with its history You are never picking up cold. The full conversation comes with it, including what the agent already tried. Read it before you type. Nothing erodes a customer faster than being asked the question they just answered. ## What to look for while you work Two things at once: the customer's problem, and why the agent could not solve it. The second one is free information and most teams throw it away. | Why it handed over | What to do after | |---|---| | It did not know | Add the answer to knowledge | | It knew but was not allowed | Check whether the brief is too restrictive | | It answered wrongly | Correct the knowledge, not just the customer | | The rules said hand over | Nothing. It worked | That last row matters. A handover is not a failure. An agent that never hands over is not a better agent, it is an unsupervised one. ## Closing the loop The habit that compounds: when you answer something the agent should have handled, write it into knowledge before you close the conversation. It takes a minute and it is the difference between a system that improves and one that merely operates. Do it while the wording is fresh. Your answer to a real customer is usually better phrased than anything you would write later in the abstract. ## Speed and the human part Response time matters more than eloquence. A fast "let me check that for you" beats a perfect answer four minutes later, because by minute four they have opened a competitor's site in another tab. --- # Connect ## Connect Source: https://docs.omazy.ai/connect/ Channels, the chat widget, and integrations with the tools you already run. How customers reach your agent, and how your agent reaches your other systems. | Surface | What it is for | |---|---| | **Widget** | The chat widget on your website. Appearance, placement, language, and what it offers before anyone types. | | **Channels** | The other places customers message you. | | **Integrations** | Connections to the tools you already run. | | **AI assistants** | Let Claude or another MCP client work on a business directly. | ## The widget is a product decision, not a snippet It is the first thing a visitor sees, and its landing state does more for resolution rate than most agent tuning. What it offers before a question is asked, whether it leads with a catalog or a question, and how it looks on a phone are all configurable, and all worth deciding deliberately. ## In this section - [Channels and integrations](/connect/channels/) the other doors in - [Connect an AI assistant](/connect/mcp/) give Claude scoped access to one business The widget has its own section: [Widget](/widget/). Still to come: appearance and layout, and the landing state in depth. --- ## Channels and integrations Source: https://docs.omazy.ai/connect/channels/ Connecting the other places customers message you, and the tools you already run. The widget covers your website. Channels cover everywhere else your customers already are, which is usually where they were going to message you anyway. ## Before you connect a second channel Get one right first. A single channel with a good agent beats four channels with a mediocre one, and every channel you add multiplies the surface where a bad answer can appear. The exception is when your customers are demonstrably somewhere else. If nobody uses your website contact form and everyone messages you on WhatsApp, the website is not the place to perfect anything. ## One agent, many doors The same agent answers across channels. That is the point: your opening hours should not depend on which app someone opened. What does vary is what each channel can display. Rich blocks like carousels and forms render fully in the widget and degrade elsewhere. Write answers that read correctly as plain text, and treat rich rendering as a bonus rather than a requirement. ## Integrations Integrations connect the tools you already run. Two rules keep this boring, and boring is what you want here: **Scope narrowly.** Grant the least access that makes the integration work. A read-only connection cannot cause an incident. **Uninstall means revoke.** Credentials belong to the install, so removing an integration actually removes access rather than hiding the button. If you find yourself pasting a long-lived key into a config box somewhere, stop and check whether there is a proper connection for it. ## When a channel goes quiet Channels fail silently more often than loudly. The usual causes, in order: 1. The upstream credential expired or was revoked by the other side. 2. The other platform changed a permission and the connection needs re-approval. 3. The channel is connected but the agent is not published to it. Check the channel's status in the console before assuming the agent broke. The agent is usually fine and simply has nobody to talk to. --- ## Connect an AI assistant Source: https://docs.omazy.ai/connect/mcp/ Connect Claude or any MCP client to one business, so it can read and change your agent, knowledge, catalog and widget. Omazy runs an MCP server. Point an AI client at it and that client can work on one of your businesses directly: read the agent's instructions, search the knowledge base, add products, change the widget. ``` https://mw.omazy.ai/api/v1/mcp ``` This is for managing a business. It is not the tooling your agent uses to answer customers, which is configured per agent and lives elsewhere. ## One connection, one business A connection is scoped to a single business and to your role in it. Sign in as a manager of one workspace and the connection can reach that workspace and nothing else, no matter what the client asks for. Belong to four businesses and you make four connections, each approved separately. The scope is decided at approval time from your actual membership, so a client cannot widen it by asking. ## Two ways to connect ### Hosted clients: approve in the browser Claude and other hosted clients have nowhere to paste a token, so they use OAuth. Add the URL above as a custom connector and the client does the rest: 1. It discovers the server and registers itself. 2. You land on an approval screen in your Omazy console. 3. You pick **which business** and **how much access**. 4. The client is handed a key scoped to that choice. You stay signed in to Omazy for the approval, which is what proves who you are. Nothing is issued if you cancel. ### Local clients: mint a token Claude Code, a script or a CLI can hold a bearer token. Mint one in the console under **Settings → Connected AI**, then: ```sh claude mcp add --transport http omazy https://mw.omazy.ai/api/v1/mcp \ --header "Authorization: Bearer " ``` The token is shown once and stored hashed, so it cannot be recovered later. It lasts 7 days by default and 90 at the most. Mint a new one rather than trying to extend it. ## Read-only or read-write You choose this when you approve the connection, and it is worth choosing deliberately. | Choice | What the client can do | |---|---| | **Read only** | Look at the business, read the agent and its Brief, search knowledge, view the widget config. Cannot change anything. | | **Read and write** | Everything above, plus edit the agent, publish knowledge, add products, change the widget. Capped at your own role. | Read-only is enforced two ways: the key is issued at member role, and it carries an allowlist naming exactly the tools it may call. A tool missing from that list is simply unavailable, so a mistake there removes access rather than granting it. "Read and write" never exceeds you. If you are a manager, the connection is a manager. It cannot create workspaces or touch billing on your behalf. ## What a connection can reach | Area | Read | Write | |---|---|---| | Business and apps | Yes | Create a business (account scope) | | Agent identity and prompt | Yes | Through the Brief | | Agent Brief, with version history | Yes | Edit, reorder, restore, publish | | Answer suggestion pills | Yes | Replace the rule list | | Knowledge base | Read and search | Add documents | | Help centre and catalog | Yes | Add articles and products | | Widget | List and read config | Change config, publish, create | Every tool, with its arguments and the role it needs, is in the [MCP server reference](/reference/platform/mcp-server/). ## Changing how the agent answers One thing to know before you let a client edit your agent: on an agent that uses the Brief, the system prompt is **generated**, not stored. Publishing the Brief composes the enabled blocks and overwrites it. So a client that writes the prompt directly produces a change that works for a while and then vanishes the next time anyone publishes from the console, with no record of what it replaced. The server refuses that write and says so, naming the tools to use instead. Edit the Brief, then publish. ## Managing connections **Settings → Connected AI** in the console lists every connection: which client, read-only or read-write, when it was last used, how many calls it has made, and a button to revoke it. Revoking is immediate. For a connection made through the browser it also kills the refresh chain, so the client cannot quietly reconnect with a token it still holds. The same page shows recent tool calls with timing and outcome. Arguments are never recorded, so the log is safe to read in front of other people. ## When it does not work **The client connects, then reports the credentials were rejected.** Sign-in worked and a key was issued, but the calls that follow never arrive. Check whether something between the client and the server is filtering AI-agent traffic: a bot or AI-scraper rule at your CDN or proxy will drop those calls while leaving the sign-in steps untouched, because they are made by different clients. The console tells you which side is at fault: a connection sitting at **0 calls and never used** was never reached. **Every call fails with an authorization error.** The key has expired. Minted tokens last 7 days by default. Reconnect or mint a new one. **A tool says it needs a different role.** The connection carries your workspace role. Ask an owner or manager to make the connection instead, or to raise your role. **A write says the connection is not scoped for it.** The connection was approved read-only. Reconnect and choose read and write. :::caution[A connection acts as you] Anything it does is attributed to you and audited under your name. Approve read-only unless the client genuinely needs to change things, and revoke connections you are no longer using. ::: --- # Automate ## Automate Source: https://docs.omazy.ai/automate/ Runbooks, automations, tasks, events, the data store, plugins and webhooks. Work that happens without anyone clicking anything. | Surface | What it is for | |---|---| | **Runbooks** | Multi-step procedures the agent can run, with approvals where a human should sign off. | | **Automations** | Triggers and their consequences: when this happens, do that. | | **Tasks** | Work items raised out of a conversation and tracked to done. | | **Events** | The stream of what happened, which automations listen to. | | **Data store** | Structured values your automations read and write. | | **Plugins** | Capabilities added to the agent beyond answering. | | **Webhooks** | Outbound calls into your own systems when something happens here. | ## Approvals Runbooks can pause and wait for a person. Use that for anything with a real consequence: refunds, cancellations, anything that writes to a system of record. An approval step costs a few seconds and prevents the class of incident that is very expensive to undo. ## In this section - [Runbooks and approvals](/automate/runbooks/) procedures, and where a human belongs Still to come: the event catalogue and automation trigger reference. --- ## Runbooks and approvals Source: https://docs.omazy.ai/automate/runbooks/ Multi-step procedures the agent can run, and the approval step that keeps the expensive ones reversible. A runbook is a procedure with steps, written once and run many times. It is the difference between an agent that can answer a question about a refund and one that can process it. ## Where the approval step goes Runbooks can pause and wait for a person. Use that for anything with a real consequence: refunds, cancellations, anything that writes to a system of record, anything a customer would be upset to have happen twice. An approval costs a few seconds. Not having one costs an afternoon, and occasionally a customer. The test is simple. If a step went wrong at three in the morning, could you undo it by lunchtime? If not, put a person in front of it. ## Writing one that survives contact with reality **Make steps small.** A step that does four things fails in four ways and tells you about one of them. **Make failure informative.** A step that stops with "error" leaves the person who picks it up doing forensics. A step that stops with "the payment provider returned insufficient funds" leaves them doing their job. **Assume it will be interrupted.** Deploys happen, connections drop. A runbook that cannot be resumed will eventually be a runbook someone finishes by hand while reading logs. ## Automations, tasks and events These get conflated, so plainly: | | Is | |---|---| | **Event** | Something happened | | **Automation** | When this event happens, do that | | **Task** | A piece of work tracked to done, often raised by an automation | | **Runbook** | A procedure with steps, run deliberately or by an automation | An automation is a reflex. A runbook is a plan. Reach for the reflex when the response is always the same, and the plan when it has steps a person might need to watch. ## Webhooks Webhooks call your systems when something happens here. Two things save pain later: return quickly and do the work asynchronously, and make your handler idempotent, because at-least-once delivery means the same event will arrive twice on the day you least want it to. --- # Insights ## Insights Source: https://docs.omazy.ai/insights/ Reports and analytics on what your agent resolved, what it escalated, and where it struggled. What actually happened, rather than what you hoped would happen. | Surface | What it is for | |---|---| | **Reports** | Volume, resolution, handover rate, and response times. | | **Analytics** | The detail underneath: which questions came up, which ones went badly. | ## The number worth watching Resolution rate on its own can be gamed by an agent that confidently answers everything. Read it next to handover rate and to the questions that ended without a useful answer. An agent that resolves less but escalates well beats one that resolves more and is wrong. ## In this section - [Reading the reports](/insights/reading-reports/) which numbers mean something Still to come: how each metric is calculated, and exporting. --- ## Reading the reports Source: https://docs.omazy.ai/insights/reading-reports/ Which numbers mean something, which ones mislead, and what to change on Monday. Reports are only useful if they change what you do. Here is which numbers earn their place and which ones flatter you. ## Resolution rate, read carefully Resolution rate on its own can be gamed by an agent that answers everything confidently. A model that never says "I do not know" will post an excellent resolution rate and a slowly worsening business. Read it next to handover rate and, more importantly, next to the conversations that ended without a useful answer. An agent that resolves less and escalates well beats one that resolves more and is wrong, every time. ## Handover rate is not a golf score Lower is not automatically better. There is a floor below which you are not saving work, you are just not helping people. What you want is handovers that happen for the reasons your brief says they should. A handover for a refund is the system working. A handover because the agent did not know your delivery times is a missing knowledge entry wearing a disguise. ## Response time The one number where the customer's experience and your metric point the same way. Nothing subtle to say about it: faster is better, and the first response matters most. ## The most useful screen is the worst-performing questions Whatever your dashboard leads with, the thing that will actually improve your agent is the list of questions that went badly. That list is a to-do list for knowledge, sorted by how often people ask. Work it weekly. Ten minutes on the top five beats an hour of tuning the brief. ## What to change on Monday In order: 1. Add knowledge for the top failed questions. 2. Fix any handover that fired for a reason not in your brief. 3. Replace starter prompts with the questions people actually ask. 4. Only then, touch the brief or the model. Most teams do this list backwards, starting with the model, which is the setting with the widest blast radius and the least evidence behind it. --- # Workspace ## Workspace Source: https://docs.omazy.ai/workspace/ Team and roles, billing and plan, LLM providers, audit and security. Settings that apply to the whole company rather than to one brand. | Surface | What it is for | |---|---| | **Team** | Members, roles, and what each role may do. | | **Billing and plan** | Your plan, seats, usage and invoices. | | **LLM providers** | Bringing your own model provider and keys. | | **Audit** | Who did what, and when. | | **Security** | Sign-in policy and session control. | | **Business profile** | The company details other surfaces read from. | ## Roles Permissions are per role, and a role is granted per member. Give people the narrowest role that lets them do their job, and revisit it when someone changes teams. The audit log is the record of what was actually done, and it is the first thing worth reading when something looks wrong. ## In this section - [Roles and permissions](/workspace/roles/) who can do what, and how narrow to go Bringing your own model provider is covered in [LLM Gateway](/reference/llm-gateway/). Still to come: plan and usage, and reading the audit log. --- ## Roles and permissions Source: https://docs.omazy.ai/workspace/roles/ Who can do what, and how to give people the narrowest role that lets them work. Permissions are granted per role, and a role is granted per member. The right instinct is the narrowest role that lets someone do their job, revisited when they change jobs. ## The shape of it | Role | Roughly | |---|---| | **Owner** | Everything, including billing and deleting things that do not come back | | **Manager** | Configure the agent, automations and channels. Not billing | | **Agent** | Work conversations. Not configuration | Most people are agents. Managers are the handful configuring things. Owners should be rare, and there must always be at least one, which is why the platform refuses to remove the last one. ## Give less than feels generous Over-granting is the default failure because it is the frictionless one. Nobody has ever been thanked for withholding a permission, and nobody notices the incident that did not happen. The practical test: if this person's laptop were compromised tonight, what could be changed? If the answer includes billing or deleting conversations, they probably did not need that role to do their actual job. ## The audit log is the record Whatever people did, the audit log has it. It is the first thing worth reading when something looks wrong, and it is considerably faster than asking six people whether they changed the brief on Tuesday. Read it before forming a theory. Most mysteries here are somebody being helpful in a way nobody else knew about. ## When someone leaves Remove the member rather than changing their password. Removing revokes access everywhere at once, including any CLI or integration token scoped to them. Changing a password leaves every existing session exactly where it was. --- # API ## API Source: https://docs.omazy.ai/reference/api/ HTTP endpoint reference for the Omazy CX middleware. The API is served at `https://mw.omazy.ai/api/v1`. :::caution[This reference covers 54 of about 725 endpoints] The endpoint reference is generated from the middleware's OpenAPI spec, and the spec only contains what has been annotated. Right now that is **54 operations across 43 paths**, out of roughly **725 registered routes**. So: everything documented here is real and current. But an endpoint missing from this page is far more likely to exist and work than not to exist. Absence is evidence of an unwritten annotation, not of a missing feature. If you need one that is not here, ask. Annotating a route is a small change and we would rather do it than have you guess. ::: ## Where things are | | | |---|---| | [Authentication](/reference/api/authentication/) | Tokens, scoping, revocation | | [Conventions](/reference/api/conventions/) | Envelope, pagination, rate limits, idempotency | | **Endpoints** | The generated reference, in the sidebar under this section | ## Response envelope Every response uses the same shape, so a client can handle success and failure in one place. ```json { "success": true, "data": { }, "meta": { } } ``` On failure, `success` is `false` and the payload carries a stable error code. Codes are listed in [Platform](/reference/platform/). ## Authentication Requests carry a bearer token scoped to an install. Tokens are minted per machine, so revoking one does not sign out the rest. ## Coming in this section The generated endpoint reference, pagination, rate limits, and idempotency. --- ## Authentication Source: https://docs.omazy.ai/reference/api/authentication/ Install-scoped bearer tokens, how they are minted, and how to revoke one machine without signing out the rest. Requests carry a bearer token: ```http Authorization: Bearer ``` ## Tokens are scoped to an install, not to you This is the design decision worth understanding, because everything else follows from it. A token belongs to one machine or one integration, not to a person. Sign in on your laptop and your CI runner and you have two tokens, not one shared between them. When the laptop is lost, you revoke that token and CI keeps running. One key per door beats one key for the building. ## Getting a token The CLI does the whole exchange for you: ```sh omazy auth login --email you@example.com ``` You get a one-time code by email, the CLI exchanges it, and the resulting token is scoped to that machine. There is no browser step, which is what lets this work over SSH and inside containers where opening a browser is not a thing that can happen. ## Revoking Revoke the install, not the account. Revoking one token leaves every other session working, which is what you want when a laptop goes missing and what you very much do not want to discover you cannot do at 2am. ## What a rejected token looks like An expired or revoked token comes back as a failure with a stable error code rather than a prose message. Branch on the code, never on the text. Messages get rewritten by people improving the wording; codes do not. See [Platform](/reference/platform/) for the full list. :::caution[Do not put a token in a browser] These tokens are for servers, CLIs and integrations. The widget authenticates differently and deliberately, using a public key that can safely appear in page source. If you find yourself pasting a bearer token into front-end JavaScript, stop: you are about to publish it to everyone who views source. ::: --- ## Conventions Source: https://docs.omazy.ai/reference/api/conventions/ The response envelope, pagination, and the rules that hold across every endpoint. Learn these once and every endpoint gets easier, which is the entire argument for having conventions at all. ## The envelope Every response has the same outer shape: ```json { "success": true, "data": { }, "meta": { } } ``` On failure, `success` is `false` and the payload carries a stable error code. This means your client needs exactly one place that understands success and failure, rather than one per endpoint. It also means you should check `success` rather than inferring it from the HTTP status. The two usually agree. Usually is not a word you want load-bearing in an error path. ## Pagination List endpoints are cursor-paginated. You get a page of results and a cursor; pass the cursor back for the next page. Keep going until there is no cursor left. Cursors are opaque. They encode position in a way that survives items being added while you are reading, which offset-based paging does not. Do not parse one, do not construct one, and do not store one and use it a week later. ## Identifiers Most objects are addressed by an id. Some are also addressable by a handle or slug, which is friendlier in a URL and in a support conversation. Watch the level. Workspace-scoped and app-scoped identifiers look similar and are not interchangeable, and passing one where the other is expected produces a "not found" that is technically honest and completely unhelpful. ## Rate limits Limits are per token. When you hit one you get a failure with a rate-limit error code rather than a silent slowdown. Back off exponentially and add jitter. Retrying immediately, in a tight loop, from every one of your workers at once is how a rate limit becomes an outage. The limit is doing its job; help it. ## Idempotency Anything that creates or charges should be safe to retry. If a request times out, you do not know whether it landed, and the honest answer to "did that work" is to ask rather than to guess by trying again. --- # CLI ## CLI Source: https://docs.omazy.ai/reference/cli/ Install the omazy binary, sign in, and drive your workspace from the terminal. `omazy` drives Omazy CX from the terminal: configure the agent, work the inbox, and manage the widget. It talks to the middleware at `https://mw.omazy.ai/api/v1`. ## Install ```sh curl -fsSL https://cli.omazy.ai/install.sh | sh ``` The installer verifies the SHA-256 of the release archive and refuses to continue if it cannot. It installs to `/usr/local/bin` when that is writable, otherwise `~/.local/bin`. Confirm it worked: ```sh omazy version ``` Windows is not covered by the installer yet. Download the `.zip` from the releases page and put `omazy.exe` on your `PATH`. ## Sign in ```sh omazy auth login --email you@example.com ``` Omazy emails a one-time code. The CLI exchanges it for tokens scoped to this machine. There is no browser step, so this works over SSH and inside containers. ## Coming in this section Full command reference, profiles and scoping, output formats, and revoking access for a single machine. --- ## Profiles and scoping Source: https://docs.omazy.ai/reference/cli/profiles/ How the CLI decides which workspace and app a command applies to, and how to stop guessing. Nearly every command needs to know two things: which workspace, and which app. Getting this wrong is the single most common way to run a correct command against the wrong brand. ## Resolution order The CLI checks three places, first match wins: 1. **The flag.** `--workspace` or `--app` on the command itself. 2. **The environment.** `OMAZY_WORKSPACE` and `OMAZY_APP`. 3. **What you pinned.** Whatever `omazy workspace use` last set. Flags beat environment, environment beats the pin. Explicit beats ambient, which is the order you would want if you thought about it, and the order you would regret if it were reversed. ## Pinning ```sh omazy workspace use acme ``` Comfortable for a day of work in one place. The failure mode is that it is invisible: a pin set on Monday is still in force on Thursday, and nothing on screen reminds you. If a command touches production, pass the flag explicitly and let the extra typing buy you certainty. ## Profiles Profiles keep separate credentials and pins side by side: ```sh omazy --profile staging agent list ``` One profile per environment is the pattern that survives contact with reality. Name them after the environment rather than after yourself, because `--profile imran` tells the next person nothing. ## Output formats ```sh omazy agent list --output json ``` | Format | For | |---|---| | `table` | Reading. The default. | | `json` | Piping into `jq` or a script. | | `yaml` | Reading a large object without going cross-eyed. | | `quiet` | Ids only, for shell loops. | Use `json` in scripts, always. The table format exists to be read by people, and its column widths and headings are allowed to change in a way that would break anything parsing them. `quiet` is the one to reach for when you want to feed ids into another command. ## Debugging ```sh omazy --debug agent list ``` `--debug` shows the requests being made and the responses coming back. It is the fastest way to find out that you are pointed at a different app than you thought, which is the answer roughly half the time. --- ## Command reference Source: https://docs.omazy.ai/reference/cli/commands/ Every command in the omazy CLI, generated from the binary's own help output. :::note[Generated from the binary] Every command below comes from `omazy --help`, walked automatically. That means it cannot drift: if the binary does not have it, it is not on this page. Covering **180 commands** across **21 groups** in `omazy` 0.1.1. ::: ## Global flags These work on every command. | Flag | Description | |---|---| | `-o, --output` | Output format: `table` (default), `json`, `yaml`, `quiet` | | `-p, --profile` | Configuration profile to use | | `--debug` | Enable debug output | | `--no-color` | Disable colored output | Most commands are scoped to a workspace and an app. Both resolve in the order flag, then environment (`OMAZY_WORKSPACE` / `OMAZY_APP`), then whatever `omazy workspace use` pinned. ## `omazy admin` Admin operations (privileged) ``` omazy admin [command] [flags] ``` ### `omazy admin analytics` Admin analytics ``` omazy admin analytics [command] [flags] ``` #### `omazy admin analytics data-sharing` Data-sharing patterns analytics ``` omazy admin analytics data-sharing [command] [flags] ``` ### `omazy admin audit` Audit log ``` omazy admin audit [command] [flags] ``` #### `omazy admin audit list` List audit entries ``` omazy admin audit list [command] [flags] ``` | Flag | Description | |---|---| | `--action string` | Filter by action | | `--actor string` | Filter by actor ID | | `--cursor string` | Pagination cursor | | `--limit int` | Page size | ### `omazy admin jobs` Background jobs (Asynq) ``` omazy admin jobs [command] [flags] ``` #### `omazy admin jobs delete` Delete a job ``` omazy admin jobs delete [command] [flags] ``` #### `omazy admin jobs get` Show one job's payload ``` omazy admin jobs get [command] [flags] ``` #### `omazy admin jobs list` List jobs ``` omazy admin jobs list [command] [flags] ``` | Flag | Description | |---|---| | `--cursor string` | Pagination cursor | | `--limit int` | Page size | | `--queue string` | Queue (critical\|default\|low) | | `--status string` | Status filter | #### `omazy admin jobs queues` Per-queue stats ``` omazy admin jobs queues [command] [flags] ``` #### `omazy admin jobs retry` Retry a failed job ``` omazy admin jobs retry [command] [flags] ``` ### `omazy admin metrics` Admin metrics ``` omazy admin metrics [command] [flags] ``` #### `omazy admin metrics bot-response` Bot response time percentiles ``` omazy admin metrics bot-response [command] [flags] ``` #### `omazy admin metrics dashboard` Top-level dashboard metrics ``` omazy admin metrics dashboard [command] [flags] ``` #### `omazy admin metrics messages-volume` Message volume metrics ``` omazy admin metrics messages-volume [command] [flags] ``` #### `omazy admin metrics token-cost` Token cost metrics ``` omazy admin metrics token-cost [command] [flags] ``` ### `omazy admin sessions` Sessions admin ``` omazy admin sessions [command] [flags] ``` #### `omazy admin sessions force-close` Force-close a session ``` omazy admin sessions force-close [command] [flags] ``` | Flag | Description | |---|---| | `--reason string` | Required reason | #### `omazy admin sessions get` Show one session with metadata ``` omazy admin sessions get [command] [flags] ``` #### `omazy admin sessions list` List sessions across users ``` omazy admin sessions list [command] [flags] ``` | Flag | Description | |---|---| | `--cursor string` | Pagination cursor | | `--limit int` | Page size | | `--status string` | Filter by status | ### `omazy admin shares` Cross-user data-share inventory ``` omazy admin shares [command] [flags] ``` #### `omazy admin shares get` Show one grant with audit trail ``` omazy admin shares get [command] [flags] ``` #### `omazy admin shares list` List grants across users ``` omazy admin shares list [command] [flags] ``` | Flag | Description | |---|---| | `--cursor string` | Pagination cursor | | `--limit int` | Page size | #### `omazy admin shares revoke` Force-revoke a grant ``` omazy admin shares revoke [command] [flags] ``` | Flag | Description | |---|---| | `--notify-user` | Notify the grantee | ### `omazy admin users` User administration ``` omazy admin users [command] [flags] ``` #### `omazy admin users delete` Soft-delete a user ``` omazy admin users delete [command] [flags] ``` #### `omazy admin users get` Show one user ``` omazy admin users get [command] [flags] ``` #### `omazy admin users list` List users ``` omazy admin users list [command] [flags] ``` | Flag | Description | |---|---| | `--cursor string` | Pagination cursor | | `--limit int` | Page size | | `--role string` | Filter by role | | `--status string` | Filter by status | #### `omazy admin users patch` Update role / status ``` omazy admin users patch [command] [flags] ``` | Flag | Description | |---|---| | `--role string` | New role | | `--status string` | New status | ## `omazy agent` Agent configuration: model, knowledge, and Brief ``` omazy agent [command] [flags] ``` | Flag | Description | |---|---| | `-a, --app omazy config set app` | App handle (default: OMAZY_APP, or omazy config set app) | | `-w, --workspace omazy config set workspace` | Workspace handle (default: OMAZY_WORKSPACE, or omazy config set workspace) | ### `omazy agent brief` The agent's Brief: structured instructions that compile into its prompt ``` omazy agent brief [command] [flags] ``` #### `omazy agent brief add` Append an instruction item to the Brief ``` omazy agent brief add [command] [flags] ``` | Flag | Description | |---|---| | `--body string` | Instruction text | | `--file string` | Read instruction from a file ('-' for stdin) | | `--section string` | Brief section (default "general") | | `--title string` | Short label for the instruction | #### `omazy agent brief delete` Remove an instruction item ``` omazy agent brief delete [command] [flags] ``` #### `omazy agent brief publish` Compile the Brief into the agent's live system prompt ``` omazy agent brief publish [command] [flags] ``` #### `omazy agent brief show` Show the Brief ``` omazy agent brief show [command] [flags] ``` ### `omazy agent knowledge` Agent knowledge documents ``` omazy agent knowledge [command] [flags] ``` #### `omazy agent knowledge add` Add a knowledge document from --body, --file, or stdin ``` omazy agent knowledge add [command] [flags] ``` | Flag | Description | |---|---| | `--body string` | Document content | | `--file string` | Read content from a file ('-' for stdin) | | `--kind string` | Document kind (default "text") | | `--source-url string` | Origin URL, shown as the citation | | `--title string` | Document title (required) | #### `omazy agent knowledge delete` Delete a knowledge document ``` omazy agent knowledge delete [command] [flags] ``` #### `omazy agent knowledge get` Show one knowledge document ``` omazy agent knowledge get [command] [flags] ``` #### `omazy agent knowledge list` List knowledge documents ``` omazy agent knowledge list [command] [flags] ``` | Flag | Description | |---|---| | `--limit int` | Maximum documents to return | | `--search string` | Filter by free text | #### `omazy agent knowledge reindex` Re-embed a document for retrieval ``` omazy agent knowledge reindex [command] [flags] ``` ### `omazy agent prompts` List the agent's published prompt versions ``` omazy agent prompts [command] [flags] ``` ### `omazy agent set` Update agent settings ``` omazy agent set [command] [flags] ``` | Flag | Description | |---|---| | `--active string` | Enable or disable the agent (true\|false) | | `--model string` | Model id (e.g. openai/gpt-4o) | | `--name string` | Agent display name | | `--temperature string` | Sampling temperature | ### `omazy agent show` Show the app's agent configuration ``` omazy agent show [command] [flags] ``` ### `omazy agent suggestions` Show the agent's open-question suggestion pills ``` omazy agent suggestions [command] [flags] ``` ### `omazy agent test` Send a message to the agent in a throwaway preview session ``` omazy agent test [command] [flags] ``` | Flag | Description | |---|---| | `-m, --message string` | Message to send (required) | ## `omazy auth` Sign in and manage authorized CLI installs ``` omazy auth [command] [flags] ``` ### `omazy auth installs` List and revoke the CLI installs registered to your account ``` omazy auth installs [command] [flags] ``` #### `omazy auth installs list` List installs ``` omazy auth installs list [command] [flags] ``` #### `omazy auth installs revoke` Revoke one install and its whole token family ``` omazy auth installs revoke [command] [flags] ``` ### `omazy auth login` Sign in and bind this machine as an authorized CLI install ``` omazy auth login [command] [flags] ``` | Flag | Description | |---|---| | `--browser` | Browser consent screen with a loopback listener | | `--browser-only` | Print the consent URL instead of opening a browser | | `--callback-port int` | Loopback callback port for --browser (0 = auto) | | `--device` | Device-code flow: authorize from another machine | | `--email string` | Email address to sign in with | | `--magic` | Send a magic link instead of a code | | `--password` | Prompt for a password instead of emailing a code | | `--provider string` | Social provider (google\|apple) | | `--scope strings` | OAuth scopes (comma-separated; default from profile) | ### `omazy auth logout` Revoke this install (or --all installs) and clear local credentials ``` omazy auth logout [command] [flags] ``` | Flag | Description | |---|---| | `--all` | Revoke every install for this user | ### `omazy auth refresh` Force a token refresh now ``` omazy auth refresh [command] [flags] ``` ### `omazy auth register` Create a new Omazy account ``` omazy auth register [command] [flags] ``` | Flag | Description | |---|---| | `--email string` | Email | | `--first-name string` | First name | | `--last-name string` | Last name | | `--password string` | Password | ### `omazy auth scopes` List the scopes the CLI can request ``` omazy auth scopes [command] [flags] ``` ### `omazy auth status` Show the current install, scopes, and expiry ``` omazy auth status [command] [flags] ``` ### `omazy auth switch-business` Re-issue tokens bound to a different authorized business ``` omazy auth switch-business [command] [flags] ``` ### `omazy auth verify-email` Complete email verification ``` omazy auth verify-email [command] [flags] ``` | Flag | Description | |---|---| | `--token string` | Email verification token | ### `omazy auth whoami` Show the authenticated user as the middleware sees them ``` omazy auth whoami [command] [flags] ``` ## `omazy bot` Bots: middleware catalog ``` omazy bot [command] [flags] ``` ### `omazy bot catalog` Browse the public bot catalog ``` omazy bot catalog [command] [flags] ``` #### `omazy bot catalog get` Show one catalog bot ``` omazy bot catalog get [command] [flags] ``` #### `omazy bot catalog list` List catalog bots ``` omazy bot catalog list [command] [flags] ``` | Flag | Description | |---|---| | `--app string` | App ID | | `--category string` | Category filter | | `--cursor string` | Pagination cursor | | `--featured-only` | Featured only | | `--per-page int` | Page size | | `--search string` | Free-text search | ## `omazy channel` Channels: availability, health, and takeoff ``` omazy channel [command] [flags] ``` ### `omazy channel availability` Channels usable in the current context ``` omazy channel availability [command] [flags] ``` | Flag | Description | |---|---| | `--app string` | App ID | | `--conversation string` | Conversation ID | | `--current string` | Current channel | | `--takeoff-only` | Only takeoff-eligible channels | ### `omazy channel health` Channel connectivity health ``` omazy channel health [command] [flags] ``` ### `omazy channel takeoff` Generate a deep-link channel handoff URL ``` omazy channel takeoff [command] [flags] ``` | Flag | Description | |---|---| | `--conversation string` | Conversation ID | | `--to string` | Target channel slug | ## `omazy chat` Chat sessions, messages, and conversation context ``` omazy chat [command] [flags] ``` ### `omazy chat agents` Show agent participation rollup ``` omazy chat agents [command] [flags] ``` ### `omazy chat archive` Archive a session ``` omazy chat archive [command] [flags] ``` ### `omazy chat around` Context window of messages around a target ``` omazy chat around [command] [flags] ``` | Flag | Description | |---|---| | `--message string` | Target message ID | ### `omazy chat context` Read / patch conversation context (OCC) ``` omazy chat context [command] [flags] ``` #### `omazy chat context get` Get current context (and OCC version) ``` omazy chat context get [command] [flags] ``` #### `omazy chat context patch` Patch context (uses If-Match unless --force) ``` omazy chat context patch [command] [flags] ``` | Flag | Description | |---|---| | `--force` | Skip OCC and overwrite | | `--json string` | Context JSON object (default "{}") | ### `omazy chat get` Show one session ``` omazy chat get [command] [flags] ``` ### `omazy chat list` List chat sessions ``` omazy chat list [command] [flags] ``` | Flag | Description | |---|---| | `--cursor string` | Pagination cursor | | `--limit int` | Page size | | `--status string` | Filter by status | ### `omazy chat messages` List messages ``` omazy chat messages [command] [flags] ``` | Flag | Description | |---|---| | `--cursor string` | Pagination cursor | | `--limit int` | Page size | ### `omazy chat new` Create or resume a chat session ``` omazy chat new [command] [flags] ``` | Flag | Description | |---|---| | `--app string` | App ID | | `--bot string` | Bot profile ID | | `--type string` | Session type | ### `omazy chat pin` Pin a message ``` omazy chat pin [command] [flags] ``` ### `omazy chat pinned` List pinned messages ``` omazy chat pinned [command] [flags] ``` ### `omazy chat send` Send a message ``` omazy chat send [command] [flags] ``` | Flag | Description | |---|---| | `--body string` | Message body | | `--content-type string` | Message content type | | `--reply-to string` | Parent message ID | ### `omazy chat status` Update session status ``` omazy chat status [command] [flags] ``` ### `omazy chat unpin` Unpin a message ``` omazy chat unpin [command] [flags] ``` ## `omazy config` Manage CLI configuration + profiles ``` omazy config [command] [flags] ``` ### `omazy config get` Read a config value ``` omazy config get [command] [flags] ``` ### `omazy config profile` Show / switch profiles ``` omazy config profile [command] [flags] ``` ### `omazy config set` Write a config value ``` omazy config set [command] [flags] ``` ## `omazy gdpr` GDPR: erasure (Article 17) + export (Article 20) ``` omazy gdpr [command] [flags] ``` ### `omazy gdpr erase` Erasure operations ``` omazy gdpr erase [command] [flags] ``` #### `omazy gdpr erase list` List erasure requests ``` omazy gdpr erase list [command] [flags] ``` #### `omazy gdpr erase request` Initiate an erasure request ``` omazy gdpr erase request [command] [flags] ``` | Flag | Description | |---|---| | `--reason string` | Optional reason | | `--scope string` | Erasure scope: full\|session\|message (default "full") | | `--target-id string` | Target session/message ID (when scope is not full) | #### `omazy gdpr erase session` Erase a single session ``` omazy gdpr erase session [command] [flags] ``` #### `omazy gdpr erase status` Show one erasure request ``` omazy gdpr erase status [command] [flags] ``` ### `omazy gdpr export` Export your data (streams JSON to stdout or --output-file) ``` omazy gdpr export [command] [flags] ``` | Flag | Description | |---|---| | `--force` | Allow streaming to a TTY | | `--output-file string` | Write to file instead of stdout | ## `omazy health` Probe middleware health ``` omazy health [command] [flags] ``` ### `omazy health detailed` Probe middleware detailed health (DB, Redis, etc.) ``` omazy health detailed [command] [flags] ``` ## `omazy inbox` Live conversations: claim, reply, transfer, resolve ``` omazy inbox [command] [flags] ``` | Flag | Description | |---|---| | `-a, --app omazy config set app` | App handle (default: OMAZY_APP, or omazy config set app) | | `-w, --workspace omazy config set workspace` | Workspace handle (default: OMAZY_WORKSPACE, or omazy config set workspace) | ### `omazy inbox claim` Assign the conversation to yourself ``` omazy inbox claim [command] [flags] ``` ### `omazy inbox dispositions` List the resolution reasons configured for this app ``` omazy inbox dispositions [command] [flags] ``` ### `omazy inbox escalate` Raise the conversation for supervisor attention ``` omazy inbox escalate [command] [flags] ``` | Flag | Description | |---|---| | `--reason string` | Why it needs escalating | ### `omazy inbox heartbeat` Mark yourself online (presence ages out without this) ``` omazy inbox heartbeat [command] [flags] ``` ### `omazy inbox list` List conversations in the inbox ``` omazy inbox list [command] [flags] ``` | Flag | Description | |---|---| | `--assignee string` | Filter by assigned operator id, or 'me' | | `--limit int` | Maximum conversations to return | | `--status string` | Filter by status (open, pending, resolved, …) | ### `omazy inbox messages` Show a conversation transcript ``` omazy inbox messages [command] [flags] ``` | Flag | Description | |---|---| | `--limit int` | Maximum messages to return | ### `omazy inbox presence` Show which operators are online ``` omazy inbox presence [command] [flags] ``` ### `omazy inbox reply` Send an operator reply into the conversation ``` omazy inbox reply [command] [flags] ``` | Flag | Description | |---|---| | `-m, --message string` | Reply text | | `--saved-response string` | Send a saved response by key instead | ### `omazy inbox resolve` Close the conversation with a disposition ``` omazy inbox resolve [command] [flags] ``` | Flag | Description | |---|---| | `--disposition omazy inbox dispositions` | Resolution reason key (see omazy inbox dispositions) | | `--note string` | Closing note | ### `omazy inbox show` Show one conversation with its operator metadata ``` omazy inbox show [command] [flags] ``` ### `omazy inbox transfer` Hand the conversation to another operator ``` omazy inbox transfer [command] [flags] ``` | Flag | Description | |---|---| | `--note string` | Handover note | | `--to string` | Target operator user id (required) | ### `omazy inbox why` Show why the agent answered as it did (retrieval + tool calls) ``` omazy inbox why [command] [flags] ``` ## `omazy mcp-server` Browse the public MCP servers catalog ``` omazy mcp-server [command] [flags] ``` ### `omazy mcp-server list` List public MCP servers ``` omazy mcp-server list [command] [flags] ``` | Flag | Description | |---|---| | `--cursor string` | Pagination cursor | | `--limit int` | Page size | ## `omazy notification` User notifications ``` omazy notification [command] [flags] ``` ### `omazy notification list` List notifications ``` omazy notification list [command] [flags] ``` | Flag | Description | |---|---| | `--cursor string` | Pagination cursor | | `--limit int` | Page size | | `--unread-only` | Filter to unread | ### `omazy notification read` Mark one notification as read ``` omazy notification read [command] [flags] ``` ### `omazy notification read-all` Mark every notification as read ``` omazy notification read-all [command] [flags] ``` ## `omazy ops` DevOps scripts wrapped over SSH ``` omazy ops [command] [flags] ``` ### `omazy ops backup` Run Postgres + ClickHouse backup ``` omazy ops backup [command] [flags] ``` ### `omazy ops connect` Open an SSH tunnel via the configured host ``` omazy ops connect [command] [flags] ``` ### `omazy ops deploy` Build, push, and restart on remote ``` omazy ops deploy [command] [flags] ``` | Flag | Description | |---|---| | `--service string` | Single service to redeploy | ### `omazy ops health-check` Probe endpoints + DB/Redis on remote ``` omazy ops health-check [command] [flags] ``` ### `omazy ops logs` Tail remote service logs ``` omazy ops logs [command] [flags] ``` | Flag | Description | |---|---| | `--follow` | Follow | | `--service string` | Service name | | `--tail int` | Lines to tail | ### `omazy ops migrate` Database migrations ``` omazy ops migrate [command] [flags] ``` | Flag | Description | |---|---| | `--yes` | Confirm destructive operation | #### `omazy ops migrate create` Create a new migration file ``` omazy ops migrate create [command] [flags] ``` | Flag | Description | |---|---| | `--name string` | Migration name | #### `omazy ops migrate down` Roll back one migration (destructive: requires --yes) ``` omazy ops migrate down [command] [flags] ``` #### `omazy ops migrate up` Apply pending migrations ``` omazy ops migrate up [command] [flags] ``` ### `omazy ops restart` docker compose restart ``` omazy ops restart [command] [flags] ``` | Flag | Description | |---|---| | `--service string` | Service name | ### `omazy ops setup` Initial server provisioning (interactive) ``` omazy ops setup [command] [flags] ``` ## `omazy ouchat` OU AI personal assistant ``` omazy ouchat [command] [flags] ``` ### `omazy ouchat context` Show the assistant's aggregated context ``` omazy ouchat context [command] [flags] ``` ### `omazy ouchat plan` Ask OU AI to plan a goal ``` omazy ouchat plan [command] [flags] ``` | Flag | Description | |---|---| | `--goal string` | What you want to accomplish (required) | ### `omazy ouchat start` Open an OU AI session ``` omazy ouchat start [command] [flags] ``` | Flag | Description | |---|---| | `--app-id string` | Bind the session to an app | ### `omazy ouchat suggest` Ask OU AI for next-step suggestions ``` omazy ouchat suggest [command] [flags] ``` | Flag | Description | |---|---| | `-q, --query string` | Context for the suggestions | ## `omazy realtime` Realtime WebSocket fan-out ``` omazy realtime [command] [flags] ``` ### `omazy realtime connect` Subscribe to realtime events (line-delimited JSON to stdout) ``` omazy realtime connect [command] [flags] ``` | Flag | Description | |---|---| | `--topic strings` | Topic filter (repeatable) | | `--watch` | Keep streaming until interrupted (default true) | ## `omazy search` Full-text search via OpenSearch ``` omazy search [command] [flags] ``` ### `omazy search conversations` Search conversations ``` omazy search conversations [command] [flags] ``` | Flag | Description | |---|---| | `--cursor string` | Pagination cursor | | `--limit int` | Page size | ### `omazy search messages` Search messages ``` omazy search messages [command] [flags] ``` | Flag | Description | |---|---| | `--conversation string` | Limit to one conversation | | `--cursor string` | Pagination cursor | | `--limit int` | Page size | ### `omazy search typeahead` Autocomplete suggestions ``` omazy search typeahead [command] [flags] ``` ## `omazy server-admin` Wraps middleware cmd/admin over SSH (promote/whoami) ``` omazy server-admin [command] [flags] ``` ### `omazy server-admin promote` Promote a user (idempotent + audited) ``` omazy server-admin promote [command] [flags] ``` | Flag | Description | |---|---| | `--dry-run` | Print the SSH command without executing | | `--email string` | User email | | `--role string` | Role (admin\|support\|analyst) | ### `omazy server-admin whoami` Read-only user lookup ``` omazy server-admin whoami [command] [flags] ``` | Flag | Description | |---|---| | `--email string` | User email | ## `omazy share` MCP data-share grants ``` omazy share [command] [flags] ``` ### `omazy share audit` Audit log for your grants ``` omazy share audit [command] [flags] ``` | Flag | Description | |---|---| | `--cursor string` | Pagination cursor | | `--limit int` | Page size | ### `omazy share create` Mint a new data-share grant ``` omazy share create [command] [flags] ``` | Flag | Description | |---|---| | `--expires string` | RFC 3339 expiry timestamp | | `--mcp-server string` | MCP server ID | | `--scopes strings` | Scopes (comma-separated) | ### `omazy share get` Show one grant ``` omazy share get [command] [flags] ``` ### `omazy share list` List your grants ``` omazy share list [command] [flags] ``` | Flag | Description | |---|---| | `--status string` | Filter by status (active\|revoked\|expired) | ### `omazy share revoke` Revoke a grant ``` omazy share revoke [command] [flags] ``` ## `omazy user` User self-service ``` omazy user [command] [flags] ``` ### `omazy user me` About the current user ``` omazy user me [command] [flags] ``` #### `omazy user me avatar` Upload an avatar image ``` omazy user me avatar [command] [flags] ``` #### `omazy user me settings` Read / write settings ``` omazy user me settings [command] [flags] ``` #### `omazy user me settings get` Show settings ``` omazy user me settings get [command] [flags] ``` #### `omazy user me settings set` Patch one setting ``` omazy user me settings set [command] [flags] ``` #### `omazy user me show` Show profile ``` omazy user me show [command] [flags] ``` #### `omazy user me update` Patch profile fields ``` omazy user me update [command] [flags] ``` | Flag | Description | |---|---| | `--bio string` | Bio | | `--first-name string` | First name | | `--language string` | Language (BCP 47) | | `--last-name string` | Last name | | `--timezone string` | Timezone (IANA) | ## `omazy widget` Live-chat widget: deployments, config, publish, metrics ``` omazy widget [command] [flags] ``` | Flag | Description | |---|---| | `-a, --app omazy config set app` | App handle (default: OMAZY_APP, or omazy config set app) | | `-w, --workspace omazy config set workspace` | Workspace handle (default: OMAZY_WORKSPACE, or omazy config set workspace) | ### `omazy widget config` Widget appearance + behaviour config ``` omazy widget config [command] [flags] ``` #### `omazy widget config features` Show the deployment's feature toggles ``` omazy widget config features [command] [flags] ``` #### `omazy widget config get` Print the widget's config as JSON ``` omazy widget config get [command] [flags] ``` #### `omazy widget config set` Replace the widget config from a JSON file or stdin ``` omazy widget config set [command] [flags] ``` | Flag | Description | |---|---| | `-f, --file string` | JSON file to read ('-' for stdin) (default "-") | ### `omazy widget create` Create a widget deployment ``` omazy widget create [command] [flags] ``` | Flag | Description | |---|---| | `--name string` | Deployment name (required) | ### `omazy widget domains` List domains the widget has been seen loading on ``` omazy widget domains [command] [flags] ``` ### `omazy widget events` Recent widget events ``` omazy widget events [command] [flags] ``` | Flag | Description | |---|---| | `--limit int` | Maximum events to return | ### `omazy widget list` List the app's widget deployments ``` omazy widget list [command] [flags] ``` ### `omazy widget metrics` Conversation and engagement counters ``` omazy widget metrics [command] [flags] ``` | Flag | Description | |---|---| | `--since string` | RFC3339 start time | ### `omazy widget publish` Promote the staged config to the live deployment ``` omazy widget publish [command] [flags] ``` ### `omazy widget show` Show a deployment with its current config ``` omazy widget show [command] [flags] ``` ### `omazy widget snippet` Print the HTML embed snippet for a widget ``` omazy widget snippet [command] [flags] ``` ### `omazy widget verify` Check that the embed snippet is live on a page ``` omazy widget verify [command] [flags] ``` | Flag | Description | |---|---| | `--url string` | Page URL to fetch and inspect (required) | ## `omazy workspace` Workspaces: membership, apps, and the active selection ``` omazy workspace [command] [flags] ``` | Flag | Description | |---|---| | `-a, --app omazy config set app` | App handle (default: OMAZY_APP, or omazy config set app) | | `-w, --workspace omazy config set workspace` | Workspace handle (default: OMAZY_WORKSPACE, or omazy config set workspace) | ### `omazy workspace apps` Apps in the workspace ``` omazy workspace apps [command] [flags] ``` #### `omazy workspace apps create` Create an app (and its agent) in the workspace ``` omazy workspace apps create [command] [flags] ``` | Flag | Description | |---|---| | `--handle string` | URL handle (defaults to a slug of --name) | | `--name string` | Display name (required) | #### `omazy workspace apps list` List apps ``` omazy workspace apps list [command] [flags] ``` #### `omazy workspace apps show` Show the active app ``` omazy workspace apps show [command] [flags] ``` ### `omazy workspace capabilities` Show your resolved permissions in the workspace ``` omazy workspace capabilities [command] [flags] ``` ### `omazy workspace current` Print the pinned workspace and app ``` omazy workspace current [command] [flags] ``` ### `omazy workspace list` List the workspaces you belong to ``` omazy workspace list [command] [flags] ``` ### `omazy workspace members` List workspace members ``` omazy workspace members [command] [flags] ``` ### `omazy workspace show` Show the active workspace ``` omazy workspace show [command] [flags] ``` ### `omazy workspace use` Pin the workspace future commands default to ``` omazy workspace use [command] [flags] ``` | Flag | Description | |---|---| | `--app-handle string` | Also pin this app handle | --- # SDK ## SDK Source: https://docs.omazy.ai/reference/sdk/ The Python plugin SDK. Build a plugin that adds tools, prompts, automations and webhooks to an Omazy workspace. The SDK is for building **plugins**: Python packages that extend what an agent can do beyond answering from what it has read. An agent without plugins knows things. An agent with plugins can also do things. That is the whole distinction, and it is a larger one than it sounds, because "can I check my order status" and "can you check my order status" are separated by exactly one integration. ```sh pip install omazy-plugin-sdk ``` ## A plugin is a Python module No framework to inherit from and no directory ceremony to memorise. You write functions, decorate the ones you want exposed, and ship a manifest describing what the plugin is and what it needs. ```python from omazy_plugin_sdk import tool @tool def order_status(order_id: str, ctx) -> dict: """Look up the current status of an order.""" return ctx.http.get(f"/orders/{order_id}").json() ``` The docstring is not decoration. It is what the model reads to decide whether this tool is the right one to call, which makes it the most load-bearing comment you will ever write. ## The eleven component types A plugin can contribute any of these. Most plugins use two or three. | Component | Contributes | |---|---| | **Tool** | A function the agent can call mid-conversation. | | **Resource** | Data exposed over MCP. | | **System prompt fragment** | Instructions merged into the agent's brief. | | **Automation template** | A ready-made automation the workspace can enable. | | **Webhook handler** | An endpoint for inbound calls from another system. | | **Scheduled sync** | Work that runs on a timer. | | **Lifecycle hooks** | Install, upgrade and uninstall behaviour. | | **Action** | An operation a person can trigger from the console. | | **Settings schema** | The configuration form shown at install time. | ## What `ctx` gives you Every decorated function receives a context object. It is how a plugin reaches the outside world without knowing anything about how the platform is deployed: an HTTP client with credentials already attached, the current workspace and app, storage, logging, and metering. Take the credentials point seriously. A plugin should never carry its own copy of a secret. Ask `ctx` and you get one scoped to the install, which means uninstalling actually revokes access rather than merely hiding the button. ## Metering If your plugin does something that costs money, meter it. The SDK gives you a meter to record units of work, which is what allows a workspace to see what a plugin actually costs rather than discovering it later in an invoice. ## Coming in this section Writing your first plugin, the manifest specification, testing locally, publishing, and the threat model you should read before you handle anyone else's data. --- ## Your first plugin Source: https://docs.omazy.ai/reference/sdk/first-plugin/ From pip install to a tool the agent can call, in about fifteen minutes. The shortest useful plugin is one tool and a manifest. Everything else is optional and can wait until you need it. ## 1. Install ```sh pip install omazy-plugin-sdk ``` ## 2. Write a tool ```python from omazy_plugin_sdk import tool @tool def check_stock(sku: str, ctx) -> dict: """Check whether a SKU is in stock and how many units remain.""" response = ctx.http.get(f"https://inventory.example.com/stock/{sku}") response.raise_for_status() return response.json() ``` Three things are doing real work here, and none of them are obvious: **The type hints** become the tool's parameter schema. `sku: str` is what tells the model to pass a string rather than an object it invented. **The docstring** is the tool's description. The model chooses between your tools by reading these, so write them for a capable colleague who has never seen your system. "Check stock" is worse than the version above, because it does not say what comes back. **The return value** must be serialisable. Return a dict, not your ORM object. ## 3. Describe it The manifest says what the plugin is, what it needs, and what it contributes. It is also where you declare settings, so the console can render an install form without you writing any UI. ## 4. Run it locally Run the plugin server and point a development workspace at it. You get the same call path a published plugin gets, so anything that works here works installed. ## 5. Test the boring failures first New plugins usually break in the same four places, in the same order: 1. The upstream API is slow and the tool times out. 2. The upstream API returns an error and the tool raises instead of explaining. 3. The model calls the tool with a plausible but wrong argument. 4. The tool works and returns something the model cannot summarise. Number two is the one worth fixing early. A tool that raises gives the agent nothing to say. A tool that returns `{"error": "no such SKU"}` lets the agent tell the customer something true. Failing informatively beats failing loudly. ## What not to do Do not store credentials in your plugin. Ask `ctx`. An install-scoped credential disappears when the plugin is uninstalled, and a hard-coded one does not, which is the difference between an uninstall and a rumour of one. --- # LLM Gateway ## LLM Gateway Source: https://docs.omazy.ai/reference/llm-gateway/ One door to every model provider, with budgets, guards and per-call cost recorded on the way through. Every answer your agent gives costs money and takes time, and both are easy to discover only at the end of the month. The gateway is the single door all model traffic goes through, which is what makes those two numbers visible while you can still do something about them. Calling a provider directly is a bit like giving everyone in the building their own front door key. It works right up until you want to know who came in. ## What it does | Surface | Answers | |---|---| | **Providers** | Which vendors and models are available, and whose keys pay for them. | | **Budgets** | How much may be spent, over what period, by whom. | | **Guards** | Which models are permitted, and what a request may contain. | | **Usage** | How many calls, which models, how many tokens. | | **Spend** | What it actually cost. | | **Forecast** | What it is on track to cost. | ## Bring your own keys You can run on the platform's pooled capacity or supply your own provider credentials. Own keys mean your own rate limits and your own bill, which is usually what a larger workspace wants once traffic is predictable. Credentials are stored encrypted and are never returned by the API after they are set. If you need to know whether a key still works, the gateway will tell you it is healthy. It will not tell you what it is, and neither will anybody else, which is the entire point. ## Budgets and guards are different tools They get conflated, so it is worth separating them plainly. A **budget** is about money. It caps spend over a period and it does not care which model burned it. A **guard** is about permission. It decides whether a particular request is allowed at all: which models may be used, and what may be sent to them. A budget stops you spending too much on the right thing. A guard stops you doing the wrong thing cheaply. You want both. ## When a provider has a bad day Upstream rate limits do not always arrive wearing a name badge. A gateway can return a generic unavailable error when what actually happened is that your provider account hit its tokens-per-minute ceiling. If calls start failing in bursts and recovering on their own, suspect your provider's throughput limit before you suspect the model. ## Coming in this section Configuring providers and keys, setting budgets, writing guards, and reading usage against spend. --- ## Providers and models Source: https://docs.omazy.ai/reference/llm-gateway/providers/ Connecting a model provider, choosing a model, and what happens when one is unavailable. A provider is a vendor account. A model is one thing that vendor will run for you. Connecting the first does not automatically give you every one of the second, and assuming otherwise is the most common surprise here. ## Connecting a provider Console, Workspace, LLM providers. You supply a credential, the gateway probes it, and the provider comes up healthy or it does not. The probe uses one nominated model. A healthy provider means that model answered. It does not certify every model the vendor lists, so treat health as "the door opened", not "everything inside works". ## The model catalogue The gateway caches each vendor's advertised model list so the console has something to show without calling out on every page load. Two consequences worth knowing: - A model added by the vendor this morning may not appear in the list yet. - Vendors will usually serve models that were never in the list at all, so an absent name is not proof of an unavailable model. The catalogue is a convenience, not a contract. ## Choosing a model The temptation is to pick the largest model available and stop thinking about it. Resist it, for two reasons that have nothing to do with cost. Bigger models are slower, and in a live chat a slow correct answer loses to a fast good one more often than anyone likes to admit. And a model that is excellent at reasoning is not automatically excellent at following a brief, which is most of what an agent actually does. Change the model, then run your agent's tests before publishing. Model choice is a configuration change with the blast radius of a code change. ## Failure modes | Symptom | Usual cause | |---|---| | Bursts of failures that clear on their own | Provider throughput limit, not the model | | Every call fails immediately | Credential rejected or revoked | | One model fails, others fine | That model was deprecated or renamed by the vendor | | Answers arrive but are truncated | An output token limit is set lower than the answer needs | That last one deserves a word. An unset maximum is not the same as an unlimited one. Leave it unset and you inherit a default, and the default is smaller than you think. Long answers get guillotined mid-sentence, which reads to a customer as the agent losing its train of thought. --- # Platform ## Platform Source: https://docs.omazy.ai/reference/platform/ Architecture, the response envelope, auth, rate limits, error codes and versioning. How the platform behaves, for people building against it rather than clicking through it. ## What lives here | Topic | Covers | |---|---| | **Architecture** | The services, what each owns, and how a message becomes an answer. | | **Response envelope** | The `success`, `data` and `meta` shape every endpoint returns. | | **Auth** | Install-scoped tokens, PKCE, and how revocation works. | | **MCP server** | The tool surface an MCP client administers a business through, and the OAuth flow that reaches it. | | **Rate limits** | The limits, the headers, and what to do when you hit one. | | **Error codes** | Every code the platform emits, what triggers it, and what to do. | | **Versioning** | What is stable, what can change, and how changes are announced. | | **Changelog** | What shipped, in order. | ## Error codes The error code is the stable part of a failure. Messages get reworded, HTTP status codes are coarse, but the code is what you should branch on and what support will ask you for. All 33 of them are listed in [Error codes](/reference/platform/error-codes/), with what triggers each one and what to do about it. ## MCP server An MCP client can administer a business over [the MCP server](/reference/platform/mcp-server/): 26 tools covering the agent and its Brief, knowledge, catalog and widget, reached through OAuth 2.1 with per-connection read-only or read-write access. ## Coming in this section The architecture overview, rate limits, and the changelog. --- ## Error codes Source: https://docs.omazy.ai/reference/platform/error-codes/ Every error code the platform emits, what triggers it, and what to do about it. When something fails, the payload carries a stable `code`. That code is the part you should branch on. Messages get reworded by whoever is improving the wording that week. HTTP statuses are coarse: a 403 could be four genuinely different problems with four genuinely different fixes. The code is the only part that is both specific and promised not to change. Treat the message as something to show a person and the code as something to write an `if` about. ```json { "success": false, "error": { "code": "WORKSPACE_SUSPENDED", "message": "workspace is suspended" } } ``` ## Authentication and permission You are not who you need to be, or you are but you still may not do that. | Code | Status | Trigger | What to do | |---|---|---|---| | `UNAUTHORIZED` | 401 | No token, or the token was rejected | Sign in again. `omazy auth login` | | `FORBIDDEN` | 403 | Authenticated, but the role lacks the permission | Ask an owner to widen the role | | `INSUFFICIENT_SCOPE` | 403 | The token is valid but was not granted this scope | Re-authorise with broader consent | | `ACCOUNT_REQUIRED` | 403 | The action needs a completed account | Finish account setup, usually an email | | `LEGAL_HOLD` | 403 | A legal hold blocks this change | Not overridable. Talk to whoever set it | | `WORKSPACE_SUSPENDED` | 423 | The workspace is suspended | An owner resolves billing | The distinction between `UNAUTHORIZED` and `FORBIDDEN` is the one people get backwards. `UNAUTHORIZED` means we do not know who you are. `FORBIDDEN` means we know exactly who you are and the answer is still no. Retrying the login fixes the first and does nothing at all for the second. ## Plan and limits Nothing is broken. You have simply asked for more than the current plan or the current minute allows. | Code | Status | Trigger | What to do | |---|---|---|---| | `PAYMENT_REQUIRED` | 402 | Payment is needed to continue | Check Billing | | `UPGRADE_REQUIRED` | 403 | The feature exists on a higher plan | Upgrade, or use the included alternative | | `RATE_LIMIT_EXCEEDED` | 429 | Too many requests | Honour `Retry-After`, back off exponentially | | `COMPUTE_BUDGET_EXCEEDED` | 429 | Refresh rate or compute budget exceeded | Poll less often | | `PAYLOAD_TOO_LARGE` | 413 | The body exceeded the size limit | Send less, or upload and reference it | | `MODEL_NOT_ALLOWED` | | The requested model is blocked by a guard | Pick a permitted model, or change the guard | On `RATE_LIMIT_EXCEEDED`, add jitter to your backoff. Retrying immediately from every worker at once turns a rate limit into an outage, and the limit was the thing trying to prevent that. ## Capability not configured The feature exists. It has not been switched on here. | Code | Status | Trigger | |---|---|---| | `NOT_CONFIGURED` | 501 | The thing exists but is not set up for this app | | `NOT_IMPLEMENTED` | 501 | The operation is not available on this deployment | | `STORAGE_DISABLED` | 503 | Object storage is not configured | | `SEARCH_DISABLED` | 503 | The search backend is not configured | | `ANALYTICS_DISABLED` | 503 | The analytics store is not configured | | `INGEST_DISABLED` | 503 | Event ingest is switched off | | `INSPECTOR_UNAVAILABLE` | 503 | The queue inspector is not configured | | `CIPHER_UNAVAILABLE` | | Credential encryption is not configured | These are the friendliest failures on this page, because the fix is a setting rather than a rewrite. `CIPHER_UNAVAILABLE` is the one to escalate rather than work around: it means the server cannot encrypt a credential you were about to give it, and a stored secret is not something to improvise. ## Validation and conflict The request was understood and refused on its merits. | Code | Status | Trigger | What to do | |---|---|---|---| | `BAD_REQUEST` | 400 | Malformed or missing parameters | Read the message, fix the request | | `UNPROCESSABLE_ENTITY` | 422 | Well-formed but not valid here | Usually an id that belongs to another app | | `FORM_VALIDATION` | 422 | One or more fields failed validation | Field details are in the payload | | `UNSUPPORTED_MEDIA_TYPE` | 415 | Wrong content type | Check `Content-Type` | | `NOT_FOUND` | 404 | No such object, or not visible to you | Check the id, and check the scope | | `CONFLICT` | 409 | The change collides with current state | Re-fetch and retry | | `CHANNEL_EXISTS` | 409 | That channel is already connected | Edit the existing one | | `HANDLE_TAKEN` | | The handle is in use | Pick another | | `HANDLE_RESERVED` | | The handle is reserved | Pick another | | `LAST_OWNER` | | Removing or demoting the only owner | Promote someone first | `NOT_FOUND` deserves suspicion. It is also what you get when the object exists but sits in a different workspace or app than the one your request was scoped to. Before hunting for a deleted record, check that you are pointed where you think you are. `omazy --debug` will tell you in one line. `LAST_OWNER` exists because a workspace with no owner cannot be recovered by anyone inside it. The rule is not being difficult, it is the only thing standing between you and a locked room with the key inside. ## Upstream and internal Not your request. | Code | Status | Trigger | What to do | |---|---|---|---| | `UPSTREAM_REJECTED` | 502 | A provider refused the call | Check provider health and credentials | | `UPSTREAM_UNAVAILABLE` | 503 | A provider could not be reached | Retry with backoff | | `INTERNAL_ERROR` | 500 | Something failed on our side | Retry once, then send us the request id | For `INTERNAL_ERROR`, capture the request id. It is the difference between us finding the exact failure in seconds and asking you to describe what happened. ## The CLI explains some of these for you The CLI attaches a suggested remedy to the codes it recognises, so a failed command tells you what to do rather than only what went wrong. It covers 18 of the codes above. It also still carries four remedies for codes this platform does not emit (`AUTH_TOKEN_EXPIRED`, `AUTH_TOKEN_REPLAY`, `BUSINESS_NOT_AUTHORIZED`, `OCC_CONFLICT`). They are harmless, and they are listed here so nobody spends an afternoon looking for where `OCC_CONFLICT` comes from. The answer is that it does not. --- ## MCP server Source: https://docs.omazy.ai/reference/platform/mcp-server/ The workspace MCP server: transport, OAuth discovery and authorization, the access model, and every tool with its arguments and required role. The workspace MCP server lets an MCP client administer one business. It speaks Streamable HTTP with JSON-RPC 2.0 request bodies. ``` POST https://mw.omazy.ai/api/v1/mcp ``` `POST` is the only method. `GET` returns 405: there is no server-initiated stream. For connecting a client, see [Connect an AI assistant](/connect/mcp/). This page is the specification. ## Server identity | Field | Value | |---|---| | `name` | `omazy-admin` | | `title` | Omazy Workspace | | `capabilities` | `tools` | `description`, `websiteUrl` and `icons` are also returned, but only when the negotiated protocol revision defines them. Older revisions get the three fields above and nothing else, because sending fields a revision does not define is how you break strict clients. ## Protocol negotiation Send your revision in `initialize`. The server answers with the revision it will actually use, which may be older than the one you asked for. Use the value in the response, not the one you sent. ## Authorization **Every method requires a token, including `initialize` and `ping`.** There is no unauthenticated handshake. This is deliberate and worth understanding if you are building a client. Answering `200` to an unauthenticated `initialize` tells the client that no authorization is needed here, so after it completes OAuth it has no auth requirement recorded and never attaches the token it just obtained. The connection then fails in a way that looks like the server rejected a valid key. An unauthenticated call gets `401` with a challenge: ```http WWW-Authenticate: Bearer realm="OAuth", resource_metadata="https://mw.omazy.ai/.well-known/oauth-protected-resource", error="invalid_token", error_description="Missing or invalid access token" ``` That header is the entry point. A client with no prior knowledge of this server can start from the `401` alone. ### Discovery Both documents sit at the origin root, per RFC 9728 and RFC 8414. | Document | Path | |---|---| | Protected resource | `/.well-known/oauth-protected-resource` | | Authorization server | `/.well-known/oauth-authorization-server` | The protected-resource document names the resource, the authorization server, and the scopes: ```json { "resource": "https://mw.omazy.ai/api/v1/mcp", "resource_name": "Omazy Workspace", "authorization_servers": ["https://mw.omazy.ai"], "bearer_methods_supported": ["header"], "scopes_supported": ["mcp:read", "mcp:write"] } ``` ### OAuth endpoints | Purpose | Endpoint | |---|---| | Dynamic registration (RFC 7591) | `/api/v1/oauth/register` | | Authorize | `/api/v1/oauth/authorize` | | Token | `/api/v1/oauth/token` | | Revoke (RFC 7009) | `/api/v1/oauth/revoke` | | Property | Value | |---|---| | Grant types | `authorization_code`, `refresh_token` | | Response types | `code` | | PKCE | `S256`, required | | Client authentication | `none` (public clients) | | Scopes | `mcp:read`, `mcp:write` | Registration is open, and the `redirect_uri` allowlist is frozen at registration and matched exactly. Hosted `https://` callbacks are accepted, as are loopback `http://` on `127.0.0.1`, `::1` and `localhost`, and custom schemes. Public plain-http and URIs carrying a fragment are rejected. Authorization codes are single-use and expire in 60 seconds. Refresh tokens rotate on use, and replaying a spent one revokes the entire token family including its access key, on the assumption that a replay means a copy leaked. ### Access tokens An issued access token is an ordinary workspace API key. The OAuth flow is a minting front-end, not a second identity system, so the auth gate, the role model and the request log are the same whether a key arrived through OAuth or was minted by hand. Keys last 7 days by default, 90 at the most, and are stored hashed. Refresh before expiry or reconnect. ## The access model Three things bound what a call can do. All three apply. **Workspace scope.** A key is bound to one workspace, decided at approval time from the approver's real membership. A client cannot widen it by asking, and a tool that needs a workspace refuses an account-scoped key outright. **Role.** Tools declare a minimum role, and the key carries the approver's actual role. `RoleAccount` tools are reachable by any authenticated key, `RoleMember` needs member or above, `RoleManager` needs manager or above. **Scope allowlist.** A key may carry an explicit list of tool names. When present, anything not on it is refused regardless of role. This is what implements read-only, and it is the reason read-only is not expressed by role alone: `workspace.create` is an account-scoped tool, so a member-role key could otherwise still reach it. A read-only connection is issued at member role carrying this allowlist: ``` workspace.list workspace.get app.list knowledge.list knowledge.search account.readiness agent.get agent.brief agent.brief_item_versions widget.list widget.config_get ``` Anything absent is unavailable, so an error in that list withdraws access rather than granting it. ## Tools 26 tools. `app` is optional everywhere it appears and defaults to the workspace's default app. ### Business and apps | Tool | Role | Required | Does | |---|---|---|---| | `workspace.list` | account | | Businesses the account belongs to. | | `workspace.get` | member | | The business this key is scoped to. | | `workspace.create` | account | `business_name` | New business, default app, draft agent. | | `app.list` | member | | Apps in the business. | | `account.readiness` | member | | Onboarding readiness score. | ### Agent | Tool | Role | Required | Does | |---|---|---|---| | `agent.get` | member | | Full agent config: identity, live prompt, model config, capabilities, suggestion pills, and which layer owns the prompt. | | `agent.update` | manager | | Agent metadata. Refuses `system_prompt` on a Brief-managed agent. | | `agent.suggestions_set` | manager | `rules` | Replace the answer suggestion pills. Live immediately. | ### Agent Brief The Brief is the versioned source the system prompt is composed from. Edits are staged; nothing reaches the live agent until you publish. | Tool | Role | Required | Does | |---|---|---|---| | `agent.brief` | member | | Items, composed text, published text, and `dirty`. | | `agent.brief_item_set` | manager | | Create a block, or edit one by `item_id`. | | `agent.brief_item_enable` | manager | `item_id`, `enabled` | Include or exclude a block. | | `agent.brief_item_versions` | member | `item_id` | Prior versions of a block, newest first. | | `agent.brief_item_restore` | manager | `item_id`, `version` | Roll a block back. | | `agent.brief_item_delete` | manager | `item_id` | Delete a block and its history. | | `agent.brief_publish` | manager | | Compose the enabled blocks into the live prompt. | ### Knowledge, help centre, catalog | Tool | Role | Required | Does | |---|---|---|---| | `knowledge.list` | member | | Documents on the agent. | | `knowledge.search` | member | `query` | Retrieval over the knowledge base. | | `knowledge.add` | manager | `kind`, `title` | Add a document. Indexed on publish. | | `helpcenter.add_article` | manager | `title`, `body` | Add a help article. | | `catalog.add_product` | manager | `name` | Add a product. | ### Widget | Tool | Role | Required | Does | |---|---|---|---| | `widget.list` | member | | Deployments, with ids for the config tools. | | `widget.config_get` | member | `widget_id` | The editable config document. | | `widget.config_set` | manager | `widget_id`, `config` | Replace the config. Draft unless `publish` is true. | | `widget.create` | manager | | Create and publish a widget, returning the install snippet. | `widget.config_set` replaces the whole document. Read the current one, apply your edits, send it all back. ### Onboarding | Tool | Role | Required | Does | |---|---|---|---| | `intake.propose` | manager | `website_url` | Read a website and propose brand, persona and FAQs. | | `onboard_business` | manager | `website_url` | Run the onboarding pass end to end. | ## The prompt is generated, not stored `agents.system_prompt` is a derived column on any agent that has Brief items. Publishing composes the enabled blocks and overwrites it wholesale. A direct write to the prompt therefore survives only until the next publish. The change looks applied, the agent answers differently for a while, and then it reverts with no record of what it replaced. `agent.get` reports which layer owns the prompt: ```json { "prompt": { "characters": 6037, "source": "brief", "brief_items": 11, "warning": "system_prompt is composed from the Brief and will be overwritten on the next publish..." } } ``` `source` is `brief` or `direct`. When it is `brief`, `agent.update` refuses a `system_prompt` write and names the tools to use instead. It also refuses when it cannot determine the answer, because an irreversible overwrite is not a safe default for an unknown. Pass `force: true` to override, accepting that the next publish undoes it and the Brief is not updated to match. The supported path is `agent.brief_item_set`, then `agent.brief_publish`. ## Failures Protocol failures come back as JSON-RPC errors: `-32700` parse error, `-32601` unknown method, `-32602` unknown tool, `-32001` unauthorized. Tool failures do not. They return a normal result carrying `isError`, which is what lets a model read the message and correct itself: ```json { "content": [{ "type": "text", "text": "this agent's system_prompt is composed from 11 Brief items..." }], "isError": true } ``` The message is prose, meant to be read. A stable failure code is recorded against every call in the activity log, but it is **not** returned in the response, so there is currently nothing machine-readable to branch on. Treat `isError` as the signal and the text as the explanation. ## Observability Every call is recorded with its tool, outcome, failure code and duration. Arguments are never stored. Owners and managers can read this in the console under **Settings → Connected AI**, alongside each connection's last-used time and call count. A connection showing zero calls has never reached the server, which distinguishes a client that failed to connect from one that connected and did nothing. ---