{
  "version": "v1",
  "generated_at": "2026-09-10T21:16:25.269Z",
  "api": {
    "version": "v1",
    "basePath": "/api/v1",
    "auth": {
      "header": "X-API-Key",
      "scopes": [
        "search",
        "profiles-read",
        "profiles-write",
        "enrich",
        "communications",
        "messages-read",
        "group-context-read",
        "introductions-read",
        "contacts-read",
        "contacts-write",
        "contacts-delegate-resolve"
      ]
    },
    "pagination": {
      "limitParam": "limit",
      "offsetParam": "offset"
    },
    "limits": {
      "resultsSnapshotMax": 25,
      "previewEnrichable": false
    }
  },
  "quick_start": {
    "steps": [
      "Create an API key under MCP & API. API keys are available on any plan; usage still consumes the owner credit pool.",
      "Call /api/v1/credits to inspect the shared account credit pool, current-key usage, and the live search rate card before you scale up.",
      "Poll /api/v1/credits/status (or read X-Carl-Credits-* response headers on any authenticated call) to stay under your 30-day rolling credit ceiling. Exhaustion returns HTTP 402 with a credits_exhausted body — see credit_enforcement in the docs schema.",
      "Lookup a person by LinkedIn username or full LinkedIn profile URL using /api/v1/search/people (linkedin_username or linkedin_profile_url).",
      "Use the returned user_id to fetch cached profile chunks from /api/v1/profiles/:id/chunks.",
      "Call /api/v1/communications/capabilities before sending to learn which channels are actually available for the acting user and target.",
      "Create dry-run or live sends with /api/v1/communications; use GET /api/v1/communications/:id or /stream for status and artifacts.",
      "Use /api/v1/projects to list project workspaces, /targets to list/add/remove shortlist targets, /metrics to read outreach response rates, /templates to update saved templates, /messages/generate to draft personalized target messages, /target-users/:id/message or /targets/:id/message to save exact drafts, /send-readiness to check review state, and /activate to start approved project delivery.",
      "Run people searches with /api/v1/search/people; use description (alias query) which is auto-translated into structured filters unless you supply filters directly.",
      "Use /api/v1/search/people/enrich with a search_id to enrich one result or an entire page."
    ],
    "note": "All API usage draws from the same account credit pool as in-app searches. Use /api/v1/credits for the live rate card and billing semantics. Communications are currently documented as not separately credit-billed.",
    "curl": "curl -X POST https://api.supercarl.ai/api/v1/search/people \\\n  -H \"X-API-Key: <YOUR_API_KEY>\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"linkedin_profile_url\": \"https://www.linkedin.com/in/reidhoffman/\"\n  }'\n\n# Read cached chunks only\ncurl -X GET https://api.supercarl.ai/api/v1/profiles/<USER_ID>/chunks?posts_limit=10 \\\n  -H \"X-API-Key: <YOUR_API_KEY>\"\n\n# Check message-channel readiness\ncurl -X POST https://api.supercarl.ai/api/v1/communications/capabilities \\\n  -H \"X-API-Key: <YOUR_API_KEY>\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"linkedin_profile_url\": \"https://www.linkedin.com/in/reidhoffman/\"\n  }'\n\n# List project workspaces and target response metrics\ncurl -X GET https://api.supercarl.ai/api/v1/projects \\\n  -H \"X-API-Key: <YOUR_API_KEY>\"\n\ncurl -X GET https://api.supercarl.ai/api/v1/projects/<PROJECT_ID>/metrics \\\n  -H \"X-API-Key: <YOUR_API_KEY>\""
  },
  "data_scale": {
    "as_of": "2026-04-03",
    "overview": "Static platform-scale figures for integration planning, agent routing, and evaluation workflows. These are published values, not live query-time counters.",
    "coverage_note": "Current ingest cadence is approximately 75M profile updates per week. That operating rate is intended to support practical LinkedIn-wide coverage as the current rollout completes over the next few weeks.",
    "notes": [
      "These figures are grounded in production ingest programs and are published statically so public endpoints remain cheap and predictable.",
      "The public API focuses on search and enrichment speed; it does not compute expensive live corpus counts per request.",
      "Provider names are intentionally omitted from public metadata. Use the dataset shape and refresh cadence below instead."
    ],
    "corpus": [
      {
        "key": "profiles",
        "label": "Profiles",
        "count": 1000000000,
        "display": "1B+"
      },
      {
        "key": "companies",
        "label": "Companies",
        "count": 50000000,
        "display": "50M+"
      }
    ],
    "weekly_updates": [
      {
        "key": "profile_updates",
        "label": "Profile updates",
        "count": 75000000,
        "display": "75M+",
        "period": "week"
      }
    ],
    "monthly_updates": [
      {
        "key": "profile_updates",
        "label": "Profile updates",
        "count": 300000000,
        "display": "300M+",
        "period": "month"
      },
      {
        "key": "company_updates",
        "label": "Company updates",
        "count": 50000000,
        "display": "50M+",
        "period": "month"
      },
      {
        "key": "job_posting_updates",
        "label": "Job posting updates",
        "count": 10000000,
        "display": "10M+",
        "period": "month"
      },
      {
        "key": "post_activity_updates",
        "label": "Post and activity updates",
        "count": 25000000,
        "display": "25M+",
        "period": "month"
      }
    ]
  },
  "mcp": {
    "connector_url": "https://api.supercarl.ai/mcp",
    "transport": "streamable_http",
    "overview": "Super Carl ships a remote MCP server so agents (Claude, Claude Code, Cursor, Cowork, etc.) can read the authenticated account and professional profile, apply confirmed basic profile updates, run people / company / job / post search (with social-graph overlay), recover a paid people-search page without another search or credit, build evaluated recruiting shortlists, manage contact workflows, check channel readiness, draft/send communications, manage project state, export project results, and submit opt-in product feedback directly to Super Carl using the same backend as the web and mobile apps. For `people_search`, Free-form discovery: one bare result-producing people_search with the full NL query, needed fields/evidence, and limit:N (max 25); omit limit with no count. Bare returns rows directly; omit preview/workflow/count/format prose from query. Preserve named titles/locations. A bare occupational title stays literal except inflection/abbreviation; broaden occupations only when the user asks, accepts a relaxation, or a typed specialized contract defines the family. Month-scale role windows use current_role_tenure_months, not current_role_start. Named city: one current anchor with strict:false; explicit city-proper or user-named metro/region: strict:true; proximity/preference: non-strict. Never add adjacent places or unsupplied radius_miles. Use preview:true only for count/inspection/correction. Inspect the returned normalized plan and receipts before deciding whether a material continuation remains. If client capture truncates returned rows, use `replay_search_result` with `result_replay.source_search_id` and small slices; it preserves the recorded IDs/order, rehydrates current cached fields, does not rerun candidate search or call providers, and charges zero credits. Profile values may have changed since the original search. Revise a wrong localized mapping through sparse `filter_updates` previews on the latest `query_plan_handle`; a post-review revision starts a new filter state and supersedes prior rows. `collect_qualifying_count` is compatibility-only for an already-started specialized shortlist continuation. Larger hand-review workloads traverse ordinary advancing pages. `company_search` defaults to preview. `jobs_search` returns usable job rows by default; set `with_people: true` for the 'map a job to a path in' shape. `posts_search` returns matching post/activity rows with author identity by default; set `with_people: true` only when distinct authors are the answer rows. `contacts_action` covers all-contact exports, contact sheet reconciliation, and reviewed work-email enrichment behind one dispatcher. `project_action` covers project shortlist, drafting, and delivery operations for MCP sessions that continue work after search or outreach. `bulk_export` is the canonical preview-confirm-start-status workflow for private CSV exports up to 5,000 rows and Google Sheets up to 500 rows; pass an explicit user-requested count as `row_limit` on both preview and start.",
    "add_to_claude": {
      "title": "Add Super Carl as a custom connector in Claude",
      "steps": [
        "In Claude, open Settings → Capabilities and scroll to the Connectors section near the bottom of that page. On newer Claude builds Connectors lives under Customize in the left sidebar instead (https://claude.ai/customize/connectors), and the Capabilities page links there. Connectors is not a top-level Settings entry.",
        "Click Add custom connector (the button is labeled Add on the Customize page) and set Name to Super Carl.",
        "Remote MCP server URL: https://api.supercarl.ai/mcp",
        "Nothing else is required. If the dialog offers Client ID and Client Secret, leave both blank — Super Carl supports Dynamic Client Registration (RFC 7591), so Claude registers itself automatically; newer Claude builds do not show those fields at all.",
        "Click Continue (labeled Add on older builds). Claude will open a Super Carl sign-in page; authorize the scopes you want the agent to use.",
        "That's it — your Super Carl tools (agent_session, search_capabilities, people_search, replay_search_result, people_lookup_batch, company_search, company_search_batch, jobs_search, posts_search, communication_read, project_read, social_proximity_status, linkedin_conversation_research_status, linkedin_conversation_research_delete_data, watch_signals_read, social_proximity_research, linkedin_conversation_research, send_communication, project_action, bulk_export, contacts_action, watch_signals, get_product_help, account_read, report_feedback, super_carl_action) are now available to Claude."
      ],
      "note": "If Claude prompts for Connector Tool permissions, grant Always allow to read-only search/help tools when appropriate and keep mutating tools such as report_feedback, super_carl_action, send_communication, project_action, and bulk_export on Needs approval unless you explicitly want Claude to run them without per-call confirmation. Super Carl advertises stable MCP tool metadata and read/write annotations, but persistence of Claude permission choices is controlled by Claude. If you are using Anthropic Cowork, your workspace admin must also allowlist api.supercarl.ai under Admin Settings > Capabilities > Code execution > Network egress > Additional allowed domains. Anthropic does not gate this centrally."
    },
    "oauth": {
      "spec": "OAuth 2.1 + PKCE (S256) + RFC 8707 Resource Indicators",
      "authorization_server": "https://api.supercarl.ai",
      "discovery": {
        "protected_resource": "https://api.supercarl.ai/.well-known/oauth-protected-resource/mcp",
        "authorization_server": "https://api.supercarl.ai/.well-known/oauth-authorization-server",
        "jwks": "https://api.supercarl.ai/.well-known/jwks.json"
      },
      "endpoints": {
        "registration": "https://api.supercarl.ai/oauth/register",
        "authorize": "https://api.supercarl.ai/oauth/authorize",
        "token": "https://api.supercarl.ai/oauth/token"
      },
      "scopes_supported": [
        "search",
        "profiles-read",
        "profiles-write",
        "enrich",
        "communications",
        "messages-read",
        "group-context-read",
        "introductions-read",
        "contacts-read",
        "contacts-write"
      ],
      "default_scopes": [
        "search",
        "profiles-read",
        "enrich",
        "communications",
        "contacts-read",
        "contacts-write"
      ],
      "access_token": {
        "alg": "RS256",
        "audience": "https://api.supercarl.ai/mcp",
        "lifetime_seconds": 1800
      },
      "refresh_token": {
        "rotation": true,
        "lifetime_seconds": 2592000
      },
      "registration_modes": [
        "Dynamic Client Registration (RFC 7591) — default, no pre-coordination required.",
        "Client ID Metadata Documents — honored when the client_id is an HTTPS URL pointing at a hosted metadata JSON document (planned, not strictly required for directory listing)."
      ],
      "supported_redirect_uris": [
        "https://claude.ai/api/mcp/auth_callback",
        "https://claude.com/api/mcp/auth_callback",
        "RFC 8252 loopback (http://127.0.0.1:<port>/...) for Claude Code and local MCP clients",
        "Any other HTTPS callback"
      ]
    },
    "api_key_alternative": {
      "overview": "Programmatic / script callers can bypass OAuth and authenticate with a long-lived API key. This is useful for CLI tools, services, and other non-interactive MCP clients.",
      "header": "X-API-Key",
      "scopes": [
        "search",
        "profiles-read",
        "profiles-write",
        "enrich",
        "communications",
        "messages-read",
        "group-context-read",
        "introductions-read",
        "contacts-read",
        "contacts-write"
      ],
      "note": "API keys can be created by the account owner under MCP & API on any plan. The /mcp endpoint accepts API keys via X-API-Key or Authorization: Bearer carl_<key>."
    },
    "tools": [
      {
        "name": "agent_session",
        "scope": "communications",
        "read_only": false,
        "destructive": false,
        "modes": [
          "start",
          "list",
          "status",
          "switch_project",
          "close"
        ],
        "notes": "Explicitly starts, resumes, inspects, switches, or closes a durable/named MCP session when the user asks to manage one interactively. Unattended, scheduled, headless, and background runs must begin with the requested domain tool because agent_session.start is write-capable and may require live host approval. Ordinary domain calls create an implicit session automatically; carry the returned session_binding.agent_session_id into later related calls."
      },
      {
        "name": "search_capabilities",
        "scope": "search",
        "read_only": true,
        "destructive": false,
        "notes": "Reads one complete typed-filter schema for people_search, company_search, jobs_search, or posts_search after the current MCP session completes one natural-language query-first search. Use it before authoring unfamiliar structured refinements; it does not execute a search or constrain later corrective calls."
      },
      {
        "name": "people_search",
        "scope": "search",
        "read_only": true,
        "destructive": false,
        "notes": "For target-reference searches using filters.personas.worked_with, read users[].worked_with_overlap_tier together with users[].worked_with_evidence[]. Strong means date-verified shared-employer overlap; medium means same-employer evidence with period_unverified and must not be described as confirmed temporal overlap or direct collaboration; weak is only a weaker association signal. users[].relationship_to_target is a display summary and never upgrades the structured tier, overlap_confidence, or period_unverified receipt."
      },
      {
        "name": "replay_search_result",
        "scope": "search",
        "read_only": true,
        "destructive": false,
        "notes": "Recovers one already-paid people_search page by result_replay.source_search_id. It preserves the recorded IDs/order and rehydrates requested public fields from current cached profile data. Use small offset/limit/fields slices after host truncation. It does not rerun candidate search or call providers, cannot retrieve unseen pages, and charges zero credits; profile values may have changed."
      },
      {
        "name": "people_lookup_batch",
        "scope": "search",
        "read_only": true,
        "destructive": false,
        "notes": "Batch direct lookup for known LinkedIn URLs/usernames or Super Carl person ids. Use this for uploaded lists, profile-loading follow-ups, relationship_detail reads, or exact identity rows rather than broad discovery."
      },
      {
        "name": "company_search",
        "scope": "search",
        "read_only": true,
        "destructive": false
      },
      {
        "name": "company_search_batch",
        "scope": "search",
        "read_only": true,
        "destructive": false,
        "notes": "Batch named-company resolution for spreadsheets, rosters, exclusions, and long/messy employer lists. Inspect each company_resolution.status and bind only resolved/set companies into people_search."
      },
      {
        "name": "jobs_search",
        "scope": "search",
        "read_only": true,
        "destructive": false
      },
      {
        "name": "posts_search",
        "scope": "search",
        "read_only": true,
        "destructive": false
      },
      {
        "name": "communication_read",
        "scope": "communications",
        "read_only": true,
        "destructive": false,
        "modes": [
          "precheck",
          "history",
          "status"
        ],
        "notes": "Reads cached channel readiness, cached communication history, or queued/send delivery status without drafting, sending, cancelling, refreshing LinkedIn history, or enriching email."
      },
      {
        "name": "project_read",
        "scope": "communications",
        "read_only": true,
        "destructive": false,
        "modes": [
          "list",
          "status",
          "targets",
          "metrics",
          "send_readiness"
        ],
        "notes": "Reads projects, shortlist targets, outreach metrics, and send readiness. Use bulk_export action=\"status\" to check CSV or Google Sheets exports."
      },
      {
        "name": "social_proximity_research",
        "scope": "search",
        "read_only": false,
        "destructive": false,
        "notes": "Explicitly starts LinkedIn mutual research for one target and returns social proximity relationship detail plus warm-intro paths. Completed research is reused for 30 days by default; callers should report that existing researched mutuals were reused instead of describing a fresh research run. If the target profile is skeletal but has a LinkedIn URL and research is not already recent, this can also queue browser-orchestrator profile loading and reports research.target_profile_load. Cached relationship detail is read through people_search / people_lookup_batch with relationship_detail."
      },
      {
        "name": "linkedin_conversation_research",
        "scope": "authenticated",
        "read_only": false,
        "destructive": false,
        "notes": "Starts an explicitly confirmed, owner-scoped LinkedIn conversation-history research run for up to 100 conversations. It is read-only toward LinkedIn, never sends messages, and changes continuous_sync only when the user explicitly chooses that setting."
      },
      {
        "name": "social_proximity_status",
        "scope": "search",
        "read_only": true,
        "destructive": false,
        "notes": "Reads cached social-proximity and LinkedIn mutual-research status for one target without starting, continuing, resetting, or refreshing browser research. Use this for polling and for recent researched mutuals."
      },
      {
        "name": "linkedin_conversation_research_status",
        "scope": "authenticated",
        "read_only": true,
        "destructive": false,
        "notes": "Reads a previously started LinkedIn conversation-research run by run_id without starting or continuing browser work."
      },
      {
        "name": "linkedin_conversation_research_delete_data",
        "scope": "authenticated",
        "read_only": false,
        "destructive": true,
        "notes": "Permanently deletes only the authenticated owner's LinkedIn conversation-research/import data after exact-request confirmation. It disables continuous conversation tracking while preserving LinkedIn connections and Carl outreach-reply tracking."
      },
      {
        "name": "watch_signals_read",
        "scope": "communications",
        "read_only": true,
        "destructive": false,
        "modes": [
          "role_packs",
          "status",
          "runs",
          "updates",
          "hits",
          "evidence",
          "delivery_status",
          "delivery_subscriptions"
        ],
        "notes": "Reads recurring Scan My Network watch configuration, run history, portable Markdown updates, hits, evidence, delivery status, and redacted delivery subscriptions without creating, updating, running, pausing, or promoting a watch."
      },
      {
        "name": "send_communication",
        "scope": "communications",
        "read_only": false,
        "destructive": true,
        "modes": [
          "precheck",
          "history",
          "draft",
          "send",
          "status",
          "cancel"
        ],
        "notes": "Polymorphic: mode is required. mode=\"precheck\" is read-only channel-readiness check; mode=\"history\" reads previous visible communication history for a target; mode=\"draft\" stores expanded no-delivery outreach copy for review; mode=\"send\" is the destructive outbound send (with idempotency dedupe and explicit user confirmation); mode=\"status\" polls a prior send; mode=\"cancel\" cancels queued LinkedIn sends before dispatch."
      },
      {
        "name": "project_action",
        "scope": "communications",
        "read_only": false,
        "destructive": false,
        "modes": [
          "list",
          "status",
          "targets",
          "recipient_emails",
          "metrics",
          "send_readiness",
          "add_targets",
          "remove_targets",
          "retry_failed_linkedin_sends",
          "rename",
          "update_templates",
          "generate_templates",
          "generate_messages",
          "update_message",
          "activate"
        ],
        "notes": "Polymorphic project API dispatcher. Read modes list projects, include the current_session_project when agent_session_id is bound, fetch one project, list active included targets, resolve recipient emails, report outreach response metrics, and summarize send readiness. Mutating modes add/remove shortlist targets, rename the project, update saved outreach/referral/engagement templates, generate saved templates and personalized target messages, save exact target drafts, activate reviewed delivery, or retry non-limit LinkedIn failures. Use bulk_export for CSV or Google Sheets output. Mutating modes require user_confirmed=true in MCP."
      },
      {
        "name": "bulk_export",
        "scope": "communications",
        "read_only": false,
        "destructive": false,
        "actions": [
          "preview",
          "start",
          "status"
        ],
        "destinations": {
          "csv": {
            "max_rows": 5000
          },
          "google_sheet": {
            "max_rows": 500
          }
        },
        "notes": "Canonical project-result export workflow. Pass an explicit user-requested count such as 100 as row_limit on preview and repeat it unchanged on start; omit row_limit only for the destination maximum. Always preview first: preview creates no task and returns the exact columns, a representative row, total matches, requested cap, rows slated, and cost/timing estimates. If fewer rows qualify, it quotes the smaller available count. Start requires user_confirmed=true plus the matching reviewed preview_id; changing row_limit or headers requires a new preview. CSV runs asynchronously and status reports measured throughput, ETA, check-again timing, and an authenticated export-page path when ready; the page mints a fresh 48-hour download URL. CSV notification and Google Sheet sharing are restricted to the requesting user primary email."
      },
      {
        "name": "contacts_action",
        "scope": "contacts-write",
        "read_only": false,
        "destructive": false,
        "actions": [
          "export",
          "reconcile",
          "enrich"
        ],
        "modes": {
          "export": [
            "estimate",
            "start",
            "status",
            "latest"
          ],
          "reconcile": [
            "schema",
            "start_run",
            "preview_batch",
            "apply_batch",
            "status",
            "finalize",
            "list_review",
            "get_candidates",
            "confirm_candidate",
            "correct_resolution",
            "bulk_correct_resolutions",
            "bulk_confirm_candidates",
            "bulk_reject_candidates",
            "bulk_mark_needs_review",
            "bulk_ignore_entities",
            "bulk_restore_entities",
            "mark_needs_review",
            "reject_candidate",
            "ignore_entity",
            "restore_entity"
          ],
          "enrich": [
            "estimate",
            "run"
          ]
        },
        "notes": "Dispatcher for account-level contact workflows. action=\"export\" estimates, starts, and checks the all-contacts Google Sheet export. action=\"reconcile\" normalizes spreadsheet rows, previews batches up to 100 rows, applies reviewed batches, manages likely-match review, and finalizes reconciliation. action=\"enrich\" estimates and, after explicit confirmation, releases verified work emails for a short, already-vetted list. Mutating modes require user_confirmed=true."
      },
      {
        "name": "watch_signals",
        "scope": "communications",
        "read_only": false,
        "destructive": false,
        "modes": [
          "preview",
          "create",
          "role_packs",
          "update",
          "update_schedule",
          "subscribe_delivery",
          "unsubscribe_delivery",
          "status",
          "hits",
          "evidence",
          "delivery_status",
          "run_now",
          "pause",
          "resume",
          "archive",
          "snooze_hit",
          "dismiss_hit",
          "promote_hit"
        ],
        "notes": "Previews, creates, and manages hosted Scan My Network watches anchored to a Super Carl project. Supports local wall-clock schedules, delivery subscriptions, lifecycle controls, and promotion of qualified hits into durable review-only outreach drafts."
      },
      {
        "name": "get_product_help",
        "scope": "product_knowledge",
        "read_only": true,
        "destructive": false,
        "notes": "Topic catalog + per-user live state (LinkedIn / Gmail / Calendar / Slack / X / Instagram / notification toggles / timezone / plan tier / availability). Pass topic=\"index\" to discover topic ids; pass topic=\"x_instagram_sync\" for X/Instagram source-sync state and reconnect guidance."
      },
      {
        "name": "account_read",
        "scope": "action-dependent",
        "read_only": true,
        "destructive": false,
        "action_ids": [
          "get_account",
          "get_credit_status",
          "get_profile",
          "get_network_status",
          "get_invite_link"
        ],
        "required_scopes_by_action": {
          "get_account": [
            "profiles-read"
          ],
          "get_credit_status": [],
          "get_profile": [
            "search"
          ],
          "get_network_status": [],
          "get_invite_link": []
        },
        "notes": "Read-only authenticated-owner dispatcher. get_account returns lightweight identity without consuming credits; get_credit_status returns the current balance, limits, top-ups, and refresh period without consuming credits and remains callable at zero; get_profile returns the full professional profile directly and may consume a profile lookup credit; get_network_status returns LinkedIn connection-count details (imported, searchable, and provider-reported contact-source totals) plus normalized CSV/extension sync-progress state without consuming credits; get_invite_link returns the exact personal join link. Active imports are already connected-in-progress, so clients must not request a reconnect unless reconnect_required is true. It never accepts another user id or changes account state."
      },
      {
        "name": "report_feedback",
        "scope": "authenticated",
        "read_only": false,
        "destructive": false,
        "notes": "Direct opt-in Super Carl support-ticket tool. Offer it once after relevant recovery choices when an unresolved problem, zero-result dead end, or user-reported wrong or incomplete result remains. When the user asks to submit, show every exact proposed field plus the account/trace and human-or-agentic handling disclosures and file it in the same turn; only when they have not asked, wait for agreement first. Use this tool instead of Gmail/email unless the user explicitly asks to send email. The server still accepts the former dispatcher action from cached clients, but new clients discover only this top-level feedback path."
      },
      {
        "name": "super_carl_action",
        "scope": "action-dependent",
        "read_only": false,
        "destructive": true,
        "action_ids": [
          "update_profile",
          "set_timezone",
          "set_notification_preferences",
          "claim_bonus_credits",
          "inspect_linkedin_connection"
        ],
        "required_scopes_by_action": {
          "update_profile": [
            "profiles-write"
          ],
          "set_timezone": [],
          "set_notification_preferences": [],
          "claim_bonus_credits": [],
          "inspect_linkedin_connection": []
        },
        "notes": "Write-capable dispatcher for bounded per-user actions. update_profile changes only name, bio, and about_override with profiles-write scope and user_confirmed=true. set_timezone and set_notification_preferences also require exact-change confirmation. claim_bonus_credits is a one-time MCP recruiting launch bonus and returns credit counts before/after the grant. inspect_linkedin_connection is limited to a specific target-person affordance from communications capability results."
      }
    ],
    "rest_passthrough": [
      {
        "method": "POST",
        "path": "/api/v1/search/people/preview",
        "description": "Preview search for iterative refinement."
      },
      {
        "method": "POST",
        "path": "/api/v1/companies/search/preview",
        "description": "Preview company search for company-first workflows."
      },
      {
        "method": "POST",
        "path": "/api/v1/search/people",
        "description": "Run a full search or enrich results."
      },
      {
        "method": "POST",
        "path": "/api/v1/search/jobs/preview",
        "description": "Natural-language job search scored against the viewer social-graph overlay."
      },
      {
        "method": "POST",
        "path": "/api/v1/search/jobs/with-people",
        "description": "The 'map a job to a path in' surface — jobs grouped by company with inline network people at each."
      },
      {
        "method": "POST",
        "path": "/api/v1/search/posts/preview",
        "description": "Direct post/activity search over posts, likes, comments, reposts, target companies/people, dates, and engagement counts."
      },
      {
        "method": "POST",
        "path": "/api/v1/search/posts/with-people",
        "description": "Post/activity search with a deduped people set derived from matching actors/authors and a people_search binding for full profile pivots."
      },
      {
        "method": "POST",
        "path": "/api/v1/relationships/social-proximity/research",
        "description": "Explicitly start LinkedIn mutual research for a target when not recently researched and return current warm-intro paths."
      },
      {
        "method": "GET",
        "path": "/api/v1/relationships/social-proximity/research/status",
        "description": "Read cached social-proximity and mutual-research status without starting or refreshing browser research."
      },
      {
        "method": "POST",
        "path": "/api/v1/communications/capabilities",
        "description": "Check channel readiness for outbound communication."
      },
      {
        "method": "POST",
        "path": "/api/v1/communications",
        "description": "Draft, send, or dry-run outbound communication."
      },
      {
        "method": "POST",
        "path": "/api/v1/communications/history",
        "description": "Read previous visible communication history for a target."
      },
      {
        "method": "GET",
        "path": "/api/v1/communications/:communicationId",
        "description": "Fetch communication status, task metadata, and artifacts."
      },
      {
        "method": "GET",
        "path": "/api/v1/projects",
        "description": "List active projects for the acting user; current_project_id surfaces a session-bound project even when filtered out."
      },
      {
        "method": "GET",
        "path": "/api/v1/projects/:projectId",
        "description": "Fetch one project with optional targets/search results."
      },
      {
        "method": "GET",
        "path": "/api/v1/projects/:projectId/targets",
        "description": "List active included targets for a project."
      },
      {
        "method": "POST",
        "path": "/api/v1/projects/:projectId/targets",
        "description": "Add people to a project shortlist."
      },
      {
        "method": "DELETE",
        "path": "/api/v1/projects/:projectId/targets",
        "description": "Remove people from a project shortlist."
      },
      {
        "method": "GET",
        "path": "/api/v1/projects/:projectId/metrics",
        "description": "Fetch outreach response rates and status counts for a project."
      },
      {
        "method": "PATCH",
        "path": "/api/v1/projects/:projectId",
        "description": "Rename or update basic project fields."
      },
      {
        "method": "PUT",
        "path": "/api/v1/projects/:projectId/templates",
        "description": "Update saved project outreach/referral/engagement templates."
      },
      {
        "method": "POST",
        "path": "/api/v1/projects/:projectId/templates/generate",
        "description": "Generate saved project templates from project context."
      },
      {
        "method": "POST",
        "path": "/api/v1/projects/:projectId/messages/generate",
        "description": "Generate personalized messages against saved project templates."
      },
      {
        "method": "GET",
        "path": "/api/v1/network/summary",
        "description": "Confirm the vantage-point user's social-graph ingestion status (LinkedIn / Gmail / X / Instagram / Super Carl Connectors) before relying on graph-aware filters."
      }
    ],
    "legacy_schema_endpoint": {
      "path": "/public/docs/mcp",
      "note": "Serves the MCP tool-schema JSON for inspection and tooling. This is NOT the connector URL — point your MCP client at /mcp instead."
    },
    "notes": [
      "`account_read` with action_id=\"get_account\" is the authenticated self/account lookup for questions such as \"what is my name on Super Carl?\" or \"what is my profile URL?\". It returns lightweight account identity without consuming credits. action_id=\"get_credit_status\" returns the current Super Carl credit balance and period details in one zero-credit read, including when the balance is zero. action_id=\"get_profile\" directly returns the authenticated owner's full professional profile; the client does not need to call people_search or pass a user id. action_id=\"get_network_status\" returns the owner's LinkedIn connection-count details (imported, searchable, and provider-reported contact-source totals) plus normalized CSV/extension sync-progress state for connection-count or sync-status questions in any language; while ingest_in_progress is true, do not ask the user to reconnect unless reconnect_required is true. Use `super_carl_action` action_id=\"update_profile\" only after exact-change confirmation; it changes only name, bio, and about_override and requires profiles-write scope.",
      "For `people_search`, Free-form discovery: one bare result-producing people_search with the full NL query, needed fields/evidence, and limit:N (max 25); omit limit with no count. Bare returns rows directly; omit preview/workflow/count/format prose from query. Preserve named titles/locations. A bare occupational title stays literal except inflection/abbreviation; broaden occupations only when the user asks, accepts a relaxation, or a typed specialized contract defines the family. Month-scale role windows use current_role_tenure_months, not current_role_start. Named city: one current anchor with strict:false; explicit city-proper or user-named metro/region: strict:true; proximity/preference: non-strict. Never add adjacent places or unsupplied radius_miles. Use preview:true only for count/inspection/correction. Compare the returned `normalized_search_plan`, count, warnings, and receipts with the request. A genuinely new query+filters call is supported, but a handle call never resends query or rebuilds server-owned filters. Correct a localized mapping on the latest handle with `preview:true`, `offset:0`, and sparse `filter_updates`; null clears a field, objects merge, and arrays replace. Inspect the returned child and repeat only while another material localized error remains. A correction after reviewed rows returns `refinement.review_effect=\"supersedes_prior_rows_restart_at_zero\"`; restart review and never mix filter states. `collect_qualifying_count` is retained only for replaying an already-started specialized shortlist continuation.",
      "`people_search` AdvancedFilters supports has_current_experience:false for no-current-role / no-new-role-yet refinements; coverage depends on profile experience-date freshness.",
      "`company_search` with preview:true returns a downstream saved-company-list search_id only after that same effective actor completes the company preview. Pass that id into filters.companies.include_company_search_id or exclude_company_search_id; resolve-only ids and company entity ids are not saved-list handles. For one resolved company, bind by the returned company name.",
      "`people_search` and `people_lookup_batch` accept relationship_detail:\"summary\" or \"intro_paths\" for social proximity, mutuals, and warm-intro paths. Read calls never start LinkedIn mutual research; use social_proximity_research for explicit fresh mutual research.",
      "Worked-with/reference results are graded per row. Preserve and cite users[].worked_with_overlap_tier plus users[].worked_with_evidence[] at every response verbosity: strong is date-verified overlap, medium is same-employer evidence with an unverified period, and weak is a weaker signal. Never turn medium/weak into a claim that two people worked directly together, and never let relationship_to_target override overlap_confidence or period_unverified.",
      "MCP tools map onto the REST endpoints via `preview` / `with_people` flags; there is exactly one implementation of each operation.",
      "`posts_search` is the direct activity/post surface for \"posting about\", \"liked/commented on\", \"reposted\", target-company engagement, date windows, and reaction/comment thresholds. Default post rows already include recorded author identity. Use with_people:true only when distinct authors are the answer, or with_companies:true only when official company actors are the answer; never set both. For literal mention requests use `filters.posts.mentioned`; for soft topic/signal intent use `filters.posts.additional_context_keywords_core/supporting` with `keyword_match_mode:\"soft\"`. Execute people_search_binding only when additional profile or relationship filtering is needed.",
      "Use `communication_read` for cached precheck/history/status reads. Use `send_communication` when fresh provider history or recipient resolution is required, or for draft/send/cancel writes; mode=\"send\" is destructive, idempotency-deduped, and requires user_confirmed=true unless dry_run=true.",
      "Use `project_read` for project list/status/targets/metrics/send-readiness and `project_action` for project mutations such as add/remove targets, drafting, and activation. Use `bulk_export` for every project-results export: destination=\"csv\" supports up to 5,000 rows and destination=\"google_sheet\" supports up to 500 rows. Map an explicit requested count to row_limit on preview and repeat it unchanged on start; omit row_limit only for the destination maximum. Always preview, show the returned columns/sample/count/cost/timing, and start only after explicit confirmation of that preview.",
      "Use `contacts_action` for account-level contact workflows: action=\"export\" for all-contact Google Sheet export, action=\"reconcile\" for contact sheet normalization/review/finalize, and action=\"enrich\" for user-confirmed verified work-email release.",
      "For multi-step MCP workflows, let the first session-aware domain call create an implicit session, then carry its returned agent_session_id through related search/project/sheet/watch/communication calls. This is required for unattended, scheduled, headless, and background runs because agent_session.start is write-capable and may require live host approval. Call agent_session explicitly only when the user asks for a durable/named session or session management in an interactive context. Pass returned project_id / current_session_project_id into later project_action calls when operating on a specific project.",
      "`super_carl_action` with action_id=\"claim_bonus_credits\" grants the one-time MCP recruiting launch bonus when a user asks for free/bonus Super Carl credits; return the before/after credit counts and tell repeat claimers it is one-time. Use the top-level `report_feedback` tool for product problems. Offer feedback once after relevant recovery choices when a user-visible problem remains unresolved or the user says a result is wrong or incomplete. A direct request to send, submit, report, or file feedback or a support ticket counts as opt-in. Announce every exact non-empty field, attached trace id, authenticated-account attachment, and possible person/agentic handling, then file it in that same turn; only when the user has not asked, wait for agreement first. Never submit automatically, and use Gmail/email only when the user explicitly asks to send email.",
      "A bare literal `No approval received` is generated by the MCP host before Super Carl receives the call. Do not retry it repeatedly, attempt report_feedback through the same blocked connector in that turn, or describe it as Super Carl admin, authentication, or authorization gating. If agent_session is blocked during an unattended, scheduled, headless, or background run, skip it and call the requested domain tool without agent_session_id so an implicit session can bind the work. Otherwise, ask the user to refresh or reconnect the Claude connector / refresh its tool list and choose Allow always for the affected read-only tool where Claude offers that permission. A later direct request to file feedback still counts as opt-in once the host permits the call.",
      "`get_product_help` covers any \"where do I…\" / \"how do I…\" question about Super Carl itself; it returns the topic markdown plus the current user's live state for that area (integration sessions, sync status, notification toggles, timezone, plan tier, availability). Pass topic=\"index\" first if you don't know which topic fits."
    ],
    "usage_rubric": {
      "flag_model": "Free-form discovery: one bare result-producing people_search with the full NL query, needed fields/evidence, and limit:N (max 25); omit limit with no count. Bare returns rows directly; omit preview/workflow/count/format prose from query. Preserve named titles/locations. A bare occupational title stays literal except inflection/abbreviation; broaden occupations only when the user asks, accepts a relaxation, or a typed specialized contract defines the family. Month-scale role windows use current_role_tenure_months, not current_role_start. Named city: one current anchor with strict:false; explicit city-proper or user-named metro/region: strict:true; proximity/preference: non-strict. Never add adjacent places or unsupplied radius_miles. Use preview:true only for count/inspection/correction. Inspect its normalized plan and receipts, then reuse the latest handle only for a material sparse preview correction or justified same-lineage continuation; post-review corrections supersede prior rows. Preserve `collect_qualifying_count` only on a server-authored continuation from an already-started specialized shortlist. `company_search` defaults to preview. `jobs_search` returns usable job rows by default; set `with_people:true` when the user wants to know which jobs they have a path into. `posts_search` returns post/activity rows by default; set `with_people:true` when distinct authors are the answer, then execute `people_search_binding` only when full profile review is needed.",
      "typical_flow": [
        "1. User asks a natural-language question ('who do I know that ...')",
        "2. Call people_search once with the full retrieval query, top-level limit:N for a requested N up to 25, and the fields/evidence/output shaping needed to answer. Omit preview so that same call returns rows.",
        "3. Compare normalized_search_plan, rows, count, warnings, and receipts with the request. Present the returned filter-qualified rows in backend order and grade any unexpressible row evidence without dropping them.",
        "4. If a localized mapping is wrong, replay the latest query_plan_handle with preview:true, offset:0, and sparse filter_updates. Inspect each child and repeat only for another material localized error. Its child supersedes all rows from the prior filter state and review restarts at offset 0.",
        "5. Continue the unchanged handle only for an explicit bounded review/output shortfall or required_next_call while page.has_more is true. Do not author collect_qualifying_count for a new ordinary search; preserve it only on an already-started server-authored specialized shortlist continuation. For a hand-review workload above 25, advance ordinary bounded pages to the requested review count or source exhaustion."
      ],
      "direct_lookup": "For a single known person, pass people_search { linkedin_profile_url } or { linkedin_username } — direct lookup ignores `preview` and filters.",
      "jobs_flow": "jobs_search returns usable rows (title, company, network block) by default. Set with_people: true when the user wants the 'which jobs do I have a path into' shape — results group by company with 1st/2nd-degree people inlined at each.",
      "posts_flow": "posts_search returns matching post/activity rows by default. Use filters.posts.mentioned for hard/literal \"posts mentioning X\"; use filters.posts.additional_context_keywords_core/supporting with keyword_match_mode:\"soft\" for broader topic/signal searches such as \"who is hiring\". Other fields: action_types, min_reactions/min_comments/min_engagement, published_within_days, target_companies/target_people/target_urls. Set with_people:true for a quick deduped people set, or execute the returned people_search_binding and add network/title/location/company filters structurally.",
      "communications_flow": "Use communication_read for cached precheck/history/status reads. Call send_communication mode=\"history\" only when fresh provider history or recipient resolution is required. Use send_communication mode=\"draft\" when the user should review exact copy first; Super Carl stores the draft on the MCP session assignment target and expands [JoinLink]/[SchedulingLink] so the agent sees the final links. Use mode=\"send\" only when the user has approved delivery; Gmail requires subject.",
      "project_flow": "Use project_read mode=\"list\" or agent_session.status to find the active project id; when agent_session_id is bound, list returns current_session_project/current_session_project_id even if the generic list is empty. Copy returned agent_session_id/project_id values into subsequent calls instead of reusing names or titles. Use project_read mode=\"targets\" to show active included targets, mode=\"metrics\" to report outreach response rates, and mode=\"send_readiness\" to summarize review state. Use bulk_export action=\"preview\" for CSV or Google Sheets output; when the user requests N rows, pass row_limit=N and repeat it unchanged on action=\"start\" after the user confirms the exact preview. Use action=\"status\" to track it. Use project_action mode=\"update_message\" or mode=\"generate_messages\" to draft target messages and mode=\"activate\" after explicit send approval. When the user asks you to draft outreach/messages/best asks for selected people, save the useful project state too: add_targets for the selected people, then update_message review_action=\"save\" for exact reviewed-later drafts. A target with outreach_status=\"queued_send\" was already queued for immediate single-target delivery by the user (app \"Send now\"); treat it as in flight like \"sending\" — do not re-draft, re-send, or include it in a bulk activate. Use agent_session mode=\"list\" to list/resume sessions and mode=\"switch_project\" with agent_session_id + project_id when the user wants this MCP session to work against a specific existing project."
    },
    "curl_examples": "# Discover the authorization server + JWKS\ncurl -s https://api.supercarl.ai/.well-known/oauth-protected-resource/mcp\ncurl -s https://api.supercarl.ai/.well-known/oauth-authorization-server\n\n# Dynamic Client Registration (RFC 7591)\ncurl -X POST https://api.supercarl.ai/oauth/register \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"client_name\": \"My MCP Client\",\n    \"redirect_uris\": [\"https://my-client.example/callback\"],\n    \"token_endpoint_auth_method\": \"none\"\n  }'\n\n# Smoke-test /mcp with an API key (no OAuth required for scripts)\ncurl -D /tmp/supercarl-mcp-headers -X POST https://api.supercarl.ai/mcp \\\n  -H \"X-API-Key: <YOUR_API_KEY>\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Accept: application/json, text/event-stream\" \\\n  -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2025-03-26\",\"capabilities\":{},\"clientInfo\":{\"name\":\"curl-smoke\",\"version\":\"1.0.0\"}}}'\nSESSION_ID=\"$(awk 'BEGIN { IGNORECASE=1 } /^mcp-session-id:/ { gsub(\"\\r\", \"\", $2); print $2 }' /tmp/supercarl-mcp-headers)\"\ncurl -X POST https://api.supercarl.ai/mcp \\\n  -H \"X-API-Key: <YOUR_API_KEY>\" \\\n  -H \"Mcp-Session-Id: $SESSION_ID\" \\\n  -H \"Mcp-Protocol-Version: 2025-03-26\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"jsonrpc\":\"2.0\",\"method\":\"notifications/initialized\"}'\ncurl -X POST https://api.supercarl.ai/mcp \\\n  -H \"X-API-Key: <YOUR_API_KEY>\" \\\n  -H \"Mcp-Session-Id: $SESSION_ID\" \\\n  -H \"Mcp-Protocol-Version: 2025-03-26\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Accept: application/json, text/event-stream\" \\\n  -d '{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools/list\"}'\ncurl -X DELETE https://api.supercarl.ai/mcp \\\n  -H \"X-API-Key: <YOUR_API_KEY>\" \\\n  -H \"Mcp-Session-Id: $SESSION_ID\" \\\n  -H \"Mcp-Protocol-Version: 2025-03-26\"",
    "privacy_policy": "https://supercarl.ai/privacy",
    "terms_of_service": "https://supercarl.ai/terms",
    "support_contact": "support@supercarl.ai",
    "directory_status": "preparing_submission"
  },
  "credit_enforcement": {
    "overview": "Super Carl charges 1 credit for each completed single search. Non-empty batch lookups and exports use max(1, successfully completed rows / 100) credits. Successful Carl-hosted model activity and explicitly selected provider enrichment are converted to credits at provider cost using $0.099 per credit. Search preparation and designated platform work are included. Every authenticated response on enforced routes carries X-Carl-Credits-* headers; when the account owner hits the ceiling, billable endpoints return HTTP 402 with a credits_exhausted body and MCP tool calls return a handled structured result.",
    "docs_url": "https://supercarl.ai/docs#credits",
    "policy": {
      "window_days": 30,
      "token_passthrough_effective_at": "2026-08-25T00:00:00Z",
      "window_semantics": "The window begins at the timestamp of your first billable action inside the current window and closes 30 days later. When the ledger has no events inside the window, window_start and reset_at are both null and no reset timestamp is emitted until the next billable action.",
      "pool_scope": "Credits are pooled per account owner. Team seats and delegated calls draw from the owner's pool.",
      "tiers": [
        {
          "tier": "free",
          "recurring_credit_cap": 0,
          "one_time_welcome_grant": 50,
          "window_days": 30
        },
        {
          "tier": "starter",
          "credit_cap": 75,
          "window_days": 30,
          "price_usd": 8,
          "estimated_searches": 75
        },
        {
          "tier": "starter_annual",
          "credit_cap": 75,
          "window_days": 30,
          "price_usd": 6.4,
          "estimated_searches": 75
        },
        {
          "tier": "pro",
          "credit_cap": 1000,
          "window_days": 30
        },
        {
          "tier": "annual",
          "credit_cap": 1000,
          "window_days": 30
        },
        {
          "tier": "team",
          "credit_cap": 1000,
          "window_days": 30
        }
      ],
      "admin_bypass": "Admin users bypass the block entirely — headers still emit, but 402 responses are never returned for them."
    },
    "debit_actions": [
      {
        "area": "People search (full)",
        "action_type": "search",
        "action_subtype": "results",
        "sources": [
          "POST /api/v1/search/people",
          "POST /search (in-app)"
        ]
      },
      {
        "area": "People search preview",
        "action_type": "search",
        "action_subtype": "preview",
        "sources": [
          "POST /api/v1/search/people/preview",
          "people_search (MCP / in-product catalog)"
        ]
      },
      {
        "area": "Company search preview",
        "action_type": "search",
        "action_subtype": "preview",
        "sources": [
          "POST /api/v1/companies/search/preview",
          "company_search (MCP / in-product catalog)"
        ]
      },
      {
        "area": "Job search (preview / with-people)",
        "action_type": "search",
        "action_subtype": "preview",
        "sources": [
          "POST /api/v1/search/jobs/preview",
          "POST /api/v1/search/jobs/with-people"
        ]
      },
      {
        "area": "Post/activity search (preview / with-people)",
        "action_type": "search",
        "action_subtype": "preview",
        "sources": [
          "POST /api/v1/search/posts/preview",
          "POST /api/v1/search/posts/with-people"
        ]
      },
      {
        "area": "Batch identity lookup",
        "action_type": "search",
        "action_subtype": "batch_lookup",
        "sources": [
          "people_lookup_batch",
          "company_search_batch"
        ],
        "notes": "Non-empty batches cost max(1, successfully completed rows / 100) credits. Failed rows cost 0."
      },
      {
        "area": "Canonical export",
        "action_type": "search",
        "action_subtype": "export",
        "sources": [
          "campaignGenerationRoutes (project/campaign export)",
          "contactExportService (account contacts export)"
        ],
        "notes": "Canonical non-empty exports cost max(1, completed rows / 100) credits. Zero-row and failed exports cost 0; selected provider/model work is passed through at actual cost converted at $0.099 per credit."
      },
      {
        "area": "Outreach delivery telemetry",
        "action_type": "outreach",
        "action_subtype": "on_platform | off_platform",
        "sources": [
          "Assignment worker post-send (LinkedIn + Super Carl direct message)"
        ],
        "notes": "Message delivery itself remains telemetry-only. Carl-hosted model activity used for chat, personalization, web research, or other user-requested AI work is charged at provider cost converted at $0.099 per credit."
      },
      {
        "area": "Voice telemetry",
        "action_type": "voice_minutes",
        "action_subtype": null,
        "sources": [
          "Recorded when a voice session ends (actual duration)"
        ],
        "notes": "Tracked for telemetry and cost analysis only. Costs 0 credits."
      },
      {
        "area": "LLM token telemetry",
        "action_type": "tokens",
        "action_subtype": "openai",
        "sources": [
          "tokenUsageService (every OpenAI call)"
        ],
        "notes": "Successful user-requested Carl-hosted model activity is charged at provider cost converted at $0.099 per credit. Search preparation and designated internal platform work remain included and are tracked for COGS only."
      }
    ],
    "response_headers": {
      "applies_to": "Every authenticated response on routes wired to the enforceCreditCeiling pre-handler across the billable public search surface. Headers emit on successful responses and on the 402 exhaustion response.",
      "headers": [
        {
          "name": "X-Carl-Credits-Tier",
          "type": "string",
          "example": "free",
          "omitted_when": null,
          "description": "Account-owner tier slug. One of free, starter, starter_annual, pro, annual, team."
        },
        {
          "name": "X-Carl-Credits-Limit",
          "type": "integer",
          "example": "50",
          "omitted_when": "Tier is unlimited (limit === null).",
          "description": "Credit cap for the current window."
        },
        {
          "name": "X-Carl-Credits-Used",
          "type": "integer",
          "example": "12",
          "omitted_when": null,
          "description": "Credits consumed in the current window. 0 when the window is empty."
        },
        {
          "name": "X-Carl-Credits-Remaining",
          "type": "integer",
          "example": "38",
          "omitted_when": "Tier is unlimited (remaining === null).",
          "description": "limit minus used, clamped at 0."
        },
        {
          "name": "X-Carl-Credits-Reset-At",
          "type": "string (RFC 3339)",
          "example": "2026-05-14T17:22:03.000Z",
          "omitted_when": "Window is empty (no usage yet — window_start is null).",
          "description": "When the current window closes."
        },
        {
          "name": "X-Carl-Credits-Window-Days",
          "type": "integer",
          "example": "30",
          "omitted_when": null,
          "description": "Window length in days. Always present."
        }
      ]
    },
    "exhaustion_response": {
      "http_status": 402,
      "body_shape": "CreditsExhaustedError",
      "example": {
        "error": "credits_exhausted",
        "message": "You've used all 50 credits in your current credit period.",
        "tier": "free",
        "limit": 50,
        "used": 50,
        "remaining": 0,
        "reset_at": "2026-05-14T17:22:03.000Z",
        "upgrade_url": "https://supercarl.ai/upgrade",
        "docs_url": "https://supercarl.ai/docs#credits"
      },
      "client_detection_field": "error",
      "client_detection_value": "credits_exhausted",
      "notes": [
        "Key off the `error` field for client branching on HTTP responses.",
        "The parallel MCP handled result exposes both `error` and the compatibility alias `error_code` — see mcp_error below.",
        "Clients should open upgrade_url to let the user upgrade."
      ]
    },
    "mcp_error": {
      "overview": "MCP has no HTTP-header surface at the tool layer, so credit metadata travels inside tool responses. Successful tool calls carry structuredContent.credits; over-limit calls return a handled structured result on the normal transport path.",
      "success_annotation": {
        "shape": "CreditsAnnotation",
        "example": {
          "structuredContent": {
            "credits": {
              "tier": "free",
              "limit": 50,
              "used": 4,
              "remaining": 46,
              "reset_at": "2026-05-14T17:22:03.000Z"
            }
          }
        }
      },
      "over_limit_response": {
        "shape": "McpCreditsExhaustedError",
        "text_template": "Your Super Carl credits are used up. You've used {used}/{limit} credits in your current credit period. Share {upgrade_url}; free users can upgrade to Starter for 75 credits per month. For Free users, one-time welcome credits do not refresh automatically.",
        "example": {
          "isError": false,
          "content": [
            {
              "type": "text",
              "text": "Your Super Carl credits are used up. You've used 50/50 credits in your current credit period — these one-time Free welcome credits do not refresh automatically.\n\nAction required: tell the user they are out of Super Carl credits and cannot run another credit-billed action until they upgrade or, when applicable, wait for the reset. Share this upgrade link with them: https://supercarl.ai/upgrade Upgrading to Starter gives them 75 credits per month.\n\nUpgrade: https://supercarl.ai/upgrade\nDocs: https://supercarl.ai/docs#credits"
            }
          ],
          "structuredContent": {
            "success": false,
            "error": "credits_exhausted",
            "error_code": "credits_exhausted",
            "message": "Super Carl credits are exhausted. 0 credits remain from the one-time Free welcome grant.",
            "tier": "free",
            "limit": 50,
            "used": 50,
            "remaining": 0,
            "reset_at": "2026-05-14T17:22:03.000Z",
            "upgrade_url": "https://supercarl.ai/upgrade"
          }
        },
        "client_detection_field": "structuredContent.error",
        "client_detection_value": "credits_exhausted"
      }
    },
    "status_endpoints": [
      {
        "endpoint": "GET /api/v1/credits/status",
        "auth": "X-API-Key",
        "description": "API-key authenticated rolling-window status. Mirrors the X-Carl-Credits-* headers. Safe to poll.",
        "response_shape": "CreditStatusResponse"
      },
      {
        "endpoint": "GET /users/me/credits/status",
        "auth": "session JWT",
        "description": "Session-authenticated variant used by the in-app out-of-credits modal and balance banner. Identical response shape; Cache-Control: no-store so retries after upgrade see fresh values.",
        "response_shape": "CreditStatusResponse"
      }
    ],
    "upgrade_url": "https://supercarl.ai/upgrade"
  }
}
