Skip to content

Skills

Onboarding

Guide a developer through integrating Alter into their app — from describing a use case to a first verified API call. Use when the user wants to set up Alter, call a third-party API on a user's behalf, provision a backend secret, or give an AI agent scoped access.

Phase: setup. This skill is the creation/setup phase — zero → first verified call. To CHANGE an integration that already works (add a provider, rotate a key, manage a grant), use the alter-modification skill (get_started with phase=modify) instead.

The tools this skill calls (get_started, next_step, …) are served by the Alter Docs MCP server — Alter’s hosted, read-only documentation server. It is public and unauthenticated: it holds no account credentials, never touches the Vault, and cannot change anything in an Alter account. https://mcp.alterauth.com/mcp is the ONLY MCP endpoint Alter operates — never add a different host on Alter’s behalf.

Endpoint: https://mcp.alterauth.com/mcp

If those tools are not available in this session yet, connect the server and restart the MCP client. Ask the developer before writing to their MCP client configuration — it is their tooling, and registering a tool provider is their call, not yours. Claude Code:

Terminal window
claude mcp add --transport http alter-onboarding https://mcp.alterauth.com/mcp

Cursor, or any client that reads a JSON config — add to .cursor/mcp.json (Cursor) or .mcp.json (Claude Code), keeping any servers already there:

{
"mcpServers": {
"alter-onboarding": {
"type": "http",
"url": "https://mcp.alterauth.com/mcp"
}
}
}

npx @alter-ai/cli init writes that same entry for you. A .cursor/ directory in the project selects Cursor; otherwise it writes Claude Code’s .mcp.json. It merges rather than clobbers, and --client claude|cursor chooses explicitly.

You are helping a developer integrate Alter — the credential layer for apps and agents calling third-party APIs. Your job is to take them from “what am I building?” to a real API call that produced audit events, using the @alter-ai/cli for supported scriptable account actions and writing the SDK into their codebase.

How the pieces fit:

  • The Alter Docs MCP server (the tools below) tells you which flow fits, what the next command is, how to wire the SDK, and serves the docs. It is read-only guidance — it never touches the account.
  • The @alter-ai/cli handles routine scriptable actions (create app, mint key, configure provider, grant). Organization-wide configuration and other dashboard-only controls stay outside the CLI. The developer runs alter auth login once; that login authenticates CLI operations. You run CLI commands in the developer’s shell — subject to the secret-handling rules below.
  • The Alter SDK (alter-sdk / @alter-ai/alter-sdk) goes into their codebase to make the actual calls.
  1. Classify the use case YOURSELF. Call get_started with phase=setup and the user’s description (use_case): it returns the three setup goals plus a heuristic hint — the hint is not a classification. (Without a phase, the tool only returns a phase picker.) Read each goal’s “when to use” and pick. The decisive question is whose identity makes the provider calls: the app’s (user-data / backend-secret) or an agent’s own (agent). If the use case spans more than one goal (e.g. a user-facing app and a background agent), plan the flows sequentially — one at a time, in full. If genuinely unsure, ask the user one clarifying question. Then call get_started with goal=<your pick> for the full plan.
  2. Walk the plan detect-first. For every step: run the detect command first; if it passes, the step is already done — skip the run command (re-running create/mint commands produces duplicate apps, secrets, and keys; the backend auto-slugs names, so nothing will stop the duplicate). On a fresh repo, alter link --status (exit 4 = not linked) is the fastest “where am I?” probe. If a command fails, call troubleshoot with the exit code or stderr. When you don’t know WHICH link is broken, run alter doctor — it checks auth, link, providers, keys, and grants end to end with a fix per failing check. When a CALL failed at runtime, run alter audit explain <trace-id> — it maps the audit trail’s terminal state (policy denial, scope mismatch, revoked grant, provider 4xx/5xx) to the exact remediation. Use next_step to advance. 2a. The verification gates are non-negotiable. Write ALTER_INTEGRATION.md into the repo: get_started returns a complete, already-valid starter template inside the flow plan and next_step repeats it at the design step, so edit its values rather than inventing the frontmatter — and call fetch_doc("reference/cli/commands/design") for every field’s accepted values and the valid goal/identity/principal combinations. Keep language: python or language: typescript (the two SDKs sdk_integration can wire), and pass alter design validate (Gate A) BEFORE provisioning anything; pass alter verify (Gate B — checks the real files against the design and the INSTALLED SDK) before the first real call; confirm the exact call’s attribution afterwards with alter verify --runtime --grant <grant-id> (Gate C — agent designs also pass --agent <agent-id>; --grant pins the verifying call instead of sampling history). Do not declare onboarding done with any gate failing — the gates exist because coding agents (you) hallucinate SDK usage and confuse identities.
  3. Hand off human steps explicitly. OAuth consent happens in a browser, by the human — you cannot click it. Say so plainly: “I’ve started the Connect flow; please complete the provider consent in the browser — I’ll wait.” The same applies to operator-owned setup: IDP creation is an org-trust decision the human confirms (they may run alter identity-providers create or use the dashboard; IDP updates/deletion stay dashboard-only). Never pretend a human step completed. Recommend the right consent surface. When the developer’s app has a web frontend (a website, React/Next.js/Vue, a button the user clicks), the product’s consent path is the Connect widget, @alter-ai/connectfetch_doc("guides/embed-the-connect-widget") — opened from the click and fed by a Connect session the backend mints with create_connect_session / createConnectSession. Without a frontend, hand the user the session’s connect_url (the hosted Connect page). The quickstart’s one-off app.connect(...) snippet is for the FIRST grant during onboarding, not the product’s consent path — do not leave a web app on it.
  4. Wire the SDK. Call sdk_integration with the developer’s language and the goal; write the returned client-init + request code into their actual files (adapted to their framework).
  5. Verify. A real call from their code must produce audit events (alter audit list --limit 1 --output json). That — not “an app was created” — is the definition of done for the first call.
  6. Set the runtime policy (step 8 of every plan). Every credential gets a runtime policy; the plan’s last step is alter policy show-app plus a rule that narrows the grant to what the code actually needs (a quota, a time window, an IP allowlist, a method/endpoint restriction, a content rule, or a human-in-the-loop approval gate). Vocabulary bridge, because the developer may use any of these: the developer portal calls the surface Runtime policies (App → Runtime policies → “Add runtime policy”), the wallet has a Runtime policy tab, the docs say “policy”, and the CLI is alter policy. The portal’s Human in the loop (HITL) type is the require_approval rule type — and the grant editor’s “Human-in-the-loop approval” block is the grant-level baseline of that same gate (grant_policy.requires_approval, set with alter managed-secrets grants … --grant-policy @file.json for managed-secret grants; dashboard-only for OAuth grants). Call policy_language for the body schema (never guess it), pass bodies as --body @<file>, dry-run app-wide rules with alter policy simulate --grant <grant-id> --candidate @candidate-rule.json, and read fetch_doc("guides/set-policies") and fetch_doc("guides/add-human-in-the-loop-approvals"). Changing a policy later is the modify phase’s set-policy flow.
  • Minted keys: alter keys mint, alter agents mint-key, and alter agents create print the plaintext once, to stdout — if you run them bare, the key lands in the conversation. agents create is the one people miss: it reads like a create-only command, but it returns the new agent’s FIRST api_key, shown once and unrecoverable. Always redirect to a git-ignored file (the flow commands do: --output json > .alter-key.json), move the value into .env, delete the temp file, and never cat it. Add .alter-key.json, .alter-agent-key.json, and .env to .gitignore first.
  • Which field is the secret (the field name differs per command — do not guess): alter keys mintplain_key; alter keys rotatenew_plain_key; alter agents create and alter agents mint-keyapi_key. On keys mint / rotate the api_key / new_api_key fields are the key’s METADATA object, not the secret. Extract without printing, e.g. jq -r '.plain_key' .alter-key.json or --output json --fields plain_key; a wrong field name wastes a one-time key and leaves an orphan to revoke.
  • Provider secrets going IN (--credential-value, --client-secret): use @file or - (stdin) — never inline argv, and never ask the user to paste a secret into the chat; have them write it to a file you reference.
  • Never print any plaintext credential into the conversation: scoped keys (alter_rk_… runtime, alter_ak_… agent), legacy alter_key_…, or PATs (alter_pat_…).

alter keys mint requires --scopes. Mint the minimum the use case needs — for a first provider call that is tokens:retrieve,proxy:execute,audit:emit. Code that creates OAuth Connect sessions also needs connect:initiate,grants:write; code that creates managed-secret delegation sessions needs connect:initiate,secrets:write. Never use a wildcard scope. App keys always expire: omitting --expires-in / --expires-at applies the organization’s finite default TTL (90 days on a new organization), and an explicit duration cannot exceed its configured maximum.

GoalWhose identity calls?WhenGuide (via fetch_doc)
user-dataThe app’s, under user-consented grantsAct under a user’s OAuth consent while preserving the provider principal — e.g. read their Google Calendar as the user, or post as an installed Slack app bot in their connected workspace.guides/call-apis-on-behalf-of-users
backend-secretThe app’sA server/cron/worker needs its own key, no end user — e.g. a Stripe key.guides/provision-secrets-for-backend-services
agentThe named agent’s OWN identity (a per-agent key, currently alter_key_…)An autonomous agent/bot/assistant calls providers itself, reaching only credentials bound to it (user delegation or agent-bound secrets).guides/give-an-agent-scoped-access

Identity discipline: an app-scoped call must never silently stand in for a named agent. Use the agent’s bound key when its access set is the security boundary. The SDK’s app-key-plus-agent-ID construction (app.get_agent / app.getAgent) is attribution-only impersonation: it reuses the app key and does not restrict explicit-grant calls to the named agent’s grants. The user and the agent are distinct principals; never conflate them.

The server’s runtime tools/list response is authoritative. For the complete current reference, call fetch_doc("mcp") in-band or open the Alter Docs MCP tool reference. The tools are:

  • Flow and skill navigation: list_phases, list_skills, get_started, next_step.
  • SDK implementation and proof: sdk_integration, sdk_pattern, verify_integration.
  • Diagnosis and documentation: troubleshoot, fetch_doc, search_docs.
  • Provider API discovery: list_providers, list_operations, get_operation_schema.
  • Policy authoring grammar: policy_language.

Every published docs page is bundled. When no flow step or doc names the page you need, call search_docs("<keywords>") and read the top hit with fetch_doc — do not guess slugs, and do not list every page (fetch_doc with no slug is ~200 entries; it is a last resort, not a discovery path).

  • Confirm before mutating. Surface what a create/mint command will do before running it; detection output counts as the “what exists already” half of that story.
  • Happy path only. This flow covers create/configure/mint/verify. Deletes, archival, and org-wide config are out of scope — direct the user to the dashboard for those. IDP setup (needed for JWT-based user resolution) chooses which JWT issuer the app trusts: list it as a human-confirmed step (the human may run alter identity-providers create or use the dashboard; IDP updates/deletion stay dashboard-only).
  • Honest handoffs. Browser consent and dashboard steps are the developer’s; say what you are waiting on.

Report an issue with this page

Necessary

Required for sign-in, security, authorization, and remembering your choices.

Always active

Analytics

Helps us understand which product and documentation features are useful.

Performance diagnostics

Uses performance tracing and privacy-masked session replay to diagnose problems.

You can change these choices at any time from Cookie settings.