API Keys & MCP
Issue a Bearer token, paste it into Claude Cowork or any MCP-compatible agent, and your brain becomes scriptable. Six tools cover the verbs that matter: log a capture, update a prospect, append to a plan, mark a meeting outcome, read an account, pull a brief.
Overview
The API keys surface turns salesascode into a tenant-scoped MCP server. Issue a key at /settings/api-keys, paste it into Claude Cowork (or any MCP client), and the agent gets six tools it can call against your account — every write hits the same panel_data store the in-app panels use, so external and in-app activity stay in sync.
Different from Connect to Cowork — that's your control-plane home (plan, entitlements, install steps). API keys are the credential the plugins actually authenticate with.
How it helps you
- Claude Cowork integration. Do work in Cowork, have it update your salesascode account directly. No copy-paste, no manual sync.
- Scriptable brain. Cron a script that pulls your daily brief, posts it to Slack, and logs your "what I'm working on" capture back into the brain.
- Honest auth model. Keys are scoped per-action, hashed at rest, shown once on creation, revocable at any time. Audit log records every call.
- MCP-native. No custom SDK to ship. Any MCP-compatible client (Cowork, Claude Desktop with HTTP MCP, custom Claude agents) works out of the box.
How to use it
- Open /settings/api-keys → click + New key.
- Name it (e.g. "Claude Cowork"), pick the scopes the agent needs.
- Copy the key — it's shown once. Paste it into the agent's credential field.
- Point the agent at
https://<your-domain>/api/v1/mcpas the MCP server URL. - The agent calls
tools/listto introspect, thentools/callto invoke. Every call lands in your audit log.
The six tools
log_capture
Drop a free-form note or transcript into the brain. Optionally attach to an account or prospect.
update_prospect
Create or update a prospect (name, title, email, notes, arbitrary fields).
add_to_plan
Append content to a named section of an account plan ("next_steps", "risks", "champion_notes").
mark_meeting_outcome
Record the outcome of a meeting ("advance" / "stall" / "lost" or free-form) with notes + next step.
read_account
Read an account record. Optionally pull in the plan, recent meetings, or recent captures.
pull_brief
Structured "today / week" summary — accounts active, recent captures, recent meetings, open plan sections.
POST /api/ingest/usage
Push product-usage / consumption data for an account by domain (seats used, active users, contract terms, usage signals). A vendor-neutral REST endpoint — point your warehouse job or ETL at it and the pre-call brief renders consumption-vs-contract, each line cited to the ingested telemetry.
set_meddpicc
Put MEDDPICC evidence on an opportunity by id — any of the eight dimensions as short text. Provided dimensions merge onto the existing record; others are left untouched. This is how a deal earns the evidence the curiosity engine reads to find the gaps worth asking about, without a CRM.
Control-plane tools (Sales as Code plugins)
The Sales as Code Cowork plugins call five more tools against this same MCP endpoint. They are the bridge between the work surface (Cowork) and the control plane (salesascode.com): every skill verifies the licensed session first, then reports run telemetry and brain state when it finishes. These tools need no extra scope — a valid key acting on its own account is enough.
verify_session
Returns the account, plan, and entitled plugins. Every skill calls this first and stops if it errors.
check_entitlement
Is this account licensed for a named plugin? Skills stop and ask the user to connect their account if not.
emit_telemetry
Record a run event — plugin, skill, counts, outcome, duration. Metadata only. Powers the dashboard rollup.
sync_brain
Push brain state for a scope (signal-weights, icp-registry, decision-log, funnel-brain, ...). No raw contact PII by default.
pull_config
Pull the account's plan, entitled plugins, and feature flags so a skill can adapt at run start.
These back the per-account rollup dashboard. The plugins and the full contract live in cowork-marketplace/ (see docs/control-plane-mcp.md).
Entitlement is now enforced per tool. Any tool that reads or writes your data or brain — the six data verbs plus emit_telemetry, sync_brain, start_run, run_heartbeat, connect_integration, and the studio tools — requires the account to hold at least one active paid entitlement (a plugin, bundle, suite, membership, or team seat). Without one, the server returns not_entitled (MCP error -32003 / HTTP 403) with a pointer to /pricing. The onboarding trio stays open on purpose: verify_session, check_entitlement, and pull_config are always callable so a new user can connect a plugin and check status before buying. Entitlement is additive to the scope check — a call must clear both.
Security model
- Keys shown once. Only the SHA-256 hash + a short prefix are persisted. We could not show you the raw key again if we wanted to.
- Scoped per key. A key with only
capture.writecannot read accounts, append to plans, or read briefs — even if the agent tries. - Entitlement-gated. Data & brain tools also require an active paid entitlement, checked on every call server-side — not just at the storefront. A valid key on a non-paying account can still onboard (
verify_session/check_entitlement/pull_config) but is deniednot_entitledon anything that touches tenant data or the brain. - Audit log. Every tool call writes a row to
api_key_calls: which key, which tool, ok/error/denied, duration, request/response size. Visible to the issuing user. - Revoke at any time. Click Revoke; the next call from that key returns 401 immediately. No CDN cache, no propagation delay.
- Tenant-scoped. A key issued by user A can never touch user B's data. The auth middleware looks up
user_idfrom the key, all subsequent queries are scoped to that user.
Use cases
Claude Cowork integration
Have Cowork log captures and update prospects as you work — your salesascode brain stays current without context-switching.
Cron-driven workflows
A nightly script pulls your brief, summarizes it, posts to Slack. Or a meeting bot logs the outcome into your account plan automatically.
Build your own agent
Wire any Claude API loop to the MCP server. Tools/list discovers everything; you don't ship custom integrations.
Team-wide workflows
An ops team can issue per-process keys ("post-call writer", "weekly forecaster") with the minimum scopes each needs.
Transports: HTTP, REST, and stdio
HTTP is the canonical MCP transport — point any HTTP MCP client at /api/v1/mcp. Every tool is also a plain REST endpoint (below). For clients that only speak stdio (Claude Desktop, some Cowork setups), the repo ships a thin local bridge that forwards stdio JSON-RPC to that same HTTP endpoint:
- Issue a key here and set it as
SALES_AS_CODE_API_KEY. - Run
npm run mcp:stdio, or point Claude Desktop'sclaude_desktop_config.jsonatnode scripts/mcp-stdio.mjswith the key in the server'senvblock. - The bridge relays each message to
/api/v1/mcp— same tools, same scopes, same audit log. It holds no logic of its own.
Full config example: /docs/api → stdio.
Team keys
By default a key is per-user — it authorizes against your own plan. A team owner can toggle Issue as a team key in the New key dialog to mint a team key instead. A team key still writes into the owner's workspace, but the control-plane tools (verify_session, check_entitlement, pull_config) resolve entitlements against the team's plan: the union of the owner's grants and every plugin the team's seat packs license. Per-user keys are unchanged and keep working exactly as before.
- Owner-only. The server rejects a team-key request from anyone who isn't the team's owner — it never silently downgrades to a personal key.
- Membership-bound. If the owner leaves the team, the team key stops authorizing (the next call returns
team_membership_revoked). - Introspectable.
verify_sessionreturnsscope: "team"and theteamid so a skill knows which plan it's running under.
OAuth-provider mode (optional)
Pasting a key is the default. When a deployment enables it (MCP_OAUTH_ENABLED=true), a client can instead obtain access through a standard OAuth 2.0 authorization-code + PKCE flow — you approve it on a consent screen using your existing login, and the client never sees a key you had to copy. The clever part: the OAuth access token is a managed key. It's minted as a scoped api_keys row, shows up in this list as OAuth (<client>), and you revoke it here like any other.
- Discovery.
/.well-known/oauth-authorization-server+/.well-known/oauth-protected-resourceadvertise the flow (S256 PKCE). - Consent via your session. The authorize screen shows the requested scopes and returns you to the client on approval.
- Honest scope. PKCE required; no dynamic client registration and no refresh tokens (revoke the minted key here instead). Codes are single-use, 10-minute lifetime. Full request/response shapes: /docs/api → OAuth-provider mode.
The HTTP REST surface
The same tools are also available as plain REST endpoints if you prefer not to speak MCP. POST JSON to /api/v1/tools/<name> with a Bearer auth header. Same Bearer key, same scopes, same audit log.
curl -X POST https://salesascode.com/api/v1/tools/log_capture \
-H 'authorization: Bearer sak_…' \
-H 'content-type: application/json' \
-d '{"source":"cowork","text":"Acme champion confirmed budget for Q3"}' Related
- Connect to Cowork — where you paste this key and install the plugins.
- Cowork Rollup — the dashboard the
emit_telemetry/sync_braincalls power. - Content Studio — approve drafts the
studio_*tools surface. - GTM Context Dossier — the onboarding scan that seeds your brain state.