Create an API key
Go straight to key management in the hosted app. If you’re signed out,
you’ll be asked to sign in and then land right back on the API keys page.
- Discover your resources — list your projects, their servers, eval suites, and chat sessions, so every ID the other routes need is self-serve
- Validate a server — connect, initialize, and capture a capability snapshot
- Run the doctor — the probe → connect → initialize → capabilities workflow
- Check OAuth requirements — does this server need an OAuth grant?
- List tools, prompts, and resources — the server’s MCP primitives
- Call a tool / render a prompt — execute primitives and get the MCP result back verbatim
- Read a resource by URI
- Export a full snapshot — tools, resources, and prompts as one JSON document
- Manage a project’s hosts — list, read, create (from a built-in
template like
claude/chatgpt/cursor, or from a full host config), rename, and delete the named model + capability profiles you run chats and eval suites against - Manage a project’s environments — list, read, create, edit, archive, and restore the named execution bundles (host + optional server group + optional pinned skills and plugin versions) that eval suites and journeys run against, and preview what one resolves to before launching it
- List a project’s chatboxes — name, access mode, attached servers, and share link — and read one chatbox’s settings: model, system prompt, tool-approval policy, and resolved servers
- Author eval suites — create a runnable suite (name, default model, servers, test cases) synchronously and get a
suiteIdback; no run is started and no credits are spent - Run eval suites asynchronously — create a run from a saved suite, get a
202+runIdimmediately, then poll status, per-iteration results (tool calls, token usage, latency), and full traces. Runs appear live in the hosted UI, taggedsource: "api" - Import OAuth tokens — complete OAuth yourself (e.g. the SDK’s
runOAuthLogin) and push the tokens; subsequent calls inject and refresh them server-side - Run a headless agent turn — send a message history, get back the assistant reply, the platform operations it invoked, references to any resources it created (eval suites), and token usage; the model is pinned server-side and billed to the project
Base URL
Authentication
Every request needs an MCPJam API key in theAuthorization header:
Creating a key
- Open Settings → API keys in the hosted app (the card at the top of this page takes you there directly).
- Click Create API key, name it, and pick the organization it will act in.
- Copy the key (
sk_…) immediately — it is shown exactly once and never stored by MCPJam in retrievable form.
Scope
A key is bound to one MCPJam organization at creation and acts as you, inside that organization. Per-request authorization still applies: a call only succeeds if the key’s owner can access that project and server. A key can never reach projects outside its organization.Two deliberate restrictions while in preview:
- API keys cannot manage API keys. Requests to the key-management surface
with an
sk_…bearer fail with403 FORBIDDEN. Create and revoke keys in the UI. - Guest sessions cannot use the API. Sign in to create keys.
Keep keys secret
Treat an API key like a password:- Load it from an environment variable or secret manager — never commit it to source control or ship it in client-side code.
- Scope keys narrowly: one key per integration, named so you can tell them apart.
- Rotate by creating a replacement key, switching traffic, then revoking the old one.
- If a key leaks, revoke it immediately in Settings → API keys.
401 UNAUTHORIZED with details.reason: "ORPHANED_KEY", the key
is no longer bound to an organization and cannot be used — create a new key
from Settings.
Conventions
Requests. Operations on servers arePOST with a JSON body (most accept
an empty {}). Reads — the catalog listings and eval-run polling — are
GET and take their options (cursor, limit, filters) as query
parameters.
Responses. Three envelope shapes, used consistently:
Pagination. Collections are cursor-based. Pass the previous response’s
nextCursor as cursor in the next request (body field on POST lists,
query parameter on GET lists). Cursors are opaque — don’t parse them.
Authoring vs running suites. POST /eval-suites creates a runnable suite
(the suite plus its test cases) synchronously and responds 201 with the
suiteId, WITHOUT running anything — author once, run later. POST /eval-runs
is the async counterpart: it validates and creates a run synchronously, then
detaches execution and responds 202 with a runId. Poll
GET /eval-runs/{runId} until status is completed, failed, or
cancelled.
IDs. Every identifier the API takes is discoverable through the API
itself: GET /projects lists your projects, GET /projects/{projectId}/servers
lists each project’s servers, and GET /projects/{projectId}/eval-suites
lists its eval suites. Start from GET /me to confirm which account a key
acts as.
Errors
Errors always use the canonical body{ code, message, details? }. The code
is stable and machine-readable; message is human-readable and may change;
details is an optional, unstructured bag.
New error codes may be added over time; treat unknown codes as
non-retryable failures unless the HTTP status says otherwise.
Rate limits
Each key gets 60 requests per minute sustained, with bursts up to 10. Exceeding it returns429 RATE_LIMITED with a Retry-After header (in
seconds):
Retry-After, add jittered exponential backoff, and expect these limits
to be tuned during the preview.
Endpoints
Each endpoint is fully documented — request and response schemas, examples, and an interactive playground — under Endpoints in the sidebar. Catalog — discover the IDs everything else takes:
Project environments — the named execution bundles (one host, optionally a
standalone server group, optionally pinned skills and plugin versions) that eval
suites and journeys run against. Reads need project membership; every write
needs project admin. Writes are revisioned: pass the
revision you last read
back as expectedRevision, and a stale value returns CONFLICT (409) instead
of overwriting a concurrent edit.
Archive and restore are explicit sub-actions rather than a
DELETE because the
row is kept and restore has real semantics: it re-checks the name and drops
plugin pins whose version no longer exists.
Server diagnostics & primitives (/projects/{projectId}/servers/{serverId}/...):
Eval runs (
/projects/{projectId}/...):
Eval result ingestion (
/projects/{projectId}/eval-ingest/...) — how @mcpjam/sdk saves results from eval runs executed outside the platform (local dev, CI). The {projectId} segment accepts the literal default for the key org’s Default project. Most users never call these directly — set MCPJAM_API_KEY and the SDK reporter does:
Agent (
/projects/{projectId}/agent) — a headless assistant turn for
building conversational surfaces (the MCPJam Slack app is the first consumer):
Chatboxes (
/projects/{projectId}/chatboxes...) — read-only:
The same surface is available as an OpenAPI
specification
for Postman, Swagger UI, or client generation.
Run evals from the API
The shortest useful loop — create a run with one inline test, then poll:model takes an id from the hosted catalog — provider/name form, e.g.
anthropic/claude-haiku-4.5 — and runs on your organization’s credits.
Provider-native ids (e.g. claude-sonnet-4-5) are bring-your-own-key: pass
the key in modelApiKeys. A model the API can’t execute is rejected at
create time with VALIDATION_ERROR; details.hostedModels lists the valid
hosted ids for that provider.
Reruns are even shorter: { "suiteId": "..." } reruns the suite exactly as
configured, connecting the suite’s saved server selection (the 202 response
lists the resolved servers). Pass serverIds to override the selection; a
suite with no saved selection requires it (VALIDATION_ERROR with
details.reason: "NO_SAVED_SERVER_SELECTION" otherwise).
Running against a project environment
A project environment is launchable only through a suite that has it attached. Attach them first withPATCH /projects/{projectId}/eval-suites/{suiteId} and an environmentIds
array (send null to detach them all; [] is rejected — use null), then
pass environmentId on the run. It must be one of that suite’s attached
environments; anything else is a 400 with
details.reason: "ENVIRONMENT_NOT_ATTACHED", raised before any case is
authored or any server connected.
Omitting environmentId is meaningful, and depends on the suite:
- no attached environments → the run uses the saved server selection, as before;
- exactly one attached → that environment is used automatically. The
202response’senvironmentfield says so; - several attached →
400withdetails.reason: "ENVIRONMENT_REQUIRED", naming the candidates.
serverIds is not required and is
rejected alongside it (400), rather than accepted and ignored: honoring
both would connect a different set than the run is stamped with. The same goes
for a serverIds override on an environment-based suite
(details.reason: "ENVIRONMENT_SERVERS_NOT_OVERRIDABLE").
The run is pinned to the environment revision resolved at launch: if the
environment changes in between, the run is rejected with CONFLICT (409)
rather than executing against a different configuration than the one whose
tools were captured. Use
GET /projects/{projectId}/environments/{environmentId}/resolve to see what it
will connect before launching.
Every run records which environment it used. GET .../eval-runs/{runId} (and
the run listings) return environment: { id, name, revision } — read from the
run’s immutable snapshot, not the suite’s current attachments — or null for a
run that used a saved server selection.
Per-organization
concurrency is capped (default 2 concurrent runs); exceeding it returns
429 with details.reason: "CONCURRENT_RUN_LIMIT" — wait for an active run
to finish.
If a server answers 401 OAUTH_REQUIRED, complete the OAuth flow yourself
(the SDK’s runOAuthLogin handles interactive, headless, and
client-credentials flows) and push the result to
POST .../oauth/import-tokens once. Subsequent calls inject the stored token
and refresh it server-side.
Not in the API yet
To set expectations while in preview, these are not available over the API today (most exist in the hosted inspector UI):- Creating or revoking API keys (UI-only by design — see Authentication)
- Chat and conformance suites
- Browser-based OAuth flows initiated by the API (use
oauth/import-tokensafter completing OAuth yourself)
Versioning & stability
- The API is path-versioned (
/api/v1). When it reaches general availability, breaking changes will require a new version path. - During the preview, breaking changes to v1 may still happen; we’ll note them in the changelog.
- Additive changes — new endpoints, new optional request fields, new response fields, new error codes — are considered non-breaking and can ship at any time. Write clients that ignore unknown fields.
- Error codes are stable identifiers; error messages are not.

