GTM OS User Guide
GGTM OS User guide
GTM OS user guide

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.

Written for GTM OS release 0.2.5 (CLI 0.2.5). Console paths are written as Group > Page; every page lives under /org/<orgId>/.

Building or customizing GTM OS by prompting Claude? The engineer guide goes deeper, with copy-paste recipes for every engine and a troubleshooting table.

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 to prod.
  • 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.
Quickstart

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.

  1. 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%\.wslconfig and restart Docker.
    • Node 22 or newer, and Claude Code.
    • The gtm CLI, from npm:
    npm i -g @gtmos/cli
    docker info --format '{{.ServerVersion}}'
    node -v
    gtm version --offline
    A Docker server version, v22.x or newer, and the gtm CLI version.
  2. Make an empty folder and start Claude 1 min

    mkdir my-gtm && cd my-gtm
    claude

    This folder becomes your agent workspace. The GTM OS instance itself goes in a subfolder later.

  3. Run gtm init inside Claude, then restart Claude 1 min

    In 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.json and docs/gtmos/.

    Quit Claude Code and start it again in the same folder (claude). Claude reads new commands only at startup.

  4. Run /setup-gtmos 20 to 40 min

    /setup-gtmos

    Claude 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.
    PhaseClaude doesYou do
    InstallRuns gtm setup instance ... --no-harness into ./instance (a few minutes; safe to re-run).Nothing.
    PasswordsTells you they are in instance/.env as GTMOS_OWNER_PASSWORD and GTMOS_APPROVER_PASSWORD. Never prints them.Give the approver password to the approver privately.
    Agent keyMints its own agent key (preset Full GTM engineer), runs gtm init --url ... --key ... and gtm doctor.Set GTMOS_AGENT_TOKEN in your environment and restart Claude (see Connect Claude Code). Then run /mcp: gtmos shows connected.
    ConnectionsStores and checks keys, links your Composio apps, sets the CRM of record and the Search Console property.Steps 5 and 6 below.
    Sending domainPrints the SPF, DKIM and DMARC records and checks them with nslookup.Add the records at your DNS host.
  5. Log in to the console as owner and as approver Needs a human

    1. Open the console URL Claude printed (default http://localhost:3000).
    2. Sign in as the owner with GTMOS_OWNER_PASSWORD.
    3. 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.
  6. Connect your tools in Integrations > Connections Needs a human

    1. Owner window: Integrations > Connections.
    2. On the Composio card click Connect Composio, sign in to Composio, accept. You come back to the console.
    3. 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.
    4. Under Add a connection, pick the provider, paste the key, add it: anthropic or gemini or openai (one required), looot (data), resend (email, if you do not send from Gmail or Microsoft 365). Then run Test on each.
    5. 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.

  7. 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
    1. 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.
    2. Claude recommends the closest blueprint, applies it in dev, then customizes each engine for you. Engines start in Dry Mode.
    3. 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".
  8. Go live: sign, promote, approve Needs a human

    Open CRM > Environments. The Go live card is at the top.

    1. Approver window: click Review & sign all proposed (N), read the diffs, confirm. (The owner cannot sign what the owner's agent proposed.)
    2. Owner window: click Promote dev → prod, check the list, confirm. This proposes one approval.
    3. 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.

  9. 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?".
  10. Run the go-live check 5 min

    /go-live-check
    One 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.

Next: everything Claude built can be changed by asking it. See Customize anything with your agent.

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.

  1. Install the instance into its own folder (owner plus approver; passwords are generated into its .env):
    gtm setup gtm-instance --org "Acme" --email you@acme.com --approver-email cofounder@acme.com
    Port busy? Add --console-port, --api-port, --mcp-port or --postgres-port. Local images only? Add --version <tag> --no-pull. The ports bind to 127.0.0.1 unless you add --bind <address> (see Self-host on a server).
  2. Sign in as the owner (it prompts for the password, or reads GTMOS_PASSWORD). Your org id is ORGANIZATION_ID in gtm-instance/.env.
    export GTMOS_API_URL=http://127.0.0.1:3001 GTMOS_ORG=<org id>
    gtm login --email you@acme.com --console-url http://localhost:3000
    Run the export GTMOS_TOKEN=... line it prints.
  3. Create the agent key (shown once):
    gtm token create --name claude-code --client claude-code --preset full-gtm-engineer --expires-days 90
  4. 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
  5. Start claude in 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

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.

RoleCan do
owner, adminEverything, including approve, reject and execute proposals. Only these roles approve.
memberRead and write records. Cannot approve or change settings.
builderDelegated 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.

  1. Settings > Members, Invite a member: email, role (member, admin or owner), Invite. Pending invitations are listed below.
  2. 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>
  3. Stop an agent everywhere: Settings > Agents, Kill. Revive it there later.
  4. 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.

At least two people with owner or admin. Without a second one, nothing your agent proposes can go live.
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.

VariableWhat it does
EMAIL_TRANSPORTsmtp, 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_PORTYour mail server. The port defaults to 587.
SMTP_SECUREtrue or false. Left empty, implicit TLS is used only on port 465 and STARTTLS otherwise.
SMTP_USER, SMTP_PASSThe login. It is used only when both are set, so an internal relay that accepts mail from a trusted network works without them.
EMAIL_FROMThe 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_URLThe 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.
  1. 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>
  2. Run gtm up again so the containers are recreated with the new values (changes to .env apply only then).
  3. Check it: gtm doctor lists 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.

This is not the daily update or open-ask reminder mail. Those use the organization's own Resend connection (see Run the system day to day), not these variables.
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 .env and run gtm up again.
  • EMAIL_TRANSPORT=smtp set but still no mail: SMTP_HOST is missing, which downgrades the transport to none.
  • Mail is rejected by the provider: the EMAIL_FROM address 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:

WhatWhere
Model keys, looot, Resend, HubSpot, Meta Ads, Cal.com, webformIntegrations > Connections, Add a connection
Your own apps (CRM, Slack, Typeform, Stripe, Ahrefs, Search Console...)Integrations > Connections, Composio card
Internal toolsIntegrations > Connections, MCP servers card
LinkedInIntegrations > Connections, Unipile card
Google / Microsoft mail, calendar, Search Console, GA4, Google AdsSettings > Connected accounts
Slack alerts and channelsSettings > 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.

  1. Integrations > Connections, Composio card, click Connect Composio (recommended: OAuth sign-in, tokens refresh on their own).
  2. Prefer a key? Click Advanced: paste a Connect key ck_... (connect.composio.dev) or a project key ak_... (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.
  3. Click Sync connected apps: apps already connected in your Composio account become ACTIVE; apps no longer connected become inactive.
  4. A new app: search the App catalog, click Connect, finish the hosted sign-in (the link lasts about 10 minutes).
  5. Several accounts for one app? Next to the app click Use an existing connection, then Use this on the right one.
  6. CRM of record section: pick HubSpot, Salesforce, Pipedrive, Attio or Zoho, then Sync now. It also syncs every 6 hours.
  7. Any other app: on the connected app click Map to ontology (see Bring data in).
"Signed in to Composio", your apps listed as 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 an ak_ 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 INITIATED or EXPIRED: 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).

  1. Integrations > Connections, Add a connection.
  2. Provider: anthropic, gemini, openai or openrouter (at least one of these four), looot, resend. Give it a label, paste the key, add it.
  3. Run Test on the new connection. A stored but untested key reads "Stored, not verified".
Required connections shows the provider as Connected. Keys are live at once, with no restart.

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/.env after 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.

  1. Settings > Connected accounts.
  2. Choose Google or Microsoft, tick the features (Read mail, Send mail, Calendar; Google also Search Console, Google Ads), connect and accept the consent screen.
  3. For Search Console, GA4 and Google Ads, click Use for org on the Google account. Those read through the org default account.
  4. Ask your agent to set the Search Console property, or give it: sc-domain:acme.com or https://www.acme.com/.
The account marked Connected and Org default. Mail and calendar sync every 10 minutes and log activity with known contacts. Sync now forces a sync.
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.

  1. Settings > Slack, connect the workspace and accept the consent.
  2. Channels: search, join, or Create a channel with the GTM OS bot in it.
  3. People matching: Match by email links members to Slack users; fix the rest with Set match.
Connected workspace with the bot user and granted scopes.

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.

  1. Sender. Best: a Gmail or Microsoft 365 account with Send mail in Settings > Connected accounts. Otherwise a resend connection (verify your domain in Resend first).
  2. 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 example v=DMARC1; p=none; rua=mailto:dmarc@<domain>). Check:
    nslookup -type=TXT acme.com
    nslookup -type=TXT _dmarc.acme.com
  3. Lane. Your agent writes the lane (definition.lane: daily cap, sending domain, sender postal address, budget). Sign and promote it (Environments).
  4. Activate the lane. Engines > Outreach, open the Outreach workbench link, Sending lanes, click Activate lane (optional DKIM selector). Or:
    gtm lane activate <laneKey> --env prod
    A different person approves and executes it in Approvals.
  5. Mailbox. Bind it when you send: pick it in Sending mailbox on the workbench or on a new sequence.
The activation result shows SPF, DKIM and DMARC checks. In prod all three must pass. New mailboxes warm up (20 a day, +20 a day, up to 200).
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.

  1. Integrations > Connections, Unipile (LinkedIn sending) card.
  2. Fill API key, DSN (your account's API host, for example api8.unipile.com:13814) and optionally the LinkedIn account id.
  3. Click Save and test.
  4. 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.
  5. 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_SECRET on the instance, and owners can reveal it there.
"Unipile connected." LinkedIn steps in a sequence send through Unipile only after a different person approves them, within your seat caps, with an accept-wait step after a connection request. LinkedIn is high risk for your account: an owner accepts that once, explicitly, before the first live use. A real Unipile account is required, and live sending is not yet verified against one in this release.

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).

  1. 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.
  2. Test it: the tool catalog loads.
  3. Needs a human Mark each tool Read or Write (approval). An unmarked tool cannot be used.
  4. Ask your agent to bind a capability to it, or map its records (see example prompt).
The server marked Connected with its tools listed.
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.

  1. Top of Integrations > Connections: Required connections. States: Connected, Stored not verified, Failed, Required: missing, Not connected, Not needed.
  2. From a terminal:
    gtm doctor --strict
Exit code 0 with --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.

  1. 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.
  2. Or from a terminal with your owner session (gtm login first):
    gtm token create --name claude-code --client claude-code --preset full-gtm-engineer --expires-days 90
  3. 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 ~/.zshrc or ~/.bashrc, then open a new terminal:

    export GTMOS_AGENT_TOKEN='<agent key>'
  4. In your workspace folder, connect it to the instance (writes .mcp.json with Bearer ${GTMOS_AGENT_TOKEN}, AGENTS.md for Codex, .claude/ and docs/gtmos/):
    gtm init --url http://127.0.0.1:3001 --key <agent key> --yes
  5. Restart the agent in that folder. In Claude Code run /mcp.
  6. 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 /mcp to 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 login and its session token.
  • gtm init warns that .mcp.json held 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.

WhatWhat it isHow you get it
Skills107 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 subagents11 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 commandsThe 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 agents16 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

PackAgentsStarts when
CRM (stdlib/pack.crm-agents)inbound responder, meeting prep, follow-up, pipeline review (deal drift), deal deska 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 triageweekdays 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 hygieneMondays 06:00 UTC; nightly 02:00 UTC and when an account is created
Growth (stdlib/pack.growth-agents)SEO and GEO watchdaily 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 researcha 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.

  1. After you upgrade the CLI, take the newest skills and subagents in each project folder:
    gtm init --refresh
    Skills and agent files you edited are kept.
  2. Ask for a role by name, for example "gtm-operator, brief me on today" or "deal-reviewer, review the deals closing this month".
  3. Watch or stop agents in Settings > Agents (kill, revive). An evaluation pass by eval-reviewer shows 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.

ScreenStepsYou do
1. WelcomeCompany, MotionCompany 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 dataConnectionsSee 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 definitionsDefinitionsUpload 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 whatRules, TeamBuying roles, consent per channel, trust rules (Dry Mode, caps, quiet hours), autonomy per engine, seats and role profiles.
5. The buildBuildCheck engine readiness, run the build, follow its live log, clear what waits for a human.
6. First motion and 90 daysPlan, LaunchShape 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.
Setup shows every step done, the build log ends with "ready", and the first-win card lights up its first milestones.
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.

  1. Run /build-gtm-system (optionally with a goal, for example /build-gtm-system B2B SaaS sales-led, outbound + SEO).
  2. 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.
  3. Claude reads the live state, recommends the closest general blueprint with a reason, and applies it in dev.
  4. 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.
  5. You approve proposals (approver window), then go live with sign and promote.
Each engine reported as verified, decisions in .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.

BlueprintWhat it sets upEnginesRequired connections
b2b-saas-sales-ledThe 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, signalsModel provider, looot, email sender, CRM. Optional: LinkedIn seats, booking calendar.
enterprise-abmA 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, signalsModel provider, looot, email sender, CRM. Optional: LinkedIn seats, booking calendar.
b2b-saas-plgSelf-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, scoreModel provider, looot, email sender. CRM optional.
devtoolsA 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, scoreModel provider, looot, email sender. CRM optional.
agency-servicesAn 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, scoreModel provider, looot, email sender. Optional: CRM, booking calendar.
b2b-marketplaceA 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, scoreModel provider, looot, email sender. CRM optional.
dtc-b2cA 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, scoreModel provider, looot, email sender, ad platform (Google Ads or Meta Ads).
outboundA 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, scoreModel provider, looot, email sender. Optional: CRM, LinkedIn seats.
seoA 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, searchSearch Console, model provider, looot.
full-gtmEvery 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, signalsModel 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

CommandWhat it does
gtm blueprint listThe 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.

  1. 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.
  2. Each open milestone says what to do next. A fact GTM OS cannot read reads "unknown", never a guess.
  3. Ask your agent "what is my next first-win step?" for the same answer.
Milestones turn done only from real events (an executed operation, a live connection, an approval), never from clicks.

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 doctor shows 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

For engine-by-engine knobs, prompts and checks, see Engine by engine in the engineer guide.

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.

WhatWhat changesWhere you check it
Scoring modeldefinition.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
RoutingTerritories, 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
Playsdefinition.play: trigger (manual, event, schedule, metric, webhook, segment), steps, guardrailsOperate > Runs
Lanes and sequencesdefinition.lane (daily cap, checks, budget, sending domain); definition.sequence (ordered steps, waits, send windows, A/B variants, exits); sequences with a lane and mailboxCRM > Sequences
Unit economicsdefinition.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 voicedefinition.brand_voice key default (tone, preferred and avoided words)Drafted messages
Workflow automationQualified-account handoff (owner, follow-up task, enrol in sequence) and signal reactions (re-score, route, enrol, alert). On by default.Settings > Workflow automation
Research agentWorkspace profile, enabled, auto-apply verified facts, daily budget, re-check days, meeting-prep lead timeCRM > Research, Settings

Simulate a play before it runs (zero writes):

gtm play simulate <playKey> --env dev --since 30d
After sign and promote, scoring an account shows your model version (definition.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)

  1. Settings > Custom fields, pick Companies, Contacts or Deals.
  2. 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.
  3. Create field. It takes effect at once, no approval, and appears on the record page.
  4. 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.list filters and for your agent.

Custom objects

  1. Check first whether a module already has it (for partners: stdlib/gtm.partner in Settings > Packages).
  2. CRM > Model, Define a custom object: label, description, fields. Validate.
  3. Needs a human Publish; a different person approves it in Approvals.
The field on the record page (not yet as a list column or filter); the object under CRM > Records.
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

  1. Connect the CRM on the Composio card, pick it under CRM of record, click Sync now.
Last sync with counts. Records land as companies, contacts and deals. A run reads up to 10,000 records per object ("Truncated" if more).

From any other app

  1. On the connected app in the Composio card, click Map to ontology.
  2. Pick a list tool, preview live sample data, map fields to a target (company, contact, deal, signal or a custom object).
  3. Propose it; a person signs it (and promotes it if prod should run it); then Sync now.
Sync counts: created, updated, unchanged, skipped, invalid, unresolved. "Unresolved" means the contact or company is not in GTM OS yet; nothing is invented.

From a CSV

  1. CRM > Import, What are you importing?: companies, contacts or deals.
  2. Choose the CSV file (or paste it). The first row is read as headers.
  3. Map your columns, then Preview: every row shows Create, Update or an error. For contacts, a company column links each contact to an account: GTM OS matches by website first, then by name.
  4. 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.
"Import complete" with a result per row. Contacts whose company matched no account, and contacts that matched more than one, are listed so you can fix them. The import never creates accounts. Yes/no and number cells are accepted in typed columns.

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

  1. CRM > Hot list: accounts ranked by score, latest signal, verdict, owner. Filter by verdict, signal, owner, activity; sort by score, latest signal or name.
  2. Click Why this score on a row for the reasons.
  3. 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.
  4. 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).
The last scan's result: signals created, spend against the cap.

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

  1. CRM > Companies, select rows, choose Enrich in the bulk actions.
  2. Read the cost preview, confirm.
Empty fields filled. Spend above the cap shows "This spend needs approval before it runs."
  • 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

  1. Engines > Score: To score, Accounts, Rescan. Click Qualify on an account.
  2. Open the account for its qualifications, or the company page for Why this score.
  3. Reps flag a score they disagree with from the lead queue (Flag this score).
  4. 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.
A verdict (Qualified, Needs review, Not qualified) with evidence. Signals fade by family, intent is weighted by who it comes from, hard disqualifiers and negative reply or bounce behavior count, and the ICP default is neutral.

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

  1. Engines > Outreach lists pending sends. Click the Outreach workbench link (top right).
  2. Pick a contact, Draft message, edit subject and body.
  3. Pick the Sending lane and the Sending mailbox (Choose a mailbox…), then Propose send.
  4. Needs a human A different person approves and executes it in Approvals.
  5. 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.
  6. 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.
  7. 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.
  8. What's working on the Outreach page shows reply and meeting rates per group once a group has at least 30 sends.
The proposed message with an approval link. A sent email cannot be un-sent. Every send is checked at propose time and again right before it runs: Dry Mode, engine mode, the claims gate, brand safety, suppression, consent, reply stop, quiet hours, caps and the lane. The first message to someone nobody has ever written to always needs a person.

Merges, identity and data quality

  1. Engines > Merges (and CRM > Data quality): quality score, field fill rate, Duplicate candidates, merges waiting for approval, recent merges with undo.
  2. 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.
  3. Uncertain identity matches wait in Operate > Needs a human.

SEO & GEO Search Console required

  1. Engines > SEO & GEO: readiness metrics and pages. Click the Page workbench link.
  2. Track a new page, Track page, then Audit page, Get recommendation, Propose change (a person approves).
  3. Site visibility: Run visibility for Search Console totals, top queries and pages (and Ahrefs metrics when Ahrefs is connected through Composio).
  4. 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.
  5. 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.
Audit findings, a recommendation, and visibility for your domain. Visits from AI assistants are credited to SEO & GEO.

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.

  1. 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.
  2. 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.
  3. Webflow reads three fields from each draft: title into name, address into slug and body into post-body. If your collection uses other field names, set the field map (webflow_field_map under the cms provider settings, with keys title, slug and body; your agent can set it, there is no form field for it).
  4. 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.
The Publish tab shows a status timeline, and the target reads Ready. Without a connected target it says Not connected and offers no publish.
  • 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_pct and refresh_age_days under cms). A refresh is a revised draft through the same flow.

Performance Ads

  1. Connect Google Ads (connected Google account with Google Ads) or a meta_ads connection.
  2. Engines > Performance Ads, then the Campaign workbench link: pacing and performance per campaign.
  3. Propose a change: new budget (increases capped at 2x) or status, Propose change. A person approves: this is real money.
  4. 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.
MER, aMER, ACoS and CAC payback tiles, and a table of what each platform reports against your real numbers. A number that cannot be computed (for example untagged campaigns) shows a dash and the reason.

Routing

  1. Engines > Routing: territories (criteria, stickiness, overflow), manual assignment and the assignment history with the reason for each decision.
  2. Team capacity: caps per type, weights and out-of-office per person. A full owner's records are spread over the eligible pool.
  3. 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

  1. 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.
  2. 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

  1. 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.
Visits from AI assistants (Copilot, Claude and others) are credited to SEO & GEO. Ad click-id values are opt-in, never shown to agents, removed after 90 days and blocked by Global Privacy Control.

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.
  1. Settings > Search index: records indexed and with vectors per type, the embedding model. Reindex now (owner or admin).
"Reindex queued." Vectors need an OpenAI or Gemini key.

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.

  1. 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.
  2. 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.
  3. 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.
  4. Generate now rebuilds today's card from live data, replaces the stored one and delivers it to everyone who opted in.
A card dated today in the history, with a number against each section. A card that has not been generated yet reads "Live preview".

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.

  1. Read the strip at the top, including Waiting on you: open asks you own, directly or through your role.
  2. 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.
  3. Click an ask to open it. You can answer, resolve, drop, reassign or Remind now.
  4. 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.
  5. 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 ask shows its number and a status. Nobody gets more than one batched reminder a day, and an overdue ask escalates once.

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.

  1. 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.
  2. 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.
  3. 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.
  4. 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.
A batch reads Pulled back, or Partly pulled back when some actions could not be reversed.

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.

  1. Owners and admins: Run audit, Check parity and Sync now.
  2. 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.
Every source reads as healthy, or the flagged object names the reason.

Approvals Needs a human

Goal: nothing customer-facing or live happens without a second person.

  1. Approvals (top of the sidebar) or Operate > Needs a human. Open a proposal: The change shows exactly what will run.
  2. Approver: Approve or Reject. Then Execute runs it.
  3. Veto your agent's proposal: as the person the agent acts for, open it and click Withdraw (veto my agent's proposal).
  4. Pause everything customer-facing: Settings > Safety, Enable dry mode. Effects become drafts; a banner shows on every page. More in Dry Mode and guardrails.
WhoCan approve?Can execute?
The proposerNoNo
The person whose agent proposed itNo (the button shows; the server refuses)No
Another owner or adminYesYes
Any agentNeverNever
The approval timeline: Proposed, Approved, Executed (or Rejected, Withdrawn, Expired). Proposals expire after 7 days. A decided approval shows its final state instead of Approve and Reject buttons, and the Focus Queue lists only approvals still waiting on a person. When the content makes factual claims, the approval shows a claims panel (see Proof library and claims).
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.

  1. 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).
  2. 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.
  3. 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.
  4. 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.
Every engine page shows a live or dry dot, and the Actions page lists each draft and execution with its gate. Sends stay drafts, with no approval, while Dry Mode applies.

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.

  1. Settings > Proof library, add proof: case studies, metrics, testimonials and logos. Give each a permission (public, anonymized or internal only) and an optional expiry.
  2. 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.
  3. A second person activates each proof (four-eyes). Customer phrases mined from your calls, replies and won deals are approved one by one.
  4. 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.
Every factual claim in outbound content (statistics, results, named customers, outcomes with no number) reads Backed, Close (similar wording, never treated as backed) or No proof, when an action is proposed and again right before it runs. It covers outreach on every channel, sequence steps, SEO publishes and meeting invites.
Common errors
  • POLICY_DENIED blocked_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.

  1. The environment switcher in the console header picks which environment you view (default prod).
  2. 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.
  3. Same from a terminal:
    gtm definition sign-all --env dev
    gtm promote --all dev prod --preview
    gtm promote --all dev prod
  4. 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
"Everything is live." on the Go live card; no "N definitions are in dev, not live" banner. A change applied in 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.

  1. Settings > Packages. Tabs: Installed and Available.
  2. Install: Available, Install (a proposal).
  3. Enable or disable per environment with the toggle on the installed package (a proposal).
  4. Upgrade: Preview upgrade vX -> vY (changes, breaking or not), then Propose upgrade to vY.
  5. Remove: Uninstall, then Propose uninstall (your data is never deleted).
  6. Needs a human Another admin approves each in Approvals.

CLI equivalents:

gtm pkg enable <name> --env <env>
gtm pkg upgrade <name> --dry-run
The engine packages installed and enabled in every environment. gtm 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.

Changed in 0.2.5. The console (3000), API (3001) and MCP (3004) ports, like Postgres, bind to 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_ADDRESSMeaning
127.0.0.1 (default)Reachable from this machine only. Use it with a reverse proxy on the same machine.
0.0.0.0Reachable on every network interface. Only behind a firewall that allows exactly the ports you intend.
A private IP such as 10.0.0.5Reachable on that interface only, for a proxy on another host in the same private network.

Put a reverse proxy in front (recommended)

  1. 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.com at 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.
  2. Keep GTMOS_BIND_ADDRESS=127.0.0.1, and set the public URLs in .env (the origins a browser really uses, with https:// and no trailing slash), then run gtm up again:
    PUBLIC_API_URL=https://api.acme.com
    PUBLIC_CONSOLE_URL=https://app.acme.com
    PUBLIC_MCP_URL=https://mcp.acme.com
  3. Proxy each host to its local port. With Caddy (it gets and renews the HTTPS certificates) the whole Caddyfile is:
    app.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
    }
    nginx works the same way: one server block per host with proxy_pass to its local port, the Host and X-Forwarded-Proto headers, and the Upgrade headers for the console.
  4. Check it:
    curl https://api.acme.com/health
    curl -I https://app.acme.com
    ss -tlnp | grep -E ':(3000|3001|3004|5432)\b'
The health call answers 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 .env do not match what the browser uses (for example http:// in .env and https:// 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.

SettingWhat it does
GTMOS_BIND_ADDRESSWhich network address the console, API and MCP ports bind to. Default 127.0.0.1. See Self-host on a server.
GTMOS_LIVEMust 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_PROVIDERSWhich 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-editOff 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_FROMHow 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 recordsA 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 keysFollow 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 keyGTMOS_MASTER_KEY seals every stored credential. Backups do not contain it. Keep it somewhere safe, apart from the dump.
org.offboardNeeds a real approval by a second owner or admin, then a run by someone other than the proposer.
Credentials are encrypted at rest and never shown again after you save them. Every action is written to a tamper-evident audit log (Settings > Audit log).

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.

  1. 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 upgrade stops and tells you which version to install.
    npm i -g @gtmos/cli@latest
  2. See what is available (from the instance folder, or add --dir <path>):
    gtm upgrade --check
  3. Rehearse. It prints every command and changes nothing:
    gtm upgrade --channel stable --dry-run
  4. Upgrade. --yes is required because it migrates your live database:
    gtm upgrade --channel stable --yes
  5. In Claude Code run /mcp to reconnect, then refresh your project files in each project folder:
    gtm init --refresh
A plain 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

  1. 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.
  2. Preflight: one update at a time, and free disk at least twice the database size plus 2 GB.
  3. The target images are pulled, and each digest must match the signed digest.
  4. The additive-only guard reads every pending migration and refuses anything that drops, renames, retypes, deletes or overwrites.
  5. The API, worker, MCP and console stop gracefully. A backup is taken and read back to verify it.
  6. 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.
  7. 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 --refresh in 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 upgrade plans it as an extra hop and takes a backup at each hop.

Check it worked

  1. gtm doctor shows the sources you expect as live, and its Harness section has no stale line.
  2. GET /health reports the new version on the API, worker, MCP server and console.
  3. The upgrade reported that verification passed, and a snapshot exists in your backups folder.
  4. Row counts of your main tables match what you noted before.
  5. A rescan preview looks reasonable before the first rescan.
  6. Dry Mode send check: propose one send to an internal address and confirm that a different person has to approve it.
  7. 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 doctor and GET /health to 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.dump

Keep 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:

  1. 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
  2. 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
  3. Put .env and docker-compose.yml back from their .pre-upgrade-<stamp> copies, then docker compose --env-file .env -f docker-compose.yml up -d. The renamed gtmos_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 --check

Use 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 sql a 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 --env and GTMOS_API_URL set, 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-run prints 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 is https://ay-automate.github.io/gtmos-releases/releases.json, and GTMOS_RELEASE_MANIFEST_URL overrides 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-automate unless 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 --check fetches 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 in releases.json for 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 seeFix
"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 upgradeRun /mcp to reconnect.
A port is already in use during setupRe-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.v1Sign and promote your definition.scoring_model default.
"This is a Composio user API key (uak_...)"Use Connect Composio.
AUTH_EXPIRED Composio sign-in expiredClick Reconnect Composio.
lane_requiredPick a sending lane; activate it first.
mailbox_requiredConnect Gmail or Microsoft 365 with Send mail; pick the mailbox.
Sends stay drafts, no approval appearsDry Mode is on: Settings > Safety.
NOT_CONFIGURED "Google Search Console is required for SEO & GEO"; gtm doctor ERROR connector.gscConnect 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 dataSpend is above the per-call cap; approve it, or price smaller batches.
402 insufficient_balanceTop 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 arrivedDemo mode uses a fixture sender. Run live with a real sender.
Readiness says FIXTUREDemo 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 upgradeThe 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_LIVESet GTMOS_LIVE to true or false in .env and run gtm up.
gtm upgrade says the CLI is too oldnpm i -g @gtmos/cli@latest, then run it again.
gtm upgrade did not move my instanceA plain gtm upgrade never moves a pinned install. Use --channel stable or --to <version>.
POLICY_DENIED blocked_unbacked_claimThe 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 editAn owner or admin turns on CRM settings > Members can import CSVs and bulk-edit records.
CSV import lists contacts with no company matchFix the company column (website or exact name) and import those rows again. The import never creates accounts.
gtm doctor reports a stale harnessRun 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 arrivesEmail 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 arrivesThe 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 stuckClose heavy apps, restart Docker Desktop. Never delete volumes of a stack you need.
Flexibility

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.

AreaYour agent can change
EnginesScoring models and thresholds, ICP fit, signal weights, plays and triggers, lanes, sequences, brand voice, workflow flags, research settings, model routing
Data modelCustom fields, custom objects and relationships, identity rules
DataImports, CRM sync, any-app source mappings, enrichment runs, de-duplication, record fixes (as proposals)
IntegrationsConnections and keys you give it, Composio apps already authorized, MCP servers, capability bindings
Blueprints and packagesApply the closest blueprint, customize it, propose package installs and upgrades
ScreensDeclarative 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.partner module 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_model key default in 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.
Reference

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

CommandWhat it doesKey flags
gtm upWrites 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 bootstrapCreates the first operator and organization on a self-hosted instance. Idempotent.--email, --password, --org, --slug, --name, --dir
gtm onboardCreates 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 addAdds 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 secretsGenerates 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 loginSigns in and prints the GTMOS_TOKEN export line.--email, --password, --console-url
gtm token create | list | revokeAgent 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 doctorOne 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

CommandWhat it doesKey flags
gtm planPreviews what a change would do against your live configuration.--env, --org, --from <blueprint>, --repo, --json
gtm applyProposes 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 checkFor CI: plans across environments and exits non-zero on issues or drift.--envs a,b, --org, --from, --repo
gtm mirror exportWrites the current configuration onto the optional git mirror.--repo, --env, --org
gtm definition propose-for-signing | sign | sign-allMoves 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 promoteProposes promoting one resource (<kind> <key> <from> <to>), or with --all <from> <to> every signed definition under one approval.--all, --preview
gtm rollbackProposes rolling one resource back to an earlier version.<kind> <key> --to-version <n>, --env
gtm blueprint list | show | upstream | upgradeBrowse 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 | inspectdiscover 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 | upgradeOffline 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 | uninstallProposes the lifecycle change through the approval gate (needs your instance).--org, --env, --purge

Watch and diagnose

CommandWhat it doesKey flags
gtm logsLists durable flow runs, newest first.--status, --flow, --limit, --json
gtm trace <runId>Shows one run with its step journal.--json
gtm describePrints the operation catalog. Needs no login.--class Q|D|T|E|M
gtm hooks listShows the registered guards, transforms, listeners and lifecycle hooks.
gtm profileProfiles one object type: fill rate, cardinality and a drift baseline. Reads the database.--object-type, --org
gtm codegenRegenerates 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-dispatchSeeds the first tick of a self-rescheduling background job for one organization.--org

Run and upgrade the instance

CommandWhat it doesKey flags
gtm upgradeUpgrades 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 versionThe CLI and instance versions and what the release list offers. Same as gtm upgrade --check.--dir
gtm updater install | uninstall | status | runThe updater service behind Settings > Updates. run runs its loop in this terminal.--dir, --once
gtm org exportExports 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 | destroyDeploys 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 | serveThe 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.

Reference

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 and custom.* 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 init writes 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_CONFIGURED instead. 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

IntegrationWhere it stands in 0.2.5
HubSpot, Google Ads, Meta Ads, Google Search Console, GA4, Resend, loootNative 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.
SalesforceThe native adapter is fixture-first: it is tested against recorded Salesforce responses, not against a real organization. Use the Composio path.
LinkedIn Ads, TikTok AdsRead-only through Composio (campaign performance reads). No changes are made from GTM OS.
WebflowPublishing through Composio, tested with simulated Composio responses and not against a real site.
WordPress.comDrafts only. It never publishes.
Self-hosted WordPressOnly through the signed webhook and a receiver you run.
LinkedIn through UnipileHigh risk, off until an owner or admin accepts it, tested against recorded Unipile responses and not yet verified live.
Gmail and Google CalendarPolled. The wire formats are not yet verified against a live account.
Microsoft TeamsA 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.