Run your go-to-market on GTM OS
GTM OS is a governed system for your revenue team: a CRM, eight engines in the console (Score, Outreach, Performance Ads, SEO & GEO, Enrichment, Merges, Plays and Routing), signals and a research agent. Your coding agent builds and runs it. A person approves anything that touches a customer or goes live, and every factual claim in what you send is checked against proof you approved.
Rules that explain most of what you will see
- Four-eyes. Whoever proposes a change can never approve it. You need a second person (the approver) from day one.
- Dev first, then prod. Config your agent writes lands in
dev. It goes live only after a person signs it and promotes it toprod. - looot for outside data, Composio for your own apps. looot supplies enrichment, emails, lists and intent. Composio connects your CRM, Slack, Search Console and other apps.
- Dry first, then live. Start in Dry Mode (for the whole organization and for each engine) and see what would have happened before anything real goes out.
From an empty folder to live engines
Claude Code runs the install and the configuration. It stops and asks you only for what a person must do: sign-ins, keys, DNS records and approvals. Plan for about an hour.
-
Check the prerequisites 5 min
- Docker Desktop installed and running.
- 10 GB or more free disk on the drive you install to (images plus database).
- 4 GB or more free RAM for the stack (Postgres, API, worker, MCP, console). Close heavy apps. On Windows, Docker runs in WSL 2: if containers get killed, raise the memory limit in
%UserProfile%\.wslconfigand restart Docker. - Node 22 or newer, and Claude Code.
- The gtm CLI, from npm:
npm i -g @gtmos/clidocker info --format '{{.ServerVersion}}' node -v gtm version --offlineA Docker server version,v22.xor newer, and the gtm CLI version. -
Make an empty folder and start Claude 1 min
mkdir my-gtm && cd my-gtm claudeThis folder becomes your agent workspace. The GTM OS instance itself goes in a subfolder later.
-
Run
gtm initinside Claude, then restart Claude 1 minIn the Claude prompt, the ! prefix runs a shell command:
! gtm init"No GTM OS instance connected yet. Open Claude Code here and run /setup-gtmos". The folder now holds.claude/(skills, slash commands),.mcp.jsonanddocs/gtmos/.Quit Claude Code and start it again in the same folder (
claude). Claude reads new commands only at startup. -
Run
/setup-gtmos20 to 40 min/setup-gtmosClaude checks the prerequisites, then asks you one grouped question:
- use an instance already running on this machine, or install a new one;
- organization name, owner email (you), approver email (a different person or identity);
- version (
stable, or a local image tag) and ports (defaults 3000 console, 3001 API, 3004 MCP, 5432 Postgres). The ports are reachable from this machine only (127.0.0.1). To reach the instance from elsewhere, see Self-host on a server; - which keys you have ready: a model provider (Anthropic, Gemini or OpenAI; one is required), looot, Resend.
Phase Claude does You do Install Runs gtm setup instance ... --no-harnessinto./instance(a few minutes; safe to re-run).Nothing. Passwords Tells you they are in instance/.envasGTMOS_OWNER_PASSWORDandGTMOS_APPROVER_PASSWORD. Never prints them.Give the approver password to the approver privately. Agent key Mints its own agent key (preset Full GTM engineer), runs gtm init --url ... --key ...andgtm doctor.Set GTMOS_AGENT_TOKENin your environment and restart Claude (see Connect Claude Code). Then run/mcp: gtmos shows connected.Connections Stores and checks keys, links your Composio apps, sets the CRM of record and the Search Console property. Steps 5 and 6 below. Sending domain Prints the SPF, DKIM and DMARC records and checks them with nslookup.Add the records at your DNS host. -
Log in to the console as owner and as approver Needs a human
- Open the console URL Claude printed (default
http://localhost:3000). - Sign in as the owner with
GTMOS_OWNER_PASSWORD. - Open a second browser profile (or a private window) and sign in as the approver with
GTMOS_APPROVER_PASSWORD. Keep both windows open.
Mission Control (the home page) in both windows. The approver also sees Settings, because the approver is an admin. - Open the console URL Claude printed (default
-
Connect your tools in Integrations > Connections Needs a human
- Owner window: Integrations > Connections.
- On the Composio card click Connect Composio, sign in to Composio, accept. You come back to the console.
- Claude then links your apps. To do it yourself click Sync connected apps: every app already connected in your Composio account appears under Connected apps as
ACTIVE. - Under Add a connection, pick the provider, paste the key, add it:
anthropicorgeminioropenai(one required),looot(data),resend(email, if you do not send from Gmail or Microsoft 365). Then run Test on each. - Using SEO & GEO? Make sure Search Console is connected (through Composio or Settings > Connected accounts). It is required for that engine.
Required connections at the top of the page: your model provider, looot and Composio read Connected. Anything left reads Required: missing with the fix.You can paste a key to Claude instead and it stores it with
connections.create. A key pasted in chat stays in the transcript, so the console is the private option. -
Set up and build your system 20 to 40 min
Two ways in, and you can mix them: the setup wizard in the console (Setup), or your agent with:
/build-gtm-system- Answer the discovery interview (offer, ICP, signals, personas, regions, channels, CRM, volumes, approver). Pasting a one-page brief saves time. In the wizard these are the cards you review.
- Claude recommends the closest blueprint, applies it in
dev, then customizes each engine for you. Engines start in Dry Mode. - When Claude says a proposal is waiting: approver window, Approvals, open it, Approve, then Execute.
Claude reports each engine as built and verified, and lists Open items in.claude/memory/setup-status.md. A banner says "N definitions are in dev, not live". -
Go live: sign, promote, approve Needs a human
Open CRM > Environments. The Go live card is at the top.
- Approver window: click Review & sign all proposed (N), read the diffs, confirm. (The owner cannot sign what the owner's agent proposed.)
- Owner window: click Promote dev → prod, check the list, confirm. This proposes one approval.
- Approver window: Approvals, open the promotion, Approve, then Execute.
The Go live card reads "Everything is live." and the "in dev, not live" banner is gone.Sending email? Activate the lane once it is in prod: Email sending, step 4.
-
See the engines working 15 min
- Signals: CRM > Hot list, click Scan for signals now (owner or admin; capped per run).
- Accounts: CRM > Companies, open one; its Overview shows the score and Why this score.
- Score: Engines > Score, click Qualify on an account.
- Workflow: Settings > Workflow automation; the qualified-account handoff and signal reactions are on by default.
- Outreach: the approver sees proposed messages in Approvals.
- SEO & GEO: Engines > SEO & GEO, open the page workbench link, Site visibility, click Run visibility.
- Search: Settings > Search index, click Reindex now.
- Agent: open a company, Agent tab, ask "Why is this account a fit for us?".
-
Run the go-live check 5 min
/go-live-checkOne line per check marked GO, NO-GO or N-A with its evidence, then one overall result. NO-GO comes with an ordered list of fixes.Your first-win checklist on Home shows the same journey as eight milestones computed from what really happened in your workspace.
Prefer to do it by hand?
Goal: install an instance and connect an agent workspace without /setup-gtmos. Commands are bash; on PowerShell use $env:NAME = "value" instead of export.
- Install the instance into its own folder (owner plus approver; passwords are generated into its
.env):Port busy? Addgtm setup gtm-instance --org "Acme" --email you@acme.com --approver-email cofounder@acme.com--console-port,--api-port,--mcp-portor--postgres-port. Local images only? Add--version <tag> --no-pull. The ports bind to127.0.0.1unless you add--bind <address>(see Self-host on a server). - Sign in as the owner (it prompts for the password, or reads
GTMOS_PASSWORD). Your org id isORGANIZATION_IDingtm-instance/.env.Run theexport GTMOS_API_URL=http://127.0.0.1:3001 GTMOS_ORG=<org id> gtm login --email you@acme.com --console-url http://localhost:3000export GTMOS_TOKEN=...line it prints. - Create the agent key (shown once):
gtm token create --name claude-code --client claude-code --preset full-gtm-engineer --expires-days 90 - Put the key in
GTMOS_AGENT_TOKEN(per OS), then connect your workspace folder and check:gtm init --url http://127.0.0.1:3001 --key <agent key> --yes gtm doctor - Start
claudein the workspace and run/mcp.
gtm doctor prints one line per provider (LIVE, UNVERIF, FAILED, FIXTURE or MISSING) with the remediation for each.How-to guides
Each guide: the goal, the steps, what you should see, and the common errors. Needs a human marks steps your agent cannot do for you.
Accounts and team
Goal: the right people with the right roles, including a second approver.
| Role | Can do |
|---|---|
owner, admin | Everything, including approve, reject and execute proposals. Only these roles approve. |
member | Read and write records. Cannot approve or change settings. |
builder | Delegated agency role: everything except approve. |
| Role profile (on top of a role) | Optional narrowing. Built-in profiles are Operator, Ops lead, Exec and Engineer, and you can add your own. A profile is a base role plus operations the person never runs, and the KPIs and actions of their Mission Control. It only ever narrows: a member keeps what both their role and the base role allow. Agents act under the profile of the person they act for. |
There is no read-only viewer role. Role profiles live in Settings > Members (a Profile column and a Role profiles editor). In the setup wizard, saving the operator, ops lead and exec seats assigns the matching profile. Changes to your own profile may only narrow you; widening needs another owner or admin.
- Settings > Members, Invite a member: email, role (member, admin or owner), Invite. Pending invitations are listed below.
- Or provision a verified teammate from a terminal (run it yourself, so your agent never sees the password):
gtm member add --email approver@acme.com --password '<password>' --role admin --org <org id> - Stop an agent everywhere: Settings > Agents, Kill. Revive it there later.
- Agencies: Settings > Delegations lists access granted to an agency org; revoke it there.
Invitations are sent by email. If your instance has no email transport yet, set one up first (Email for invitations, password reset and nudges), or use gtm member add above, which needs no email.
Common errors
Role "member" does not hold action.approve: ask an owner or admin.- "An action proposed by your own agent must be approved by a different person": you created the agent key, so a second admin approves.
Email for invitations, password reset and nudges Needs a human
Goal: your own instance can send its system mail: team invitations, password reset, email verification and setup reminders. This is a different thing from the sending lane you use for outreach (see Email sending).
GTM OS does not ship with a mail provider. You choose one for your instance in .env. SMTP works with almost any provider, so it is the usual choice.
| Variable | What it does |
|---|---|
EMAIL_TRANSPORT | smtp, webhook or none. Left empty, GTM OS works it out: SMTP_HOST set means smtp, otherwise a webhook address for that kind of mail means webhook, otherwise none. If you choose smtp without SMTP_HOST, or webhook without the address, it falls back to none instead of failing. |
SMTP_HOST, SMTP_PORT | Your mail server. The port defaults to 587. |
SMTP_SECURE | true or false. Left empty, implicit TLS is used only on port 465 and STARTTLS otherwise. |
SMTP_USER, SMTP_PASS | The login. It is used only when both are set, so an internal relay that accepts mail from a trusted network works without them. |
EMAIL_FROM | The sender address (SMTP_FROM is also read). With neither set, mail goes out from no-reply@localhost, which a mail server will usually refuse, so set it. |
RESET_PASSWORD_WEBHOOK_URL, VERIFICATION_EMAIL_WEBHOOK_URL, INVITATION_EMAIL_WEBHOOK_URL, OPERATOR_NUDGE_WEBHOOK_URL | The webhook route, one address per kind of mail. GTM OS posts JSON with the recipient, the action link, the subject, the text and the kind of mail, and your service sends it. The generated .env lists the first three; add the nudge one by hand if you use it. |
- Set the variables in
.env, for example:EMAIL_TRANSPORT=smtp SMTP_HOST=smtp.example.com SMTP_PORT=587 SMTP_USER=postmaster@example.com SMTP_PASS=<password> EMAIL_FROM=GTM OS <no-reply@example.com> - Run
gtm upagain so the containers are recreated with the new values (changes to.envapply only then). - Check it:
gtm doctorlists the email transport, then invite a teammate in Settings > Members and confirm the mail arrives.
gtm doctor shows the identity email transport as live, and an invitation reaches the invited address.What breaks without it
- Invitations (Settings > Members > Invite) and email verification need a transport. A person who signs up and is never sent a verification mail cannot accept an invitation meant for them.
- Password reset mail is not sent, so people who forget a password cannot reset it themselves.
- Setup reminder emails to your own team (operator nudges) are not sent. The reminder still appears in the console, and the nudge says email is not configured.
- The instance still starts and the first owner can be created, because a missing mailer never blocks setup. Each skipped mail writes a warning with the recipient and the kind of mail to the API log. The link itself is never logged by the release images, because a reset or verification link is a secret. Only a local development run outside production prints the link to the terminal.
- A mail server that is configured but broken is an error you will see, unlike a missing one.
Without email, today
Add a teammate with gtm member add (see Accounts and team). It creates a verified account without sending anything.
Common errors
- Nothing arrives and the API log says "no email transport is configured":
SMTP_HOST(or the webhook address) is empty in the running container. Check.envand rungtm upagain. EMAIL_TRANSPORT=smtpset but still no mail:SMTP_HOSTis missing, which downgrades the transport to none.- Mail is rejected by the provider: the
EMAIL_FROMaddress or domain is not allowed to send through that server.
Connect your tools
Goal: every engine you use has what it needs. Where things live:
| What | Where |
|---|---|
| Model keys, looot, Resend, HubSpot, Meta Ads, Cal.com, webform | Integrations > Connections, Add a connection |
| Your own apps (CRM, Slack, Typeform, Stripe, Ahrefs, Search Console...) | Integrations > Connections, Composio card |
| Internal tools | Integrations > Connections, MCP servers card |
| Integrations > Connections, Unipile card | |
| Google / Microsoft mail, calendar, Search Console, GA4, Google Ads | Settings > Connected accounts |
| Slack alerts and channels | Settings > Slack |
Keys are sealed on save and never shown again. Connecting needs an owner or admin, or their agent (the Full GTM engineer key can store keys you give it). Sign-in buttons (OAuth) always need a person to click.
The Connections page
- A grid of logo tiles in 11 categories (CRM, email and outbound, ads, analytics, search and SEO, conversations and calls, enrichment and data, models, messaging, warehouse and billing, other and MCP). Each tile says whether it connects Native or Via Composio and lets you choose.
- Search the grid (press / to jump to the search box) and sort by relevance to your setup (the default), A to Z or connected first.
- A Needed strip at the top lists what your chosen blueprint and the engines you picked still need. Statuses are honest: live, stored but not verified, failed, or not connected. In demo mode a tile says it uses built-in sample responses, not your data.
- Connectors with no native card yet (Instantly, lemlist, Smartlead, Gong, Mixpanel, Amplitude) connect through Composio.
Composio: connect your own apps Needs a human
Goal: GTM OS can read and act in your CRM and other SaaS apps. Composio is never used to buy data.
- Integrations > Connections, Composio card, click Connect Composio (recommended: OAuth sign-in, tokens refresh on their own).
- Prefer a key? Click Advanced: paste a Connect key
ck_...(connect.composio.dev) or a project keyak_...(platform.composio.dev, project, Settings, API Keys). The key is verified live before it is saved. One Composio credential per org; the last one saved wins. - Click Sync connected apps: apps already connected in your Composio account become
ACTIVE; apps no longer connected become inactive. - A new app: search the App catalog, click Connect, finish the hosted sign-in (the link lasts about 10 minutes).
- Several accounts for one app? Next to the app click Use an existing connection, then Use this on the right one.
- CRM of record section: pick HubSpot, Salesforce, Pipedrive, Attio or Zoho, then Sync now. It also syncs every 6 hours.
- Any other app: on the connected app click Map to ontology (see Bring data in).
ACTIVE, and under CRM of record a Last sync with counts.Common errors
- "This is a Composio user API key (uak_...) ... Nothing was saved.": use Connect Composio, a
ck_or anak_key. AUTH_EXPIRED"Composio sign-in expired or was revoked": click Reconnect Composio.- "Composio: hubspot is not connected for this organization": connect it on the card, then check it reads ACTIVE.
- App stuck
INITIATEDorEXPIRED: the sign-in link timed out; click Reconnect. - A project key (
ak_) sees only its own developer project, not apps connected in Composio Connect. - Pipedrive, Meta Ads, LinkedIn Ads, Webflow and some others need your own auth app in Composio ("Needs your own auth app").
API keys: model, looot, email
Goal: a model provider and looot are live (and Resend if you send through it).
- Integrations > Connections, Add a connection.
- Provider:
anthropic,gemini,openaioropenrouter(at least one of these four),looot,resend. Give it a label, paste the key, add it. - Run Test on the new connection. A stored but untested key reads "Stored, not verified".
OpenRouter is a model router: one key reaches the models of many vendors (named <vendor>/<model>), and it is used for text generation only. Semantic search needs an OpenAI or Gemini key (Anthropic and OpenRouter have no embedding model here). Without one, search is full-text only.
Common errors
- Do not put keys in
instance/.envafter install: env changes apply only when containers are recreated. - looot
402 insufficient_balance: top up your looot balance.
Google and Microsoft accounts Needs a human
Goal: mail and calendar sync, sending from your mailbox, and Search Console, GA4 and Google Ads data.
- Settings > Connected accounts.
- Choose Google or Microsoft, tick the features (Read mail, Send mail, Calendar; Google also Search Console, Google Ads), connect and accept the consent screen.
- For Search Console, GA4 and Google Ads, click Use for org on the Google account. Those read through the org default account.
- Ask your agent to set the Search Console property, or give it:
sc-domain:acme.comorhttps://www.acme.com/.
Common errors
NOT_CONFIGURED"Google Search Console is required for SEO & GEO": connect Search Console here (or through Composio) and set the property.- "Needs reconnect": click Reconnect.
Slack Needs a human
Goal: alerts and approvals reach your team in Slack.
- Settings > Slack, connect the workspace and accept the consent.
- Channels: search, join, or Create a channel with the GTM OS bot in it.
- People matching: Match by email links members to Slack users; fix the rest with Set match.
For alerts, the native Slack connection is preferred over Composio's Slack toolkit.
Microsoft Teams. Teams appears in Integrations > Connections as a chat tile for alerts and approvals, and it connects through Composio. It has no settings page of its own like Settings > Slack, so there is no channel or member matching screen for it. CRM > Conversations lists Teams as one of the reply channels.
Email sending: domain, lane, mailbox
Goal: real emails can go out, only through an approved lane and a bound mailbox.
- Sender. Best: a Gmail or Microsoft 365 account with Send mail in Settings > Connected accounts. Otherwise a
resendconnection (verify your domain in Resend first). - DNS Needs a human: at your DNS host add SPF (one TXT starting
v=spf1), DKIM (the record your provider shows at<selector>._domainkey.<domain>) and DMARC (TXT at_dmarc.<domain>, for examplev=DMARC1; p=none; rua=mailto:dmarc@<domain>). Check:nslookup -type=TXT acme.com nslookup -type=TXT _dmarc.acme.com - Lane. Your agent writes the lane (
definition.lane: daily cap, sending domain, sender postal address, budget). Sign and promote it (Environments). - Activate the lane. Engines > Outreach, open the Outreach workbench link, Sending lanes, click Activate lane (optional DKIM selector). Or:
A different person approves and executes it in Approvals.
gtm lane activate <laneKey> --env prod - Mailbox. Bind it when you send: pick it in Sending mailbox on the workbench or on a new sequence.
Common errors
lane_required"refusing an un-laned email send": pick a sending lane, and activate it first.mailbox_required: connect a mailbox with Send mail and pick it.mailbox_daily_cap: wait, or add mailboxes.- "lane ... envelope budget exhausted ... re-approval required": activate the lane again, or raise its budget.
- Lane activation refused in prod: fix DNS, activate again.
LinkedIn through Unipile Needs a human
Goal: read LinkedIn profiles, inbox and connections, and send LinkedIn steps through the same approval path as email.
- Integrations > Connections, Unipile (LinkedIn sending) card.
- Fill API key, DSN (your account's API host, for example
api8.unipile.com:13814) and optionally the LinkedIn account id. - Click Save and test.
- Needs a human Accept the LinkedIn risk, once. LinkedIn may restrict or ban accounts that send automated messages, so LinkedIn sending is off until an owner or admin accepts that. Open Outreach, the Sending lanes panel, and click Accept the LinkedIn risk. It is recorded in the audit log, and there is one acceptance for connection requests and one for messages (the button accepts whichever is still missing). A person does it, never an agent. Until then every LinkedIn send is refused, and the panel says so.
- The same panel shows the Unipile webhook address and the header it expects. Point Unipile at it so acceptances and replies arrive at once (an hourly check is the backstop). The secret comes from
UNIPILE_WEBHOOK_SECRETon the instance, and owners can reveal it there.
Add your own MCP server
Goal: an internal tool becomes a data source or capability for GTM OS (the opposite direction from your agent's MCP).
- Integrations > Connections, MCP servers card, Add an MCP server: server name (lowercase), server URL (a streamable-HTTP endpoint, usually ending
/mcp), optional auth header name and value. Credentials go in the header, never in the URL. - Test it: the tool catalog loads.
- Needs a human Mark each tool Read or Write (approval). An unmarked tool cannot be used.
- Ask your agent to bind a capability to it, or map its records (see example prompt).
Common errors
- "initialize failed ... did not answer with a JSON-RPC result": the URL is not an MCP endpoint (often missing
/mcp). FORBIDDEN"no declared effect": mark the tool Read or Write.
Required connections and readiness
Goal: know what is live and what is missing.
- Top of Integrations > Connections: Required connections. States: Connected, Stored not verified, Failed, Required: missing, Not connected, Not needed.
- From a terminal:
gtm doctor --strict
--strict means every needed item is live. A model provider is always required; looot while you use Signals or Enrichment; a sender while you use Outreach; Search Console while you use SEO & GEO. An applied blueprint makes its declared connections required.Connect Claude Code, Codex or any MCP agent
Goal: your agent talks to GTM OS through MCP with its own key, acting for you.
- Settings > Developer, Connect Claude Code / Codex: choose the Coding agent (Claude Code, Codex or Other MCP client), an Agent name, Preset: Full GTM engineer, then Create agent key. Copy the key now: it is shown once. The page also shows the org id, the MCP endpoint and setup snippets.
- Or from a terminal with your owner session (
gtm loginfirst):gtm token create --name claude-code --client claude-code --preset full-gtm-engineer --expires-days 90 - Put the key in your environment, never in a file.
Windows PowerShell (persistent; then open a new terminal):
[Environment]::SetEnvironmentVariable('GTMOS_AGENT_TOKEN', '<agent key>', 'User')macOS / Linux: add this line to
~/.zshrcor~/.bashrc, then open a new terminal:export GTMOS_AGENT_TOKEN='<agent key>' - In your workspace folder, connect it to the instance (writes
.mcp.jsonwithBearer ${GTMOS_AGENT_TOKEN},AGENTS.mdfor Codex,.claude/anddocs/gtmos/):gtm init --url http://127.0.0.1:3001 --key <agent key> --yes - Restart the agent in that folder. In Claude Code run
/mcp. - Later, pull fresh docs, skills and subagents from your live org (files you edited are kept):
gtm init --refresh --yes
/mcp lists gtmos as connected. The agent's first call is usually gtm_describe.| Your agent can (Full GTM engineer) | Your agent cannot |
|---|---|
| Everything an owner or admin does in the console: records, custom fields, pipelines, definitions (proposed), research profile and settings, CRM settings, workflow flags, connections and keys you give it, MCP servers, syncs, scans, blueprints, proposals. | Approve, reject or execute; sign definitions or publish ontology changes; create keys, agents, roles or members; click OAuth consent (it gives you the link); switch Dry Mode; mark MCP tool effects; turn on research auto-apply. |
Codex and other clients default to record.read, record.write, engine.configure. Choose the Full GTM engineer preset if they should manage connections too.
Common errors
UNAUTHORIZED"No valid session or agent token": the variable is missing, wrong, revoked or expired (keys default to 90 days). Create a new key, set it, restart.- MCP tools fail after an upgrade or restart of the stack: run
/mcpto reconnect. - "This agent key does not hold the "engine.configure" scope": create a new key with the Full GTM engineer preset.
- "GTMOS_TOKEN holds an API key. Keys cannot mint keys": use
gtm loginand its session token. gtm initwarns that.mcp.jsonheld a literal key: it rewrote it; revoke that key (gtm token revoke <key-id>) and use a fresh one.
Skills, subagents and agents
Goal: know what your coding agent arrives with, and how GTM OS runs agents of its own.
| What | What it is | How you get it |
|---|---|---|
| Skills | 107 task playbooks your agent loads when they fit (setup, onboarding, each engine, each data tool, SEO, ads and more). | gtm init installs them. The skills release is pinned and checked against a checksum manifest, and a copy is bundled for offline use. |
| Role subagents | 11 specialists you can ask for by name: gtm-operator, revops-analyst, deal-reviewer, sales-researcher, content-strategist, seo-refresher, customer-success-manager, competitive-analyst, ads-analyst, data-steward, eval-reviewer. | Written to .claude/agents, .agents/agents and .codex/agents by gtm init. |
| Builder subagents and slash commands | The architect, ontology designer, play builder, connector wirer and others behind /build-gtm-system, /go-live-check and the rest (see Customize anything). | gtm init. |
| Organization agents | 16 agents in 6 packs (CRM, revenue, data, growth, ops, core) that run inside GTM OS on a trigger: a schedule, an event or a manual run. A triggered run knows which event or occurrence it is running for. | Installed when you enable a pack (Settings > Packages). They follow the same governance as any agent you register: scoped, bounded, audited, and never able to approve. |
The 16 organization agents
| Pack | Agents | Starts when |
|---|---|---|
CRM (stdlib/pack.crm-agents) | inbound responder, meeting prep, follow-up, pipeline review (deal drift), deal desk | a form is submitted, a meeting is booked, a transcript is ingested, Mondays 07:00 (UTC), a deal changes stage |
Revenue (stdlib/pack.revenue-agents) | signal watch, routing steward, scoring steward, reply triage | weekdays 07:00 UTC, hourly 09:00 to 17:00 UTC on weekdays, Mondays 05:00 UTC, a reply is received |
Data (stdlib/pack.data-agents) | enrichment, data hygiene | Mondays 06:00 UTC; nightly 02:00 UTC and when an account is created |
Growth (stdlib/pack.growth-agents) | SEO and GEO watch | daily 06:00 UTC |
Ops (stdlib/pack.ops-agents) | onboarding guide, ops copilot (read only) | only when you run them |
Core (stdlib/pack.core-agents) | SDR, account research | a verified funding, hiring or intent surge signal; or when you run it |
They read, draft and propose. Every send, CRM write, merge and publish they propose is approved by a different person, the claims gate checks the content, and Dry Mode ends at the draft. They cite the records behind what they say. They never run a paid enrichment themselves: it quotes the cost and hands it to a person. Turning a pack on or off is a request a second admin approves, in Settings > Agents under Agent packs. Enable stdlib/pack.core-skills before the core agents, because they use its skills. Pack agents that start on a trigger keep the storm guards: no bulk sources and the default fire cap.
- After you upgrade the CLI, take the newest skills and subagents in each project folder:
Skills and agent files you edited are kept.
gtm init --refresh - Ask for a role by name, for example "gtm-operator, brief me on today" or "deal-reviewer, review the deals closing this month".
- Watch or stop agents in Settings > Agents (kill, revive). An evaluation pass by
eval-reviewershows what organization agents did before you widen their autonomy.
.claude/agents holds the 11 role subagents, and gtm doctor has no stale Harness line. An operation that spends money inside itself is never granted to an agent skill.Your agent reaches GTM OS over MCP with eight meta-tools: gtm_describe (always first, it lists every operation), gtm_query, gtm_get, gtm_search, gtm_act, gtm_approvals, gtm_lineage (the audit trail behind one approved or executed action: who proposed it, who approved it, what ran) and gtm_lineage_trace (the receipts behind a record's values: the surviving value, the rule that chose it, the sources). Everything the console does is an operation, so console and MCP stay in step. The agent proposes. A person approves.
Setup wizard Needs a human
Goal: turn what your company already knows (answers, website, connected data, documents) into the definitions your engines run on. Nine steps on six screens. Open Setup in the console. Your agent can run the same steps over MCP (say "set up GTM OS for my company") and you can take turns: what one saves, the other sees.
| Screen | Steps | You do |
|---|---|---|
| 1. Welcome | Company, Motion | Company profile, team size, an optional private ARR band, markets, motions, your stack, up to three pains and a few free answers, hours a week. Pick the engines and a blueprint (the suggestions show the facts they rest on). |
| 2. Connect your data | Connections | See real coverage per source, connect sources, Pull now, and send access requests to the people who hold the keys. Each missing grant becomes a numbered open ask. |
| 3. Your definitions | Definitions | Upload setup documents (ICP notes, stage definitions, pricing, brand voice, suppression lists, a DPA). Review each drafted card with its evidence: edit, skip or approve. |
| 4. Rules and who approves what | Rules, Team | Buying roles, consent per channel, trust rules (Dry Mode, caps, quiet hours), autonomy per engine, seats and role profiles. |
| 5. The build | Build | Check engine readiness, run the build, follow its live log, clear what waits for a human. |
| 6. First motion and 90 days | Plan, Launch | Shape the 90-day plan, refresh the operating-system brief, preview and run Launch. |
Rules that hold on every screen
- No invented values. Every suggestion shows the facts it rests on: an answer you gave, a page of your site, a document, a real count from a connected source. A value nobody knows yet stays empty and becomes a numbered open ask with an owner. With no model configured the page says so and fills only what the data supports.
- Every saved answer changes engine behavior. A card is published through the operation that owns that definition, with the same permissions, approvals and audit as anywhere else.
- The proposer never approves. Change sets, journeys, blueprints and promotions are approved by a different person in Approvals. Definitions are signed by someone other than the proposer.
Good to know
- Documents: PDF with text, Word, Excel, CSV, JSON, Markdown and text, up to 10 MB each and 50 per organization. Scanned PDFs and images are refused with the reason. Suppression lists, a DPA and any file with emails or phone numbers are restricted: only owners and admins read their text, and they are never sent to a model.
- The ARR band is private: only a human owner or admin sets or reads it, and it is hidden from agents.
- Cards approved on screens 3 and 4 land in
dev. Launch is human only: it signs and promotes the setup definitions and turns an engine live only where its autonomy card says so. - Seats narrow what a role may do and never widen it. Never put the sole owner in the Operator or Exec seat. GTM OS refuses any change that would remove the last person who can still run the build, approve cards and launch.
- If the wizard says someone else changed the session, re-read it and redo the step.
Common errors
- A card is refused with a list of issues (for example an ICP with no rules and no closed-won deals): fill it with your own criteria, or skip it for now.
- The revenue card offers only connected revenue sources. With none, it opens the ask "Connect a CRM with deals or Stripe".
- The do-not-contact card is empty after an agent drafted the cards: an owner or admin redrafts it, because only they can read the restricted documents.
Build your GTM system with your agent
Goal: a full, customized setup from one conversation.
- Run
/build-gtm-system(optionally with a goal, for example/build-gtm-system B2B SaaS sales-led, outbound + SEO). - Answer the discovery interview, asked in batches: business and offer, ICP and tiers, personas, markets and compliance, channels, CRM and tools, data volumes, team and approver.
- Claude reads the live state, recommends the closest general blueprint with a reason, and applies it in
dev. - Engine by engine (data first, then Score, routing and plays, then Outreach, SEO & GEO, Ads), it explains, shows defaults, asks only what matters, writes the definitions, and verifies live.
- You approve proposals (approver window), then go live with sign and promote.
.claude/memory/decision-records.md, and human tasks as Open items in .claude/memory/setup-status.md.Blueprints you can start from: b2b-saas-sales-led, enterprise-abm, b2b-saas-plg, devtools, agency-services, b2b-marketplace, dtc-b2c, and the starters outbound, seo, full-gtm (the last two require Search Console). Browse them in CRM > Blueprints, View & apply.
Blueprints: what each one sets up
Goal: pick the closest general template, know what it creates and which connections it makes required. A blueprint seeds a whole motion (personas, segments, scoring, suppression, bindings, lanes, plays, metrics) in one change set. They are general templates: start from the closest and customize, never one per customer.
| Blueprint | What it sets up | Engines | Required connections |
|---|---|---|---|
b2b-saas-sales-led | The classic sales-led motion: an enterprise ICP, firmographic and intent scoring, a human-run outbound lane with deliverability guardrails, an opportunity pipeline on a compliance floor, and identity resolution. 34 resources. | CRM, enrichment, outreach, research, score, signals | Model provider, looot, email sender, CRM. Optional: LinkedIn seats, booking calendar. |
enterprise-abm | A named-account motion: a hand-picked target list, a buying committee rather than a single lead, account-level intent and engagement scoring, a multithreaded lane, an account journey, and research across web, news, pages and people. 34 resources. | CRM, enrichment, outreach, research, score, signals | Model provider, looot, email sender, CRM. Optional: LinkedIn seats, booking calendar. |
b2b-saas-plg | Self-serve product-led growth: product-qualified signals (activation, seat expansion, usage surge, plan limits) and a low-touch nurture lane that engages a person once an account crosses the bar. 17 resources. | CRM, enrichment, outreach, score | Model provider, looot, email sender. CRM optional. |
devtools | A developer-first motion where signups and API usage are the signal: adoption scoring (API usage growth, team spread, production keys, repo activity) and a help-first DevRel lane. 16 resources. | CRM, enrichment, outreach, score | Model provider, looot, email sender. CRM optional. |
agency-services | An agency selling done-for-you services to small businesses: outside-in scoring (ad spend, weak web presence, review velocity, marketing hiring) and a high-volume services lane with strict suppression and a tight bounce guardrail. 21 resources. | CRM, enrichment, outreach, research, score | Model provider, looot, email sender. Optional: CRM, booking calendar. |
b2b-marketplace | A two-sided marketplace: a supply acquisition lane, a second segment and a liquidity metric for the demand side, scoring that favours providers who fulfil fast at quality. 18 resources. | CRM, enrichment, outreach, research, score | Model provider, looot, email sender. CRM optional. |
dtc-b2c | A consumer lifecycle motion: scoring on recency, frequency and basket value, a lifecycle email lane (welcome, replenish, win-back) with strict consent and suppression and a tight complaint-rate guardrail, and ad pacing gated by unit economics. 14 resources. | Enrichment, outreach, performance ads, score | Model provider, looot, email sender, ad platform (Google Ads or Meta Ads). |
outbound | A generic zero-to-running outbound starter: persona and segment, scoring on researched signals, suppression, an email lane, account enrichment and the plays that run it (first touch, daily rescore, weekly enrichment). 19 resources. | CRM, enrichment, outreach, research, score | Model provider, looot, email sender. Optional: CRM, LinkedIn seats. |
seo | A search and generative-engine starter: tracked pages, freshness monitoring, a daily audit play, AI-answer visibility (ChatGPT, Perplexity, Gemini, Copilot, Google AI Overview), and Google rank and keyword data. 15 resources. | CRM, search | Search Console, model provider, looot. |
full-gtm | Every built engine on one shared foundation (identity, pipeline, compliance, suppression): scoring, signals and research, enrichment, identity and data quality, outreach, CRM sync, SEO and GEO, and performance ads. 49 resources. | CRM, enrichment, outreach, performance ads, research, score, search, signals | Model provider, looot, email sender, CRM, ad platform, Search Console. Optional: LinkedIn seats, booking calendar. |
In the table, "model provider" means Anthropic, OpenAI, Gemini or OpenRouter. "Email sender" means Gmail, Microsoft or Resend. "CRM" means HubSpot, Salesforce or Composio. Applying a blueprint makes its declared connections required in readiness, and optional ones are listed without being required. Browse and apply them in CRM > Blueprints.
Blueprints on the command line
| Command | What it does |
|---|---|
gtm blueprint list | The catalog: the installable blueprints and the generic starters, offline. |
gtm blueprint show <id> | One blueprint's motion and the resources it seeds, offline. |
gtm blueprint upstream --org <id> | Which blueprint and version your organization derives from (reads your instance). |
gtm blueprint upgrade <id> [--to <file>] [--from <file>] --org <id> [--env <env>] [--dry-run] [--yes] [--allow-breaking] | A reviewable diff from the installed blueprint to the new one, never a clobber. Without --yes it applies nothing. With --yes it proposes a change set a different person approves. A diff that removes a resource needs --allow-breaking. |
To start from a blueprint, gtm plan --from <id> previews it and gtm apply --from <id> --env dev proposes it. A different person approves, then you sign and promote the definitions (see Environments and going live). A search blueprint needs --gsc-property on a live instance.
First wins and operator nudges
Goal: always know the next useful step, based on what really happened in your workspace.
- Open Home or Setup. The first-win card lists eight milestones: the instance is ready, a real data source is connected (not a fixture), the first sync finished, a person signed the first definition, the first Dry Mode draft was produced, accounts have fit scores, a second person approved a proposed action, and the first live message went out.
- Each open milestone says what to do next. A fact GTM OS cannot read reads "unknown", never a guess.
- Ask your agent "what is my next first-win step?" for the same answer.
Operator nudges
- Owners and admins get an in-app nudge when something needs them.
- Email is opt-in. Each person turns it on in their own notification preferences. Every email carries a signed unsubscribe link that works without signing in, and nobody gets more than one a week.
- If email is not configured on the instance, the nudge says so plainly instead of pretending it was sent.
gtm doctorshows the email transport as an optional line. - An owner or admin can invalidate every earlier unsubscribe link (
notification.preferences.revoke_links, a human-only operation).
Does your harness match?
gtm init records which CLI, which skills release and which operation list it generated your project files from. gtm doctor compares that with what is running and reports a stale harness with the fix: gtm init --refresh. A project created before 0.2.5 reads "unstamped", which is informational.
Customize engines
Goal: the engines follow your rules, not the defaults. Ask your agent in plain words; it edits the definition in dev, and you sign and promote it.
| What | What changes | Where you check it |
|---|---|---|
| Scoring model | definition.scoring_model: key default (signal weights, qualified and review thresholds), key icp_fit (firmographic fit; always set your own), key signals (half-life, hot and warm). Scoring also proposes new versions from outcomes (see Score). | Company page, Why this score; Engines > Score |
| Routing | Territories, team capacity (caps per type, weights, out-of-office, stickiness) and the handoff's owner pool. There is no routing definition kind. | Engines > Routing, Hot list owner column |
| Plays | definition.play: trigger (manual, event, schedule, metric, webhook, segment), steps, guardrails | Operate > Runs |
| Lanes and sequences | definition.lane (daily cap, checks, budget, sending domain); definition.sequence (ordered steps, waits, send windows, A/B variants, exits); sequences with a lane and mailbox | CRM > Sequences |
| Unit economics | definition.unit_economics: the target the ads engine paces and allocates toward (target CAC or ROAS, or a payback limit with gross margin) | Engines > Performance Ads |
| Brand voice | definition.brand_voice key default (tone, preferred and avoided words) | Drafted messages |
| Workflow automation | Qualified-account handoff (owner, follow-up task, enrol in sequence) and signal reactions (re-score, route, enrol, alert). On by default. | Settings > Workflow automation |
| Research agent | Workspace profile, enabled, auto-apply verified facts, daily budget, re-check days, meeting-prep lead time | CRM > Research, Settings |
Simulate a play before it runs (zero writes):
gtm play simulate <playKey> --env dev --since 30ddefinition.scoring_model:default:vN), not score.default.v1.Common errors
- "My change is not used": it is still in dev, unsigned, or you checked another environment. Engines read only signed definitions in the call's environment.
CONFLICT"ChangeSet ... is stale": a newer version landed; ask your agent to re-propose from the current state.
Customize the data model
Goal: track the fields and objects your business needs.
Custom fields (companies, contacts, deals)
- Settings > Custom fields, pick Companies, Contacts or Deals.
- Create a field: label, type (text, long text, number, date, checkbox, select, url, email, phone, user), options for a select, then Required, Show on record, Show in tables, Allow filtering (select and user fields only) and Allow agent fill (with instructions) as needed.
- Create field. It takes effect at once, no approval, and appears on the record page.
- Know what the two list flags do today: Show in tables and Allow filtering are saved, but the Companies and Contacts lists do not read them yet. Columns and filters for custom fields arrive in a later release. Meanwhile the field works in
records.listfilters and for your agent.
Custom objects
- Check first whether a module already has it (for partners:
stdlib/gtm.partnerin Settings > Packages). - CRM > Model, Define a custom object: label, description, fields. Validate.
- Needs a human Publish; a different person approves it in Approvals.
Common errors
FORBIDDEN"... cannot change custom fields (field.create)": the key lacks engine.configure or acts for a member.- Object keys must start with
custom.. You can add fields to a built-in object but never redefine its fields.
Bring data in
Goal: your accounts, contacts and deals in GTM OS, clean and de-duplicated.
From your CRM
- Connect the CRM on the Composio card, pick it under CRM of record, click Sync now.
From any other app
- On the connected app in the Composio card, click Map to ontology.
- Pick a list tool, preview live sample data, map fields to a target (company, contact, deal, signal or a custom object).
- Propose it; a person signs it (and promotes it if prod should run it); then Sync now.
From a CSV
- CRM > Import, What are you importing?: companies, contacts or deals.
- Choose the CSV file (or paste it). The first row is read as headers.
- Map your columns, then Preview: every row shows Create, Update or an error. For contacts, a
companycolumn links each contact to an account: GTM OS matches by website first, then by name. - Commit (owner or admin by default; see Security defaults). Large files run in chunks, and files that link contacts to companies run in chunks of 200 rows.
From looot (lists and enrichment)
Ask your agent, for example "find 50 logistics companies in Germany with 200 to 2,000 staff". It prices the request first and asks before more than about 100 paid calls. Each call is capped (default $0.05); above the cap it waits for an approval.
After every import
Run the data-quality pass in CRM > Data quality, or ask your agent to normalize, backfill domains, scan duplicates and propose merges.
Use each engine day to day
Goal: know which button does what.
Hot list and signals
- CRM > Hot list: accounts ranked by score, latest signal, verdict, owner. Filter by verdict, signal, owner, activity; sort by score, latest signal or name.
- Click Why this score on a row for the reasons.
- Scan for signals now runs the daily looot signal scan on demand (owner or admin; capped per run, never above $1). The button is disabled, and nothing is queued, while the organization-wide Dry Mode is on, because a scan spends real looot credits. It is also disabled while the scan is switched off in Settings > Feature flags (
signals.scan). The page says which of the two applies. - CRM > Signals is the feed of signals with their evidence. A review queue lets you verify or dispute them. With the verification gate on Strict (the default for new workspaces) an unverified third-party signal never scores and never reaches a seller. Signals from your own systems (first party) are verified by default, so a first test that only uses a scan or looot data can show an empty Hot list until you verify some signals in the review queue. Workspaces that existed before the gate run it Off until an owner or admin sets it (owners and admins choose Off, Hide from sellers or Strict on the Signals page, and every account is rescored when the choice changes which signals count).
Companies, contacts, deals
- CRM > Companies / Contacts: search, filter, create, bulk actions. A company page has tabs: Overview (firmographics, score, signals, custom fields), Contacts, Deals, Tasks, Activity, Research, Evidence, Agent.
- CRM > Deals: pipeline board and table. Edit pipelines and stages in Settings > Pipelines.
- CRM > Tasks: due today, overdue, done.
- The Evidence tab shows where each value came from. A value you edit by hand is never overwritten by automation.
Enrichment
- CRM > Companies, select rows, choose Enrich in the bulk actions.
- Read the cost preview, confirm.
- Engines > Enrichment shows the mode, fill rate, spend, bindings, freshness windows, newer enriched values and runs.
- Every value written carries when it was verified, a freshness window and a confidence. Stale values that enrichment owns are re-checked on a schedule. A value you edited by hand is never touched.
- A newer enriched value replaces the one on a record only through an approval, with undo.
- A monthly spend cap applies, and when money is limited the best accounts are enriched first.
- If a provider is down (unavailable, a timeout or a server error), enrichment moves on to the next provider in the binding instead of stopping. Lookups run with your workspace key; the instance key is only a fallback.
Score
- Engines > Score: To score, Accounts, Rescan. Click Qualify on an account.
- Open the account for its qualifications, or the company page for Why this score.
- Reps flag a score they disagree with from the lead queue (Flag this score).
- The Learning section shows the last run, proposed changes with their evidence and lift, calibration and rep disagreement. Scoring records each verdict, measures what happened afterwards (a positive reply, a meeting, an opportunity, a won deal), and proposes a new scoring model version. Needs a human A person other than the proposer approves it.
The first rescan after upgrading to 0.2.5 re-scores accounts once and scores can move. Ask your agent for a rescan preview first (score.rescan_preview). The upgrade rescore never hands off, routes or enrolls anyone.
Outreach
- Engines > Outreach lists pending sends. Click the Outreach workbench link (top right).
- Pick a contact, Draft message, edit subject and body.
- Pick the Sending lane and the Sending mailbox (Choose a mailbox…), then Propose send.
- Needs a human A different person approves and executes it in Approvals.
- Cadences: CRM > Sequences, New sequence (name, goal, sending lane, sending mailbox). Edit its Steps: ordered steps, waits in days or business days counted in the recipient's timezone, send windows, templates or briefs, weighted A/B variants, and exits on reply, unsubscribe, bounce, a booked meeting or a stage change. Enrol people from a company page with Enroll people in sequence. Members, Messages and Replies tabs track it.
- Signal plays (funding, hiring, intent) enrol one person per signal into an approved sequence, with a daily cap. Personalization is tiered (1:1, 1:few, 1:many) and grounded in your proof library.
- Reply next steps are proposals, never automatic. Gmail and Outlook delivery-failure reports are applied as hard or soft bounces and never read as replies. Undoing a bounce (
outreach.bounce_undo) is human only. - What's working on the Outreach page shows reply and meeting rates per group once a group has at least 30 sends.
Merges, identity and data quality
- Engines > Merges (and CRM > Data quality): quality score, field fill rate, Duplicate candidates, merges waiting for approval, recent merges with undo.
- The duplicate scan follows your signed identity rule. Choose the survivor, Merge. Conflicts are decided per field by a strategy and listed on the approval. The merge is a proposal; once executed it can be reversed with Unmerge, and fields you set by hand on the survivor are kept.
- Uncertain identity matches wait in Operate > Needs a human.
SEO & GEO Search Console required
- Engines > SEO & GEO: readiness metrics and pages. Click the Page workbench link.
- Track a new page, Track page, then Audit page, Get recommendation, Propose change (a person approves).
- Site visibility: Run visibility for Search Console totals, top queries and pages (and Ahrefs metrics when Ahrefs is connected through Composio).
- Plan content with topic clusters, briefs and drafts. A draft passes a quality gate and moves through lifecycle states. Pages needing a refresh are listed with the reason.
- Needs a human Publish an approved draft to your CMS: Webflow through Composio, WordPress.com as drafts only, or a signed webhook (also the route for self-hosted WordPress). The body published is the approved draft, bound to its hash.
Set up SEO publishing
The SEO & GEO engine page has tabs: Overview, Visibility (Search Console totals, top queries and pages), GEO (AI-answer visibility), Keywords (opportunities), Clusters (topic clusters), Factory (briefs and drafts), Fix queue (audit findings to fix), Publish queue, Refresh calendar (pages due a refresh), Settings (the tracked-visibility schedule, alert thresholds and the spend cap per run; turning it on needs a live Search Console) and Pages.
- Needs a human Connect where pages go. For Webflow or WordPress.com, connect it in Integrations > Connections > Composio with your own credentials. For your own site, including self-hosted WordPress, register a publish endpoint with a URL and a signing secret (
webhooks.create) and put a small receiver on your site. Composio has no toolkit for self-hosted WordPress. - Open an approved item in Factory, then its Publish tab, then CMS targets > Settings. Set the default target and the Site URL (without it a new page's live address is unknown). For Webflow add the collection id the pages live in. For WordPress.com add the site id. For the webhook add the endpoint id.
- Webflow reads three fields from each draft: title into
name, address intoslugand body intopost-body. If your collection uses other field names, set the field map (webflow_field_mapunder thecmsprovider settings, with keystitle,slugandbody; your agent can set it, there is no form field for it). - Choose the CMS target (each shows Ready, Needs setup or Not connected), check the preview of exactly what will be sent, and click Propose publish. A different person approves it in Approvals. The page then moves from Approved to Published once the CMS confirms.
- WordPress.com stages drafts only. The toolkit never publishes, so the page is never called published.
- The body is read from the approved draft and bound to its hash. Editing the draft voids the approval, and the claims gate and brand safety run again when the publish executes. A retry never creates a second page.
- Undo: Propose take-down on the same tab. A different person approves it.
- Refresh: the Refresh calendar lists pages that decayed, with the reason: clicks down (20% by default), ranking down (3 places), a lost AI citation, or age (180 days by default). The two thresholds are organization settings (
refresh_traffic_drop_pctandrefresh_age_daysundercms). A refresh is a revised draft through the same flow.
Performance Ads
- Connect Google Ads (connected Google account with Google Ads) or a
meta_adsconnection. - Engines > Performance Ads, then the Campaign workbench link: pacing and performance per campaign.
- Propose a change: new budget (increases capped at 2x) or status, Propose change. A person approves: this is real money.
- Set a target in
definition.unit_economics(ask your agent) and the engine paces toward it. A nightly allocator proposes budget moves. It never executes them. With no target set, behavior is as before.
Routing
- Engines > Routing: territories (criteria, stickiness, overflow), manual assignment and the assignment history with the reason for each decision.
- Team capacity: caps per type, weights and out-of-office per person. A full owner's records are spread over the eligible pool.
- Lead-to-account matching attaches a lead on its own only on an exact domain match. Use the match preview to see who a lead would route to.
Plays
- Engines > Plays: every play, its trigger, a dry run, manual fire and runs. The Results tab shows runs, actions, outcomes, credited revenue and spend per play.
- Nine plays ship in the blueprints: speed to lead, meeting prep, meeting follow-up, stage-stuck nudge, closed-lost win-back, renewal runway, champion change, content refresh and enrich on create.
Attribution
- Settings > Money > Attribution: choose position-based or a per-channel model next to the equal split. Until you choose, your numbers stay on the equal split.
Research agent
- CRM > Research: what it researches now, scheduled re-checks, failures to retry or cancel.
- Settings there: Workspace profile (enter your website, Refresh), Research enabled, Auto-apply verified facts, Daily budget (USD), re-check days, meeting-prep lead time.
- Per company: the Research tab.
Search index
- Settings > Search index: records indexed and with vectors per type, the embedding model. Reindex now (owner or admin).
Agent tab and AI chat
- Open a company, contact or deal, Agent tab: ask about that record.
- CRM > AI chat: ask about the workspace. Its changes (log activity, tasks, edits, stage moves, enrolments) are only proposed. Needs a model provider.
Watch the system
- Mission Control: today's move, KPIs, risks, the overnight engine log.
- Operate: Needs a human, Runs (step-by-step traces), Monitors, Cost (every cost line back to the run that spent it).
- Settings > Navigation: hide whole tab groups you do not use.
Run the system day to day
Goal: know which page answers which daily question: what happened, what waits on me, what was sent and can it be pulled back, what to build next, what customers said, and whether the data is whole.
Daily update Operate > Daily update
A fixed report card with four numbered sections: Done yesterday (what each engine executed and how many have an outcome, approvals given, asks closed), Waiting on a human (approvals pending, items held by a guardrail, signals to review), Open questions (the numbered asks with owner, due date and an Overdue mark) and Sync health (the last result per source sync). It covers the day before. The history on the right opens any earlier card.
- Get it yourself. In Send it to me, switch Daily update on and choose the channels: In-app, Email, Slack DM. Nobody receives it until they opt in, and you see only what you are allowed to see.
- Needs a human Schedule it for the team (owner or admin). In Schedule: Generate every day, the local hour (0 to 23), the time zone, and an optional team Slack channel id. The default is 08:00 UTC, with no AI summary.
- Optionally switch on Add an AI summary. It is written from the card by your model, it needs a live model route, and it is always labelled "AI summary (written by <model> from the card; check before acting)". It is written from organization-wide records only, so private and team records never appear in it. If it cannot be produced, the card says why instead of inventing one.
- Generate now rebuilds today's card from live data, replaces the stored one and delivers it to everyone who opted in.
Email and Slack delivery use the organization's own Resend connection and Slack connection (matched members only). Without them the delivery reports not configured, and in-app always works. This is separate from the instance mail described in Email for invitations, password reset and nudges.
Open asks Operate > Open asks
The numbered ledger of every question the system is waiting on a person to answer. Each ask is numbered (#1, #2, and so on), has an owner, a due date, a priority (Low, Normal, High, Urgent) and a status (Open, Answered, Resolved, Dropped). New asks come from you, from the setup wizard (a missing access grant becomes an ask), from Studio, from stage-mapping reviews and from data completeness flags. Kinds you can create are Question, Document request, Access request, Approval needed and Data gap.
- Read the strip at the top, including Waiting on you: open asks you own, directly or through your role.
- Filter by status, kind, priority or overdue, tick Waiting on me, or search by words or by number (for example
#12). The list shows the exact total and a Load more button. - Click an ask to open it. You can answer, resolve, drop, reassign or Remind now.
- New ask: choose the kind and priority, write one clear question and the context, choose an owner (a person or a role) and a due date.
- Owners and admins: open the chasing settings. Send reminders, remind every N days (1 to 30), the local hour, the time zone, the default due date (1 to 90 days), how many days overdue before it escalates (0 to 30), the escalation contact (the owners and admins by default), and the channels In-app, Email and Slack DM.
The Waiting on you badge in the header counts the open asks you own plus, if you are an approver, the approvals waiting for your decision. It is hidden at zero, refreshes about every minute, and opens Open asks filtered to you. Agents see open asks in their context and do not ask the same thing twice. Reminders go by email through the organization's Resend connection, and by Slack when the member is matched in Settings > Slack. People outside your team are reminded by email only.
Actions Operate > Actions
The ledger of everything the engines drafted or did: a draft, a send, a budget change, a page publish, a CRM write, a merge, a task, a meeting. Each row shows the engine, its status (Drafted, Pending approval, Approved, Executed, Failed, Undone, Rejected, Cancelled), the gate it went through, the outcome it produced and its undo.
- Pick a window (7, 30 or 90 days) on the strip at the top. It counts drafted, approved, executed and with an outcome, per engine, with a conversion rate.
- On the Actions tab, filter by engine, status, gate, type or outcome, or search. Click a row for the approval, the audit trail, its batch and its undo plan.
- Undo one action from its detail. A sent message cannot be un-sent, so for a message the undo records a retraction note. An undo that changes the outside world is proposed for approval, and the row shows Undo pending until it is decided.
- On the Batches tab, each send batch (a cadence rescan, a batch send) shows its counts. Pull back cancels every draft and every send not yet out, withdraws proposals waiting for approval (or rejects them if an owner or admin does it), revokes approved ones, and leaves anything executing right now alone. The box Also undo what already executed is ticked by default. Add a reason if you want one kept on the record. View actions opens the Actions tab filtered to that batch.
Studio CRM > Studio
Ask for a new play or app in a sentence. The system drafts the spec, every open question becomes a numbered ask, and it ships once a second person approves. The page has three tabs: Plays (the play catalog scored on impact and ease for your organization, sortable and filterable, each score explained), Requests (the board: ask, spec, numbered asks, approval, shipped) and Scoring (the weights behind the scores; owners and admins change them). Click Ask for a play, describe what you wish existed, and follow the request across the board.
Conversations CRM > Conversations
Conversation memory, in four tabs. Replies: every reply with its label (interested, not now, objection, referral, unsubscribe, out of office, bounce and the labels you define) and what the label triggered; you can relabel a reply, re-run it, or link it to a record. Calls: transcripts; add one, sync from Fireflies, edit, archive. Coverage: how much conversation GTM OS holds per source. Labels: your reply labels, the words and senders that recognise each, and the effect of each (stop a sequence, suppress an address, raise a signal, open an ask, create a follow-up task).
Data health CRM > Data health
How much of each connected source GTM OS actually holds. Per source (your CRM of record and each signed source mapping) you see its state, its connection and its last and last full sync. Per object you see the source count (and how it was counted), what is mirrored, what is held on unmapped stages, a completeness bar against the threshold, a flag when it falls below, and the latest parity check against the live source. Click an object for the field-by-field differences (money to the cent) and its audit history.
- Owners and admins: Run audit, Check parity and Sync now.
- Audit settings: the flag threshold (1 to 100 percent), the parity sample size, auto backfill (it never runs while the organization or the CRM write-back engine is in Dry Mode) and the nightly audit.
Approvals Needs a human
Goal: nothing customer-facing or live happens without a second person.
- Approvals (top of the sidebar) or Operate > Needs a human. Open a proposal: The change shows exactly what will run.
- Approver: Approve or Reject. Then Execute runs it.
- Veto your agent's proposal: as the person the agent acts for, open it and click Withdraw (veto my agent's proposal).
- Pause everything customer-facing: Settings > Safety, Enable dry mode. Effects become drafts; a banner shows on every page. More in Dry Mode and guardrails.
| Who | Can approve? | Can execute? |
|---|---|---|
| The proposer | No | No |
| The person whose agent proposed it | No (the button shows; the server refuses) | No |
| Another owner or admin | Yes | Yes |
| Any agent | Never | Never |
Common errors
CONFLICT"ActionApproval has expired": propose it again.- "Action payload no longer matches the approved payload hash": it changed after approval; propose again.
- "Lane "x" is paused": a monitor paused it after failures; investigate, then resume it in Operate > Monitors.
Dry Mode and guardrails
Goal: see what would have happened before anything real goes out, and keep going live a deliberate choice, one engine at a time.
- Organization-wide. Settings > Safety, Enable dry mode. Customer-facing actions become drafts: logged, never executed, no approval created. A banner shows on every page. Turning it on or off is human only (owner, admin or builder).
- Per engine. In Settings > Safety (the Engines card) and in the header of each engine page, every engine has its own Dry or Live mode: outreach and sequences, meeting booking, performance ads, SEO & GEO publishing, lifecycle plays, routing notifications, CRM write-back, enrichment spend and merges. The stricter of the organization mode and the engine mode applies.
- Needs a human Going live is an owner or admin action. It asks you to confirm and is audited. Each engine shows a shadow accuracy report from its dry runs, so you can judge it before you flip it.
- Guardrails (Settings > Safety): emails a day, new accounts contacted a week, ad spend a day (above it a person approves), a send window in the recipient's timezone with quiet hours, and allowed channels. A hit holds the action, raises an alert and runs it later by itself. It never drops it. Held actions are listed with a Cancel button.
One rule is not configurable: the first message or meeting invite to someone nobody has ever written to is never sent automatically, whatever the autonomy setting.
Proof library and claims Needs a human
Goal: nothing you send states a fact you cannot back up.
- Settings > Proof library, add proof: case studies, metrics, testimonials and logos. Give each a permission (public, anonymized or internal only) and an optional expiry.
- Or upload a document. Claims are extracted and kept only when the quote is verbatim in the source and any numbers appear in that quote.
- A second person activates each proof (four-eyes). Customer phrases mined from your calls, replies and won deals are approved one by one.
- Claims check (same page): choose the policy. Block is the default: an unbacked claim stops the action. The alternative routes it to an approver, who acknowledges each claim in the claims panel (the proposer and the agent cannot). An acknowledgement is recorded.
Common errors
POLICY_DENIEDblocked_unbacked_claim: add active proof for the claim, change the wording, or (with the route-to-approver policy) have an approver acknowledge it.- A customer is never named unless its proof is public. Proof that was archived or expired between approval and run fails the run: propose it again.
Settings pages that shape reporting and rules
Goal: know what each less obvious page controls before you change it. Most are for owners and admins, and the server enforces that.
Money Settings > Money
Your money definitions, which every report, dashboard and agent uses. Where does revenue truth live? picks the one source of money truth (your CRM deal amounts, or a warehouse or billing object), and revenue numbers and attribution are reconciled against it. Attribution sets the window and the model (see Attribution). Currency and FX rates sets the reporting currency and whether market rates or your finance team's dated rate sheet apply, with the drift between the two shown. Reconciliation compares your revenue truth source with the CRM's won-deal amounts (there is nothing to reconcile while the CRM is the truth). Your words for deals holds your deal types and the raw CRM values that mean each one, so synced deals are typed automatically; removing a type never rewrites deals, they show as retired.
Journeys Settings > Journeys
The lifecycles companies, people and deals follow, each with its funnel, and the stage maps that say what each CRM or source stage value means. Choose for each value: map it to a journey stage, ignore it, or ask GTM OS to guess. Saving a journey proposes a change in dev that a different person approves, then it is signed and promoted like any definition. A mapping decision takes effect at once and is applied again to the records carrying the value. A value no map knows reads Needs you, and its records keep their stage until someone decides.
Brand and compliance Settings > Brand & compliance
Three cards, all human-only to change. GDPR mode: when on, every outreach send on any channel and every contact enrichment needs a lawful basis recorded for that channel (on the contact's Consent tab); when off, only a withdrawal, a refusal or an expiry blocks a channel. Buying roles: the roles a person can hold on a deal and at their company. The defaults are there for every workspace and you add your own; deal contacts and account roles must use one of them, and removing a role never rewrites stored records. Brand safety: your voice, words to avoid, claims no draft may ever make, and voice samples. Every outreach message and SEO or GEO draft is scanned after it is generated, and a violation blocks the send or publish with the reason and a Regenerate option. Try it scans any text you paste.
Navigation Settings > Navigation
Owners and admins turn whole tab groups (Engines, CRM, Integrations, Operate, Apps) on or off for the workspace. A hidden group disappears from the sidebar for every member, not only for you. Mission Control, Approvals and Settings cannot be hidden.
Feature flags Settings > Feature flags
Per-organization flags read by the API, the worker and the MCP server. Any member can read them, and only owners and admins change them. A value is free-form JSON, so a flag can hold a switch, a number, a string or a small configuration object. Examples you meet in this guide are signals.reaction, signals.scan and the nav.section.<group> flags that Navigation writes.
Notifications Notifications
Open it from the bell in the header. Your own list of tasks assigned to you, due dates, approval requests and setup reminders, with Load more and Mark all read. At the top is your own switch to also receive setup reminders by email, at most once a week; it needs the instance email transport (see Email for invitations, password reset and nudges).
Adapters CRM > Adapters
The adapter builder for a source. Introspect a connected source, audit completeness first, review every proposed field mapping with its confidence, answer the numbered questions, publish the signed mapping through the usual definition flow, start its nightly and incremental syncs, and read or download the mapping document.
Data health
Described under Run the system day to day.
Environments and going live
Goal: config moves from dev to prod safely. Every org has dev, staging and prod. The runtime reads prod. Effects in dev and staging go to a sink.
- The environment switcher in the console header picks which environment you view (default prod).
- CRM > Environments, Go live card:
- Needs a human Review & sign all proposed (N): someone other than the proposer (and other than the person whose agent proposed) signs.
- Promote dev → prod: proposes one approval for everything signed; another member approves and executes it.
- Same from a terminal:
gtm definition sign-all --env dev gtm promote --all dev prod --preview gtm promote --all dev prod - One resource only, or roll back:
gtm promote definition.lane primary-email dev prod gtm rollback definition.scoring_model default --to-version 3 --env prod
dev reads "Not applied (dev)" rather than "executed": applying, promoting and rolling back act in the change set's own environment, whichever environment you are viewing.Common errors
- "Definitions cannot be published directly into a prod-class environment": publish to dev, sign, promote.
- "Cannot promote ... the source version (vN) is "draft", not "signed"": sign it first.
- "Cannot sign: you proposed this version for signing -- a different person must sign it (four-eyes)": another admin signs.
- "Illegal promotion prod -> dev": promote upward only.
Packages
Goal: install, enable and upgrade engines, screens, connectors and modules.
- Settings > Packages. Tabs: Installed and Available.
- Install: Available, Install (a proposal).
- Enable or disable per environment with the toggle on the installed package (a proposal).
- Upgrade: Preview upgrade vX -> vY (changes, breaking or not), then Propose upgrade to vY.
- Remove: Uninstall, then Propose uninstall (your data is never deleted).
- Needs a human Another admin approves each in Approvals.
CLI equivalents:
gtm pkg enable <name> --env <env>
gtm pkg upgrade <name> --dry-rungtm doctor prints the exact enable command if one is missing.Engine app updates. Settings > Packages shows a card when engine apps have updates. An upgrade of GTM OS never changes an installed engine app by itself. An owner or admin proposes the update (one approval), edits your workspace made to the app are merged in, and if an edit overlaps the release that app is not updated and you choose Keep mine or Take new. Every update is a new config version you can roll back.
Self-host on a server
Goal: people reach GTM OS at https://app.acme.com while the instance stays closed to everything except your proxy.
127.0.0.1 by default. An existing deployment becomes local-only on its next gtm up or gtm upgrade. If you only use GTM OS on one machine, nothing changes for you.GTMOS_BIND_ADDRESS | Meaning |
|---|---|
127.0.0.1 (default) | Reachable from this machine only. Use it with a reverse proxy on the same machine. |
0.0.0.0 | Reachable on every network interface. Only behind a firewall that allows exactly the ports you intend. |
A private IP such as 10.0.0.5 | Reachable on that interface only, for a proxy on another host in the same private network. |
Put a reverse proxy in front (recommended)
- DNS and firewall Needs a human: point
app.acme.com(console),api.acme.com(API) and, if coding agents outside the server need it,mcp.acme.comat the server. Use one parent domain so the login cookie is shared. Open only ports 80 and 443. Do not open 3000, 3001, 3004 or 5432. - Keep
GTMOS_BIND_ADDRESS=127.0.0.1, and set the public URLs in.env(the origins a browser really uses, withhttps://and no trailing slash), then rungtm upagain:PUBLIC_API_URL=https://api.acme.com PUBLIC_CONSOLE_URL=https://app.acme.com PUBLIC_MCP_URL=https://mcp.acme.com - Proxy each host to its local port. With Caddy (it gets and renews the HTTPS certificates) the whole
Caddyfileis:nginx works the same way: oneapp.acme.com { reverse_proxy 127.0.0.1:3000 } api.acme.com { reverse_proxy 127.0.0.1:3001 } mcp.acme.com { reverse_proxy 127.0.0.1:3004 }serverblock per host withproxy_passto its local port, theHostandX-Forwarded-Protoheaders, and theUpgradeheaders for the console. - Check it:
curl https://api.acme.com/health curl -I https://app.acme.com ss -tlnp | grep -E ':(3000|3001|3004|5432)\b'
ok, the console answers 200 or redirects to the login page, and every line of the last command shows 127.0.0.1, never 0.0.0.0 or *.Open the ports on purpose instead
Edit GTMOS_BIND_ADDRESS in .env and run gtm up again, or create the instance with gtm up --bind 0.0.0.0. gtm up rewrites docker-compose.yml on every run, so never edit the compose file to open or close ports. The .env value is kept across runs. A --bind flag passed to an existing instance is ignored and reported, because the existing .env wins.
Upgrading an older instance
The first gtm up or gtm upgrade on 0.2.5 prints a warning when your .env has no GTMOS_BIND_ADDRESS, and writes 127.0.0.1 into it so the choice is explicit from then on. If a teammate's browser, a webhook or an MCP client on another machine was reaching the instance by IP address, choose a reverse proxy or 0.0.0.0 as above.
Common errors
- Login redirects loop or cookies are not stored: the public URLs in
.envdo not match what the browser uses (for examplehttp://in.envandhttps://in the browser). - Nothing answers by IP address after an upgrade: the ports are local-only now. See above.
Security defaults an operator must know
Goal: know what is closed by default and the few settings that change it.
| Setting | What it does |
|---|---|
GTMOS_BIND_ADDRESS | Which network address the console, API and MCP ports bind to. Default 127.0.0.1. See Self-host on a server. |
GTMOS_LIVE | Must be true or false in production. The API, worker and MCP images refuse to start without it. true means an unconfigured model, connector or sender fails with NOT_CONFIGURED. false serves visibly fixture demo data. New installs write true. An upgrade writes false into an older .env, which is how it already behaved. Set true and restart the API and worker to go live. |
GTMOS_PLATFORM_CREDENTIAL_PROVIDERS | Which instance-level keys in .env an organization may fall back to when it has no connection of its own. Setting it replaces the default list, it does not add to it. The default shares the model keys, Resend, looot, Composio and the Google Ads developer token, and does not share CRM, ads-account, LinkedIn or Search Console credentials. The upgrade keeps the providers you already use. gtm doctor names the variable when a key is outside the list. |
| CRM settings > Members can import CSVs and bulk-edit | Off by default: members can preview a file but not commit an import or a bulk edit. An owner or admin can turn it on. |
EMAIL_TRANSPORT, SMTP_*, EMAIL_FROM | How the instance sends invitations, password reset, verification and setup reminder mail. Unset means no system mail is sent, and a mail you expect never arrives. The link in a reset or verification mail is a secret and is not written to the logs of the release images. See Email for invitations, password reset and nudges. |
| Private and team records | A record set to private or team is visible only to its owner, its creator and the organization's owners and admins, in lists, search, counts, timelines and the dashboard. Opening it by id reads "not found". An agent sees what the person it acts for sees. |
| Agent keys | Follow the person's current role: demote or remove the person and their agents narrow or stop at once. Keys expire (90 days by default) and are shown once. |
| Your master key | GTMOS_MASTER_KEY seals every stored credential. Backups do not contain it. Keep it somewhere safe, apart from the dump. |
org.offboard | Needs a real approval by a second owner or admin, then a run by someone other than the proposer. |
Upgrade GTM OS
Goal: move your instance to a newer release without losing anything. An upgrade never touches, removes or loses your data. Every release only adds (new tables, nullable or defaulted columns, indexes). The updater refuses any release that does not, and there is no override.
Which version am I on?
The CLI version and the instance version are separate things. gtm upgrade --check shows both, plus what the signed release list offers, and changes nothing. Releases before 0.2.5 published images only as 0.2.2 and stable. CLI versions 0.2.3 and 0.2.4 shipped no images, so an instance installed with either still runs the 0.2.2 images.
- Upgrade the CLI first. It renders the compose file your instance runs on, so it must be at least as new as the release you move to. If it is too old,
gtm upgradestops and tells you which version to install.npm i -g @gtmos/cli@latest - See what is available (from the instance folder, or add
--dir <path>):gtm upgrade --check - Rehearse. It prints every command and changes nothing:
gtm upgrade --channel stable --dry-run - Upgrade.
--yesis required because it migrates your live database:gtm upgrade --channel stable --yes - In Claude Code run
/mcpto reconnect, then refresh your project files in each project folder:gtm init --refresh
gtm upgrade never moves a pinned install. With neither --channel nor --to it only re-pulls and re-verifies the release you already run. To move, choose the target on purpose: --channel stable, or --to <version> for an exact release.What happens, step by step
- The signed release list is fetched and its signature checked. The path is planned by its rules: no downgrade, no skipped required stop, additive-only releases, a minimum CLI. Any failed check refuses the upgrade before anything is pulled.
- Preflight: one update at a time, and free disk at least twice the database size plus 2 GB.
- The target images are pulled, and each digest must match the signed digest.
- The additive-only guard reads every pending migration and refuses anything that drops, renames, retypes, deletes or overwrites.
- The API, worker, MCP and console stop gracefully. A backup is taken and read back to verify it.
- A rehearsal restores that backup into a scratch database, migrates it and compares it with your live data (tables, columns, types, row counts, row checksums). Any difference refuses the upgrade.
- The version is pinned in
.env, the migration runs inside the new image, services start, and health and schema are verified. The newest five backups are kept.
What is preserved
- All your data: records, history, definitions, approvals, connections and keys.
- Your apps and configuration. An upgrade changes no installed engine app. See Packages for engine app updates.
- Your
GTMOS_MASTER_KEY. Backups do not contain it. Keep it safe, apart from the dump.
Rollback
A failed upgrade rolls back by swapping the images back: the previous compose and .env files and the previous images are restored and the services restart on them. The database is never restored, dropped or recreated by an upgrade, and the verified backup is kept. This works because every migration is additive, so the previous code runs on the migrated database.
Restoring a backup is a manual last resort, for example if something other than the upgrade damaged your data. Never restore over the live database: rename it first, then restore into a new one. gtm upgrade --help prints the exact commands.
Moving to 0.2.5 from 0.2.2 (or CLI 0.2.3 or 0.2.4 on 0.2.2 images)
- Upgrade the CLI first. This is not optional.
- Ports become local-only. See Self-host on a server. If people or systems reach the server by IP address, choose a reverse proxy or
GTMOS_BIND_ADDRESS=0.0.0.0. - Read Security defaults:
GTMOS_LIVE, instance keys, CSV import permission, private records. - Account scores can move once on the first rescan (preview it). Visits from AI assistants move from "Website (other)" to SEO & GEO credit. New workspaces start with the signal verification gate on Strict.
- Run
gtm init --refreshin each project folder for the new skills and subagents. - You are already on a newer 0.2.x? The path is the same. If a release in between is marked as a required stop,
gtm upgradeplans it as an extra hop and takes a backup at each hop.
Check it worked
gtm doctorshows the sources you expect as live, and its Harness section has no stale line.GET /healthreports the new version on the API, worker, MCP server and console.- The upgrade reported that verification passed, and a snapshot exists in your backups folder.
- Row counts of your main tables match what you noted before.
- A rescan preview looks reasonable before the first rescan.
- Dry Mode send check: propose one send to an internal address and confirm that a different person has to approve it.
- Settings > Proof library: add one proof and check the claims check passes and blocks as expected.
With the updater service installed (gtm updater install), instance admins can also update from Settings > Updates.
If something looks wrong
- The upgrade refused before changing anything: read the message. It names the failed check (signature, path rule, CLI too old, disk, a non-additive migration).
- It failed part way: the images were swapped back and your backup is in the backups folder. Run
gtm doctorandGET /healthto confirm what is running. - You need data from a backup: follow the manual steps in
gtm upgrade --help, and do not skip renaming the live database.
Backup, export, restore and verify a release
Goal: know what is backed up before an upgrade, how to take your own copy, how to leave with an organization's data, how to restore by hand if you ever must, and how to check that a release is the one that was published.
The backup an upgrade takes
Every gtm upgrade stops the API, worker, MCP server and console, then writes a full-instance backup of the database (every organization in it) as a compressed pg_dump file, backups/gtmos-pre-upgrade-<stamp>.dump in the instance folder. It is read back to prove it is readable, and a rehearsal restores it into a scratch database, migrates it and compares tables, columns, types, row counts and row checksums with your live data. Next to it the upgrade keeps .env.pre-upgrade-<stamp> and docker-compose.yml.pre-upgrade-<stamp>. The newest five are kept. The backup is never used automatically: a failed upgrade swaps the images back and leaves the database alone.
Take your own backup
There is no gtm backup command in 0.2.5. The upgrade uses pg_dump in custom format run inside the Postgres container, and you can run the same from the instance folder in a POSIX shell (the redirect corrupts a binary file in Windows PowerShell):
mkdir -p backups
docker compose --env-file .env -f docker-compose.yml exec -T postgres pg_dump -U gtmos -d gtmos -Fc --no-password > backups/gtmos-manual.dumpKeep the master key apart
A dump does not contain GTMOS_MASTER_KEY. Every stored credential (connections, OAuth tokens, SSO secrets) is sealed with that key, so a restored database without it cannot decrypt them. Copy the key from .env into a password manager or secret store when you install, and again whenever you change it, and keep it somewhere other than next to the dumps. The dump itself is not encrypted and contains personal data, so protect the folder it lives in.
Restore by hand (last resort)
Never restore over the live database, and never as a reflex: an upgrade does not need it. If data was damaged by something other than an upgrade, keep the current database first, then restore into a new one. gtm upgrade --help prints the exact commands. In short, from the instance folder:
- Stop the services, and rename the live database so nothing is lost:
docker compose --env-file .env -f docker-compose.yml stop api worker mcp console docker compose --env-file .env -f docker-compose.yml exec -T postgres psql -U gtmos -d postgres \ -c "ALTER DATABASE gtmos RENAME TO gtmos_before_restore_<stamp>" docker compose --env-file .env -f docker-compose.yml exec -T postgres createdb -U gtmos -O gtmos gtmos - Restore in three sections, with one repair between the first two (a function that a 0.2.2 check constraint calls needs its search path set), using the dump you chose:
docker compose --env-file .env -f docker-compose.yml exec -T postgres pg_restore -U gtmos -d gtmos --section=pre-data < backups/gtmos-pre-upgrade-<stamp>.dump docker compose --env-file .env -f docker-compose.yml exec -T postgres psql -U gtmos -d gtmos \ -c "ALTER FUNCTION public.is_valid_record_link_object_type(uuid, text) SET search_path = public" docker compose --env-file .env -f docker-compose.yml exec -T postgres pg_restore -U gtmos -d gtmos --section=data < backups/gtmos-pre-upgrade-<stamp>.dump docker compose --env-file .env -f docker-compose.yml exec -T postgres pg_restore -U gtmos -d gtmos --section=post-data < backups/gtmos-pre-upgrade-<stamp>.dump - Put
.envanddocker-compose.ymlback from their.pre-upgrade-<stamp>copies, thendocker compose --env-file .env -f docker-compose.yml up -d. The renamedgtmos_before_restore_<stamp>database stays until you drop it yourself.
Go back to the previous release after a successful upgrade
You do not need a database restore. Every release migration only adds things, so the previous release runs on the migrated database. From the instance folder, using the oldest stamp from that upgrade run:
docker compose --env-file .env -f docker-compose.yml stop api worker mcp console
cp .env.pre-upgrade-<stamp> .env
cp docker-compose.yml.pre-upgrade-<stamp> docker-compose.yml
docker compose --env-file .env -f docker-compose.yml up -d
gtm upgrade --checkUse docker compose and not gtm up, which renders the compose file again and replaces the copy you just restored. The previous images must still be in the local cache or on the registry, and a pulled image must match its signed digest, so do not remove old images until you are sure.
Export one organization: gtm org export
gtm org export --org <id> --out <dir> [--format parquet|sql] [--env <env>] [--dry-run] [--database-url <url>]- It writes a portable bundle for one organization: one Parquet file per organization table (the default), or with
--format sqla single dump of column-by-column INSERT statements. It also writes a manifest that lists each table with its row count and file, and records that the export was scoped to the one organization. - With
--envandGTMOS_API_URLset, it adds the configuration of that environment as config files (your definitions and settings). - It connects as the restricted application database role (
DATABASE_URL, or--database-url) with the one organization selected, so row-level security applies and it can only read that organization's rows. It does not read user accounts or sessions, and it only reads: it never changes the database. --dry-runprints the plan and writes nothing.
Verify a release
- The signed release list. Releases are announced in
releases.json, signed with an ed25519 key (releases.json.sig). The CLI trusts only the release keys built into it. The default address ishttps://ay-automate.github.io/gtmos-releases/releases.json, andGTMOS_RELEASE_MANIFEST_URLoverrides it (an air-gapped site serves a mirrored list that is still signed). - What the list says. For each release: the version, date and channel, whether it is a security release, the minimum CLI version, the oldest version it can be reached from, any required stops, whether it is additive-only, and the sha-256 digest of each of the api, worker, mcp and console images. A list may claim at most 90 days of life.
- What the CLI checks, before anything changes. The signature, the schema and the expiry; the path (no downgrade, no skipped required stop, additive-only releases, a minimum CLI); that the images come from an allowed registry (
ghcr.io/ay-automateunless you mirror); and after the pull, that every pulled image's digest equals the signed digest. Any failure refuses the upgrade. - Check it yourself.
gtm upgrade --checkfetches and verifies the list and prints what it offers, and changes nothing. To check an image by hand, pull it and compare its registry digest with the one inreleases.jsonfor that service.
Your own cloud (BYOC)
gtm byoc plan|apply|destroy --cloud aws --region <region> provisions one GTM OS instance in your own AWS account with Terraform (or OpenTofu 1.6 or later): a managed Postgres database, the api, console, worker and MCP services on ECS with a one-shot migration task, an object bucket, Secrets Manager and a load balancer. It uses the same four images and secrets as self-host. Honest scope: AWS is the only cloud. plan works offline without cloud credentials and shows what it would create. apply and destroy refuse to run without --yes, real credentials and Terraform installed. The installer and its dry run are built and tested, but a live deployment into a real AWS account has not been performed. A runbook exists in the product repository; it is not part of these public docs.
Troubleshooting
Errors carry a remediation field: read it first. If nothing matches, ask your agent to read docs/gtmos/guides/08-troubleshooting.md.
| You see | Fix |
|---|---|
| "No commands match /setup-gtmos" | Restart Claude Code in the folder after gtm init. |
/mcp shows gtmos failed, or UNAUTHORIZED "No valid session or agent token" | Set GTMOS_AGENT_TOKEN, open a new terminal, restart Claude. Expired or revoked key: create a new one. |
| MCP tools stop working after a stack restart or upgrade | Run /mcp to reconnect. |
| A port is already in use during setup | Re-run setup with --console-port, --api-port, --mcp-port or --postgres-port. |
| "An action proposed by your own agent must be approved by a different person" | The approver (second admin) approves. |
| "An action cannot be executed by the identity that proposed it" | The approver or another member executes. |
| Banner "N definitions are in dev, not live" | CRM > Environments > Go live: sign all, promote, approve. |
Score shows score.default.v1 | Sign and promote your definition.scoring_model default. |
| "This is a Composio user API key (uak_...)" | Use Connect Composio. |
AUTH_EXPIRED Composio sign-in expired | Click Reconnect Composio. |
lane_required | Pick a sending lane; activate it first. |
mailbox_required | Connect Gmail or Microsoft 365 with Send mail; pick the mailbox. |
| Sends stay drafts, no approval appears | Dry Mode is on: Settings > Safety. |
NOT_CONFIGURED "Google Search Console is required for SEO & GEO"; gtm doctor ERROR connector.gsc | Connect Search Console (Connected accounts, Use for org, or Composio) and set the property. |
| "The GSC property must be a Search Console property" | Use sc-domain:acme.com or https://www.acme.com/. |
| "... does not hold the "engine.configure" scope" / "connection.manage" | New agent key with the Full GTM engineer preset. |
APPROVAL_REQUIRED on enrichment or data | Spend is above the per-call cap; approve it, or price smaller batches. |
402 insufficient_balance | Top up looot. Do not retry. |
Search results say mode: "text_only" | Add an OpenAI or Gemini key, then Settings > Search index > Reindex now. |
| "Sent" in demo mode but nothing arrived | Demo mode uses a fixture sender. Run live with a real sender. |
| Readiness says FIXTURE | Demo data, not real. Configure the provider; never present it as real. |
VALIDATION_FAILED "Unknown environment" | Typo in the environment name; use dev, staging or prod. |
| The console or API stops answering by IP address or hostname after an upgrade | The ports are local-only since 0.2.5. Put a reverse proxy in front, or set GTMOS_BIND_ADDRESS=0.0.0.0 in .env and run gtm up. See Self-host on a server. |
The API, worker or MCP container exits at start and the log names GTMOS_LIVE | Set GTMOS_LIVE to true or false in .env and run gtm up. |
gtm upgrade says the CLI is too old | npm i -g @gtmos/cli@latest, then run it again. |
gtm upgrade did not move my instance | A plain gtm upgrade never moves a pinned install. Use --channel stable or --to <version>. |
POLICY_DENIED blocked_unbacked_claim | The content states a fact with no active proof. Add proof in Settings > Proof library, or reword it. See Proof library and claims. |
A member gets FORBIDDEN on a CSV import or bulk edit | An owner or admin turns on CRM settings > Members can import CSVs and bulk-edit records. |
| CSV import lists contacts with no company match | Fix the company column (website or exact name) and import those rows again. The import never creates accounts. |
gtm doctor reports a stale harness | Run gtm init --refresh in the project folder. "Unstamped" on an older project is informational. |
A key is set in .env but the organization reads "not configured" | The provider is not in GTMOS_PLATFORM_CREDENTIAL_PROVIDERS. Add it, or add a connection in the organization. |
| No nudge email arrives | Email is opt-in per person (notification preferences), and the instance needs an email transport. gtm doctor shows it. See Email for invitations, password reset and nudges. |
| An invitation, verification or password reset email never arrives | The instance has no email transport (SMTP_HOST or a webhook address is empty). Set one in .env and run gtm up again, or add the teammate with gtm member add. |
| Disk or RAM low, Docker stuck | Close heavy apps, restart Docker Desktop. Never delete volumes of a stack you need. |
Customize anything with your agent
Your agent (Claude Code, Codex, or any MCP agent) reaches GTM OS through the same operations the console uses. If you can do it in the console, your agent can do it, and much more you would not want to click through: engines, the data model, data, integrations, blueprints and screens. You describe the outcome. It reads the live schema, writes the change in dev, verifies it, and tells you what a person must approve.
| Area | Your agent can change |
|---|---|
| Engines | Scoring models and thresholds, ICP fit, signal weights, plays and triggers, lanes, sequences, brand voice, workflow flags, research settings, model routing |
| Data model | Custom fields, custom objects and relationships, identity rules |
| Data | Imports, CRM sync, any-app source mappings, enrichment runs, de-duplication, record fixes (as proposals) |
| Integrations | Connections and keys you give it, Composio apps already authorized, MCP servers, capability bindings |
| Blueprints and packages | Apply the closest blueprint, customize it, propose package installs and upgrades |
| Screens | Declarative apps (record page, queue, table, dashboard) under Apps |
Slash commands in your workspace: /build-gtm-system, /go-live-check, /define-object, /create-play, /wire-connector, /build-app, /onboard-blueprint, /describe-ops, /compile-rule, /tune-economics, /design-abm, /set-brand-split.
Example prompts
Add a custom object
Add a custom object Partner with a name, a tier (gold, silver, bronze) and a link to the company. Let deals point to the partner who referred them.- Claude does
- Checks the
stdlib/gtm.partnermodule first; otherwise proposes and validates an ontology ChangeSet. - You do
- Publish it in CRM > Model; another admin approves.
Map an app to signals
Map our Typeform demo-request responses to signals, every hour, matched to contacts by email.- Claude does
- Previews the Typeform list tool live, writes a source mapping (append mode, hourly), proposes it, syncs once signed.
- You do
- Connect Typeform on the Composio card; sign the mapping.
Post qualified accounts to Slack
Create a Slack bot that posts every newly qualified account to #sales with its top reasons, and wire it in. Use Composio if that is simpler.- Claude does
- Recommends the native Slack connection for alerts (or Composio's Slack toolkit), creates or joins the channel, writes a play that reacts to qualification and posts, and simulates it.
- You do
- Click the Slack consent; sign and promote the play; approve posts that go out through the gate.
Tune scoring
Change the scoring weights: hiring signals 35, funding 25, and qualify accounts at 65 instead of 60.- Claude does
- Edits
definition.scoring_modelkeydefaultin dev and shows the diff; after promotion, scores an account to prove your version is used. - You do
- Sign and promote (Environments > Go live).
Use an internal API
Add our internal API as an MCP server (https://tools.acme.com/mcp) and use its company lookup tool in enrichment.- Claude does
- Registers and tests the server, binds an enrichment capability to the tool, verifies it in readiness.
- You do
- Paste the auth value in the MCP servers card if you keep it private; mark the tool Read; sign the binding.
Add a screen
Build a table app of open deals by stage with value and owner, and a queue of accounts that need review.- Claude does
- Scaffolds declarative apps from the object types and proposes them.
- You do
- Approve; open them under Apps.
What always needs a person
- Approve, reject, execute any proposal, sign definitions and publish ontology changes (four-eyes).
- OAuth consent clicks: Composio sign-in and app connects, Google, Microsoft, Slack. Your agent gives you the link.
- Dry Mode on or off (Settings > Safety).
- Agent keys, agents, roles, members, gate policies, and MCP tool effects (no agent can widen its own authority).
- Keys you choose to keep private: paste them in the console instead of the chat.
- DNS records at your domain host, and turning on research auto-apply.
CLI reference
Every gtm command, grouped by task. gtm help prints the same list for your installed version. Commands that talk to a running instance read GTMOS_API_URL (the base address, without /v1), GTMOS_TOKEN (a session token or an agent key) and GTMOS_ORG (the organization id). Where --org is shown, those variables are the default.
Install and first run
| Command | What it does | Key flags |
|---|---|---|
gtm up | Writes a pinned docker-compose.yml and a .env with generated secrets into a folder, then starts the released images with Docker. Never overwrites an existing .env without --force. | --version, --channel, --registry, --dir, --dry-run, --no-start, --force, --no-pull, --bind, --postgres-port, --api-port, --console-port, --mcp-port |
gtm setup [dir] | First run in one command: up, wait for the API, bootstrap, onboard, add a second approver, start the worker, write the harness. Idempotent. gtm setup --help lists every option. | --org, --email, --blueprint, --approver-email, --password, --no-harness |
gtm bootstrap | Creates the first operator and organization on a self-hosted instance. Idempotent. | --email, --password, --org, --slug, --name, --dir |
gtm onboard | Creates an organization and seeds it from a blueprint in one run. --dry-run creates nothing. | --org, --email, --password, --slug, --name, --blueprint, --env, --dry-run |
gtm member add | Adds a verified teammate (for example the second approver) without sending email. Idempotent. | --email, --password, --role admin|member, --org, --name |
gtm init [dir] | One-command workspace bootstrap: checks the instance and your agent key, writes the Claude Code and Codex harness and docs/gtmos/, and prints GO or NO-GO. gtm init --refresh updates files made by an older CLI. gtm harness is the same scaffold on its own. | --url, --key, --env, --yes, --refresh, --offline |
gtm init secrets | Generates the random secrets a fresh self-host needs (BETTER_AUTH_SECRET, GTMOS_MASTER_KEY, AUDIT_CHECKPOINT_SIGNING_KEY). | --write [file], --force, --bytes |
gtm init --org <id> --blueprint <file> | Seeds the config store directly from a blueprint (it proposes a change set). Add --git for an optional git mirror of your configuration. | --org, --env, --blueprint, --git, --repo |
gtm login | Signs in and prints the GTMOS_TOKEN export line. | --email, --password, --console-url |
gtm token create | list | revoke | Agent keys for MCP: create shows the key once, list shows agent, scopes and last use, revoke <key-id> stops one at once. | --name, --client claude-code|codex|other, --preset full-gtm-engineer|read-write|read-only, --scopes, --expires-days, --json |
gtm doctor | One line per model provider, capability backend, connector and sender: live, fixture or not configured, with the fix. Also checks default content per environment and your harness. | --status live|fixture|not_configured, --json, --strict (exit 1 unless everything needed is live) |
Config as code
| Command | What it does | Key flags |
|---|---|---|
gtm plan | Previews what a change would do against your live configuration. | --env, --org, --from <blueprint>, --repo, --json |
gtm apply | Proposes the change set. A different person approves. With --from the blueprint is validated first. | --env, --org, --from, --param name=value, --sender-physical-address, --gsc-property, --breaking |
gtm check | For CI: plans across environments and exits non-zero on issues or drift. | --envs a,b, --org, --from, --repo |
gtm mirror export | Writes the current configuration onto the optional git mirror. | --repo, --env, --org |
gtm definition propose-for-signing | sign | sign-all | Moves a definition from draft to proposed to signed. Signing is for a person other than the proposer, never an agent. sign-all signs every proposed definition in an environment except the ones you proposed. | <kind>/<key>, --env, --version, --kind, --json |
gtm promote | Proposes promoting one resource (<kind> <key> <from> <to>), or with --all <from> <to> every signed definition under one approval. | --all, --preview |
gtm rollback | Proposes rolling one resource back to an earlier version. | <kind> <key> --to-version <n>, --env |
gtm blueprint list | show | upstream | upgrade | Browse blueprints, see where an organization's blueprint came from, and diff an upgrade. See Blueprints. | --org, --to, --from, --env, --dry-run, --yes, --allow-breaking |
gtm lane activate <laneKey> | Proposes activating a sending lane. A different person approves, which grants the lane's budget and lets it go live. | --env, --json |
gtm play simulate <playKey> | Replays a play over the last N days with no writes. | --env, --since 30d, --json |
gtm app generate <objectType> | Scaffolds a declarative app from an object type. | --kind record_page|queue|table|dashboard, --metrics, --out, --label, --icon |
gtm connect <provider> | Binds a source: stores the connection, sets its binding mode, runs preflight and profiling, and prints the access board. | --mode replicate|soft_merge|federate|in_zone|sheet, --object-type, --org, --env, --secret, --label, --fill-rate-floor, --json |
gtm capabilities discover | inspect | discover lists the backend operations that can serve a capability and can pin one into your bindings. inspect shows an operation's price and inputs for free. | --pin, --repo, --backend native|composio|looot|nango|mcp|fixture, --capability |
gtm pkg scaffold | validate | build | test | sign | publish | registry list | provenance | lock | upgrade | Offline package tooling: write a starter manifest, lint it, hash it with a software bill of materials, check eval files, sign it with Ed25519, publish to a local registry, and plan an upgrade. | --tier, --dir, --out, --key or --generate-key, --registry, --to, --dry-run, --yes, --env, --org |
gtm pkg install | enable | disable | uninstall | Proposes the lifecycle change through the approval gate (needs your instance). | --org, --env, --purge |
Watch and diagnose
| Command | What it does | Key flags |
|---|---|---|
gtm logs | Lists durable flow runs, newest first. | --status, --flow, --limit, --json |
gtm trace <runId> | Shows one run with its step journal. | --json |
gtm describe | Prints the operation catalog. Needs no login. | --class Q|D|T|E|M |
gtm hooks list | Shows the registered guards, transforms, listeners and lifecycle hooks. | |
gtm profile | Profiles one object type: fill rate, cardinality and a drift baseline. Reads the database. | --object-type, --org |
gtm codegen | Regenerates the SDK, OpenAPI document, reference docs and llms.txt from the live operation registry. Meant for working on GTM OS itself. | --org |
gtm enqueue-retention-sweep | enqueue-listener-dispatch | enqueue-webhook-dispatch | Seeds the first tick of a self-rescheduling background job for one organization. | --org |
Run and upgrade the instance
| Command | What it does | Key flags |
|---|---|---|
gtm upgrade | Upgrades an installed instance: signed release list, pull, additive-only check, verified backup, rehearsal, migrate, verify, image swap back on failure. See Upgrade GTM OS. | --to, --channel stable|beta|edge, --dir, --dry-run, --yes, --wait-timeout, --skip-rehearsal, --keep-compose, --check |
gtm version | The CLI and instance versions and what the release list offers. Same as gtm upgrade --check. | --dir |
gtm updater install | uninstall | status | run | The updater service behind Settings > Updates. run runs its loop in this terminal. | --dir, --once |
gtm org export | Exports one organization's data and configuration. See Backup, export, restore and verify. | --org, --out, --format parquet|sql, --env, --dry-run, --database-url |
gtm byoc plan | apply | destroy | Deploys into your own AWS account with Terraform. | --cloud aws, --region, --registry, --version, --project, --api-url, --console-url, --dry-run, --force, --yes |
gtm fleet list | rollout | billing | token | agent | serve | The managed-fleet control plane for an operator who runs many instances, and the instance-side agent that enrolls and pulls a desired version. | --channel, --version, --region, --health, --wave, --dry-run, --durable, --json |
Anything that approves, signs, executes or switches Dry Mode stays with a person, so none of these commands does it for an agent.
Glossary
- Operation
- One capability (for example
score.score_account). Console, CLI, API and MCP all call the same operations. Classes: Q read, D data write, T stage change, E customer-facing effect, M config. - Engine
- An installed package that does one GTM job. Eight appear in the console: Score, Outreach, Performance Ads, SEO & GEO, Enrichment, Merges, Plays and Routing. Signals and the research agent support them.
- Definition
- Versioned config an engine reads: scoring model, lane, play, binding, source mapping, segment, persona.
- Blueprint
- A general template that seeds a whole motion (definitions, plays, lanes, metrics) in one change.
- Package
- Anything installable: engines, screens, connectors, ontology modules, blueprints.
- Ontology
- The data model: built-in
gtm.*objects plus your custom fields andcustom.*objects. - Proposal / approval
- A pending action that runs only after another person approves and executes it.
- Four-eyes
- The proposer, and the person whose agent proposed, can never approve, sign or execute it.
- Environment
dev,staging,prod. Runtime reads prod.- Sign / promote
- A person signs a definition in dev; promoting copies signed versions up to prod.
- Lane
- A sending channel with caps, checks, budget and domain. Every send needs an activated lane.
- Agent key
- A
gtm_...key your agent uses. It acts for you and can never approve. - Dry Mode
- A switch that turns customer-facing actions into drafts. It exists for the whole organization and for each engine, and the stricter one applies.
- Claims gate
- The check that every factual claim in outbound content is backed by active proof, when an action is proposed and again before it runs.
- Proof library
- Your approved case studies, metrics, testimonials and logos, each with a permission and an optional expiry.
- First win
- One of eight milestones, computed from real events, that shows how far your workspace has come.
- Harness
- The files
gtm initwrites in your project (skills, subagents, commands, MCP config, docs) so your agent can run GTM OS. - looot
- The third-party data provider: enrichment, emails, phones, lists, intent, SEO keyword data.
- Composio
- The bridge to your own apps: CRM, Slack, Typeform, Search Console, Ahrefs and more.
- Source mapping
- A signed rule that maps any app's records into GTM OS objects.
- Readiness
- The live status of every connection: live, stored not verified, failed, fixture, not configured.
- Demo vs live mode
- Demo serves fake fixture data where a key is missing. Live fails with
NOT_CONFIGUREDinstead. Real go-live needs live mode.
Known limitations and what is not in this release
Plan around these before you promise them to your team.
Not built yet
- Create in CRM. There is no gated "create a record in the CRM" action. CRM writes stay update-only, and high-risk Composio writes need per-record approval.
- Custom field display and filters. "Show in tables" and "Allow filtering" on a custom field are stored, but the Companies list has no such columns or filters yet. Record filters over the API and MCP do work.
- Company context for AI steps. Only brand voice, proof and customer phrases reach drafts. The rest of your company context does not yet.
- Connectors without a native card: Instantly, lemlist, Smartlead, Gong, Mixpanel and Amplitude. Use Composio.
- Deferred engines and features: new engines (ABM, sales intelligence, lifecycle, reporting); the ads creative loop, audience sync and offline conversions; routing SLAs and escalation; merge batches and learning from rulings; an enrichment catalog with triggers for job changes; play holdouts and a larger play library; GEO gap fixes and a link graph; more vendor skill packs; engine-dependent agents and per-agent evaluation sets.
- White-label (custom branding or domain per client) is not available. Agencies use delegations.
- Expansion and reorder for existing customers is not a shipped motion. Your agent can compose a custom segment and play, and renewal runway ships as a play.
- No DNS operation: lane activation checks SPF, DKIM and DMARC, but you add the records at your DNS host.
- There is no viewer role.
Works, with a caveat
- Nudge email needs SMTP. Setup reminders to your own team are emailed only when the instance has an email transport, and only to people who opted in. Otherwise they appear in the console only. See Email for invitations, password reset and nudges.
- CSV import limits. One import call takes up to 1,000 rows (the console splits larger files into chunks for you) and 10,000,000 characters of CSV text. Members cannot commit an import or a bulk edit unless an owner or admin turns that on. A contact or deal whose company is not found is imported without a company link: the import never creates a company. A row that looks like an existing record, or like an earlier row of the same file, is held for review instead of written.
- Dry Mode stops customer-facing actions only. While it is on, every customer-facing (E-class) operation becomes a logged draft with no approval. Data writes (D), stage transitions (T) and configuration (M) operations still run, so a record edit, an import or a config change is real.
- The strict signal gate can look like an empty Hot list on a first test. New workspaces start on Strict, so third-party signals (a scan, looot data) wait for a person to verify them before they count or reach a seller. See Hot list and signals.
- Signal scan quality. Web search results are not yet checked as being about the account. Set up a news binding for better results, or switch the scan off.
- LinkedIn sending is high risk for your account, needs your explicit acceptance and a real Unipile account, and is not yet verified live.
Rough edges
- The Records and Model pages still list ontology keys and type names, because they are model-builder pages.
- A few small icon buttons are under 28 px wide at phone width.
- Derived and metric lineage is empty, and relationship cardinality is recorded but not enforced.
Integration maturity
| Integration | Where it stands in 0.2.5 |
|---|---|
| HubSpot, Google Ads, Meta Ads, Google Search Console, GA4, Resend, looot | Native connectors with a live path (needs your credential) and a fixture path. Google Ads and Meta Ads can also be read through Composio, which is read-only. |
| Salesforce | The native adapter is fixture-first: it is tested against recorded Salesforce responses, not against a real organization. Use the Composio path. |
| LinkedIn Ads, TikTok Ads | Read-only through Composio (campaign performance reads). No changes are made from GTM OS. |
| Webflow | Publishing through Composio, tested with simulated Composio responses and not against a real site. |
| WordPress.com | Drafts only. It never publishes. |
| Self-hosted WordPress | Only through the signed webhook and a receiver you run. |
| LinkedIn through Unipile | High risk, off until an owner or admin accepts it, tested against recorded Unipile responses and not yet verified live. |
| Gmail and Google Calendar | Polled. The wire formats are not yet verified against a live account. |
| Microsoft Teams | A chat tile through Composio for alerts and approvals, with no settings page of its own. |
Needs a live check with real accounts
The code is tested against contracts and a live database, but treat these as unverified until you have checked them with a real account:
- Unipile: a LinkedIn message, a connection request and the accept wait.
- Webflow and WordPress.com publishing through Composio, and self-hosted WordPress through the signed webhook.
- Gmail: a real bounce report arriving in a connected mailbox.
- Resend: send, bounce and complaint events.
- Salesforce: the native adapter is not verified against a real organization. Use the Composio path.