<!-- Markdown rendering of https://salesascode.com/docs/library/api-keys. The HTML page is canonical. -->

# Advanced: API Keys & MCP · Docs · Sales as Code

[← All feature docs](https://salesascode.com/docs/library)

Feature docs · /settings/api-keys

# Advanced: API Keys & MCP

The power-user path. The default product needs no key: connect your tools and it runs on a schedule. If you want to drive your account from your own scripts or an MCP-compatible agent, issue a Bearer token 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.

[Open Advanced: API Keys & MCP →](https://salesascode.com/settings/api-keys) [All docs](https://salesascode.com/docs/library)

## Overview

This is an advanced option under **Settings, Advanced**, not the main flow. Most people never need it: connect your CRM, inbox, and calendar on [Connect](https://salesascode.com/settings/connect) and the work runs on a schedule. If you want to script your account, the API keys surface turns it into a tenant-scoped MCP server. Issue a key at [/settings/api-keys](https://salesascode.com/settings/api-keys), point any MCP client at your account, and `tools/list` returns 27 tools. Every write hits the same `panel_data` store the in-app surfaces use, so scripted and in-product activity stay in sync. The cards below cover the ones you reach for first; ask your client for the full list rather than trusting this page to stay current.

Different from [Connect your tools](https://salesascode.com/docs/library/connect), that page links your CRM, inbox, and calendar for the scheduled runs. API keys are the credential for the advanced, self-driven path.

## How it helps you

- **Script your own agent.** Do work in your own agent or client and have it update your 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 works out of the box over HTTP.

## How to use it

- Open [/settings/api-keys](https://salesascode.com/settings/api-keys), click **+ New key**.
- Name it (e.g. "nightly brief"), 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/mcp` as the MCP server URL.
- The agent calls `tools/list` to introspect, then `tools/call` to invoke. Every call lands in your audit log.

## The six tools

// capture.write

#### log_capture

Drop a free-form note or transcript into the brain. Optionally attach to an account or prospect.

// prospect.write

#### update_prospect

Create or update a prospect (name, title, email, notes, arbitrary fields).

// plan.write

#### add_to_plan

Append content to a named section of an account plan ("next_steps", "risks", "champion_notes").

// meeting.write

#### mark_meeting_outcome

Record the outcome of a meeting ("advance" / "stall" / "lost" or free-form) with notes + next step.

// account.read

#### read_account

Read an account record. Optionally pull in the plan, recent meetings, or recent captures.

// brief.read

#### pull_brief

Structured "today / week" summary, accounts active, recent captures, recent meetings, open plan sections.

// usage.write

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

// meddpicc.write

#### 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

Five more tools cover the control plane: check the session, check a licence, report run telemetry, push brain state, pull config. They need no extra scope, a valid key acting on its own account is enough.

Your own client is what calls these. The scheduled runs do not: they run inside our servers and write the same tables directly, so nothing in your rollup depends on an agent remembering to report. The descriptions below say "every skill" because they are written for a skill you drive over MCP.

// session

#### verify_session

Returns the account, plan, and entitled products. Every skill calls this first and stops if it errors.

// license

#### check_entitlement

Is this account licensed for a named product? Skills stop and ask the user to connect their account if not.

// telemetry

#### emit_telemetry

Record a run event, product, skill, counts, outcome, duration. Metadata only. Powers the Activity rollup.

// brain

#### sync_brain

Push brain state for a scope (signal-weights, icp-registry, decision-log, funnel-brain, ...).

// config

#### pull_config

Pull the account's plan, entitled products, and feature flags so a skill can adapt at run start.

These back the per-account Activity rollup. The full contract, every tool with its arguments and scopes, is on [API & MCP](https://salesascode.com/docs/api).

**Entitlement is 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 product, bundle, suite, membership, or team seat). Without one, the server returns `not_entitled` (MCP error `-32003` / HTTP `403`) with a pointer to [/pricing](https://salesascode.com/pricing). The onboarding trio stays open on purpose: `verify_session`, `check_entitlement`, and `pull_config` are always callable so a new user can connect 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.write` cannot 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 denied `not_entitled` on 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_id` from the key, all subsequent queries are scoped to that user.

## Use cases

// your agent

#### Wire your own client

Have your agent log captures and update prospects as you work, your brain stays current without context-switching.

// scripts

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

// custom agents

#### Build your own agent

Wire any agent loop to the MCP server. Tools/list discovers everything; you don't ship custom integrations.

// team automation

#### 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**, 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 your stdio client's config at `node scripts/mcp-stdio.mjs` with the key in the server's `env` block.
- 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](https://salesascode.com/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 product 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_session` returns `scope: "team"` and the `team` id so a skill knows which plan it's running under.

## OAuth-provider mode (optional)

Pasting a key is the default for this advanced path. 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-resource` advertise 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](https://salesascode.com/docs/api#oauth).

## 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":"script","text":"Acme champion confirmed budget for Q3"}'
```

## Related

- [Connect your tools](https://salesascode.com/docs/library/connect), the default flow, link your CRM, inbox, and calendar for the scheduled runs.
- [Activity](https://salesascode.com/docs/library/rollup), the rollup the `emit_telemetry` / `sync_brain` calls power.
- [Content Studio](https://salesascode.com/docs/library/studio), approve drafts the `studio_*` tools surface.
- [GTM Context Dossier](https://salesascode.com/docs/library/gtm-context-dossier), the onboarding scan that seeds your brain state.

// deep walkthrough · 2 min

---

More for agents: [llms.txt](https://salesascode.com/llms.txt), [full reference](https://salesascode.com/llms-full.txt), [OpenAPI](https://salesascode.com/openapi.json), [sitemap](https://salesascode.com/sitemap.xml).
