SKILL.md
WayAI Skill
WayAI is a SaaS platform for AI-powered communication hubs. Each hub combines AI agents and a human team across channels (WhatsApp, Email, Instagram, Telegram, native App). This workspace stores hubs as code — one folder per hub (hub.yaml + agents/*.{yaml,md} + evals/, journeys/, resources/) synced bidirectionally to the platform via the wayai CLI.
Platform is the source of truth. Workspace files are the edit surface — changes flow through files → wayai push → platform. Always wayai pull before editing to catch out-of-band changes.
How to use this skill: this file is the complete concept map — every WayAI primitive is defined here with enough depth to decide what to build and which files to touch. Field-level schemas, per-provider specifics, and mechanics live in references/; each domain section below ends with a pointer to its deep-dive file — open it when you're about to author or debug that domain. Before generating a full hub from scratch, read [references/canonical-example/README.md](references/canonical-example/README.md) once — it shows how the pieces wire together. The full routing table is at the end ([Reference Documentation](#reference-documentation)).
Agent Guidelines
- At the start of every session, before anything else, get current — every time, even for a quick task: update the CLI, then check whether this skill is stale and refresh it if so. A stale CLI or skill is the most common cause of a step below not working. Run the procedure as written in [Workflow → Existing hub](#existing-hub) steps 1–2 (cold start: [State machine](#state-machine) rows 1–1c), and take the skill-install command from row 1b rather than retyping it — its
mkdir -p .claudeprefix is load-bearing for Claude Code, and row 1c carries what to do when the install fails - Talk like a person, not a manual: the user may be new to WayAI — plain language, no jargon. Keep answers short and to the point; don't explain what they didn't ask about. If they seem stuck or ask what something means, give a one-line answer and move on — don't turn a reply into a tutorial
- Interface: setup runs on the
wayaiCLI and workspace files — the only automation surface, and it needs filesystem/shell access (code-harness agents — Claude Code, Codex, Cursor, OpenCode). Drive everything below through it. If you do not have shell access (app-harness agents — Claude Desktop, ChatGPT, etc.), you cannot configure WayAI yourself — hand the work to the person, the same shape as the OAuth connection handoff below (one URL, one action, one return signal): "Openhttps://app.wayai.pro. Set it up there, or run this with a shell-having agent — the one-prompt install is onhttps://wayai.pro. Tell me when done." Then keep answering from this skill, and never report config you didn't make - Only provide information from this skill, tool descriptions, or reference documentation
- Do not invent URLs, paths, or steps
- Hub config flows through files + the
wayaiCLI; one-time setup (orgs, OAuth) goes through the platform UI. Publishing preview → production is now CLI-capable (wayai publish) or UI - Always
wayai pull -ybefore editing — catches out-of-band changes - Always
wayai push -yimmediately after editing — editing and pushing are a single action - Never auto-commit — show
git diff, wait for user approval
Quick Decision: What Can I Do?
| Entity | How |
|---|---|
| Hub settings, agents, agent instructions, tools, kanban, states, resources, evals, journeys, outbound, custom tools | CLI (wayai push) |
Eval journeys (hub-as-code) — journeys/<slug>.yaml, flat folder |
CLI (wayai push / wayai pull; pull after first create to sync step ids) |
| Connections — non-OAuth (Agent providers, STT/TTS, Tool API key, MCP Bearer Token) | CLI (auto-created from org credentials) |
| Connections — OAuth (WhatsApp, Instagram, MCP OAuth) | Platform UI |
| Set/rotate a connection's credential directly (incl. production) | CLI (wayai set-connection-credential) or UI |
| Org credentials — create / rotate / edit | CLI (wayai create-credential / wayai update-credential) or UI |
| Org-level shared resources (org-as-code) | CLI (wayai org pull / push / diff) |
| Bases — schemas, records, relationships, files, toolsets (the Data surface) | CLI (wayai bases, wayai records, wayai record-types, …; config-as-code via wayai pull/push bases/<base>) — open [references/bases/README.md](references/bases/README.md) first |
| Skills sync to providers | CLI (wayai sync-skills) |
| Conversation testing | CLI (wayai send-message, wayai conversations, wayai delete-history) |
| Diagnose why a hub misbehaves (audio/TTS not delivered, agent silent, a tool failing) — check connection/credential health FIRST | CLI (wayai alerts) — surfaces active Status & Notices alerts (e.g. an invalid provider key shows as connection_auth 401). Run this before reading code or filing a report |
| Diagnose an inexplicable agent reply (wrong date, ignored rule, hallucinated value) — don't reason from the transcript; read what the agent actually received (resolved prompt, rendered context, injected timestamps, tool calls) | CLI (wayai conversations <id> observability [--message-id <id>]) — run this before editing instructions |
| Record a post-hoc business outcome on an ended conversation (e.g. customer purchased) as an analytics dimension | CLI (wayai conversations <id> annotate --set key=value [--type ...]) |
| Analytics | CLI (wayai analytics, wayai analytics query) |
| Cost / token spend analysis (per message, model, agent, role, or credential) | CLI (wayai analytics sql over the message table) — see [references/analytics.md](references/analytics.md#raw-sql--cost-analysis) — data.* paths need an explicit cast to run at all, plus the rules that make sums correct |
| Eval runs and results | CLI (wayai run-eval, wayai eval-results) |
| List eval scenarios / raw SQL over eval results | CLI (wayai evals, wayai evals sql) |
| Capture production conversation as eval | CLI (wayai eval capture <conversation_id>) |
| Capture production conversation as a journey (full multi-turn transcript) | CLI (wayai eval journey capture <conversation_id>) |
| Stop a running eval session | CLI (wayai eval session stop <session_id>) |
| Delete eval session(s) / run history | CLI (wayai eval session delete <session_id>, or --all for every session on the hub) |
| Bug reporting | CLI (wayai report create) |
| Workspace discovery | CLI (wayai list) |
| Organization — create | CLI (wayai org create) or UI |
| Organization — update, delete | UI |
| Publish/sync a preview to production | CLI (wayai publish, alias wayai sync) or UI |
| Delete hubs | UI |
| Replicate a preview, set/clear a preview's label | CLI (wayai replicate / wayai relabel) or UI |
| Teams, team users, hub users, admins, contact approval | UI (Hub → Users tab) |
| Org tags (create; edit display name/color — the slug is permanent) | UI (referenced from hub.yaml tags: by slug) |
Entity Hierarchy
Organization ← CLI (`wayai org create`) or UI
├── Org Credentials ← CLI (`wayai create-credential`/`update-credential`) or UI — API keys stored once, reused across hubs
├── Org Tags ← UI — gate which credentials each hub can resolve
├── Org Resources ← CLI (`wayai org pull/push`) — shared knowledge/skills, fan out to linked hubs
├── Bases ← CLI (`wayai bases`) — the Data surface. Org-level, NOT inside a hub (see Bases)
└── Hub ← CLI (`wayai create`, or auto-creates on push) or UI; publish/sync via CLI (`wayai publish`) or UI
├── Connections ← auto-created from org credentials on push (non-OAuth); OAuth via UI
├── Channels ← auto-provisioned, never authored (see Channels)
├── Agents ← CLI — `agents/<slug>.yaml` + `<slug>.md`
│ ├── Tools ← CLI — native, custom HTTP, MCP, delegation
│ └── Resource links ← CLI — `resources:` block in agent YAML
├── Kanban statuses ← CLI — `hub.yaml` (workflow stages for conversations)
├── States ← CLI — `hub.yaml` (JSON-schema data agents read/write)
├── Resources ← CLI — `hub.yaml` + `resources/` folder (knowledge + skills)
├── Evals + Journeys ← CLI — `evals/`, `journeys/`
├── Outbound ← CLI — `hub.yaml` (contacts, lists, schedules)
└── Teams + Users ← UI (Hub → Users) — teams, admins, team users, hub users
Setup order: Organization (CLI wayai org create or UI) → Org Credentials (CLI or UI) → Hub (CLI wayai create, or push auto-creates, or UI) → configure agents, tools, connections via CLI.
The wayai connection (native tools) is auto-created when a hub is created — no setup needed.
Hub Types
| Type | Conversations | Channels | Use Case |
|---|---|---|---|
chat |
ONE per end user | WhatsApp, Instagram, Email, Telegram, App | Person-centered: support, sales, helpdesk |
task |
MULTIPLE per user | App only | Task-centered: invoices, inventory, approvals |
Decision: external channels (WhatsApp/Instagram/Email/Telegram) needed → chat. Object/task processing → task.
AI Modes & the Conversation Model
Hub-level ai_mode sets what the AI does:
| Mode | Behavior |
|---|---|
pilot |
AI handles end users autonomously |
copilot |
AI suggests responses to the support team (no channel delivery) |
pilot+copilot |
Switches dynamically based on who currently responds |
turned_off |
AI disabled; humans only |
A conversation is the runtime session between an end user and the hub (config entities define behavior; conversations and messages are what they act on):
conversation_status:agent(AI handles it) |team(human team handles it) |ended(closed + archived)- Status selects the active agent track: status
agent→ Pilot track replies to the end user through the channel; statusteam(withcopilot/pilot+copilotmode) → Copilot track drafts suggestions the team sees in/support - Track switches: the
transfertoteamtool (agent → team) or a team handback in the support UI (team → agent).transfertoagent/consult_agentmove between agents within a track - Close paths: the agent's
closeconversationtool, transitioning into anisTerminalStatuskanban status (any surface — agent or harness tool, team drag-drop, REST), the team UI, or the hub'sautocloseinactivedays. Ended conversations are archived and listed in the Ended tab; withinconversationretentiondaysthey still accept post-hocwayai conversations <id> annotate - An agent's reply text is delivered automatically — there is no send-message tool; tools exist for actions beyond replying
Kanban status is orthogonal to all of this: it tracks workflow stage (custom slugs like qualified), not who is responding.
Agent Roles
| Role | Track | Per Hub | Description |
|---|---|---|---|
pilot |
Pilot | 1 | Responds to end users autonomously |
copilot |
Copilot | 1 | Suggests responses to the support team |
pilotspecialist / copilotspecialist |
Both | Multiple | Delegation target — full transfer via transfertoagent |
pilotadvisor / copilotadvisor |
Both | 1 each | Advisory input via consult_agent; returns control |
monitor |
Background | 1 | Observes silently |
conversationevaluator / messageevaluator |
Background | 1 each | Async quality assessment; excluded from normal routing. Their evaluationvariables feed Analytics; the messageevaluator also scores eval runs |
summarizer |
Background | 1 | Auto-provisioned with the first pilot/copilot. Rolling JSON summary of older messages, stored as conversation state with reserved slug conversationsummary. Fires async post-turn when effective input tokens cross the summarizer agent's summarizationthresholdtokens (default 120000; see below). Non-background agents see the summary as a <conversationsummary> block and can call expandsummary(sectionid) to fetch original messages. Schema is user-editable but must satisfy the anchor invariant (sections[].id, messageidstart, messageidend) |
consultant |
Track-independent (on-demand) | Multiple | Consulted by people (and agents) in visible consult threads. Never a pilot/copilot responder, never auto-fired, and never a transfer/advisor target. An advisor advises an AI mid-turn and is invisible; a consultant is consulted by people (and agents) in visible threads. Consult turns bill as normal foreground operations. Configurable today; consult dispatch (tagging a consultant from the support composer) ships in a follow-up |
transfertoagent targets any same-track agent — a _specialist or* the entry pilot/copilot, so the pilot can act as a hub-and-spoke router (specialists transfer cross-domain requests back to it for re-dispatch). Cross-track, advisor, consultant, and background roles are never transfer targets.
For role flow, delegation, and settings depth, see [references/agents/roles-and-settings.md](references/agents/roles-and-settings.md).
Handoff context engineering
When a conversation changes hands — transfertoagent or transfertoteam — whoever resumes rebuilds history from scratch, where every prior agent's turns appear as undifferentiated assistant messages and the human team's turns appear unattributed (the model can't tell which turns it authored vs. inherited). The runtime closes that gap automatically — author your agents to cooperate with it:
- The runtime persists a durable custody marker (
This conversation was handed off from X to Y.) on eachtransfertoagentandtransfertoteam, and delivers a one-time continuation note to a receiving agent's first turn (agent→agent only — a team handoff has no AI receiver to brief). You don't write these — so don't put "you were just transferred this conversation" framing in an agent's instructions; it's handled and would double up. - Always open each agent's instructions with its identity —
You are <Agent Name>, the <role/purpose>…. The runtime reinforces identity at the handoff moment, but the system prompt is the strongest signal and the only one present on every steady-state turn; the custody marker ("…to Y") only lands if the agent knows it is Y. - A specialist must do work, not bounce. The runtime blocks delegating back to any agent that already held the conversation earlier in the same turn (A→B→A and longer revisits) — the transfer is refused with an error telling the agent to complete the task, transfer to a different agent, or return control to the user. So don't write a
_specialistwhose instructions reflexively hand the conversation back to its delegator; it'll just hit the guard. (The chain resets each user turn, so re-routing to an earlier agent on a later* turn is fine.)
Summarizer agent config
The summarizer agent exposes summarizationthresholdtokens (default 120000, min 1000, max 1000000) as a top-level key in agents/summarizer.yaml. Lower it for testing; raise it for very long conversations. The summarizer's connection defaults to the pilot's; edit agents/summarizer.yaml to change its model or system prompt. The conversation_summary state's schema is round-trippable like any other state — extra fields beyond the anchors are allowed but the anchors are load-bearing. (Previously a hub-level hub.yaml setting — relocated to the summarizer agent.)
Connections & Credentials
A connection is a configured instance of a connector (a catalog entry: LLM provider, channel API, tool API, speech service) with its credential, scoped to one hub. An org credential stores the secret once at the organization level; connections reference it by name — raw secrets never enter YAML.
| Category | Examples |
|---|---|
| Agent | OpenAI, Anthropic, Google AI Studio, OpenRouter, xAI (required for AI) |
| Channel | WhatsApp, Instagram (OAuth — UI only); Resend (email), Telegram (API Key — auto-created) |
| Tool — Native | Wayai (auto-created), External Resources (API Key) |
| Tool — Custom | User-defined HTTP endpoints (API Key, Bearer Token, Basic Auth) |
| Tool — MCP | External MCP servers (Streamable HTTP) — Bearer Token via CLI; OAuth via UI |
| Speech | STT transcribes inbound voice notes (Groq, OpenAI, ElevenLabs); TTS synthesizes spoken replies (OpenAI, Groq, ElevenLabs) |
Auto-creation rule: Non-OAuth connections (Agent, STT, TTS, Tool — Custom, Tool — MCP via Bearer Token) are auto-created from matching organization credentials when hub.yaml is pushed. Matching respects org tags (an untagged credential is global — every hub can use it; a tagged credential is visible only to hubs sharing ≥1 of its tags) and credential environment. OAuth connections must be set up in the UI first.
OAuth connection handoff (any time — not just onboarding): OAuth connections (WhatsApp, Instagram, MCP OAuth) can't be created from the CLI — they need a one-time UI flow. Whenever one is needed — first-time setup or later (a new channel, an OAuth MCP server) — hand the user the full-path connections-tab deeplink https://app.wayai.pro/settings/organizations/<orgId>/hubs/<hubId>/connections?connector=<slug> (<orgId>/<hubId> from wayai status --json; <slug> ∈ whatsapp, instagram, mcp-server), then wayai pull -y once they're done. The deeplink opens the Connections tab (and highlights the connector if a connection already exists — e.g. re-auth); to create one the user clicks Add Connection, picks the \<Connector\> card, chooses OAuth, and finishes the provider flow. Use this tab form — not /connections/new?connector=…, which takes a connector_id UUID and defaults to the first auth type (MCP → Bearer Token), so it can't reach MCP OAuth (see [navigation.md](references/navigation.md)).
For per-provider setup, credential binding (credential:, no_auth:), tags, and production-credential decoupling, see [references/connections.md](references/connections.md).
Channels
Communication endpoints on a hub — where messages arrive and replies get delivered. Channels are never authored in YAML:
app(in-app chat) andsystem(internal) channels are created automatically with the hub- WhatsApp / Instagram / Email (Resend) / Telegram channels are provisioned automatically when their Channel connection is created
Channel uniqueness (phone / page / inbound address) is enforced across production hubs only — a preview can share endpoints with its production, and external channels are testable on previews via #test CODE tester registration (see Hub Environments).
Tools
Capabilities assigned per agent in agents/<slug>.yaml. Remember: replying with text needs no tool — tools are for everything else.
| Type | Source | How |
|---|---|---|
| Native | Platform built-ins (e.g., updatekanbanstatus, getstate, sendfiles, closeconversation, readfile) |
Listed by name under tools.native |
| Custom | HTTP endpoints you define | Defined under tools.custom with connection, method, path, config |
| MCP | Tools from connected MCP servers | Dual-origin — declared per-agent under tools.mcp and/or assigned in the Platform UI. wayai push discovers + assigns in one run; a present mcp key (even []) is authoritative, an omitted one preserves UI-assigned tools. See [native-tools.md](references/agents/native-tools.md#mcp-tools) |
| Delegation | Agent-to-agent (transfertoagent, consultagent, startconsultthread), agent-to-team (transfertoteam), or agent-to-hub (delegatetohub, startconsult_thread) |
Declared under tools.delegation with target (agent display name, team name, or hub name) |
Meta tools (gettoolschema, execute_tool) let agents call tools whose schemas are excluded from the inline list. Full native catalog + params: [references/agents/native-tools.md](references/agents/native-tools.md); custom tool schema: [references/agents/custom-tools.md](references/agents/custom-tools.md); designing which tools/params to expose: [references/agents/tool-principles.md](references/agents/tool-principles.md).
Hub delegation (delegatetohub)
Treats another hub as a consultant. The tool spawns a task conversation in the target hub, and that hub's answer comes back asynchronously as a message in the consult thread the request came from — so the tagging agent's turn ends immediately and must not wait for a result.
- type: hub
tool: delegate_to_hub
target: Billing Hub # a PRODUCTION (published) hub in the same organization
context_boundary: summary # instruction | summary | transcript
targetmust be a published (production)hub_type: taskhub in the same organization. Production because it matches branching semantics; task because a task hub gives each request its own conversation — achathub keeps one conversation per user, so every delegation would pile into a single thread and mix customers. A cross-org, unpublished, or chat-type target fails the push (and, at runtime, the tool call).- The target hub must CLOSE the spawned conversation — its close is the completion signal. The request text instructs it to, but a target whose agents can never close (no
closeconversationtool, no terminal kanban status) will leave the asker waiting. (An agent-started consult carries an expiry — seestartconsult_thread; a team-started hub delegation does not, so give the target hub a way to finish.) context_boundaryis a data-sensitivity decision, not a tuning knob. Hubs can differ in team membership and what they may see, so choose deliberately — there is no safe default that fits every pair of hubs:
| Value | What crosses into the target hub |
|---|---|
instruction |
Only the agent's own request. Nothing from the conversation. |
summary (default) |
The request + the rolling conversation summary. |
transcript |
The request + the full customer transcript. |
Internal consult traffic never crosses at any setting, and the model can neither choose the target nor widen the boundary — both are admin config.
- Only available inside a consult thread, which is where the result is delivered — so assign it to a
consultant-role agent. A result arriving after the conversation is closed is dropped (the thread is recorded ascancelledatclose). - Configured via CI/YAML only in v1 — the Platform UI's tool "Add" grid omits it until the hub picker and boundary control ship, because attaching it needs both choices above.
Agent-initiated consults (startconsultthread)
Lets a non-background agent put a question to a configured consultant (or partner hub) in a consult thread the support team can see — the third initiator of the one consult substrate, alongside a human tagging a consultant and an agent delegating to a hub.
- type: agent
tool: start_consult_thread
target: Billing Expert # a `consultant`-role agent on this hub
- The mode follows the target. A same-hub, non-harness consultant answers inside the tool call; a partner hub or a harness-backed consultant answers asynchronously — the asking agent ends its turn ("I'll check and get back to you") and is brought back automatically when the answer lands. A slow sync consult converts to async by itself, so agent instructions must handle "the answer will follow" for any target.
- Consultant→consultant chains are off by default, and sync-only when enabled — set
allowconsultantchain: trueon the tool to permit them, against a same-hub, non-harness consultant.monitor, the evaluators, andsummarizercan never initiate. - Budgets are enforced: consult chain depth, plus cycle refusal in both directions (agent A → B → A inside a hub, hub A → B → A across hubs), a cap per LLM call, and a durable cap per conversation. Every consult turn bills a foreground operation, so these caps are what stop an agent multiplying cost unattended.
- Nothing stays pending forever: an unanswered consult expires and brings the asker back with a timeout notice; a consult outstanding when the conversation closes is recorded
cancelledatclose. - A team member posting in an agent-started thread takes it over permanently — the AI is no longer brought back for that thread, and further consults into it return a fixed "taken over by the team" notice.
- Configured via CI/YAML only in v1, like
delegatetohub.
Full parameters and YAML shapes: [references/agents/native-tools.md](references/agents/native-tools.md#startconsultthread).
Kanban & States
Kanban statuses are workflow stages for conversations (visible in support/task views), defined per hub in hub.yaml:
- Identity: immutable lowercase
slug(stored in conversations, analytics, tool params; never renameable) + freely editable displayname. Tools accept only slugs (display names ride along as labels) — instructions must reference statuses by slug - Behavioral flags:
isInitialStatus(exactly one per hub),triggersAgentResponse(transition fires an agent turn),allowsAgentUpdate,isTerminalStatus(entering it closes the conversation; at most one per hub),isSchedulingStatus(+eventName). Several combinations are mutually exclusive — validated server-side on every write allowednextstatuses— optional transition allowlist, enforced at runtime on every surface, with two exemptions: a non-agent caller reaching the terminal status (the REST surface — board and programmatic alike; agents stay gated), and a re-close of a still-open conversation already sitting in the terminal status — any non-agent REST caller, programmatic ones included, but an agent only when reusing a stored outcome, when the status declares no outcomes, or when the outcome it selects is unrestricted. Omit = unrestricted;[]rejected (useisTerminalStatus)- Outcomes — the terminal status only may declare
outcomes: [{slug, name, color?, fromstatuses?}](closing dispositions, e.g. resolved/canceled).fromstatusesis a non-empty source-slug allowlist; omit it to accept the outcome from any source. A genuine terminal Kanban transition requires an eligible outcome and stores its slug for analytics asdata.meta.outcome. Agent closes (closeconversation, harnessendconversation) are routed through that transition and gated identically. The team Close button (web and mobile) is routed through it too and asks for an outcome first, but as a non-agent caller it keeps the terminal exemption fromallowednextstatusesdescribed above; when no outcome is eligible from the conversation's current status it falls back to the bare close rather than stranding it. Inactivity auto-close andPOST /:id/closestay outcome-free - Followups — per-status timed messages:
inactivity(after silence),beforeevent(counting back to the event),inactivityafterevent(the post-visit chase — its first step counts from the event, each later step from the previous nudge) orinactivityafterbeforeevent(the same chase, but starting when one specificbeforeeventfires, named byafterfollowupid). The last three requireisSchedulingStatus, and none arms unless the conversation carries ascheduledevent_date, supplied per transition and never in status config. With threshold/timeUnit, quiet hours, holiday exclusion - Additional context on transition — a
triggersAgentResponsestatus may declareadditionalcontextschema(JSON-Schema form the team fills on transition) +additionalinstructions(prose template with{{path.to.field}}/{{additionaldata}}placeholders injected into the triggered turn) - Lanes — optional presentational board grouping; no behavioral effect
Full field specs, constraint matrix, warnings, and a complete example: [references/kanban.md](references/kanban.md).
States are JSON-schema data agents read/write during conversations — via native tools (getstate, updatestate, setstatepath, resetstate, all addressing a state by its slug) and the {{state(scope, slug)}} instruction placeholder. Each state has conversation or user scope, a jsonschema, and an optional initial_value (pre-populated virtual record rendered until the first real write; omit to keep state silent until written).
Kanban vs State: kanban tracks workflow progression; state tracks structured data. Both coexist. Schemas and patterns: [references/states.md](references/states.md).
Resources
Knowledge and skills attached to agents. Content lives as real files under resources/<slugified-name>/ (the filesystem is the source of truth for resource content); hub.yaml resources: declares only name/type/description.
| Type | What | Runtime behavior |
|---|---|---|
knowledge (default) |
Document collections — FAQ, catalogs, policies | The linked resources are injected as resources/<slug> mounts into the listfiles native-tool schema at turn time; the agent explores content via listfiles + read_file |
skill |
Versioned capability package — SKILL.md (frontmatter name + description) + optional references/ |
Injected as a callable tool (default, works on all providers), or run natively in a provider container (usenativeintegration: true, Anthropic/OpenAI only; auto-syncs to the provider on wayai push, wayai sync-skills re-syncs after failures or late-added connections) |
Agents link resources in agents/<slug>.yaml under a resources: block (by name, with priority). Org-level resources shared across hubs live in wayai-ws/org/ via wayai org pull/push (push fans out to linked hubs).
File handling (text vs binary, 10 MB cap), skill authoring, execution modes: [references/resources.md](references/resources.md).
Bases (the Data surface)
A base is an org-level data container — the system of record behind your hubs. Hubs hold conversations; bases hold the structured data those conversations act on. Same CLI, same login, same workspace; a separate entity with its own subtree and its own promote verb.
- Primitives: record type → record, relationship type → relationship, file type → file, plus Actions and toolsets (curated MCP tools an agent calls), triggers and inbound webhooks (change in/out), external sources (back a record type with an external API), and seed fixtures (hermetic eval data)
- The one rule: config writes (record types, relationship types, file types, triggers, inbound webhooks, Actions, toolsets, seeds) only land on a preview base; data writes work anywhere. Promotion is human-run — surface
wayai bases promote <prod> --from <preview> --dry-runand wait - Ids are immutable. A base, record type, relationship type, Action or toolset
idis its identity — there is no rename, only create-new + migrate. Choose stable lowercase slugs up front - Workspace:
wayai-ws/bases/<base>/(base.yaml+ one file per entity), reached bywayai pull bases/<base>/wayai push bases/<base>. Apull/pushthat names targets in bothhubs/andbases/is refused, never merged - Commands:
wayai basesplus the top-levelrecords,record-types,relationships,relationship-types,query-relationships,files,file-types,attachments,toolsets,actions,triggers,inbound-webhooks,seed(each takes--base), andwayai bases tokens|secrets|sql|import|batch|providers|report - Connecting a hub: today via an ordinary MCP Server connection pointed at a toolset, or as an eval
target_base:. Hub-localresources/files are a different surface and stay hub-local
Open [references/bases/README.md](references/bases/README.md) before doing any base work — it carries the full object model and routes to the per-domain files below. Nothing in this section is enough to author a schema from.
Evals
Test scenarios that run the real agent with its real tools and score the result. The primitives:
- Scenario (
evals/<name>.yamlorevals/<set>/<name>.yaml) — optional multi-turnhistory, oneinput, anexpectedresponse (text and/ortoolcalls), optionalevaluatorinstructions. Scored by the hub'smessage_evaluatoragent; a required-but-skipped tool call fails the eval even when the reply text reads fine - Scenario set — first-level subfolder (one level only).
wayai run-evalruns exactly one set per session, whole or narrowed to chosen scenarios with repeatable--eval(and--runsfor chosen repetitions) — the cheap loop when a change touches a few scenarios of a large set - Journey (
journeys/<slug>.yaml, flat folder) — a stored happy-path transcript that materializes one derived eval per agent turn. The default way to build broad regression coverage:wayai eval journey capture <conversation_id>, thenwayai pull(syncs server-minted step ids) - Per-run
variables+runs: N— reliability is a distribution, not a 1/1 sample; each run resolves{{var(name)}}against its own disjoint row - Seed
fixture:— for any eval that writes: names a [base](references/bases/README.md) fixture the platform LEASES for the session — resetting it on acquire, clearing it on release — so runs start from a known baseline instead of the last run's residue. One preview base admits one eval session at a time: a second launch is refused withfixturetargetinuse(409) rather than allowed to corrupt the first, andrun-evalwaits it out by default — as it doesfixtureseed_unavailable(409), the base briefly refusing the lease under its own write backpressure - Seed
initial_state:— pre-populate user-scope WayAI [state](references/states.md) (a recurring-customer record, a saved profile) beforeinputruns, so behavior that depends on memory of prior conversations is testable; isolated + torn down per session likefixture: - Capture —
wayai eval capture <conversation_id>freezes a production conversation's last exchange into a scenario YAML
Good practice for tool-dependent evals: compose journey + fixture: + variables for repeatable, parallel runs, and phrase evaluatorinstructions as functional outcomes, not raw call counts ("one successful booking", not "exactly one bookappointment call") — tools fail transiently, and a correct agent retries. Full YAML shapes, seed-connection setup, run pacing, and authoring/interpreting principles: [references/evals.md](references/evals.md).
Outbound
Proactive messaging — the hub contacts people before they write. Three hub.yaml blocks:
outboundcontacts— named contacts with ≥1 channel identifier (phoneE.164 /email/instagramsid) + free-form tagsoutbound_lists— named static collections of contacts (referenced by contact name)outboundschedules— cron expression + timezone + list + channel + execution mode:directmessage(template / free text sent as-is) oragent_trigger(a system message triggers the agent, which opens the conversation naturally using its tools and instructions)
WhatsApp/Instagram delivery is constrained by the 24-hour messaging window (WhatsApp falls back to an approved template; Instagram skips). Inline contacts are practical to ~500 — beyond that, import via UI/API. Shapes, channel rules, limits: [references/outbound.md](references/outbound.md).
Analytics
Every conversation lands in the analytics store with variables from five origins:
| Origin | Path | Set by |
|---|---|---|
| System metrics | data.system.* |
Platform — message counts, response times, durations, tokens (~25 metrics) |
| Agent-defined variables | data.variables.* |
evaluationvariables declared on conversationevaluator / message_evaluator agents |
| Metadata | data.meta.* |
Platform — subject, kanbanstatus, hubtype |
| Post-hoc annotations | data.annotations.* |
wayai conversations <id> annotate --set key=value — real business outcomes (purchased, churned) recorded after the conversation ends; correlate predictions vs reality |
| Eval scores | data.eval_scores.* |
Eval runs only (is_eval = true rows — excluded from production analytics) |
Conversation rows are one grain; a second table, message, decomposes each conversation's spend per message (tokens, USD cost, operations) for per-model, per-agent, and per-credential cost analysis.
Query with wayai analytics (summary + per-variable aggregates; --metric, --filter, --period), wayai analytics query (structured: multi-variable, group_by, correlations), wayai analytics sql (raw SQL over conversation and message — the surface for cost analysis), or wayai evals sql (same SQL over eval rows). Defining good variables happens on the evaluator agents ([roles-and-settings.md → Evaluation Variables](references/agents/roles-and-settings.md#evaluation-variables)); filters, aggregations, cost queries, and workflows: [references/analytics.md](references/analytics.md).
Teams, Users & Access
People entities are UI-managed (Hub → Users tab: /settings/organizations/<orgId>/hubs/<hubId>/users), never in YAML:
- Hub User — the end user the AI talks to (customer/lead/employee). Uses
/chator/task - Hub Team User — support team member handling conversations in
/support; grouped into Teams (e.g. "Tier 2 Support") thattransfertoteamtargets by name — an unknowntargetfails at runtime - Hub Admin — full hub config access. Org Owner/Admin — org level (billing, credentials, hubs). Access is per-level, not inherited (an org admin isn't automatically a hub admin)
- Contact access control — with
nonapppermission: requirepermission, unknown channel contacts are heldpending(localized auto-reply, overridable viaaccessrequestmessage) until approved/blocked by the role inaccessapproval_role
Hub Environments
| Environment | Description |
|---|---|
preview |
Default. Editable workspace for configuring and testing |
production |
Read-only. Serves live traffic. Changes flow from preview via publish/sync |
Lifecycle:
- New hubs start as
preview— edit freely.wayai create --label <l>(orwayai push --label <l>on auto-create) names the first preview at creation - Publish (CLI
wayai publish, or UI) — first promotion creates aproductionhub cloned from preview - Sync (CLI
wayai publish/ aliaswayai sync, or UI) — pushes subsequent preview changes to the linked production. The one command auto-detects first-publish vs sync; it confirms by default (shows the preview→production diff) and-yskips the prompt. Promotes the pushed preview state, sowayai pushfirst - Replicate Preview (CLI
wayai replicate [hub] --label <l>or UI) — creates a new sibling preview (from a preview or production) for experimentation - Relabel (CLI
wayai relabel <label>/--clear, or UI) — set/clear a preview'spreview_label(the sibling disambiguator). NOT editable viahub.yaml+ push — it's server-owned
Production is read-only — all config mutations flow through preview. Multiple previews can link to the same production (many-to-1). Channel uniqueness is enforced on production only — previews can share phone/email/SID with their production.
WhatsApp/Instagram/Telegram channels can be exercised on a preview before publishing — register a tester via a #test CODE claim code (see references/connections.md → Channel → "Testing a channel on a preview before publishing").
Only preview hubs are editable. wayai pull also writes the linked production hub as a read-only mirror folder (bare slug — no --<label> suffix — with a marker comment in hub.yaml) so the live production config is browsable alongside the preview; wayai push refuses it and it's excluded from auto-select. Use wayai diff --production for a clean preview-vs-production diff.
Hub Settings
| Setting | Values | Default | Description |
|---|---|---|---|
nonapppermission |
everyone, requirepermission, notallowed |
everyone |
Who can reach hub via external channels |
timezone |
IANA timezone | — | For scheduling and display |
language |
en, pt, es |
en |
Language for hub-sent text (e.g. the pending-access notice on require_permission channels) |
accessapprovalrole |
admin, team |
admin |
Who may approve/block a pending contact: hub admins only, or also support team members |
accessrequestmessage |
string | — | Optional override for the "your access is pending approval" auto-reply (else a localized default by language) |
autocloseinactive_days |
1–180 |
7 |
Days of inactivity (no user/team message) before a conversation is force-closed. Every hub has one |
conversationretentiondays |
1–30 |
7 |
Days an ended conversation's DO stays alive for post-hoc annotate before cleanup (archival still happens at close) |
endedindexretention_days |
1–730 |
365 |
Days an ended conversation stays listed in the hub index. Bounds the Ended tab and how far back {{previous_conversations(N)}} reaches |
evalretentiondays |
0–3650, or null |
platform default (90) | Days a finished eval session (its runs, results and eval conversations) is kept before retirement. 0 keeps sessions forever. Omitting the field leaves the current value unchanged — write evalretentiondays: null to clear an override and go back to the platform default. Retirement deletes transcripts permanently — eval scores in Analytics survive |
First-time setup (cold start)
The user's entry point is wayai.pro, whose onboarding section carries the install command and the example prompt. Once the skill loads (here), this section drives everything from "skill loaded" to "hub responding to test messages." The agent must self-bootstrap from this section alone — returning users (second hub, new project) skip the page.
Always start by running wayai status --json. It returns a single state snapshot. Branch from the result; re-run between steps to confirm progress before moving on (idempotent).
Phrasing templates (use verbatim)
- Agent action — narrate one line then act, then one-line receipt:
- "Installing the CLI…" → run command → "Done — wayai 2.4.1 installed."
- User handoff — exactly one URL, one action, one return signal:
- "Open <URL>. Do <one action>. Tell me when done."
State machine
| # | Detection (from status --json or env) |
Action |
|---|---|---|
| 1 | CLI missing (wayai --version not found) |
Agent runs npm i -g @wayai/cli@latest. |
| 1b | No harness skill install present at project root (none of <root>/.claude/skills/wayai/SKILL.md, <root>/.opencode/skills/wayai/SKILL.md, <root>/.agents/skills/wayai/SKILL.md exists, where <root> is git rev-parse --show-toplevel or cwd if not in a git repo) |
If you are Claude Code, first mkdir -p <root>/.claude — the skills installer links Claude Code's .claude/skills/wayai only when .claude/ already exists, else it silently skips it while still printing "symlinked: Claude Code". Then run npx skills add wayai-pro/wayai-skill -y from <root>. After it completes, verify your own harness's path resolves (Claude Code → <root>/.claude/skills/wayai/SKILL.md, following the symlink; other harnesses → <root>/.agents/skills/wayai/SKILL.md). If it resolves, exit (the harness loads the skill next turn). If it doesn't but <root>/.agents/skills/wayai/SKILL.md exists, self-heal: mkdir -p <root>/.claude/skills && ln -sfn ../../.agents/skills/wayai <root>/.claude/skills/wayai, then re-verify. If still nothing, surface the install error and halt — do not silently exit (would loop on re-entry). |
| 1c | skill.installed: true, skill.latest is set, and skill.latest is newer than skill.version (CLI nightly check populates skill.latest) |
Agent runs npx skills add wayai-pro/wayai-skill -y from <root> to refresh the install in place, then exits (the updated skill loads on the next turn). On install failure, surface the error to the user and continue with the existing skill. |
| 2 | auth.logged_in: false |
Agent runs wayai login (opens browser). User handoff: "Open the page that just opened. Sign in or sign up. Tell me when done." |
| 3 | auth.logged_in: true, orgs: [] |
Ask once: "What should we name your organization? (usually your company name)". Then agent runs wayai org create "<name>" and re-runs status --json to pick up the new org. (Manual fallback only if the CLI create errors: open https://app.wayai.pro/settings/organizations/new.) |
| 3b | git rev-parse --show-toplevel fails (cwd is not in a git repo) |
The CLI requires git for workspace detection. Before acting, surface the resolved cwd and confirm with the user — handoff: "I'll initialize a git repo at <cwd>. Confirm or pick a different folder." This prevents accidentally initializing a repo in ~ or another unintended directory. After confirmation, agent runs git init in cwd. Then checks git config --global user.name and git config --global user.email; if either is empty, asks the user once for their name and email and runs git config --global user.name "<name>" / git config --global user.email "<email>". A GitHub remote is not required for the onboarding flow — only set one up later if the user wants the GitOps/CI loop. |
| 4 | workspace.scoped: false and reposcopeblocker: null |
Agent runs wayai init --org <active_org.id> — writes the org binding into wayai-ws/wayai.yaml, the repo's one committed workspace manifest. |
| 4b | workspace.scoped: false and reposcopeblocker is set |
Do NOT run wayai init — it rewrites wayai-ws/wayai.yaml and cannot fix a repo whose two config files disagree. For kind: "conflict", tell the user both organization ids and ask which one this repo belongs to, then delete or correct the root .wayai.yaml. For kind: "invalid", show the named file and its detail and ask the user to fix it. Re-run status --json afterwards. |
| 5 | Workspace scoped, hub goal not yet known | User handoff: "What should this hub do? Describe the goal, who talks to it, and the main use case." |
| 6 | LLM credential missing for chosen provider | User handoff: "Paste your OpenAI/Anthropic/Google API key here." Then agent runs wayai create-credential --name <name> --type "Bearer Token" --stdin. |
| 7 | Hub needs an OAuth connection (WhatsApp / Instagram / MCP OAuth) | Apply the OAuth connection handoff (Connections & Credentials → OAuth connection handoff): send the full-path connections deeplink for the connector, wait for completion, then wayai pull -y. The same handoff applies any time an OAuth connection is needed later, not only here. |
| 8 | Prerequisites met | Read [references/canonical-example/README.md](references/canonical-example/README.md) once for end-to-end wiring, then generate wayai-ws/hubs/<hub>/hub.yaml + agents/.yaml + agents/.md from the user's description (per-domain refs below for individual shapes), then wayai create -y to create + push the new hub (wayai push -y also auto-creates when the workspace has just this one new folder). |
| 9 | Push succeeded | Agent runs wayai send-message "Hi" and shows the response. User handoff: "Refine, add tools, or publish?" |
| 10 | User confirms publish | Agent runs wayai publish — shows the preview→production diff, then confirms (or wayai publish -y to skip the prompt). First publish clones preview → a new production hub; later runs sync. Paid plans only — if the CLI reports publishing requires a paid plan, manual fallback: open the publish deeplink below to upgrade + Publish in the UI. |
Deeplinks (canonical URLs — never breadcrumbs)
| State | URL | ||
|---|---|---|---|
| 3 (org create — manual fallback) | https://app.wayai.pro/settings/organizations/new |
||
| 6 (credential pre-fill) | https://app.wayai.pro/settings/organizations/<org_id>/credentials?type=bearer&name=<key-name>&prefill=true |
||
| 7 (OAuth connection) | `https://app.wayai.pro/settings/organizations/<orgid>/hubs/<hubid>/connections?connector=<whatsapp\ | instagram\ | mcp-server>` |
| 10 (publish — manual fallback) | https://app.wayai.pro/settings/organizations/<orgid>/hubs/<hubid>/overview?action=publish |
Rules
- Detect, don't assume — every step starts with a fresh
wayai status --json. - Agent actions get a one-line receipt; user handoffs get one URL, one action, one return signal.
- Never invent URLs outside the table above. Never instruct "go to Settings → …" — always a deeplink. For non-onboarding URLs see [
references/navigation.md](references/navigation.md). - One question at a time during hub scoping (state 5): goal, then channel, then LLM provider.
- Never auto-commit anything created during onboarding — show
git diffand wait for user approval before any commit.
Workflow
Existing hub
- Update CLI —
wayai update(always run before any operation; if the CLI isn't installed yet, bootstrap withnpm i -g @wayai/cli@latest) - Update skill if stale — run
wayai status --json; ifskill.latestis set and newer thanskill.version, runnpx skills add wayai-pro/wayai-skill -yand exit (the refreshed skill loads on the next turn). Otherwise continue. (Cold-start onboarding runs the same check as state-machine row 1c.) - Pull —
wayai pull -y(sync local files from platform; catches out-of-band changes) - Read context — read
wayai-ws/hubs/<hub>/AGENTS.mdfor this hub's notes (purpose, decisions, ongoing work); if you're also working a base, readwayai-ws/bases/<base>/AGENTS.mdtoo. AGENTS.md-aware harnesses (Codex, Cursor, OpenCode, Aider) auto-loadAGENTS.mdnatively. Retire an obsolete root bootstrap while you're here — see [Retiring the old rootAGENTS.md](#retiring-the-old-root-agentsmd) - Edit — modify
hub.yaml,agents/.yaml,agents/.md - Push —
wayai push -y(apply to preview hub; auto-pulls server-assigned IDs back) - Test —
wayai send-message "Hello" - Review — run
git diff, ask user to confirm. Never auto-commit. User commits and pushes tomain - Go live —
wayai publish(or the platform UI) when ready: shows the preview→production diff, confirms, then first-publishes (clones preview → new production hub) or syncs subsequent changes.pushfirst — it promotes the pushed preview state, not unpushed local edits
New hub (from scratch)
- Credentials —
wayai create-credential --name "openai-key" --type "Bearer Token"(one-time per org per credential) - Init —
wayai init(interactive) orwayai init --org <uuid> - Create files —
wayai-ws/hubs/<hub>/hub.yaml+agents/.yaml+agents/.md - Create —
wayai create -ycreates the hub, non-OAuth connections, and applies all config (auto-binds the worktree).create <folder>when the workspace has more than one hub folder. (wayai push -yalso auto-creates when it resolves to a single new folder; in a multi-hub workspace it requires--hub, socreateis the explicit, unambiguous verb.) - Test —
wayai send-message "Hello"
After the hub exists, follow the existing-hub workflow.
Hub-Folder Memory
wayai-ws/hubs/<hub>/AGENTS.md is the hub-specific memory for this hub — the contextual information the config files (hub.yaml, agents/) can't capture: purpose, key decisions and why, ongoing work, business rules that only apply here, terminology, integration quirks. Read it at the start of every hub-related task. wayai pull/push seed a placeholder AGENTS.md (+ a CLAUDE.md shim) if the folder has none, absence-guarded. AGENTS.md-aware harnesses (Codex, Cursor, OpenCode, Aider) auto-load it natively when the agent's cwd is inside the hub folder.
Maintain it actively:
- After significant changes (new agent, new tool, business rule update), update
AGENTS.mdso future sessions inherit the context - If a hub folder has no
AGENTS.md(or only the seeded placeholder), fill it with what you know — purpose, current agents, recent decisions — and ask the user to confirm or enrich - Keep it focused on why decisions were made and what makes this hub different. Don't restate platform mechanics — those live in this skill
- Record agreed scope in an optional
## Build plansection — that exact heading, an optionalGoal: <one sentence>under it, then GitHub task-list items (- [ ]/- [x]), nested at most one level. Keep it under ~30 items; past that, split into phases and archive completed ones into prose above the list. Tick items as they land — an item is done when it's pushed and working, not when the YAML exists. Like the rest ofAGENTS.md, it is never synced to the platform - Bases get the same treatment. If you're also working with a base, record the context its settings can't capture in the base folder's
AGENTS.mdunderwayai-ws/bases/<base-id>/(create it + aCLAUDE.mdshim if missing)
Overflow content goes into wayai-ws/hubs/<hub>/references/:
- When
AGENTS.mdgrows past ~200 lines or starts mixing topics, extract the deeper material into focused files underwayai-ws/hubs/<hub>/references/ - Examples:
references/business-rules.md,references/integrations.md,references/glossary.md,references/<api-name>-spec.md,references/<persona>-tone.md - Keep
AGENTS.mdas the always-on entry point with a short pointer to each reference file: e.g., "Detailed pricing rules:references/pricing-rules.md" - Hub-folder
references/are not synced to the platform — they're for agent context only, just likeAGENTS.md
Retiring the old root AGENTS.md
Older CLIs seeded a repo-root AGENTS.md (plus a CLAUDE.md holding @AGENTS.md) as a session bootstrap: get current, then load this skill. Nothing writes or refreshes it any more, so what it says is frozen at whatever release seeded it while the real procedure keeps moving — a copy that still names an install command or a startup step this skill has since changed will quietly send you down the wrong one. Its content is also entirely redundant now: everything in it is stated here. Retire it the first time you work in a repo that still has one — this is about the repo root only; hub- and base-folder AGENTS.md are memory and stay.
Identify it: the seeded file is titled # Working with WayAI, and its whole body is a "check for updates, then load the WayAI skill" bootstrap plus a few lines on talking to the user — no project-specific content. (A copy from an older release may instead describe two skills to install and load; the WayAI and Data surfaces merged into this one skill, so that one is doubly out of date.) Then:
First, check whether that file is how you got here. Harnesses differ, and this decides the action:
- You load skills from your own skills directory (Claude Code →
.claude/skills/, OpenCode →.opencode/skills/) → the root file is doing nothing for you. Delete it, and deleteCLAUDE.mdtoo if it is the one-line@AGENTS.mdshim. - You auto-load
AGENTS.mdinstead (Codex, Cursor, Aider) → that root file may be the only thing that pointed you at this skill, and deleting it would leave the next session with no entry point. Keep the file and replace the stale bootstrap with a pointer, so nothing is frozen but the hop survives:
```text # Working with WayAI
Load the WayAI skill at the start of every session, before anything else: .agents/skills/wayai/SKILL.md. It holds the real instructions. ```
Either way, if the file also carries your own notes, keep those — replace or delete only the seeded bootstrap part. A root AGENTS.md that is entirely your own content was never the CLI's; leave it alone.
Everything the bootstrap used to say now lives in this skill: the update discipline is in [Agent Guidelines](#agent-guidelines), the procedure in [Workflow → Existing hub](#existing-hub).
Common CLI Commands
wayai update # Update CLI (run before any operation)
wayai login # OAuth — or `wayai login --token` for headless/CI
wayai logout # Sign out and clear stored credentials
wayai whoami # Show the authenticated identity
wayai org create # Create a new organization (you become its owner): `wayai org create "<name>"` [--region <r>] [--json]
wayai org pull # Org-as-code: fetch org-level shared resources → wayai-ws/org/
wayai org push # Org-as-code: apply local org resources to the platform (fans out to linked hubs); `wayai org diff` previews
wayai create-credential # Create org credential (--name, --type "API Key"|"Bearer Token"|"Basic Auth", --org, --stdin)
wayai update-credential # Rotate/edit an org credential (--name <cred>; --stdin/--secret rotates the secret, --rename, --description, --tag, --environment)
wayai set-connection-credential # Set a connection's credential directly — --connection <name> + either --org-credential <name> (link) or --field <f> --stdin (raw secret). Works on preview + production (the sanctioned production-credential write)
wayai init # Scope this repo to an org in wayai-ws/wayai.yaml (interactive — creates an org inline if you have none); --org <uuid> to skip prompt
wayai migrate # Move a legacy workspace/ + root org/ layout to wayai-ws/, and copy a root .wayai.yaml org binding into wayai-ws/wayai.yaml (the root file is KEPT — older CLIs still need it)
wayai pull # Pull hub config from platform (-y skips confirmation; auto-binds worktree on first pull). Also writes the linked production hub as a read-only mirror folder
wayai push # Push local changes (-y skips confirmation; auto-pulls IDs back). Auto-creates a lone new folder; a multi-hub workspace needs --hub or `wayai create`
# `pull`/`push` are ONE verb each, routing by workspace subtree: `hubs/<hub>` (above) or `bases/<base>` (the Data surface).
# The target resolves in this order: the `--hub`/`--base` flag → a positional → the folder you are cd'd into →
# `wayai-ws/wayai.yaml`'s `default_hub`/`default_base` → the sole folder across both subtrees.
# So in a workspace holding BOTH a hub folder and a base folder, a bare `wayai pull` is refused as ambiguous.
# Name the subtree — `wayai pull bases/crm`, `wayai push hubs/support` — or declare ONE default in wayai-ws/wayai.yaml.
# An invocation naming a target in BOTH subtrees is refused, never merged; run the two it prints, one at a time.
wayai pull bases/<base> # Pull a base's config into wayai-ws/bases/<base>/ (-y skips the overwrite confirm; auto-binds; mirrors a production origin read-only)
wayai push bases/<base> # Push a base folder to its PREVIEW base (--dry-run to stop after the diff; --prune to apply removals). Production is `wayai bases promote`, never push
wayai create [folder] # Explicitly create a new hub from an idless folder, then push (--label names the preview). The discoverable verb when a workspace has more than one hub folder
wayai diff # Dry-run diff of local files vs preview (read-only); --production diffs vs the linked production hub
wayai replicate [hub] # Clone a hub (preview or production) into a new sibling preview; --label <l> names it. Pulls the new preview into its own folder
wayai relabel <label> # Set a preview hub's label (--clear removes it; --hub to target). Renames the local folder. The server-owned way to change preview_label
wayai publish # Promote the preview to production (alias: wayai sync). Auto-detects first-publish (clones preview → new production) vs sync (pushes changes to the linked production); shows the preview→production diff and confirms (-y skips). Paid plans only. `wayai push` first — promotes the pushed preview state
wayai use <selector> # Scope this worktree to a hub or base (UUID, folder name, hubs/<n>, bases/<n>, --hub/--base). Replaces that axis; `--add` widens it instead
wayai unbind [<sel>] # Drop one entry from the worktree scope; bare, it clears the whole scope (both axes)
wayai send-message # Test message to a hub (preview or production). -c <id> continues a conversation
# `-f, --file <path>` attaches a file (repeatable, max 20, ~7 MB/request) delivered exactly as a real channel does — this is how image/document behaviour gets exercised in the dev loop. Message text is optional when a file is attached (an attachment-only send mirrors a photo with no caption). Images reach the model as a signed URL, not base64
wayai alerts # Active connection/credential alerts for a hub (Status & Notices). RUN THIS FIRST when a hub misbehaves (audio/TTS not delivered, agent not replying, a tool failing) — an invalid/expired provider key shows as `connection_auth` 401 here instead of forcing a guess from code. --hub <uuid|name>, --json
wayai conversations # List or inspect conversations (default text view omits message_id — use --json or `observability` to discover ids)
# `--status <agent|team|ended>` combines with `--period 7d` / `--from` / `--to`, which bound LAST ACTIVITY: `--status team --to <date>` is the idle human-queue check ("waiting on a person, nothing since <date>"). Without `--status` AND without `--org`/`--all`, a bare `--period`/`--from`/`--to` reads the analytics history instead, which bounds conversation START and covers ended conversations only
# `--org <organization_id>` / `--all` list across the hubs `wayai list` enumerates plus the production hubs those name as parents — one command instead of a loop. These always read the live listing (open + ended, windowed on LAST ACTIVITY), with or without `--status`. A production PARENT you cannot read is named as skipped and does not fail the run. A production hub whose PREVIEW you cannot see is not enumerable anywhere, so it is NOT covered and NOT reported at all — reach it with `--hub <id>`. `--limit`/`--offset` are per hub; a hub in scope that refuses is named and the command exits non-zero rather than reporting a smaller queue, and an empty scope is reported as such, never as an empty queue
# `wayai conversations <id> observability` — list LLM turns with message_id, latency, tool_calls (assistant turns only)
# `wayai conversations <id> observability --message-id <id>` — full record for one turn (prompt, completion, tool calls, tokens; --json for raw)
# `wayai conversations <id> annotate --set key=value [--type numeric|categorical|text]` — set a post-hoc business outcome (e.g. customer_purchased=true) on an ended conversation as an analytics dimension; repeat --set for multiple keys (needs the hub within its conversation_retention_days window)
wayai delete-history # Clear conversation history (testing). Full history (no --conversation-id) ALSO clears the user's user-scoped hub state, so the next send-message is a true cold-open; --conversation-id <id> deletes just one conversation and never touches state
wayai sync-skills # Sync skills to provider connections; --connection-id <uuid> to scope
wayai sync-mcp # Re-discover an MCP connection's tools (refresh stale schemas); --connection <name|uuid>; --check reports drift read-only (CI, exit 1 on drift)
wayai analytics # Summary + per-variable aggregates; --metric, --filter, --period, --json
wayai analytics query # Structured ClickHouse query (multi-variable, group_by, correlations)
wayai analytics sql # Raw single-SELECT SQL over `conversation` (one row per conversation) and `message` (per-message tokens/cost/operations — the surface for cost analysis); --schema prints both catalogs + the message-grain rules (`data.*` paths need `toFloat64OrNull(toString(...))` for numerics, `toString(...)` for grouping); --limit, --json
wayai evals # List eval scenarios for the hub (--enabled / --disabled)
wayai evals sql # Raw single-SELECT SQL over the hub's eval result rows ("SELECT …"; --schema prints the column + eval-score-path catalog; --limit, --json)
wayai run-eval # Run a scenario set's enabled evals (sole set by default; --set to pick on multi-set hubs; repeatable --eval <name> runs ONLY those scenarios and --runs 2,6 only those repetitions of one; --pacing conservative|balanced|fast|<ms> to throttle run dispatch; waits inside --timeout for a fixture held by another session or for transient base write backpressure — --no-queue fails fast and --no-wait implies it; exits 1 if --timeout expires with the session still running)
wayai eval-results # Inspect eval results (--session <id> or --eval <name>; --runs for per-run detail, --json for raw)
wayai eval capture # Capture production conversation as eval YAML (<conversation_id> [--set <name>])
wayai eval journey capture # Capture a conversation's FULL transcript as a journey (<conversation_id> [--name <n>]); then `wayai pull` to sync it to journeys/<slug>.yaml
wayai eval session stop # Cancel a running eval session (<session_id>) — recovery when a run-eval died without cancelling its own session
wayai eval session delete # Delete an eval session + its run history (<session_id>, or --all for every session on the hub; -y to skip confirm)
# Journeys are hub-as-code: edit journeys/<slug>.yaml and `wayai push` (pull after first create to sync step ids)
wayai list # List organizations and hubs
wayai status # Show workspace status
wayai report create # Create platform bug report (--title, --description, --hub, --conversation, --error). With --hub, auto-attaches the hub's active alerts so triage sees a credential/connection cause up front — check `wayai alerts` yourself first, it's often not a platform bug
wayai report edit # Amend your own pending report (<id> --title/--description/--error/--steps/--context)
wayai report list # List your reports (newest first; --status <s>, --json)
wayai report get # Show a report's status + message thread (<id>, --json)
wayai report accept # Accept a shipped fix (<id>) → addressed
wayai report contest # Contest a shipped fix or a dismissal (<id> --reason "...") → back to triage
# Bases — the Data surface. A base is org-level and a separate entity from a hub.
# `use`/`unbind` are local (this worktree's scope file) and DEPRECATED — prefer
# `wayai use bases/<id>` / `wayai unbind bases/<id>`; every other command below
# reaches the platform. Full grammar: references/bases/.
wayai bases list # List bases (--tag to filter)
wayai bases get <id> # Show one base
wayai bases create <id> --name "<name>" # Create (--environment preview|production)
wayai bases update <id> --name/--tags/--timezone/--settings/--integrations
wayai bases rename <id> --name "<name>" # Display name only — the id/slug is immutable
wayai bases tag <id> --tags a,b # Replaces the existing tags; names must exist as organization tags
wayai bases delete <id> # Tombstone; --purge (preview only) destroys storage
wayai bases create-preview <origin-id> --name "<name>" # Clone config into a new preview
wayai bases list-previews <origin-id>
wayai bases promote <production-id> --from <preview-id> # NOT `wayai publish`, which promotes a HUB
wayai bases rollback <production-id> --promotion <id>
wayai bases promotions <production-id>
wayai bases use <base> # Deprecated alias of `wayai use bases/<id>` (see Worktree scope)
wayai bases unbind # Deprecated alias — clears only the base axis of the worktree scope
wayai bases tokens|secrets|sql|import|batch|providers|report ... # namespaced — each names a concept WayAI already owns
# (`wayai report` files a PLATFORM bug; `wayai bases report` files a base one)
# Everything INSIDE a base is its own top-level namespace, each taking --base <id>:
wayai records | record-types | relationships | relationship-types | query-relationships
wayai files | file-types | attachments | toolsets | actions | triggers | inbound-webhooks | seed
wayai bases --help (and wayai <namespace> --help) prints the full tree; the grammar for every command above is in [references/bases/](references/bases/README.md). Two collisions worth holding onto: wayai use/wayai unbind maintain one worktree scope covering both hubs and bases (the selector says which — wayai use bases/crm vs wayai use <hub-uuid>), so wayai bases use/wayai bases unbind are deprecated aliases for the base axis alone; and wayai publish promotes a hub while wayai bases promote promotes a base. Hubs and bases are different entities — never substitute one for the other.
Closing the loop on a report you filed. After triage escalates and the fix ships, your report moves to shipped — you'll get an email, and wayai login/wayai status remind you once (or find it with wayai report list --status shipped). Read wayai report get <id> (status + the fixer's note in the thread), then accept if it works or contest --reason "..." if it doesn't. You can also contest a dismissed report you believe is real — it routes back to triage. Contests are bounded (a cap, and triage may mark a dismissal final); past those, contact support.
Most commands accept --hub <uuid|folder> to disambiguate when multiple hubs live in wayai-ws/hubs/.
Worktree scope
Each git checkout (main or linked worktree) carries a scope: a set of hubs and a set of bases, in one file at <git-dir>/wayai-scope (per-checkout, never tracked).
hubs: [11111111-1111-1111-1111-111111111111]
bases: [crm, billing]
It is a routing tripwire, not a concurrency lock (it does not coordinate concurrent edits) and not authorization (grants are the security boundary). It never selects a target either — it only refuses one.
How it fills up. Each axis is seeded independently by the first successful pull/push/create into a checkout whose set for that axis is empty. After that it only grows on purpose: an automatic bind never appends to a non-empty set.
How it is enforced. wayai pull, wayai push, wayai publish and wayai relabel refuse to run against a hub outside a non-empty hubs set; wayai pull bases/<base> and wayai push bases/<base> refuse a base outside a non-empty bases set. Creating a new hub or base in a checkout already scoped on that axis is refused the same way.
Changing it.
wayai use <selector> # replace that axis with this one target
wayai use --add <selector> # widen: add one target to that axis
wayai unbind <selector> # drop one entry
wayai unbind # clear the whole scope, both axes
A selector names the axis the same way pull/push do: a bare UUID is a hub, bases/<id> and --base <id> are a base, hubs/<name> and --hub <x> a hub, and a bare folder name is looked up in both subtrees (present in both → refused, never guessed). wayai status shows the current scope; status --json carries it as worktree_scope.
A worktree legitimately scoped to several entities is normal — a base serving several hubs, or delegation wiring hub A to hub B. Widen with --add for those; do not clear the tripwire to get past them.
If push/pull errors with a scope refusal, stop and ask the user before doing anything else. It usually means a prompt was meant for a different worktree. Do not run wayai unbind, wayai use (with or without --add), wayai bases unbind, wayai bases use, or edit the scope file without explicit user instruction in the current session — widening the scope is as much a session-routing action as clearing it, and both change which hub or base the user thinks you're working on.
Repository Structure
.wayai.yaml # DEPRECATED org binding — read as a fallback, never written.
# `wayai migrate` copies it into wayai-ws/wayai.yaml and
# KEEPS it (older CLIs still require it). Delete it only
# once every environment running this repo is current.
AGENTS.md / CLAUDE.md # STALE if present — an older CLI's root bootstrap; nothing
# writes or refreshes these now. Delete it, or (on a harness
# that auto-loads AGENTS.md) replace its body with a pointer
# to the skill — see "Retiring the old root AGENTS.md".
# Hub- and base-folder AGENTS.md are memory and are unaffected.
.claude/skills/wayai/ # Claude Code skill install (provisioned by `npx skills add wayai-pro/wayai-skill -y`)
├── SKILL.md
└── references/ # On-demand deep-dive references (see index below)
.opencode/skills/wayai/ # OpenCode skill install (same provisioner; same SKILL.md + references/ layout)
.agents/skills/wayai/ # Neutral skill install (same provisioner; same SKILL.md + references/ layout)
wayai-ws/ # All WayAI config-as-code (init creates wayai-ws/hubs/)
├── wayai.yaml # Workspace manifest — the repo's ONE committed config file
│ # `organization_id:` / `organization_name:` — which org this
│ # repo is scoped to. Written by `wayai init`; required.
│ # `default_hub:` / `default_base:` — OPTIONAL, which subtree a
│ # bare `wayai pull`/`push` targets. Declare AT MOST ONE:
│ # declaring both is a mixed invocation and is refused.
│ # Write these two by hand — no command creates them.
│ # `wayai init` keeps their VALUES when it rewrites the file,
│ # but YAML comments in it are not preserved.
├── org/ # Org-as-code — shared resources (wayai org pull/push)
│ ├── resources.yaml
│ └── resources/<slug>/
├── bases/ # One folder per preview base (Data surface) — `wayai pull bases/<base>`
│ └── <base-id>/
│ ├── base.yaml # Base identity; `wayai use bases/<folder>` reads it
│ ├── record-types/<id>.yaml # One file per entity, per kind. Secrets are never written
│ ├── relationship-types/<id>.yaml
│ ├── inbound-webhooks/<id>.yaml
│ ├── triggers/<id>.yaml
│ ├── toolsets/<id>.yaml
│ ├── actions/<id>.yaml
│ └── seeds/<id>.yaml
└── hubs/
└── <hub-slug>--<label>/ # One folder per preview hub (disambiguated)
├── hub.yaml # Hub config + states + connections + outbound + resources
├── agents/
│ ├── <slug>.yaml # Agent config (one per agent, slugified name)
│ └── <slug>.md # Agent instructions (one per agent)
├── evals/ # Eval scenarios (synced)
│ ├── <name>.yaml
│ └── <set>/<name>.yaml
├── journeys/ # Eval journeys (synced; flat folder, one file per journey)
│ └── <slug>.yaml
├── attachments/ # Eval/journey turn attachment files (synced; hub-root, shared by evals + journeys)
├── resources/ # Knowledge & skill resource files (synced)
├── AGENTS.md # Hub-specific memory — scaffold seeded on pull/push (NOT synced; fill it in)
├── CLAUDE.md # Per-hub Claude Code shim — `@AGENTS.md` (seeded if absent, NOT synced)
└── references/ # Hub-specific supporting files (NOT synced)
└── <hub-slug>/ # Linked production hub: READ-ONLY mirror (bare slug, no --label). Refreshed each pull; never pushed
Preview hub folders use hub-slug--<previewlabel> or hub-slug--<hubid_prefix> for disambiguation; the linked production hub mirrors to the bare hub-slug (no suffix). Existing repos that still use the legacy workspace/ + root org/ layout keep working (the CLI reads either) — run wayai migrate to move to wayai-ws/.
hub.yaml Shape
version: 1
hub_id: "abc-123-def" # set by `wayai pull` — do not edit
hub_environment: preview # set by `wayai pull` — do not edit
preview_label: experiment-a # server-owned, set by `wayai pull` — do not edit (only on previews; sets the hub folder's `--<label>` suffix). Editing here is IGNORED on push (push warns); change it with `wayai relabel <label>` / `--clear`, or set it at creation with `wayai create --label` / `wayai replicate --label` / `wayai push --label`
hub:
name: Customer Support
# description: Handles refunds, order status, and billing questions # optional, human-facing
hub_type: chat # chat | task
ai_mode: pilot+copilot # pilot | copilot | pilot+copilot | turned_off
timezone: America/New_York
non_app_permission: everyone
# tags: [retail, vip] # org tag slug names (create in UI first); gate which org credentials this hub can resolve. Omit to leave unchanged; [] clears. See references/connections.md#organization-tags
# auto_close_inactive_days: 7 # force-close a conversation after N days of inactivity (see Hub Settings)
# conversation_retention_days: 7 # keep an ended conversation's DO alive N days for post-hoc `annotate` (see Hub Settings)
kanban_statuses: # full field specs + constraints: references/kanban.md
- slug: new
name: New
order: 0
color: "#22c55e"
isInitialStatus: true
allowed_next_statuses: [in_progress, waiting_for_customer, resolved]
- slug: in_progress
name: In Progress
order: 1
color: "#3b82f6"
triggersAgentResponse: true # transition fires an agent turn; can carry additional_context_schema + additional_instructions (see references/kanban.md)
allowed_next_statuses: [waiting_for_customer, resolved]
- slug: waiting_for_customer
name: Waiting for Customer
order: 2
color: "#f59e0b"
allowed_next_statuses: [resolved]
followups:
- order: 0
type: inactivity
threshold: 30
timeUnit: minutes
instructions: "Hi! Just checking in — do you still need help?"
- slug: resolved
name: Resolved
order: 3
color: "#ef4444"
isTerminalStatus: true # at most one terminal status per hub
outcomes: # terminal-transition policy; selected slug → data.meta.outcome
- { slug: resolved, name: Resolved, color: "#22c55e", from_statuses: [in_progress, waiting_for_customer] }
- { slug: canceled, name: Canceled, color: "#ef4444" }
- { slug: abandoned, name: Abandoned, color: "#a0aec0" }
states:
- id: "state-uuid-789" # set by pull
slug: order_tracking # immutable identifier; auto-derived from name if omitted
name: order_tracking
scope: conversation # conversation | user
description: Tracks current order
json_schema:
type: object
properties:
order_id: { type: string }
status: { type: string, enum: [pending, shipped, delivered] }
initial_value: { order_id: null, status: null }
connections:
- name: anthropic
type: Agent
service: Anthropic
# sync_credentials_to_production: true # default; false keeps production's credential separate (set it via `wayai set-connection-credential`). See references/connections.md#credential-propagation-to-production-sync_credentials_to_production
- name: my-api-connection
type: Tool
service: REST API
- name: elevenlabs-tts
type: TTS
service: ElevenLabs TTS
settings: # Per-connection params (voice/model/language, etc.); keys = connector_settings_schema. See references/connections.md
voiceId: pNInz6obpgDQGcFmaJgB
modelId: eleven_multilingual_v2
hub.yaml also holds resources:, outboundcontacts:, outboundlists:, outbound_schedules: blocks. See the per-domain references for full schemas.
agents/<slug>.yaml Shape
id: "agent-uuid-123" # set by pull
name: Pilot Agent
role: pilot
connection: anthropic # connection display name
# instructions resolved by convention from agents/<slug>.md
# additional_context_template: # per-turn context, emitted as an <additional_context> tag on the last user message (same {{...}} syntax as instructions). Put high-churn placeholders ({{now()}}, {{state()}}) HERE, not in instructions, to keep the system prompt cache-stable (see references/agents/instructions.md#additional-context-cache-friendly)
# response_format: # {schema_name, schema_json} — force structured JSON output instead of free text (see references/agents/roles-and-settings.md#response-format-structured-output)
# enabled: true # default; omitted
# include_message_timestamps: false # default; when true, appends [timestamp, weekday, daypart] to user messages
# previous_conversations_count: 3 # off by default (max 20); this user's N most recent ended conversations, prepended to the FIRST user message and frozen at the first agent turn that uses it. Foreground roles only; `0` clears it (omitting the key on update leaves it as-is); summaries come from the conversation_evaluator (see references/agents/roles-and-settings.md#previous-conversations-context)
settings:
model: claude-sonnet-5
max_tokens: 4096
# temperature (only sampling knob): Sonnet 5 / Opus 4.7+ / Fable strip a non-default value — set it only on Opus 4.6 / Sonnet 4.6 & older
# reasoning per provider: Anthropic thinking_enabled + effort · OpenAI/OpenRouter/xAI reasoning_effort · Gemini reasoning_level (see roles-and-settings.md)
# file_handling_mode: no longer changes what the model receives (earlier files are always announced by path, never re-attached); metadata_only still auto-enables read_file. max_attachment_size_mb is inert (see roles-and-settings.md#file-handling-all-llm-connectors)
# deliver_preamble: true # default; pre-tool "let me check…" text is delivered as its own message (voice: own TTS clip; ignored on email). false = only the final reply (see roles-and-settings.md#pre-tool-preamble-delivery-all-llm-connectors)
# copilot_trigger: every_message # copilot-track only; on_demand suppresses the automatic per-message suggestion — the team pulls one via the "Suggest reply" button (cost/noise lever; see roles-and-settings.md)
tools:
native:
- update_kanban_status
- get_state
- update_state
delegation:
- type: agent
tool: transfer_to_agent
target: Specialist - Billing
- type: team
tool: transfer_to_team
target: Tier 2 Support
# Hub-as-agent: hand a self-contained request to ANOTHER hub in this org.
# `target` is a PRODUCTION (published) hub name. context_boundary is required
# reading — see "Hub delegation" below.
- type: hub
tool: delegate_to_hub
target: Billing Hub
context_boundary: summary # instruction | summary | transcript
custom:
- name: check_order_status
description: Check order status by email
method: post
path: /api/orders/status
body_format: json
connection: my-api-connection
config:
name: check_order_status
description: Check order status by email
parameters:
type: object
properties:
email: { type: string, description: Customer email }
required: [email]
resources:
- name: Company FAQ # links a hub resource by name (see Resources)
resource_id: "resource-uuid" # set by pull
priority: 0
For full agent options (settings per connector, additionalcontexttemplate, responseformat, file handling, native tool params, custom tool fields, composedtools, placeholders), see [references/agents/](references/agents/).
Evaluation variables — conversationevaluator / messageevaluator agents carry an evaluation_variables list (the structured fields they emit per conversation/message, which become data.variables.* in Analytics), round-tripped via pull/push. See [references/agents/roles-and-settings.md](references/agents/roles-and-settings.md#evaluation-variables).
Key Rules
- Read-only fields:
hubid,hubenvironment,id— set bywayai pull, never edit - Connection auto-creation: non-OAuth connections in
hub.yamlresolve to org credentials by matchingservice+authentication_type. Usecredentialfield to disambiguate when multiple org credentials share the same auth type. OAuth connections (WhatsApp, Instagram, MCP OAuth) must already exist (UI setup — see OAuth connection handoff) — referenced by name only - Production credentials: a connection copies its credential into production on publish/sync by default. Set
synccredentialstoproduction: falseto keep production's credential separate, then set it directly withwayai set-connection-credential(production is otherwise read-only). See [references/connections.md](references/connections.md#credential-propagation-to-production-synccredentialstoproduction) - Org tags:
hub.tags(slug names, created in the UI first) gate which org credentials the hub can resolve (matching rule in Connections & Credentials above). A tag's slug is permanent — the UI edits only its display name and color — so these lists never need rewriting. See [references/connections.md](references/connections.md#organization-tags) - Tool groups:
native(platform built-ins by name),delegation(agent-to-agent/team handoff),custom(HTTP endpoints with connection),mcp(tools from an MCP Server connection, byname+connection— push discovers + assigns; see references/agents/native-tools.md). Designing which params/tools to expose: [references/agents/tool-principles.md](references/agents/tool-principles.md) - Names are foreign keys: cross-entity references resolve by display name at push/runtime — agent
connection:, delegationtarget:(agent name or UI-managed team name), agentresources[].name, evalagent:, custom toolconnection:. A dangling name fails the push or the runtime call — when renaming anything, update its referrers in the same edit - Renaming: change the
namefield — the stableidensures it's detected as a rename, not delete + create. For agents,wayai pushauto-renames the.yamland.mdfiles - Default omission: fields matching defaults are omitted (e.g.,
enabled: true, kanban flags defaultfalse,excludeHolidaysdefaultstrue) - Entity matching (sync/diff):
idfirst (stable UUID), then fallback. Exceptions: states match byname(unique per hub regardless of scope); evals match byname + path; native tools bytool_nameper agent
Slugification & Entity Matching
Names → URL-safe slugs for filenames:
- Lowercase
- Normalize accents (NFD + strip diacritics)
- Replace non-alphanumeric with
- - Collapse consecutive
- - Trim leading/trailing
- - Limit to 50 chars
Examples: Mario's Pizza → marios-pizza; Suporte Nível 2 → suporte-nivel-2; Specialist - Billing → specialist-billing.
Editing Agent Instructions
- File:
agents/<slugified-agent-name>.mdalongside the corresponding.yaml - Always save under
wayai-ws/hubs/<hub>/agents/, never/tmpor other paths - Always
wayai pullbefore editing to avoid clobbering out-of-band changes - Instructions support dynamic placeholders:
{{now()}},{{user_name()}},{{state()}}, etc. — see [references/agents/instructions.md](references/agents/instructions.md) - Structure for reliability: order each
.mdprocedure → guardrails → voice; say what to do (not what to avoid); make every action a named tool call. Structure the prompt so the right action is the first and easiest thing the model can do. Full principles: [references/agents/prompt-principles.md](references/agents/prompt-principles.md)
Reference Documentation
One reference per domain, following the hub navigation order. Concepts live in this file; open the domain's reference when you're about to author its YAML, need field-level schemas or per-provider specifics, or are debugging behavior in that domain.
| Domain | Reference | When to read |
|---|---|---|
| Connections | [references/connections.md](references/connections.md) |
Wiring up a channel, agent provider, tool API, or speech connector; credential binding, org tags, production credentials |
| Agents | [references/agents/roles-and-settings.md](references/agents/roles-and-settings.md) |
Choosing an agent role, delegation flow, connector-specific settings, evaluation variables, response format |
| Agents | [references/agents/native-tools.md](references/agents/native-tools.md) |
Native tool catalog + parameters, meta tools (gettoolschema, execute_tool), MCP tool assignment |
| Agents | [references/agents/custom-tools.md](references/agents/custom-tools.md) |
Custom HTTP tool format, OpenAI function schema, composed_tools side effects |
| Agents | [references/agents/tool-principles.md](references/agents/tool-principles.md) |
Designing a tool surface a fallible agent calls reliably — surface curation > validation > prompt, one-tool-one-intent, fail-loud, guarded atomic ops |
| Agents | [references/agents/instructions.md](references/agents/instructions.md) |
Placeholder syntax ({{now()}}, {{state()}}, etc.) for agents/<slug>.md |
| Agents | [references/agents/prompt-principles.md](references/agents/prompt-principles.md) |
Placing & structuring context for reliable execution — context-to-slot-lifetime, cache hygiene, state vs tool-history, flow-before-style, positive framing, voice as its own section |
| Kanban | [references/kanban.md](references/kanban.md) |
Kanban field specs: flags, transitions, followups, additional-context schema/instructions, lanes, constraint matrix, warnings |
| States | [references/states.md](references/states.md) |
State JSON Schemas, scope, agent read/write, initial values |
| Resources | [references/resources.md](references/resources.md) |
Knowledge bases, skill resources, agent linkage, provider sync (wayai sync-skills) |
| Evals | [references/evals.md](references/evals.md) |
Eval scenario YAML, scenario sets, journeys-as-code, seed fixtures + variables, wayai eval capture / wayai eval journey capture, run pacing, authoring & interpreting principles |
| Outbound | [references/outbound.md](references/outbound.md) |
Outbound contacts, lists, schedules, channel rules, execution modes |
| Analytics | [references/analytics.md](references/analytics.md) |
Variable categories/types, filter operators, time analysis, query workflows |
| Bases | [references/bases/README.md](references/bases/README.md) |
Read first for any base work — the Data object model, the preview/promote rule, and the routing map to the files below |
| Bases | [references/bases/records.md](references/bases/records.md) |
Record-type schemas, records, the Filter DSL and search, datetimes, partial updates, cancellation/archival, x-fk, relationship types, relationships, batch, bulk import |
| Bases | [references/bases/querying.md](references/bases/querying.md) |
wayai bases sql — tables, mandatory scoping predicates, JSON subcolumns, the example gallery; x-search tuning and timezone configuration |
| Bases | [references/bases/files.md](references/bases/files.md) |
File types, versioned path-addressed files, history/diff, S3 mounts, record attachments |
| Bases | [references/bases/config-as-code.md](references/bases/config-as-code.md) |
wayai-ws/bases/<base>/, subtree routing and mixed-invocation refusal, base.yaml, promote/rollback, eval mode, seed fixtures and leases |
| Bases | [references/bases/integrations.md](references/bases/integrations.md) |
Canonical-first modeling and the pattern catalog; external sources, inbound webhooks, triggers, provider adapters |
| Bases | [references/bases/toolsets.md](references/bases/toolsets.md) |
Actions and toolsets (the agent-facing MCP surface), filterablefields/writablefields/base_filter/precondition, base API tokens, base credentials, modeling & tool-design principles |
| Bases | [references/bases/executors.md](references/bases/executors.md) |
Building the HTTP service that acts on the outside world for a trigger or external source |
| Canonical example | [references/canonical-example/README.md](references/canonical-example/README.md) |
End-to-end hub showing how hub.yaml + agents/* + resources/ + evals/ + journeys/ cross-reference. Read once before generating a new hub from scratch |
| Navigation | [references/navigation.md](references/navigation.md) |
App URL surface (/chat, /task, /support, /settings/...), hub-detail tabs, query-string deep links — any time you hand the user a URL |
| AGENTS.md files | [references/agents-md-template.md](references/agents-md-template.md) |
The per-hub / per-base folder memory pattern — what belongs in an AGENTS.md, and which of the two the CLI seeds |