Outside Agent
Build & publish AI agents
- Category
- AI
- Primary Subcategory
- AI Agent Builders & Deployment Platforms
Integration details
Description
Outside Agent turns a plain-language brief into a hosted AI agent you can publish to SMS, voice, and web without leaving your coding agent. Describe what the agent should do and it composes the full configuration, grounds it in your own content, runs a safety review, and ships it. Create agents, seed a knowledge base, preview replies before publishing, issue a phone number, and manage everything you’ve built, all through natural conversation.
- Integration type
- Plugin
- Verification status
- Not applicable
- Platform
- ChatGPT
- Primary Subcategory
- AI Agent Builders & Deployment Platforms
- Secondary Subcategories
- None listed
- Brand
- Outside Agent
- Access
- Account required
- First tracked
- 2026-07-28
- Tool count
- 59
- Geography
- US
The Primary Subcategory used for this profile’s headline score.
Other Subcategories where the Integration is listed.
ChatGPT Plugin Discovery Score
ChatGPT Plugin discovery is coming soon
ChatGPT can surface a Plugin when it matches a user's request.Your Plugin Discovery Score measures how often yours appears.
No spam. Unsubscribe any time.
What discovery looks like

Competing in ChatGPT AI Agent Builders & Deployment Platforms
View Category59 tools agents can invoke
Add a re-runnable unit test for ONE capability step — a persisted `{ input -> expected }` case, scored 0..1 when run. Pass `agentId` and `eval: { capabilityInstanceId, name, input, expected }`. `capabilityInstanceId` comes from listEvalTargets. `input` is the step's input — a command payload (e.g. an enum-classifier's `{ variables: {...} }`), a knowledge-base `{ query }`, or a composer `{ message }`. `expected` is one of: `{ kind: 'deterministic', matcher: { mode: 'equals'|'contains'|'regex', path?, value } }` (exact/structural, scores 0 or 1); `{ kind: 'judge', modality: 'text'|'image', criteria }` (a model grades the output 0..1 against the prose criterion); or `{ kind: 'tool-selection', expect: [tools], forbid: [tools] }` (the composer must call `expect` and must not call `forbid`). `moduleId` and the kind are derived server-side; the input and expectation are validated against the capability. Ideal to seed several at once so a creator can verify a step keeps working across edits. Pass the OPTIONAL `intentId` to LINK this eval to the `behaviorIntents[].id` it verifies (it must name a real intent on the agent): a `must` intent with a linked, passing eval is treated by the publish review as behaviorally covered, and a change that makes that eval fail is BLOCKED as a regression (ADR 0006) — the durable way to keep a requirement working across sessions. Seed a linked eval from the intent's `examples` (situation → expectedBehavior).
addCapabilityEval
Add a SCENARIO eval — a re-runnable conversation test against the WHOLE agent, not one capability instance. `eval.script.turns` is a short STATIC script of end-user messages (1..12 `{ message }` turns) dispatched in order on ONE conversation, so later turns see earlier ones as real history; keep each message meaningful regardless of the agent's exact wording in between. `eval.expected` scores the resulting transcript: `{ kind: 'judge', modality: 'text', criteria }` grades the WHOLE conversation 0..1 against prose criteria (the usual choice), `{ kind: 'deterministic', matcher }` structurally matches the agent's FINAL reply, `{ kind: 'tool-selection', expect, forbid }` set-matches every tool called across ALL turns. Scenario evals are what make persona rules ('never give medical advice') and multi-step flows ('collect name then phone') testable — free-text behavior no capability eval can target. Link `eval.intentId` to the behaviorIntent the case VERIFIES (validated against the draft) so the Quality report and the ADR-0006 publish gate count it; tag `eval.caseTag` ('happy' | 'edge' | 'adversarial') so the requirement's RIGOR is honest — a requirement only shows VERIFIED once it has ≥2 tests including an edge or adversarial probe. Returns the persisted eval. Run it with runCapabilityEval (pass the returned id) or as part of runQualitySuite.
addScenarioEval
Add an E.164 phone number to a PRIVATE agent's allow-list — the texters/callers (and signed-in web phone identities) allowed to reach it. The OWNER is always allowed and need not be listed. Idempotent (a duplicate returns the existing entry). `label` is an optional owner-facing note (e.g. "Spouse"). No effect on a public agent.
addAllowedNumber
Put ONE question straight to the creator and wait for their answer — the live 'grill' step. Pass `question` and, when the answers are few and known, `options` (2–6) for a pick list; always recommend an answer in the question text. Use it when you need the creator's decision and can't infer it from the catalog or what they've told you — one question at a time. Returns `{ elicited: true, answer }` when they reply. A live prompt needs a client that supports elicitation on a session-backed connection; when that isn't available the tool returns `{ elicited: false, note, question }` for you to ask the creator directly in chat instead — that is NOT an error. Either way, never answer on the creator's behalf for a decision that's theirs.
askCreator
Report this build server's health and WHICH publish-gate judge it runs: `{ ok, judge: "gemini" | "fake" | "unknown", knowledgeTextSeeding: "available" | "unavailable-no-embedder", server }`. `gemini` is the real Vertex safety/alignment review; `fake` is the deterministic stand-in (fine for non-LLM work, but not a real safety verdict). `knowledgeTextSeeding` reports whether the `seedKnowledgeText` tool is wired — it needs an embedding indexer (Gemini/Vertex), so when it's `unavailable-no-embedder` a knowledge-grounded agent CANNOT be seeded or exercised over MCP on this server (check this BEFORE building a KB scenario). Read-only; takes no input.
builderHealth
Read the outcome of a publish started by publishAgent, by the `buildId` it returned. Use this ONLY when publishAgent (or a prior getPublishStatus) returned `{ pending: true, buildId }` — the safety review is still running server-side. While in flight it returns `{ pending: true, buildId }` again (poll again in a few seconds); once done it returns the SAME shape publishAgent would have: the public link on success, or the blockers/failure to fix. Read-only — it does NOT start a new build (publishAgent does). `notFound` means the buildId is unknown for this creator.
getPublishStatus
Set the agent's full behavior config in one call: `purpose` (required), plus optional `audience`, `capabilities` (each `{ id, config }` where `id` is a getCatalog module id — there is NO `module` field on authored capabilities; the catalog's `authoredExamples` show this exact shape, distinct from the compiled `examples`), natural-language `guidance` (safety + handoff), and `channels` (use ["web"] for a shareable link). Overwrites any previous config. GUIDANCE SHAPE + SCOPE: each `guidance` item is `{ kind: "safety" | "handoff", text }` (a bare string is accepted as `kind: "safety"`). A PROSE-only item is a DECLARATIVE BOUNDARY the agent respects ("never share pricing", "this agent respects opt-out") — it rides verbatim into the system prompt and needs NO runtime wiring; a pure prohibition with no matching command is fine and publishes clean (prompt-only). To ENFORCE a boundary — block / escalate / notify WHEN a condition holds — give the SAME item the structured `{ when, condition?, then }` body a `reactiveRules` entry uses (a "safety" line IS a reactive rule on the reply event): e.g. `{ kind: "safety", text: "Never share billing before the caller is verified", when: { event: "MessageReceived" }, condition: { all: [{ field: "state.verified", op: "eq", value: false }] }, then: { command: "EscalateToHuman", target: "human-in-the-loop" } }` (optional `id` names the compiled policy). A structured guidance line compiles DETERMINISTICALLY (no LLM), same grammar and same synchronous payload/target validation as a reactive rule. PROSE guidance is ALWAYS prompt-only — it is NEVER compiled into a policy, so a prose line can't block a publish no matter what it says. So express anything the agent must DO as a structured guidance body (for a safety/handoff boundary) or a `reactiveRules` + `signals` behavior — NOT as prose hoping it becomes an action. Add the structured body ONLY when the line names an action to TAKE; leave a plain boundary as prose. Every `capabilities[].id` MUST be a real catalog id — an unknown id is rejected immediately with the list of valid ids (in the error's JSON detail), so call getCatalog first instead of guessing. Each capability's `config` is ALSO validated against that module's configSchema at write time — a wrong-shaped config (e.g. a FLAT data-table config where `source` must be the object `{ event, keyField, fields }`) is rejected with the failing field path, not deferred to a `simulateBuild` `invalid instance config` failure. For a NO-LLM agent (a fixed-script / rule-only bot — e.g. always reply the same line), set `replyMode: "deterministic"` and drive it entirely with `reactiveRules` (e.g. a rule `when {event: "MessageReceived"}` → `then {command: "SendMessage", target: "messenger", payload: {message: {const: "…"}}}`, with `messenger` in `capabilities`); the language model is then omitted everywhere. Reactive rules set through this tool are auto-confirmed (there is no separate confirm step on this surface), and `reactiveRules[].intentIds` is OPTIONAL — omit it for a self-contained rule and the compiler links it to a synthesized intent (no paraphrased `behaviorIntents` boilerplate needed). REVIEW DELTA by replyMode: `deterministic` runs only fast structural checks at publish (no AI judge), while `agentic` ALSO runs the LLM safety + purpose-alignment judge — so an agentic agent can surface safety/alignment blockers a deterministic one won't. Attaching `knowledge-base` only MARKS the agent as knowledge-backed — its documents are NOT set here. Add reference TEXT directly with the `seedKnowledgeText` tool (when it's available — it needs the embedding indexer); FILE documents (PDFs, docs) are uploaded by the creator in the Console via the returned `knowledgeBase.manageUrl` (with a `note`). An EMPTY knowledge base is fine — publishing still works; retrieval just finds nothing until documents are added. Attach the `web-search` capability (empty config) to give the agent a search_web tool for current, time-sensitive facts (weather, news, prices, scores) that it grounds in live web results with sources — attach it only for agents that need live info, not static persona-only ones. To have the agent produce a PDF (a quote summary, a one-time pricing sheet, an onboarding doc), attach the `document-gen` capability and route a signal to its `GenerateDocument` command — the agent's composer authors the document BODY as markdown, an optional `template` ("plain" | "quote-summary" | "pricing-sheet") adds branded chrome from `templateData` (e.g. `brandName`, `accentColor`), and per-agent brand defaults live in its `config` (`{ id: "document-gen", config: { defaultTemplate, brandName, accentColor } }`); it emits `DocumentGenerated` carrying the file's `url`. To EMAIL that PDF, attach the `email` capability and add a `reactiveRules` entry `when {event: "DocumentGenerated"}` → `then {command: "SendEmail", payload: {to: {ref: "chain.<RequestEvent>.last.payload.email"}, subject: {const: …}, body: {const: …}, attachmentUrl: {ref: "event.url"}, attachmentFilename: {const: "quote.pdf"}}}` — the recipient `email` is NOT on `DocumentGenerated`, so pull it from the upstream request event via `chain.` (no need to thread it through the PDF step); the attachment is delivered by URL (defaults to an `application/pdf` named `attachment.pdf`). For a SHARED brand identity across BOTH the agent's generated PDFs and its HTML emails, set the top-level `branding: { accentColor?, brandName?, logoUrl? }` block instead of configuring each capability separately: at publish it is folded into the `document-gen` and `email` capabilities as DEFAULTS (a value you set directly on a capability still wins), so the PDF accent + branded chrome and the email link/button/rule color all match. `accentColor` is a 3- or 6-digit hex like `#1F9D6B` (the email button text auto-picks black/white for contrast); `brandName`/`logoUrl` render in the branded PDF templates. Fonts are NOT customizable. To enable human-in-the-loop, set `escalation: { enabled: true }` (optionally `acknowledgement` — what the user is told when it escalates — and `notify` — who to alert); the agent then hands hard questions to a person and relays the answer back, and the result returns an `escalation.inboxUrl` where the creator answers them. Every escalation carries a structured context briefing combed from the conversation (summary, key details, annotated media, a recent-transcript excerpt) into both the notice email and the inbox, so the person can answer without re-reading the thread — set `escalation.contextGuideline` (e.g. "always include the order number, plan tier, and any error screenshots") to steer what that briefing extracts. If the agent ALSO has a `knowledge-base`, set `escalation.knowledgeFallback: { enabled: true }`: it gives the agent an `escalate_unanswered` tool that searches the KB FIRST and only hands a question off on a genuine no-hit (so it never escalates a question the corpus can answer — the common "what are your hours?" → human bug), plus `route_to_human` for requests that need a person by design. A no-hit emits a routable `QuestionUnanswered` event — routed to the inbox by default when escalation is enabled, and/or to any `reactiveRules` entry `when { event: "QuestionUnanswered" }` you add (a connector/email); knowledgeFallback even works with `escalation.enabled: false` to route no-hits WITHOUT a live operator (add such a rule or the question dead-letters, which review warns about). A `behaviorIntents[].examples[]` entry may carry an optional `eventPayload` (a JSON object) — the exact trigger-event payload that example's `simulateBuild` scenario fires with (merged over the auto-generated stub). Pin it when the behavior's outcome depends on specific field VALUES (e.g. a personalized reminder), so the simulation is faithful rather than just type-correct. A `behaviorIntents[]` entry may also carry an optional `sourceQuote` — the verbatim line from the source you captured with `recordSourceRequirements` that this intent was built to cover. It's pure provenance (it changes no behavior), but pinning it lets the publish review's requirement-coverage pass trace each intent back to the ask it satisfies and more reliably spot a requirement you dropped — so quote the creator's own words when an intent comes straight from something they said. PROTECTED REQUIREMENTS (multi-session safety): a `behaviorIntents[]` entry with `priority: "must"` is a durable commitment — once the agent has been published with it, a later setAuthoredConfig that DROPS or DOWNGRADES that must intent is REJECTED unless you pass `acknowledgeRemovals: [<the intent id>]` to confirm the removal is intentional (the error names exactly which ids need acknowledging). This stops a fresh editing session from silently regenerating over a hard-won requirement. Back a must intent with a capability eval that VERIFIES it (see addCapabilityEval's `intentId`): the publish review then requires each must intent to carry a passing eval and BLOCKS a change that makes a previously-passing eval fail — turning "a policy covers this" into "the agent still actually does this". To make the agent react to a CUSTOM domain fact it recognises in conversation (a candidate passed screening, an order was placed), declare it in `signals` — `[{ event: "CandidateScreened", description, fields: [{ name, description?, type? }] }]` — then write a `reactiveRules` rule whose `when.event` is that signal. A rule whose `when.event` is neither a built-in event nor a declared signal is rejected, so a typo'd event name is caught. For an OWNER-ONLY action on an agent that serves BOTH the owner and customers (e.g. "email me the demo pricing sheet", "record a quote correction", "trigger a payout"), set `audience: "creator"` on the signal — the composer then exposes that signal's tool ONLY on a turn the channel attested as the creator/owner (an SMS from the agent's `ownerPhone`, or the signed-in owner on web), so an end user can't prompt-inject it. This is the STRUCTURAL way to gate an owner action: prefer it over relying on `guidance` prose, which a prompt injection can talk past. Omit `audience` for a normal signal anyone may trigger. A `reactiveRules` entry can ALSO carry `audience: "creator"` (the behavior dual): the runtime skips that rule unless the turn's caller is the owner OR the system — so a scheduled owner-only digest (`when {event: "TimerFired"}`) still fires (system turns are trusted), while an end user can't trigger it. Use the rule-level `audience` when the owner-only behavior fires on an event an end user can cause some OTHER way than a creator-audience signal (gating the signal already blocks the signal-triggered path). TO KEEP A DURABLE PER-PERSON RECORD the agent updates over time (an RSVP/guest list, a waitlist, a status tracker), attach a `data-table` (config `{ name, source: { event, keyField, fields } }`) and write its rows from such a signal rule via `then: { command: "UpsertRecord", target: "<the data-table>", payload: { <keyField>: { ref: "endUser.id" }, <field>: { ref: "event.<field>" } } }`. Binding the table's keyField to `{ ref: "endUser.id" }` — the trusted, server-known identity of the person in the conversation, NOT a signal/event field the model fills — is what makes a returning user UPDATE their one row instead of creating a duplicate, and stops the model forging or mis-keying the identity (the derived signal tool then only ever asks for the answer fields). For a ROSTER the creator SEEDS and the agent UPDATES over time (seed each person's `name` + phone with `status: "pending"`, then a reply updates only the `status`), set `merge: true` on the data-table config so an upsert is a PARTIAL update that merges the fields it carries onto the existing row and KEEPS the rest (the seeded name survives the status change); default replaces the whole row. Call getRecipe `rsvp-guest-list` for the complete worked shape. For an AGENT-WIDE recurring cadence that belongs to the WHOLE agent rather than one conversation (a weekly Thursday-5pm announcement, a daily digest, an "every Monday" nudge), declare a STANDING schedule on the `scheduler` capability config — `{ id: "scheduler", config: { schedules: [{ label, spec }] } }` where `spec` is an ABSOLUTE RFC-5545 recurrence `{ rrule, dtstart?, tz?, exdates?, rdates? }` (no relative `after`) — and add a `reactiveRules` entry whose `when.event` is `TimerFired` (gate on `event.label`) that broadcasts/builds the digest. The platform arms the cadence once at activation under the agent-level session, so it fires conversationlessly: you do NOT need a bootstrap trigger and must NOT hand-fire a `TimerFired`. Confirm it fires with the `previewSchedule` tool. (A per-conversation `ScheduleTimer` reactive rule is only for a timer scheduled DYNAMICALLY off a conversation event.) For a PERSONALIZED per-user cadence — where each end user should get a message composed from THEIR OWN state (a weekly recap of their progress, a tailored check-in) rather than one shared announcement — pair the standing `TimerFired` rule with the `conversation-fanout` capability instead of `broadcast`/`digest`: add `conversation-fanout` to `capabilities`, then `when {event: "TimerFired"}` (gate on `event.label`) → `then {command: "FanOutTurn", target: "conversation-fanout", payload: {eventType: {const: "RecapDue"}}}`. `FanOutTurn` pushes that `eventType` into EVERY end user's OWN conversation, each running a full agentic turn — so declare `RecapDue` in `signals` and add a SECOND rule `when {event: "RecapDue"}` → `then {command: "ComposeReply", …}`, and the composer answers each user personally from their recalled facts / `user-memory`. Each pushed turn is BILLABLE to the creator's wallet (the creator always funds usage); the audience is capped. (This exists because the agent-level standing-schedule session has NONE of an end user's per-conversation state — a personalized recap must fan OUT into each conversation rather than render in the conversationless firing.) To give the creator a MANAGE-TAB view of a SUBSET of the agent's collected data — and optionally message exactly those people — set `adminViews: [{ id, title, source, filter?, broadcast? }]`: `source` is a `data-table`'s `name`, `filter` is a STRUCTURED predicate `{ match?: "all"|"any", clauses: [{ field, op, value? }] }` (op one of eq/neq/in/nin/gt/gte/lt/lte/contains/exists — NOT free text), and `broadcast: { recipientField }` adds a one-click message to the filtered rows (recipientField is the record column holding each person's phone). E.g. a `guestList` data-table carrying rsvp + phone → `{ id: "rsvp-yes", title: "Confirmed guests", source: "guestList", filter: { clauses: [{ field: "rsvp", op: "eq", value: "yes" }] }, broadcast: { recipientField: "phone" } }` lets the creator message only the confirmed guests from the published agent's Manage tab. For the WRITE counterpart — letting an OWNER submit a structured record from the Manage tab WITHOUT going through the customer chat (e.g. record a quote correction into a training table) — set `adminForms: [{ id, title, capabilityId, command?, fields }]`: `capabilityId` is the `data-table` the form writes to — its catalog id (e.g. `data-table`) is remapped to the compiled instance, or pass the instance id (e.g. `data-table-0`) directly; both resolve — `command` defaults to that table's `UpsertRecord` (the owner-write command), and `fields` are the inputs `[{ name, label, type?: \"text\"|\"number\"|\"select\", options?, optional? }]` collected into the command payload (each `name` MUST be the table's keyField or one of its configured `source.fields`). The submit is security-gated server-side by the capability's operator commands. To let the owner PASTE A LIST instead of adding one row at a time, set `bulk: true` on the adminForm (optionally `defaults: { <field>: "<value>" }` applied to every row, e.g. `{ rsvp: "pending" }`): the Manage UI then shows a TEXTAREA — one comma-separated row per line, columns in `fields` order — and the submit dispatches the data-table's batch `ImportRecords` with the parsed rows (rows with no key are skipped). Pair a bulk form with `merge: true` so a pasted seed (name + contact) survives the later per-person status updates — the proactive-roster shape: seed a guest/lead/candidate list, let the agent text them, replies update each person's status. E.g. a `quoteFeedback` data-table (keyField `quoteId`, fields `[approvedQuote, reason]`) plus `{ id: \"record-correction\", title: \"Record a quote correction\", capabilityId: \"quote-feedback-0\", fields: [{ name: \"quoteId\", label: \"Quote ID\" }, { name: \"approvedQuote\", label: \"Approved quote\", type: \"number\" }, { name: \"reason\", label: \"Reason\", optional: true }] }` gives the owner a back-office form whose writes the customer-facing agent never touches. For durable owner-only records, declare a privacy posture on the data-table config — `retention: { purpose, retainDays?, audience?: \"owner\"|\"shared\" }` (audience defaults to `owner` = Manage-tab only, never surfaced to the end-user composer) — so the publish privacy review treats purpose-bounded, owner-only storage as justified rather than a leak. To let the agent use an EXTERNAL connector you enabled (see listConnectors / enableConnector), attach the `connector-tool` capability. An AGENTIC agent does NOT auto-call a connector tool from `guidance` prose — prose alone leaves it UNREACHABLE (review flags UNREACHABLE_CAPABILITY; at runtime the composer names the tool but the reply comes back EMPTY). It reaches a connector tool ONLY by RAISING A SIGNAL (filled with the args it extracts) that a `reactiveRules` entry turns into `CallConnectorTool` — so declare a `signals` entry and a rule `when: { event: <thatSignal> }` → `then: { command: 'CallConnectorTool', target: 'connector-tool', payload: { connectorId, tool, args } }`, and the result folds back into the reply. Call `getRecipe custom-http-api-call` for the complete worked shape. Each end user authorizes an OAuth connector at runtime. To call an API with NO Composio toolkit — or to POST to a Zapier catch-hook — enable a CUSTOM `http` connector instead (enableConnector with a `manifest`; ASK the creator only for the URL — NOT the API key, which they set in the Console) and wire it the SAME signal→CallConnectorTool way — it needs no OAuth. For the INBOUND/event side, set `connectorTriggers: [{ connectorId, triggerType }]` (discover triggerTypes with listConnectorTriggers) to subscribe each end user's connected account; when one fires, the agent receives a `ConnectorTriggerReceived` event, so pair it with a `reactiveRules` entry whose `when` is `{ event: "ConnectorTriggerReceived" }` to act on it (e.g. text the user, or call a connector tool). For an AGENT-LEVEL native-webhook connector bound ONCE at build time to the creator's OWN asset (e.g. Meta Lead Ads on a store's Facebook Page — NOT a per-end-user OAuth connection), choose how an inbound event is dispatched with `connectorDispatch: [{ connectorId, mode }]`: `mint-end-user` (default) treats the subject as a NEW end user, minting them from the event's contact hint (a phone) and running a normal conversation turn — the FOLLOW-UP model, but a phone-less / email-only event is dropped; `reactive-only` fires the agent's reactive rules on the AGENT-LEVEL session with NO end user (the conversationless `TimerFired` dual) — the NOTIFY model: no phantom user is created and a phone-less / email-only lead STILL flows, so a rule can e.g. email the store manager. Pair `reactive-only` with a `reactiveRules` entry `when {event: "ConnectorTriggerReceived"}` (gate on `event.connectorId`) → `then {command: "SendEmail", target: "email", payload: {to: {const: "[email protected]"}, subject: {const: "New lead"}, body: {template: "New lead: {{name}}", vars: {name: {ref: "event.data.name"}}}}}` (attach the `email` capability). Omit `connectorDispatch` (or the entry) ⇒ `mint-end-user`, so existing agents are unaffected. For a GENERIC inbound catch-hook — the REVERSE of an outbound Zapier `http` connector: let ANY external system (Zapier, Make, a CRM, curl) POST a lead to the agent and have it reach out — set `inboundHook: { enabled: true, version: 1 }`. The result returns `inboundHook.url`, this agent's `POST` URL (`/webhooks/inbound/<agentId>/<secret>`) to paste into the external system's webhook step. The POSTed JSON body must carry `phone` (SMS is the reach-out channel — enable the `sms` channel; `name`/`email` are optional context); a body with no `phone` is rejected. Each POST mints the lead as a NEW end user and dispatches a `ConnectorTriggerReceived` event whose `data` is the raw body (NOT a `MessageReceived` — read the POSTed body fields with `{ ref: "event.data.<field>" }`, e.g. `event.data.name`; the field names are whatever your webhook POSTs) — so pair it with a `reactiveRules` entry `when { event: "ConnectorTriggerReceived" }` (gate on `event.connectorId === "inbound-hook"` if the agent also has other triggers) to greet/qualify the lead. Call getRecipe `inbound-lead-outreach` for the full copy-pasteable shape. Rotate the URL by bumping `version` (the old URL dies instantly). To let an AUTONOMOUS agent take a repeated or high-impact EXTERNAL action automatically — recurring outreach, a connector write, a payment, a booking — WITHOUT a per-action human confirmation, declare an `actionBudgets` entry that bounds it: `[{ actionClass, maxPerRecipient, window, approved }]` where `actionClass` is the kind of action ("outbound_message" | "connector_write" | "payment" | "booking"), `maxPerRecipient` is the max count per recipient/entity, `window` is `"hour" | "day" | "week"` (UTC calendar window), and `approved: true` is your explicit attestation that this volume of automatic action is intended. This is the GATE the safety judge credits: a high-impact action backed by an APPROVED budget is treated as bounded (no "unconfirmed side effect" blocker), whereas the same action with no budget — or `approved: false` — is still blocked. The platform HARD-CLAMPS every budget to a ceiling (`min(yourBudget, platformCeiling)`), so a runaway loop can never exceed it; if your request is clamped the publish result returns an `ACTION_BUDGET_CLAMPED` notice telling you the effective limit. A class may carry SEVERAL budgets to bound different windows (e.g. ≤2/day AND ≤5/week) — all are enforced. E.g. "≤ 2 reminder texts per end user per day" → `{ actionClass: "outbound_message", maxPerRecipient: 2, window: "day", approved: true }`. REACTIVE-RULE PAYLOADS ARE VALIDATED SYNCHRONOUSLY: each `reactiveRules[].then.payload` must supply every REQUIRED field of its target command — as a literal `{const: …}`, an `{ref: "event.<field>"}` (also `state.`/`settings.`, `endUser.id` — the trusted, server-known end-user identity, for binding a durable per-person key — or `chain.<EventType>.last.payload.<field>` to pull a value from an event emitted EARLIER in the SAME reactive chain WITHOUT echoing it through intermediate events or building a data-table — e.g. a final `SendEmail` whose recipient came from the original request two steps back uses `to: {ref: "chain.RequestWelcomeLetter.last.payload.email"}`; `chain.<EventType>` must name an event that can occur before this rule's `when` event, or the build is rejected — `.first`/`.last`/`.all.<n>` pick among repeats), an `{object: {k: <value>, …}}` that BUILDS an object field from several refs/consts (e.g. a scheduler `context: {object: {policyId: {ref: "event.policyId"}, stage: {const: "90"}}}`, so per-entity identity rides the timer back on TimerFired as `event.context`), or a `{template: "…{{name}}…", vars: {name: <value>}}` that interpolates `{{name}}` placeholders into a STRING (each var any value-expr — const/ref/chain) — use it for a PERSONALIZED string field like a document-gen `markdown` body or a `SendMessage` message instead of freezing a static `{const}`; EVERY `{{placeholder}}` must have a matching `vars` key (else the build is rejected TEMPLATE_PLACEHOLDER_UNBOUND), and a var that resolves to a URL embeds a per-request image via `` — and a `{const}` value must match the command's type (e.g. `SendMessage` needs a string `message`), and an `{object}` value-expr mapped to a field that expects a SCALAR (e.g. an email `body`, which must be a string) is rejected too. An invalid payload is REJECTED IMMEDIATELY, naming the exact rule and field in the error, so you fix it on the spot instead of discovering it as a runtime/review failure later. A reactive rule's `condition.field` is validated the SAME way: it MUST be rooted at a known eval-context root (`event.*`, `state.*`, `settings.*`, `endUser.*`, or `chain.*`) — a BARE field like `label` is rejected, because at runtime it resolves to `undefined` so the rule SILENTLY never fires (gate a TimerFired rule on `event.label`, never `label`). The result also returns `channelAdvisories` when a channel you set expects extra guidance you haven't added yet (e.g. adding the `voice` channel expects a call-audio privacy guidance line) — add that guidance before publishing to avoid a review blocker. It ALSO returns `capabilityAdvisories`: each attached capability's KNOWN PITFALLS — the common ways that capability is misused, each with a `detail` (what goes wrong) and `suggestedGuidance` (the fix) — so you avoid the failure mode while wiring it instead of hitting it at the end-of-build review (where the same pitfalls are re-checked; e.g. a `data-table` fed by a source event with no `retention.retainDays` grows without bound). Apply the guidance now to keep the review clean. (`schemaVersion` is an INTERNAL document-version field stamped by the platform — do NOT set it; any value you pass is overwritten. `retiredMustIntentIds` is likewise PLATFORM-MANAGED — do NOT set it; it is stamped from your `acknowledgeRemovals` and records the must-have requirements you've retired so an acknowledged removal can publish.) The result ALSO returns `instances`: the COMPILED instance id (`{ id, instanceId }`) for each attached capability, where the index is a GLOBAL 0-based position across ALL capabilities (a 2nd `broadcast` at array position 7 is `broadcast-7`, NOT `broadcast-1`) — READ this when you need a duplicate-capability rule `then.target` or an adminForm `capabilityId` instead of counting the capabilities array yourself. To let an OWNER record or CORRECT a value from the Manage tab — approve/revise/deny a quote, set a final price, log a back-office record — use the structured admin surface: pair an `adminForms` WRITE form (the owner submits the corrected value into a `data-table`) with an `adminViews` filtered 'pending' list (the queue of items awaiting the owner), NOT a stateful-escalation status machine (escalation hands a live conversation to a person; an owner record-and-correct loop is a back-office form write — see the `owner-record-and-review` recipe). Set `webWidget` to add an embeddable floating web chat widget to a website (enabled, accentColor, position, launcherLabel, greeting, avatarUrl); requires the `web` channel to be set. After publishing, call `getWidgetEmbedCode` for the install snippet. Set `activityDisplay` to control the small chips shown under the agent's replies (on its web page + widget) naming the actions it took that turn: `none` (the DEFAULT — no chips; the agent opts in), `curated` (only user-meaningful actions — it generated an image, sent an email, called a connected tool; internal reasoning + error events are hidden), or `all` (EVERY action including internal reasoning steps — a judgement, a lookup — the same breadth the test-drive shows; use it for full transparency into the agent's reasoning). Chips render live on the turn and persist when the conversation is reloaded. For a VOICE agent, set `voiceName` to a Google Chirp 3 HD voice (e.g. `en-US-Chirp3-HD-Leda`) to pick the spoken voice, and `idlePrompt` to a short per-agent line the agent speaks on the FIRST no-input nudge when a caller goes quiet (e.g. "Take your time — I'm still here whenever you're ready."); the voice sidecar escalates to a generic "are you still there?" on later silences and then politely ends the call, so `idlePrompt` just makes that first nudge sound like this agent. Both are ignored for non-voice agents. Set `voiceMode` to pick the voice PIPELINE: `cascade` (the DEFAULT — today's Chirp 3 HD TTS pipeline that `voiceName` configures) or `realtime` (Gemini Live — a single low-latency speech-to-speech model; needs a phone number and REQUIRES `voice` in `channels`, or the write is rejected). `realtimeVoice` picks the Gemini Live prebuilt voice (e.g. `Puck`, `Kore`, `Aoede`) and is only used when `voiceMode` is `realtime` — setting it while `voiceMode` stays `cascade` is accepted but ignored, not an error, so switching modes back and forth never bounces a config. QUALITY LOOP: after changing an agent's behavior (soul, behaviorIntents, capabilities, guidance), run runQualitySuite and read `rollup.regressions` — it diffs against the previous completed run and shows exactly which requirements your change made WORSE ('newly_failing', 'score_drop', 'requirement_dropped'). New behaviorIntents get their standard test battery written automatically by that same call. Don't publish a change that introduced regressions without either fixing it or telling the creator why it's acceptable.
setAuthoredConfig
Convert one OPEN flag into a frozen `scenario` eval (`origin: 'flagged'`) that the quality suite runs forever after (NOT for flags whose correlationId starts `eval:` — those dispute an existing test's rubric; fix that test and dismissFlag instead): the excerpt's user turns become the scripted conversation (the agent's side replays live each run), and the judge criteria encode the creator's correction. Returns `{ flag, eval, droppedUserTurns }` with the flag resolved 'converted' (`droppedUserTurns` > 0 means the excerpt had more user turns than the 12-turn script cap and the OLDEST were dropped — tell the creator when that happens). The script replays only the USER side of the conversation; when a user turn was an answer to an agent question, target the BEHAVIOR in the criteria rather than expecting the identical dialogue shape. Inputs: `flagId`; OPTIONAL `criteria` — REFINE THIS rather than accepting the default (the raw note wrapped in a template): state precisely what the agent MUST and MUST NOT have done so the judge scores the behavior, not the wording; OPTIONAL `intentId` — link the behaviorIntent this case verifies (add the intent first when the flag reveals a requirement the config never had; that closes the 'things I didn't consider' gap AND makes the case count toward the requirement's rigor); OPTIONAL `name` and `caseTag` (defaults to 'edge' — a real conversation the battery didn't anticipate). The new eval starts FAILING by construction (the agent just failed it for real) — after converting, fix the config and prove the fix with runQualitySuite. A concurrent conversion of the same flag loses cleanly (no duplicates).
convertFlagToEval
ONE-CALL macro for the most common agent: a pure conversational agent that answers the user in a persona, within a safety policy, on some channels. Give `name`, `purpose` (what it's for), optionally `persona` (its tone/voice) and `safetyPolicy` (boundaries), and `channels` (defaults to ["web"]). It creates the draft AND sets a complete authored config — you do NOT hand-wire the MessageReceived→ComposeReply→MessageGenerated→SendMessage chat chain or attach a messenger/composer capability; the platform's baseline supplies all of that for an `agentic` agent. Returns the created agent + the config it set; then simulateBuild / reviewAgent / publishAgent it like any draft. Use setAuthoredConfig or patchAuthoredConfig afterwards to add capabilities, connectors, or reactive rules. (For a NO-LLM fixed-script bot, use createAgent + setAuthoredConfig with replyMode "deterministic" instead.)
createConversationalAgent
Create a new draft agent for the creator. Provide a `name`. `visibility` defaults to `public` (anyone can chat over web or SMS); pass `visibility: "private"` to make it reachable ONLY by the owner and the E.164 numbers on its allow-list (a private web visitor must sign in; an un-listed SMS sender is ignored). You can also flip visibility later with setAgentVisibility, and manage access with addAllowedNumber. Returns the agent, including its `id` — pass that id to setAuthoredConfig and publishAgent. The result also echoes `ownerId` (the tenant the agent is filed under) and a `manageUrl` Console deep link, so you can confirm WHERE it landed.
createAgent
Delete one capability eval (and its run history) — pass `evalId`. Returns `{ ok: true }`.
deleteCapabilityEval
Permanently remove ONE knowledge-base source — and its indexed chunks — from an agent's knowledge base. Pass `{ agentId, sourceId }`, where `sourceId` is an `id` from getKnowledgeBase's `sources` (or a `sourceId` seedKnowledgeText returned). Use this to CORRECT the base when a fact is disproved or superseded — delete the stale source outright instead of seeding a 'disregard the above' document, which would leave contradictory passages competing in retrieval forever. (seedKnowledgeText already UPSERTS by exact title and gates near-duplicates; reach for delete when there's no clean replacement to re-seed, e.g. dropping a whole obsolete source.) Deleting a FILE source removes the WHOLE file's chunks, not one fact within it. Irreversible — re-seed to restore. No-op-safe: an unknown or already-deleted `sourceId` (or one on an agent you don't own) returns a not-found error. Returns `{ ok: true }`.
deleteKnowledgeSource
Remove a connector from the agent (by `connectorId`). The agent can no longer use that toolkit; existing end-user connections are left untouched.
disableConnector
Dismiss one OPEN flag without converting it — for a one-off the creator confirms needs no guard (a user typo, a transient outage, behavior that is actually correct). Pass `flagId`; returns the resolved flag. Prefer convertFlagToEval when in doubt: a dismissed flag protects nothing, a converted one guards the behavior forever.
dismissFlag
Compile the draft and run it against generated scenarios WITHOUT publishing, returning a COMPACT summary instead of the full traces: `{ scenarioCount, repliedCount, failures, coverage }`, where each failure is `{ scenario, command, target, code, reason }`. This is the fast way to spot a payload/wiring bug — e.g. "every scenario fails on SendMessage because the message field is missing" — without paging through the full cascade. `coverage` is the INVERSE signal: `coverage.rulesNeverFired` lists reactive rules that fired in NO scenario (usually a wrong `when.event`, an unsatisfiable `condition`, or a dangling `then.target`) — treat a non-empty list as likely dead wiring, NOT a pass; `coverage.intentsWithoutScenario` lists behaviorIntents no scenario exercised (usually because the intent has no `examples`, so it was never tested — add one). Drill into simulateBuild only when you need the full trace for one scenario. Read-only.
simulateBuildSummary
Compile the draft and run it against generated scenarios WITHOUT publishing; returns the FULL event-cascade traces per scenario. This can be LARGE — prefer simulateBuildSummary for a compact pass/fail-at-a-glance read, and only call this when you need the full cascade to debug a specific scenario.
simulateBuild
Run the SAME compile + safety/purpose review that publishAgent runs, but WITHOUT publishing — returns `{ canPublish, blockers, findings, validation, delta? }`. simulateBuild only checks behavior (rules → events); this checks publish-readiness, so use it to iterate on review blockers cheaply before going live. `canPublish: true` with no blockers means publishAgent would succeed. `blockers` is the friendly human-facing list; each carries STRUCTURED targeting — `subjectId` (the exact rule/intent/capability id flagged), `subjectKind`, `fieldPath`, and a `quote` of the offending span — so you fix the right thing in one pass instead of guessing which item the review meant. `findings` is the FULL machine shape publishAgent's report carries (id, category, severity: info|warning|blocker, confidence 0..1, summary, evidence[], subjectId, fieldPath, suggestedRemediation, ...) for EVERY severity — so you can iterate blockers off reviewAgent ALONE with no publish round-trip and never under-count. Read verdict STABILITY from `confidence`: a deterministic gate emits confidence 1 (reproducible); a finding with severity "blocker" and confidence < 0.8 is a MARGINAL LLM-judge verdict that did NOT gate publish (it may shift run-to-run). `validation` is core's static validation block (errors/warnings). To track progress across iterations, pass `priorBlockers` (the `blockers` array you got from your LAST reviewAgent call); the result then includes a `delta` of `{ cleared, introduced, kept }` so you can tell whether your last edit helped. The review also dry-runs the draft and promotes a runtime failure (a rule that fails on every message) to a distinct "a rule fails when it runs" blocker, separate from "a behavior has no rule". Note the review depends on `replyMode` (a deterministic agent skips the AI judge — see setAuthoredConfig).
reviewAgent
Enable one external connector on the agent so it can act in that third-party app on behalf of the end user. `connectorId` MUST be a real toolkit id (from listConnectors / getConnector) — an id that doesn't exist in the provider's catalog is rejected, so discover it first instead of guessing. Optionally pass `enabledTools` (a subset of the connector's tool slugs) to narrow what the agent may call — recommended for large toolkits (check `toolCount`) so you don't load hundreds of tools into the model. To actually USE the connector in conversation you ALSO need to attach the `connector-tool` capability in setAuthoredConfig and reference this `connectorId` in your guidance/reactive rules (via the ListConnectorTools / CallConnectorTool commands). The end user connects their own account at runtime via OAuth; until they do, the connector is enabled but not yet usable for that user. Optionally set `bindingPolicy` to control HOW the connector authorizes (ADR 0005): `{ default: 'creator'|'none', override: 'forbidden'|'allowed'|'required' }`. `default` says whether the agent carries a creator-set account (`creator`) or none; `override` says whether an end user may bind their own. `creator`+`forbidden` = LOCKED to the creator's one account (users can't override — e.g. a Meta Page, a shared brand inbox); `creator`+`allowed` = the creator's account is the default but a user MAY connect their own (e.g. a shared Gmail); `none`+`required` = every user must connect their own (today's default); `none`+`allowed` = optional per-user. `none`+`forbidden` is invalid and is rejected. Omit `bindingPolicy` for today's per-user behavior. CUSTOM HTTP CONNECTOR (no Composio toolkit needed): to call an API Composio does NOT cover — a carrier/AMS REST API, an internal service — OR to POST to a Zapier catch-hook, pass a `manifest` instead of relying on the catalog: `{ provider: 'http', baseUrl, auth: { type: 'none'|'bearer'|'header', header? }, tools: [{ name, method, path, params?, body?, responseMap? }] }` (auth carries NO secret field — the key is set in the Console, never here). A manifest connector needs NO catalog entry and NO per-end-user OAuth — `connectorId` is YOUR OWN chosen slug (e.g. 'coastal-ams'), the existence check is SKIPPED, and it is callable as soon as enabled (still attach `connector-tool` + reference the id in guidance/reactive rules). In each tool, `path` may contain `{param}` placeholders filled from `params` (a map of name→'string'|'number'|'boolean'); `body` (write methods) is the JSON body fields; `responseMap` projects the raw JSON into clean fields for the agent as `{ outField: '$.json.path' }` (tiny JSONPath — `$.a.b`, `$.arr[0].c`). CREDENTIAL — for `bearer`/`header` auth, DO NOT put a token here: LEAVE `secretRef` UNSET. A raw API key must NEVER pass through this tool — it would enter your context and the transcript. There is a dedicated WRITE-ONLY Console field for the key, so do NOT ask the creator for a secret NAME, do NOT tell them to set a server/env var, and do NOT hunt for an MCP tool to store the secret (there is none) — the creator types the actual key themselves in the Console (agent Configuration → "Connector API keys") AFTER you author the shape. During the interview just ask the auth TYPE (bearer vs a header name); never ask where the key is stored. Author only the SHAPE (`auth: { type: 'bearer' }`, or `{ type: 'header', header: 'X-Api-Key' }`) with no `secretRef`; the connector is then in a PENDING-CREDENTIAL state, and this tool's response returns `credentialRequired` — RELAY it to the creator: they enter the real key in the Console on the agent's Configuration page, under "Connector API keys", which stores it encrypted and mints the handle. Publish is BLOCKED until the key is set. IMPORTANT — the `baseUrl` and each tool's catch-hook `path` ARE creator-specific: when a creator says "post to my Zapier" / "connect my API", ASK for the real hook URL and use it VERBATIM; NEVER invent, guess, or leave a placeholder like `/hooks/catch/<id>/<key>` — but the API KEY is the one thing you do NOT take here (it goes to the Console). A no-auth Zapier hook needs no credential: `auth: { type: 'none' }` and it's usable immediately. And do NOT route a Zapier catch-hook or a plain REST API through listConnectors/Composio — those are custom `http` connectors defined by THIS manifest, not Composio toolkits (listConnectors only covers Composio's OAuth toolkits). Zapier is just an http connector whose tool is a POST to the catch-hook: `{ provider:'http', baseUrl:'https://hooks.zapier.com', auth:{type:'none'}, tools:[{ name:'log_call_note', method:'POST', path:'/hooks/catch/<id>/<key>', body:{ summary:'string', phone:'string' } }] }`. Outbound requests are guarded (only public hosts; loopback/private/link-local are blocked outside dev), and a manifest write is still governed by an `actionBudgets` `connector_write` entry.
enableConnector
Generate the STANDARD BATTERY for behavior intents that don't have one yet: for each intent, the platform writes three scenario evals — a 'happy' path, an 'edge' case, and an 'adversarial' probe (a user actively trying to make the agent violate the requirement) — each linked to its intent, tagged, and persisted with `origin: 'generated'`. Returns `{ generated, alreadyGeneratedIntentIds, unknownIntentIds }` where `generated` is the created evals. The battery is FROZEN once COMPLETE: an intent whose generated cases already cover all three tags is SKIPPED (listed in `alreadyGeneratedIntentIds`), so every config version replays the identical suite and version-to-version score deltas mean the AGENT changed, not the tests. An INCOMPLETE battery (an earlier interrupted write) self-heals: its remnants are replaced with a fresh trio. Delete a generated case to force regeneration. Pass `intentIds` to scope generation; omit it to cover every intent without a battery. This is what gives every requirement a comparable rigor floor. NOTE: runQualitySuite runs this automatically for every uncovered intent before grading, so you rarely need to call it yourself — use it to write tests WITHOUT grading, or with `intentIds` to scope regeneration after deleting a case. This spends model credits, so it is credit-gated.
generateEvalBattery
Resolve one AuthoredConfig recipe by `name` (from listRecipes), returning `{ name, title, description, config }` — a real, schema-valid config skeleton with the wiring already correct. The `config` is a SHAPE TO ADAPT, not a finished agent: replace the placeholder purpose/persona/message text and `<…>` markers, pick real capability ids (cross-check getCatalog) and connectors (listConnectors), then pass it to setAuthoredConfig. Returns null if no such recipe exists. Read-only.
getRecipe
Look up one connector by its toolkit `connectorId` (e.g. from listConnectors, or one you already know like "gmail"). This is the RELIABLE exact-existence check (unlike listConnectors' page-limited substring search). Returns `{ connector, providerMode }`: `connector` carries its `title`, `description`, `categories`, `noAuth`, and `toolCount`, OR is `null` when no such toolkit exists. A genuinely absent slug resolves to `connector: null` — it is NOT an error; an `ok:false` error means an ACTUAL provider failure (timeout/auth/5xx), so retry it rather than concluding the toolkit is missing. `providerMode` makes the null authoritative: `connector: null` proves absence only when `providerMode` is `live-composio` (the real credentialed catalog); on `no-catalog` (keyless dev) EVERY lookup is null and proves nothing. Use it to confirm an id and decide whether to scope `enabledTools` before enableConnector. Read-only.
getConnector
Read one of the creator's agents by id (name, status, slug, visibility). Read-only.
getAgent
Get the <script> snippet and step-by-step install instructions to add this agent as a floating chat widget on a website. Requires the agent to be PUBLISHED and to have webWidget.enabled set (turn it on with setAuthoredConfig, then publish). Returns `{ enabled, slug, snippet, scriptUrl, previewUrl, appearance, instructions: { generic, byPlatform, selected? } }`: paste `snippet` just before </body> on every page. Pass an optional `platform` (shopify | wix | wordpress | webflow | squarespace | html) to get tailored steps under `instructions.selected`. If the widget is off or the agent isn't published yet, `snippet` is null and `instructions.generic` says what to do first. Read-only.
getWidgetEmbedCode
Provision (or retry) the Twilio phone number for a PUBLISHED sms/voice agent, AND the way to READ an agent's number over this surface. Decoupled from publish: publishing kicks issuance off in the background, so call this to check/retry it explicitly — e.g. after publishAgent returned `numberPending`, or after a transient failure. IDEMPOTENT: if the agent already has an active number it is returned unchanged. Returns `{ ok: true, e164, status }`, or `{ ok: false, reason }` — `no_inventory` (no number available, try again later), `provisioning_failed` (transient infra, retry), `not_configured` (server is missing Twilio settings — NOT retryable, tell the operator), `agent_not_published` (publish first), `already_has_number` (it already has an active number). On `already_has_number` the result ALSO carries `e164` + `status` for the existing number (so you can read and hand the creator the agent's number even when the background issuance already provisioned it before you called this); while it's still provisioning, `e164` is null and `status` is `provisioning` — call again shortly.
issueNumber
Discover the TRIGGER types a connector can fire (the inbound/event side — e.g. "a new Google Task was added"). Pass `connectorId` (a real toolkit id from listConnectors). Returns each trigger's `type` (use it in setAuthoredConfig's `connectorTriggers`), `name`, and `description`. A trigger lets the agent ACT ON ITS OWN when something happens in the end user's connected account — distinct from connector tools, which the agent calls on demand. To use one: declare it in `connectorTriggers` AND add a reactive rule with `when: { event: "ConnectorTriggerReceived" }` that does something (notify, call a tool, …). Read-only.
listConnectorTriggers
List the E.164 numbers allowed to reach a PRIVATE agent (oldest first, each with its optional label). The owner is implicitly allowed and is NOT in this list. Read-only.
listAllowedNumbers
List the agent's capability evals with their latest result — pass `agentId` (optionally `instanceId` to scope to one capability). Each returns `{ id, capabilityInstanceId, moduleId, evalKind, name, input, expected, lastScore, lastRunAt, ... }`; `lastScore` (0..1) and `lastRunAt` are null until the eval has been run. Read-only.
listCapabilityEvals
List the run history for one eval, newest first — pass `evalId`. Each run is `{ id, score, output, rationale, judgeModality, configRef, ranAt }`, where `configRef` identifies which config version was exercised (for spotting regressions across edits). Read-only.
listCapabilityEvalRuns
Return the BUILT-IN capability catalog (the palette of skills with their ids, config shapes, and examples) so you use real capability ids, not guesses. PREFER getCatalogSummary first — the FULL catalog serializes every module's JSON Schema plus the worked examples and is large enough to exceed an MCP client's per-result token cap. Pass `moduleIds: ["<id>", …]` to fetch the full JSON Schema for ONLY those modules (discover the ids with getCatalogSummary); omit it only when you truly need the whole palette. For EXTERNAL connectors (Google Calendar, Gmail, a CRM, …), use listConnectors instead — those are enabled separately with enableConnector. Each module also carries the FULL `pitfalls` it declares — its KNOWN failure modes (`code`, `failureMode`, `guidance`, `severity`): how the capability is commonly misused and how to avoid it, so you wire it right the first time (getCatalogSummary lists only `code` + `severity`; this is where the prose lives — the same pitfalls are re-checked at the end-of-build review).
getCatalog
Return the COMPACT capability catalog — per module, the config keys it needs, its commands (with required + accepted payload fields) and the events it emits (the fields a rule can read as `event.<field>`) — WITHOUT the full JSON Schemas. It also carries `valueExprForms`: the full vocabulary a `then.payload` leaf may take — `{const}`, `{object}`, `{template}` (interpolate `{{name}}` placeholders in a string with `vars` — each var itself a value-expr — for a personalized body/message), and every `{ref}` root (`event.`, `endUser.id` — the trusted server-known identity of the person, bind durable keys like a data-table keyField or escalation id to it, `state.`, `settings.`, and `chain.<EventType>.last.payload.<field>` to pull a value from an event emitted EARLIER in the same reactive chain) — so you don't have to infer the ref forms from examples. An order of magnitude smaller than getCatalog: prefer this when you're wiring reactiveRules and just need the field names, and only fall back to getCatalog when you need a full schema. Each module also lists any `pitfalls` it declares — its KNOWN failure modes — but here as `code` + `severity` ONLY (no prose): enough to see THAT a capability has common misuses and how serious. Pull the full `failureMode` + `guidance` for the ones that matter with getCatalog({ moduleIds }) — the same modules you fetch full schemas for when wiring (the same pitfalls are re-checked at the end-of-build review). Read-only.
getCatalogSummary
List the canonical AuthoredConfig RECIPES — known-good starting shapes for common agent patterns (conversational, no-LLM deterministic bot, scheduled reminder, escalating reminder ladder that self-cancels, classify-and-branch routing, human escalation, RSVP/guest list, owner-uploaded roster/worklist, per-user scheduled recap, multi-step generate-then-deliver, knowledge-base grounded, connector-trigger reaction). Pass `query` with the task IN YOUR OWN WORDS (e.g. `listRecipes({ query: "chase an overdue invoice until paid" })` or `"answer from our FAQ"`) and the best-fitting recipes are ranked first; omit it to skim the whole library. Returns each recipe's `name`, `title`, `description`, and `keywords`. Fetch the full skeleton with getRecipe(name), adapt it (purpose/persona/message text + capability choices), then setAuthoredConfig it. Start here instead of composing a config from scratch. Read-only.
listRecipes
List the interactions the creator FLAGGED while reviewing real conversations on the Console — the feedback inbox this loop drains. Returns the agent's flags newest-first, each `{ id, correlationId, excerpt, note, status, evalId }`: `excerpt` is the transcript span frozen at flag time (`{ role: 'user'|'agent', text }[]` — what actually happened), `note` is the creator's correction (what SHOULD have happened — treat it as the requirement), DISPUTED TESTS: a `correlationId` starting `eval:` flags an EXISTING test from the Quality page's conversation view — the creator read the test's conversation and believes the RUBRIC is wrong, not the agent. For those, review the eval (the id follows `eval:`) and its criteria against the excerpt + note; if the creator is right, FIX THE TEST (replace it via deleteCapabilityEval + addScenarioEval with corrected criteria, or adjust the behaviorIntent) and then dismissFlag — do NOT convertFlagToEval (that would mint a duplicate test), `status` walks 'open' → 'converted' | 'dismissed' (default filter: 'open'; pass `status: 'all'` for history), and `evalId` points at the scenario eval a converted flag became. THE LOOP: a flag is a failing test the agent already failed for real. Process each open flag: (1) read the excerpt + note and decide whether it reveals a MISSING requirement — if so, add a behaviorIntent for it first (setAuthoredConfig/patchAuthoredConfig); (2) convertFlagToEval with sharpened judge criteria and the intent link; (3) fix the config so the behavior actually changes (guidance, soul, rules, or an intent example); (4) runQualitySuite and confirm the flag-derived eval passes AND nothing else regressed; (5) publish. Dismiss (dismissFlag) only what the creator confirms is a one-off. Read-only.
listFlaggedInteractions
List the creator's agents (id, name, status, slug). Read-only.
listAgents
List the connectors currently enabled on the agent (each with its `connectorId` and the `enabledTools` scope, if any). Read-only.
listEnabledConnectors
List the agent's EVAL-able capability instances — the individual steps you can write re-runnable unit tests against. Compiles the DRAFT and returns one entry per instance: `{ instanceId, moduleId, evalKind, judge, supportsDeterministic }`. Use `instanceId` as the `capabilityInstanceId` when you add an eval. `evalKind` is `capability` (feed it an input, score the output) or `tool-selection` (feed the composer a user message, assert which tools it calls). `supportsDeterministic` says whether an exact `deterministic` matcher is allowed; `judge` (`text`|`image`|null) says which model judge scores fuzzy outputs. Read-only.
listEvalTargets
List the valid `then.target` instances for the agent's reactive rules — so you don't have to guess an instance id or probe with a bad rule to learn it. Compiles the DRAFT and returns `{ targets }`, each `{ id, module, emits }`: `id` is a compiled capability instance usable DIRECTLY as a rule's `then.target`, `module` is the capability it runs (for an ATTACHED capability the bare module id — e.g. "messenger" — is ALSO a valid target and additionally enables synchronous payload validation), and `emits` are the event types that instance can produce (wire another rule's `when.event` off one to chain behaviors). The list includes the auto-wired BASELINE instances (the agentic composer + `replier`) and the `escalation` instance when human-in-the-loop is on — targets that aren't in getCatalog and are otherwise undiscoverable. Read-only.
listTargets
List the valid `when.event` values a reactive rule can fire on for this agent — so you don't have to hand-assemble them from getCatalog + the draft, or probe with a bad rule to learn them. Compiles the DRAFT and returns `{ triggerEvents }`, each `{ event, source, emittedBy }`: `event` is the type usable DIRECTLY as a rule's `when.event`; `source` is how it becomes fireable — `builtin` (`MessageReceived`, the universal external chat entry point, always present), `capability` (an event an ATTACHED capability instance emits — `emittedBy` lists the instance ids, so you can chain a rule off another rule's command), `connector-trigger` (`ConnectorTriggerReceived`, present ONLY when a connector trigger subscription is in the draft's `connectorTriggers`), or `signal` (a custom composer signal you declared). An event NOT in this list is unfireable for this draft: attach the capability/connector trigger or declare the signal first. This is the `when.event` companion to listTargets (which lists the valid `then.target`). Read-only.
listTriggerEvents
Apply a PARTIAL update to an already-configured agent without resending the whole config — tightens the iterate loop when you only want to change one field. Pass `agentId` and `set`, an object of the top-level fields to change (e.g. `{ purpose: "…" }`, `{ channels: ["web","sms"] }`, `{ guidance: [...] }`, `{ activityDisplay: "all" }`). Each key in `set` REPLACES that field wholesale (arrays/objects are NOT deep-merged — predictable); fields you omit are left untouched. The merged config is re-validated exactly like setAuthoredConfig (capability ids + reactive-rule payloads are checked, so a bad patch is rejected with the same field-level errors), and the result returns the full merged config plus any `channelAdvisories` and `capabilityAdvisories` (the known pitfalls of the attached capabilities). The agent must already have a config (create it with createAgent/createConversationalAgent and setAuthoredConfig first). PROTECTED REQUIREMENTS: patching `behaviorIntents` to drop or downgrade a published `priority: "must"` intent is rejected unless you pass `acknowledgeRemovals: [<the intent id>]` — the same durable-requirement guard as setAuthoredConfig, so an incremental edit can't silently erase a hard requirement. QUALITY LOOP: after changing an agent's behavior (soul, behaviorIntents, capabilities, guidance), run runQualitySuite and read `rollup.regressions` — it diffs against the previous completed run and shows exactly which requirements your change made WORSE ('newly_failing', 'score_drop', 'requirement_dropped'). New behaviorIntents get their standard test battery written automatically by that same call. Don't publish a change that introduced regressions without either fixing it or telling the creator why it's acceptable.
patchAuthoredConfig
Preview what the DRAFT would do with ONE user message, WITHOUT publishing and WITHOUT the scenario harness — pass `agentId` and `message`. Returns `{ reply, producedTypes, failures }`: the reply the agent would send, the event cascade it triggered (so you can see which rules fired), and any command failures. NOTE: this is a DETERMINISTIC preview — the rule wiring and which capabilities fire are faithful, but the reply TEXT is a stand-in, not the live model's exact wording (that appears once published). Use it to catch a silent agent, a mis-wired rule, or a failing command far faster than a publish round-trip. Read-only.
tryAgent
Arm the draft's AGENT-WIDE standing schedules (declared on the scheduler capability's `config.schedules: [{ label, spec }]`) on the agent-level session and advance its clock, returning each firing's cascade — so you can confirm an agent-wide cadence (a weekly announcement, a daily digest) ARMS and drives its `TimerFired` rule WITHOUT hand-firing a TimerFired. Returns `{ correlationId, until, armed, firings, events, failures? }`: `armed` is what was scheduled, `firings` are the occurrences that fell due (label + 0-based occurrence + firedAt), `events` is the produced cascade (look for the broadcast/digest each firing drove). Omit `until` for a deterministic default window (35 days past each schedule's anchor); pass an ISO `until` for a specific horizon. `armed: []` means no standing schedules are configured. Read-only — nothing is persisted or sent.
previewSchedule
Compile the agent's guidance into runtime policies, run the full safety review, and publish it to the web. On success returns the public link (the creator can share it immediately). Phone numbers are DECOUPLED from publish: when the agent has an sms/voice channel, publishing KICKS OFF number issuance in the background and returns `numberPending: true` — it does NOT wait for or fail on the number. Call `issueNumber` to check/retry the number (it's idempotent), or read it later via getAgent. If the review blocks publishing, returns the blockers to fix — adjust the config and call again. PENDING RESULT: the safety review runs server-side; if it hasn't finished within the in-request window, this returns `{ pending: true, buildId }` (NOT an error and NOT a timeout — the build keeps running). When you get that, call `getPublishStatus` with the returned `buildId` to read the final outcome; do NOT re-call publishAgent (that starts a NEW build). AFTER PUBLISH: the platform re-grades the published agent automatically in the background (the same suite runQualitySuite runs); call getQualityReport a few minutes later to read the fresh grade and its `regressions`, and treat a post-publish regression as a bug to fix, not noise.
publishAgent
Force the agent's LIVE conversations onto a specific published build NOW — the opt-in override for an urgent or safety fix. By DEFAULT a re-publish does NOT touch conversations already in flight: each one stays pinned to the build it started on and only re-resolves to the latest at its own idle gap or continue-as-new boundary (and even then only if the change is compatible). This tool is the deliberate firehose for when you can't wait for that. Pass `{ agentId, buildId, mode? }` where `buildId` is the TARGET build (the one you just published — read it from getAgent / the publish result). `mode` is `"compatible-only"` (the DEFAULT — safe): a conversation the target build is BREAKING for (it would strand that conversation's accumulated in-flight state) is SKIPPED and reported back, never migrated; only compatible conversations are swapped. `mode: "all"` is the explicit DATA-LOSS override: it migrates EVERY conversation including breaking ones, and lists each breaking one under `dataLossOverrides` so the loss is on record — use it only when the fix is more important than the in-flight state. Returns a compatibility report `{ buildId, total, migrated, skipped, endedOrIdle, skippedReasons: [{ correlationId, reasons }], dataLossOverrides? }`: `migrated` got the swap, `skipped` were breaking (compatible-only), `endedOrIdle` had already ended (nothing to migrate — they pick up the latest on their next turn anyway), and `skippedReasons` explains WHY each was breaking. A migrated conversation swaps forward-only (its future turns use the new build; past events are never re-folded) and surfaces a DefinitionUpgraded event on its next turn.
forceMigrateLiveConversations
Read the agent's EVAL COVERAGE at a glance — which capability steps have re-runnable unit tests, which steps are still UNTESTED, and which existing evals never ran, are failing, or are orphaned — so you close the gaps instead of re-deriving them from listEvalTargets + listCapabilityEvals. Compiles the DRAFT and returns `{ targets, orphanedEvals, summary }`. Each target is an eval-able instance (same shape as listEvalTargets) plus its rolled-up cases: `evalCount`, `neverRunCount` (authored but never executed), `failingCount` (last score < 0.7), and `evals` (each case's id, name, lastScore, lastRunAt). An UNCOVERED target (`evalCount: 0`) carries a `suggestion` — a concrete first case to author for that step (the right input shape and expectation kind). `orphanedEvals` are cases whose target instance no longer exists in the current draft (left behind by a config edit) — delete them or re-add them against the new instance id. `summary` totals it: `{ targetCount, coveredCount, uncoveredCount, evalCount, neverRunCount, failingCount, orphanedCount }`. Workflow to CLOSE a gap: getEvalCoverage → addCapabilityEval for each uncovered target (start from its `suggestion`) → runCapabilityEval to score each new case → getEvalCoverage again to confirm `uncoveredCount: 0` and nothing failing. Read-only.
getEvalCoverage
Read the agent's knowledge base: how many sources it has and their processing status. You can add reference TEXT directly with seedKnowledgeText; FILE uploads still go through the Outside Agent Console (the manageUrl from setAuthoredConfig/publishAgent). Use THIS tool to check whether sources have finished indexing. Returns `{ total, counts: { ready, processing, pending, failed }, ready, sources: [{ id, title, sourceType, status, errorMessage }] }`. `ready` is true once at least one source is embedded and searchable. After you seed text (or the creator uploads files) call this: if `counts.processing` or `counts.pending` is non-zero indexing is still running (call again shortly); once `ready` is true you can confirm the knowledge base was added successfully and continue. A `failed` source carries a friendly `errorMessage`. Read-only.
getKnowledgeBase
Read the Quality report — the same single view the Console Quality page shows, in one call. Returns `{ understood, built, tested, flags, latestRun, runs }`: `understood` is the requirement ledger (every behaviorIntent's `intentId`/`statement` — what the platform believes the agent must do; EVERY requirement weighs the same in the grade, the config's `priority` field is ignored by grading; if a requirement is wrong or missing, fix the config's `behaviorIntents`); `built` lists the compiled capability instances, channels, `soulPresent`, and `replyMode` (what actually got wired); `tested` describes the suite's shape (`testCount`, `scenarioCount`, `generatedCount`, `authoredCount`, `intentsWithoutBattery` — intents the next runQualitySuite will auto-cover — and `testCountByIntent`, live per-requirement test counts); `gradeStale` is true when the config or the test set changed since the latest completed run (e.g. a flag was just converted) — the standing grade doesn't cover the change until runQualitySuite runs again; `flags.openCount` says how many creator-flagged real interactions await processing (listFlaggedInteractions to read them); `latestRun` + `runs` carry the grade history (see runQualitySuite for the rollup shape). Use it to answer 'did the platform understand the ask, is it tested, and what is the grade' before and after changes. Read-only.
getQualityReport
Load an agent's REQUIREMENT LEDGER before you change it — read this FIRST when taking over or editing an existing agent, so you don't regenerate over requirements it already commits to. Returns `{ purpose, intents, ledgerStatus, unverifiedMustIntentIds, summary }`. `intents` is every recorded behaviorIntent with its `priority`, its `sourceQuote` provenance, and a `coverage` roll-up (how many linked evals, and how many are passing/failing/never-run) — so you see what the agent must do AND which requirements are actually verified. `ledgerStatus` is `healthy` (records must-have requirements), `thin` (has behavior but NO must intent — nothing protects it across edits; call suggestBehaviorIntents to backfill), or `empty` (a fresh agent). `unverifiedMustIntentIds` are the must intents with no passing eval — add one with addCapabilityEval (intentId) so a later change can't silently break them. `summary` gives capability/rule counts + channels for orientation. Pair with getAuthoredConfig for the full config. Read-only.
getAgentSpec
Read what a PUBLISHED agent actually DID at runtime — the durable domain events it folded, newest first, across all of its conversations. The investigative companion to getAgent (which shows config): use THIS to see whether a side effect actually happened. Every capability outcome is here — `EmailSent` vs `EmailFailed`, `CallFailed`, `PaymentCaptureFailed`, etc. — so you can confirm e.g. that an email truly sent rather than trusting the agent's reply text. Input: `{ agentId, limit? (default 50, max 200), conversationId?, type? (exact, e.g. "EmailFailed"), failuresOnly? (only events whose type ends in "Failed"), since? (ISO instant) }`. Returns `{ agentId, total, matched, returned, truncated, events: [{ id, type, at, source, conversationId, causationId?, failed, payload }] }` — payloads are redacted (data: URIs dropped, long strings truncated). `failed` flags failure events at a glance; `truncated` means more matched than the page returned (raise `limit` or filter by `conversationId`/`since`). Read-only.
getAgentTimeline
Capture the RAW source you are building this agent from — BEFORE you reviewAgent/publishAgent — so the safety review can verify nothing was dropped. The review runs an advisory `requirement_coverage` pass that compares this source against the assembled agent and WARNS (it never blocks) about any requirement that did not make it in; those warnings come back in reviewAgent's `findings` for you to fix. Pass `messages`: the build conversation as role-tagged turns, oldest-first — `role: "creator"` for the human's own words (paste the dropped transcript or brief as one creator message; or each of their answers), and `role: "builder"` for YOUR questions and framing. CAPTURE BOTH SIDES, in order: a terse creator answer ("yes", "after two tries", "text my cell") is only a requirement together with the builder question it answered, so recording the answer WITHOUT its question loses the requirement. Recording the full back-and-forth also lets a later reversal ("actually, drop SMS") correctly cancel an earlier ask instead of looking like a dropped one. Appends (does not replace): call once with the whole exchange, or turn-by-turn as you interview — the messages accumulate in order. This does NOT change the agent's config; it only records what to check the config against. Owner-gated to your own agent.
recordSourceRequirements
Remove an E.164 number from a private agent's allow-list (matched exactly). The owner keeps access regardless. Owner-scoped.
removeAllowedNumber
Log an improvement, blocker, or opportunity you hit WHILE building — feedback for the platform team about the build surface itself, NOT a message to the creator. Call this whenever the tools or catalog get in your way: a capability the spec NEEDED that doesn't exist (`kind: "blocker"`); a limitation you had to work AROUND but shipped anyway (`kind: "opportunity"`); or a clunky/missing/confusing MCP tool, catalog gap, or unclear error (`kind: "improvement"`). Logging it is fire-and-forget: it stores a row for the team and DOES NOT change or block this build — keep building. Input: `{ kind, title, body, severity?, capabilityArea?, agentId?, context? }`. Make `title` a one-line summary and `body` concrete — what you needed, why you couldn't do it with today's tools, and what would have helped (name the exact tool/capability id/field and quote the error or catalog absence). `severity` is `blocks-build` | `degrades` | `nice-to-have`; `capabilityArea` is a coarse tag (e.g. "scheduling", "communication", "knowledge"); pass the `agentId` you're building and any structured `context` (the failing call, ids) to help triage. Returns `{ id, status: "new", message }`. Prefer ONE feedback row per distinct gap (don't spam duplicates). This is the right channel for 'the platform should be able to…' — use it instead of silently faking a required integration or abandoning a `must` behavior.
reportBuilderFeedback
How do I improve a ChatGPT Plugin's discoverability?
The levers are the listing surface agents actually read: names, descriptions, keywords, tool metadata, and registry health. Which lever matters depends on where discovery breaks, which is what continuous measurement shows.
What are Outside Agent alternatives on ChatGPT?
As of 2026-09-10, Outside Agent competes with Botpress, Brainbase MCP, Camber, CodeWords, Codex Tasks, Dowaba AI Configuration, Expertise Live Chatbot, Graffiticode, Imagina RPG, Inistate, Manus, Mosaiq Labs, Ninjo, Noodle Seed, V7 Go (EU), YepCode, Zeiko Agents in ChatGPT AI Agent Builders & Deployment Platforms, ranked by public Discoverability Score.
Where is this profile measured?
This profile uses the geography attached to the latest public registry snapshot: US. Locale tags are intentionally omitted.