# Alt Developer API (altalt.io) > Alt is a note-taking app for recorded lectures and meetings. This file is the > machine-readable version of https://altalt.io/developers: a read-only REST API, > Standard Webhooks, and a hosted MCP server for exporting notes, transcripts, > and AI summaries into other tools. ## Fastest setup - Run `npx altalt` from the project directory for the guided setup. It can connect MCP with browser OAuth, verify REST API access and webhooks, and hand the implementation to a supported coding agent. - Run `npx altalt --mcp-only` when the only goal is connecting Alt to an MCP client such as Claude Code, Codex, or Cursor. - Use the manual instructions below when the environment cannot run Node.js or when exact API and webhook behavior needs to be implemented directly. **Status — admin-only dogfooding.** These docs are public, but MCP OAuth and API keys are currently available only to Alt administrators. The console at `https://altalt.io/account/integrations` returns `404` to everyone else. If you see a 404 or cannot approve the OAuth consent screen, the feature is not yet available to your account. Everything below is accurate for Alt administrators; public beta availability will be announced separately. **Prerequisite — cloud sync must be on.** The API serves notes that reached Alt's cloud. Personal notes only sync when the owner turns sync on in the Alt app (Settings → Sync); teamspace notes sync automatically for signed-in members. If a key returns an empty note list, sync is almost always the reason. Notes that were recorded before sync was enabled do not appear retroactively. - Base URL: `https://public-api.altalt.io/v1` - OpenAPI spec (source of truth for schemas): `https://altalt.io/developers/openapi.yaml` - MCP endpoint: `https://mcp.altalt.io/mcp` - Console — create integrations and API keys: `https://altalt.io/account/integrations` - Interactive setup wizard: `npx altalt` - CLI guide: `https://altalt.io/developers/cli` - MCP guide: `https://altalt.io/developers/mcp` ## REST and webhook authentication - Every REST and webhook-management request needs `Authorization: Bearer `. Keys look like `alt_live_...` and are created in the console. The full key is shown only once at creation. MCP clients should prefer OAuth as described below. - There is no sandbox mode — every key is live and reads real notes. Use separate integrations for staging and production. - A key belongs to either a **personal** integration (sees only the owner's personal notes) or a **teamspace** integration (sees only notes shared into that teamspace, never members' personal notes). Notes outside a key's scope return `404`. - Keys carry scopes; requests outside them fail `403 insufficient_scope`: - `notes:read` — list notes, read note metadata - `transcripts:read` — read transcript text and speaker segments - `summaries:read` — read summaries (Markdown) - `webhooks:manage` — manage webhook endpoints via the API - API access requires an active paid plan on the integration's workspace; otherwise requests fail `403 plan_required`. - Rate limit: **120 requests/minute per key**. Exceeding it returns `429 rate_limited` with a `Retry-After` header. Prefer webhooks + incremental sync over tight polling, and use `ETag` / `If-None-Match` (`304` on unchanged). Store keys in environment variables or a secret manager — never in client-side code or repositories. ## REST endpoints Full request/response schemas live in the OpenAPI spec. Summary: - `GET /v1/notes` — list notes visible to the key. Query params: `limit` (max 100), `cursor` (from `next_cursor`; page until `has_more` is false), `updated_after` (incremental sync), `include_deleted=true` (include tombstones). - `GET /v1/notes/{note_id}` — note metadata: title, transcript/summary readiness, revision, timestamps. - `GET /v1/notes/{note_id}/transcript` — timed, speaker-labeled segments (scope `transcripts:read`). - `GET /v1/notes/{note_id}/summary` — AI summary as Markdown (scope `summaries:read`). - `GET|POST /v1/webhook-endpoints`, `PATCH|DELETE /v1/webhook-endpoints/{endpoint_id}`, `POST /v1/webhook-endpoints/{endpoint_id}/test` — manage webhook endpoints (scope `webhooks:manage`). There is no single-endpoint GET. Content semantics: - Notes appear in the API once a recording has ended or a summary has been generated. Memo-only notes are never exported. - Before content is ready, transcript/summary reads return `404` with error code `transcript_not_ready` / `summary_not_ready` — retry later (webhooks tell you when). - Every externally visible change increments the note's `revision` (server-assigned, monotonic). Store the revision you applied; ignore equal or lower ones. ## Webhooks Register an endpoint (console or API) to get pushed events instead of polling: ``` curl -X POST 'https://public-api.altalt.io/v1/webhook-endpoints' \ -H "Authorization: Bearer $ALT_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"url": "https://example.com/webhooks/alt", "events": ["note.ended", "note.summary.generated", "note.updated", "note.deleted"]}' ``` Events: - `note.ended` — a recording session finished and its final transcript is fetchable. Fires once per recording session (a note with multiple sessions emits it multiple times). - `note.summary.generated` — a summary finished generating and is fetchable. - `note.updated` — externally visible fields (title, content) changed after the note ended. Debounced: rapid edits collapse into one event. - `note.deleted` — the note was deleted (`data.reason: "deleted"`) or left the key's visibility scope (`data.reason: "access_lost"`). Treat both as deletion: remove or block access to your stored copy. - Service events: `endpoint.verification` (sent on creation and URL change — answer 2xx to activate the endpoint) and `endpoint.test` (from "Send test"). Envelope (thin notification — identifiers and statuses only, never content; fetch content from the REST API): ```json { "event_id": "5f8c0a5e-...", "event_type": "note.ended", "occurred_at": "2026-08-12T05:20:00Z", "data": { "note_id": "note_abc123", "recording_session_id": "rec_456", "revision": 7, "transcript_status": "ready", "summary_status": "pending" } } ``` Signature verification — deliveries follow the Standard Webhooks spec (https://www.standardwebhooks.com/), signed with the `whsec_...` secret issued once when the endpoint was created: ``` headers: webhook-id, webhook-timestamp, webhook-signature ("v1,", possibly space-delimited list) signed_content: "{webhook-id}.{webhook-timestamp}.{raw request body}" signature: base64( HMAC-SHA256( base64url_decode(secret after "whsec_"), signed_content ) ) ``` - Verify over the raw request body — do not re-serialize the JSON. - Compare with a constant-time comparison; reject stale timestamps (±5 min). - Working receiver code (Node.js and Python): https://altalt.io/developers/quickstart Delivery contract: - Respond `2xx` within 10 seconds; anything else counts as failure. Ack first, process asynchronously. - Retries with increasing backoff: 30s → 5m → 30m → 2h → 12h → 12h (7 attempts total). Endpoints failing many consecutive deliveries are auto-disabled. - Redirects are not followed; the URL must answer directly over HTTPS. Private, loopback, and cloud-metadata addresses are rejected. - Delivery is **at-least-once**: dedupe on `event_id`. - Events can arrive out of order: apply by `revision`, and re-fetch the note from the REST API when in doubt. - Reconciliation: webhooks can be missed. Periodically call `GET /v1/notes?updated_after=`, and occasionally list all notes with `include_deleted=true` — any note ID you hold that is no longer listed was deleted or left your scope. ## MCP server Hosted MCP (Model Context Protocol) server with Streamable HTTP transport at `https://mcp.altalt.io/mcp`. Browser OAuth is recommended. Alt publishes OAuth authorization-server and protected-resource metadata, supports Dynamic Client Registration, requires PKCE (S256), and issues refreshable tokens. Compatible clients discover and configure this automatically — do not create or paste an OAuth client ID or secret. - Claude, Claude Desktop, and Cowork: open Customize → Connectors → Add custom connector, enter the MCP URL, leave OAuth client credentials empty, then Connect. - Claude Code: run `claude mcp add --transport http alt https://mcp.altalt.io/mcp`, then open `/mcp` and authenticate Alt in the browser. - Codex CLI: run `codex mcp add alt --url https://mcp.altalt.io/mcp`. The add command normally completes browser OAuth; if authentication does not start, run `codex mcp login alt`. - Cursor and other OAuth-capable clients accept a URL-only config: `{"mcpServers": {"alt": {"url": "https://mcp.altalt.io/mcp"}}` Approve only the read-only scopes and workspace you intend to expose. OAuth is currently closed beta. Connections created before the generic OAuth upgrade were revoked intentionally; if an older Alt connection no longer authenticates, remove it from the client and add it again once. API-key fallback remains supported for automation and clients without MCP OAuth: `{"mcpServers": {"alt": {"url": "https://mcp.altalt.io/mcp", "headers": {"Authorization": "Bearer alt_live_..."}}}}` Prefer an environment variable over a literal key whenever the client supports it. Read-only tools: `list_notes`, `get_note`, `get_note_transcript`, `get_note_summary`. OAuth scopes are `notes:read`, `transcripts:read`, and `summaries:read`, with the same availability and `*_not_ready` semantics as the REST API. ## Building an integration (recommended pattern for agents) 1. Read the API key from the `ALT_API_KEY` environment variable. Never hardcode it. If the user has no key yet, send them to `https://altalt.io/account/integrations` and name the scopes you need. 2. Backfill: page through `GET /v1/notes?limit=100` following `next_cursor`, then fetch transcript/summary per note as needed. 3. Stay in sync: register a webhook endpoint (verify signatures, dedupe on `event_id`, apply by `revision`) and use `updated_after` polling as a fallback reconciliation loop. 4. Honor deletions (`note.deleted`, either reason) by deleting or blocking your stored copy. Human-readable docs: https://altalt.io/developers