Skip to main content

Providers

4 operations under /v1/providers (GET POST): parameters, status codes, refusal bodies, and the same request in curl, HTTP, Python and TypeScript.

8 min read
View MarkdownEdit on GitHub

Base URL: https://api.codespar.dev

Every operation below requires a Bearer token. See Authentication.

GET /v1/providers

GEThttps://api.codespar.dev/v1/providers

List the MCP servers this project can attach to a session

The catalog POST /v1/sessions draws its servers ids from. total is everything visible to this project, filtered is what survived the query parameters, and servers is the filtered page; the route does not paginate, so filtered and servers.length agree.

Visibility is per project: another project's generated (gen_-prefixed) servers are absent, global catalog rows always present. Presence here is NOT permission to invoke — that is gated separately on a connected account, which is what POST /v1/connect/start establishes.

Query parameters

NameTypeRequiredDescription
categorystringno—
countrystringno—
qstringno—

Responses

StatusBodyDescription
200objectOK
400objectBad Request — the body or query did not match the schema.

Response 200

FieldTypeRequiredDescription
filteredintegeryes—
serversarray of ServerCatalogRowyes—
totalintegeryes—
Example request
curl -X GET https://api.codespar.dev/v1/providers \
  -H "Authorization: Bearer $CODESPAR_API_KEY"
GET /v1/providers HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEY
import os
import requests

res = requests.get(
    "https://api.codespar.dev/v1/providers",
    headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()
const res = await fetch("https://api.codespar.dev/v1/providers", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
  },
});

const data = await res.json();
const result = await cs.api.get("/v1/providers");
Example response 200
application/json
{
  "total": 0,
  "filtered": 0,
  "servers": [
    {
      "id": "servercatalogrow_0000000000000000",
      "name": "Example",
      "pkg": "string",
      "category": "string",
      "country": "string",
      "auth_type": "string",
      "tools_count": 1,
      "description": "string",
      "status": "string",
      "provider_homepage": "string",
      "provider_logo_url": "https://example.com/hook",
      "provider_logo_fallback_url": "https://example.com/hook",
      "provider_docs_url": "https://example.com/hook",
      "sandbox_available": true,
      "sandbox_url": "https://example.com/hook",
      "description_pt_br": "string",
      "subaccount_provisionable": true,
      "engine_provisionable": true,
      "engine_status": "none",
      "connect_fields": 0,
      "connectable": true
    }
  ]
}

GET /v1/providers/{slug}

GEThttps://api.codespar.dev/v1/providers/{slug}

Read one provider

The same enriched row the list returns, by slug — {slug} is the catalog primary key, the value GET /v1/providers/{slug}/auth-schema also takes. 404 for another project's generated (gen_-prefixed) provider: the list already hides it, so answering differently here would leak what the list conceals.

Responses

StatusBodyDescription
200ServerCatalogRowOK
404objectNot Found

Response 200

FieldTypeRequiredDescription
auth_typestringyes—
categorystringyes—
connect_fieldsintegeryesHow many fields this provider's connect form has: the length of fields on its auth-schema. 0 for oauth and none.
connectablebooleanyesWhether the dashboard can connect this provider itself: a credential form with at least one field that POST /v1/connections accepts, or an oauth provider with an authorization config. False for none, which needs no connection, for a form type whose catalog row declares no refs, and for Coinbase CDP (cdp, jwt_ecdsa) while the deployment holds CDP self-serve connect.
countrystringyes—
descriptionstring,nullyes—
description_pt_brstring,nullyesdescription in Brazilian Portuguese, a faithful translation kept in the catalog. Null where the catalog has none; read description then.
engine_provisionablebooleanyesHas an engine provisioning descriptor AND is not quarantined.
engine_status"none" | "available" | "quarantined"yesCarries the reason engine_provisionable is false.
idstringyes—
namestringyes—
pkgstringyes—
provider_docs_urlstring,nullno—
provider_homepagestring,nullno—
provider_logo_fallback_urlstring,nullno—
provider_logo_urlstring,nullno—
sandbox_availablebooleanno—
sandbox_urlstring,nullno—
statusstringyes—
subaccount_provisionablebooleanyes—
tools_countintegeryes—
Example request
curl -X GET https://api.codespar.dev/v1/providers/{slug} \
  -H "Authorization: Bearer $CODESPAR_API_KEY"
GET /v1/providers/{slug} HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEY
import os
import requests

res = requests.get(
    "https://api.codespar.dev/v1/providers/{slug}",
    headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()
const res = await fetch("https://api.codespar.dev/v1/providers/{slug}", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
  },
});

const data = await res.json();
const result = await cs.api.get("/v1/providers/{slug}");
Example response 200
application/json
{
  "id": "servercatalogrow_0000000000000000",
  "name": "Example",
  "pkg": "string",
  "category": "string",
  "country": "string",
  "auth_type": "string",
  "tools_count": 1,
  "description": "string",
  "status": "string",
  "provider_homepage": "string",
  "provider_logo_url": "https://example.com/hook",
  "provider_logo_fallback_url": "https://example.com/hook",
  "provider_docs_url": "https://example.com/hook",
  "sandbox_available": true,
  "sandbox_url": "https://example.com/hook",
  "description_pt_br": "string",
  "subaccount_provisionable": true,
  "engine_provisionable": true,
  "engine_status": "none",
  "connect_fields": 0,
  "connectable": true
}

GET /v1/providers/{slug}/auth-schema

GEThttps://api.codespar.dev/v1/providers/{slug}/auth-schema

The connect form for one provider

The form to render so an operator can connect this provider, and never a credential. The vault is write-only from this direction: no submitted value is readable back through any operation in this document.

fields is empty for oauth (the connect flow is a redirect, not a form), for none (there is nothing to hold) and for any provider whose auth mechanism carries no per-field input. An empty array therefore means "no form", not "not configured".

base_url and oauth_authorize_url are resolved for the environment the credential authenticates in, so what is previewed here is the host the call will actually reach.

Requires the servers:read scope.

Path parameters

NameTypeRequiredDescription
slugstringyesThe provider slug, which is also the catalog primary key.

Responses

StatusBodyDescription
200objectOK
404objectNot Found — no such provider for this caller. A generated provider belonging to another project answers here too, identically to one that does not exist: the listing already hides it, so answering differently would leak what the listing conceals.

Response 200

FieldTypeRequiredDescription
auth_type"api_key" | "path_secret" | "oauth" | "cert" | "none" | "hmac_signed" | "jwt_ecdsa" | "two_header" | "cdp"yesThe provider's declared auth mechanism, taken straight from the column. Nine values, which is the column constraint's set and NOT the six the shared types package publishes: three of these nine are wire values the published schema would reject, so validate against this list rather than against a generated type of that field.
base_urlstringyesThe provider host for this environment, or an empty string when the catalog holds no endpoint row for it.
environment"live" | "test"yesThe environment the credential authenticates in. Not a parameter of the request.
fieldsarray of objectyesIn the order the form should render them.
oauth_authorize_urlstring,nullyesWhere an OAuth connect lands the operator. Null for every other mechanism.
server_idstringyesEcho of the slug. The catalog primary key IS the slug — there is no second id.
Example request
curl -X GET https://api.codespar.dev/v1/providers/{slug}/auth-schema \
  -H "Authorization: Bearer $CODESPAR_API_KEY"
GET /v1/providers/{slug}/auth-schema HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEY
import os
import requests

res = requests.get(
    "https://api.codespar.dev/v1/providers/{slug}/auth-schema",
    headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()
const res = await fetch("https://api.codespar.dev/v1/providers/{slug}/auth-schema", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
  },
});

const data = await res.json();
const result = await cs.api.get("/v1/providers/{slug}/auth-schema", {
  path: {
    slug: "example"
  }
});
Example response 200
application/json
{
  "server_id": "srv_0000000000000000",
  "auth_type": "api_key",
  "environment": "live",
  "base_url": "https://example.com/hook",
  "oauth_authorize_url": "https://example.com/hook",
  "fields": [
    {
      "name": "Example",
      "kind": "api_key",
      "label": "Example",
      "header_name": "Example"
    }
  ]
}

POST /v1/providers/{slug}/verify-connection

POSThttps://api.codespar.dev/v1/providers/{slug}/verify-connection

Test one provider connection's credential

Issue one known-safe read against the provider with the connection's own stored credential, so an operator learns that a credential does not authenticate here instead of learning it on an agent's first real call.

The check is a read on both of its lanes. A provider with a bespoke recipe gets that recipe's GET; every other provider falls back to re-running the read its own catalog entry declares as its probe, which is the same authenticated read that confirms a freshly provisioned sandbox. Which lane ran is visible in the body: only a recipe extracts an account.

READ THE OUTCOME FROM ok, NOT FROM THE STATUS. Every failure body carries ok: false and a stable error; the status is a mapping from that code and is the less precise of the two. One status in particular carries two different bodies — see the 404.

EXACTLY ONE of the seven failures is a verdict about the credential: provider_rejected. The other six are the absence of a verdict, and they are not interchangeable with a bad credential. redirect_not_followed is the subtle one: the configured host answered by pointing somewhere else, the probe deliberately does not follow it, and so no answer about the credential was obtained from anyone.

Requires the connections:write scope. It is the scope the connection family carries, and it is wider than this operation needs: nothing in this project is written by asking a provider to judge a credential.

Path parameters

NameTypeRequiredDescription
slugstringyesThe provider slug, which is also the catalog primary key.

Responses

StatusBodyDescription
200objectOK. The credential authenticated at the provider. latency_ms is the probe's own round trip.
401objectThe PROVIDER rejected the stored credential — a verdict, and the only failure here that is one. detail carries the provider's own words. This 401 never describes the caller's own credential: one that does not authenticate is refused before the handler runs.
404object | objectNot Found, and this status carries TWO different bodies. A generated provider belonging to another project is refused before the check runs, with the bare { error, server_id } shape and no ok key. A provider that has a verify recipe but no catalog row reaches the checker and comes back as an ordinary failure with ok: false and error: "server_unknown". Branch on ok, not on the status.
409objectredirect_not_followed. The configured host answered with a redirect, which the probe does not follow, so the credential was never judged. Retrying cannot resolve it and reconfiguring can — which is why it is not a 502.
424objectnot_connected. There is no usable credential to test: no active connection for this project and provider, or the vault returned nothing for the reference it holds. This body carries the extra hint / alternative steer. Or connection_expired: the project has an authorization-code OAuth connection whose access token passed its expiry, and nothing refreshes it, so the fix is to reconnect. That body carries expires_at and no sandbox steer. Or shared_sandbox_live_project_refused: the project's connection for this provider is the platform's shared SANDBOX credential, which serves test projects only, and this is a live project (or a live-mode call). Nothing was sent to the provider; the fix is to connect the project's own credential.
501objectverify_unsupported. Nothing ships that can check this provider — no recipe and no declared probe. The first real call will report authentication errors directly.
502objectprovider_unreachable, and it is WIDER than its name. It is returned for a transport failure or timeout, and also for EVERY provider response that is not 2xx, 3xx, 401 or 403 — a provider 400 or 404 arrives here too. Read status before retrying: absent means nothing was reached and a retry is reasonable; a 4xx there means the provider answered and will answer the same way again.
503objectendpoint_missing. The provider is known but its catalog entry cannot be called as configured. An operator fixes this; a caller cannot.

Response 200

FieldTypeRequiredDescription
accountobjectnoAbsent when the check ran through the generic probe rather than a provider-specific recipe: only a recipe extracts an account summary. Its absence is not a weaker pass.
latency_msintegeryes—
oktrueyes—
providerstringyes—
Example request
curl -X POST https://api.codespar.dev/v1/providers/{slug}/verify-connection \
  -H "Authorization: Bearer $CODESPAR_API_KEY"
POST /v1/providers/{slug}/verify-connection HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEY
import os
import requests

res = requests.post(
    "https://api.codespar.dev/v1/providers/{slug}/verify-connection",
    headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()
const res = await fetch("https://api.codespar.dev/v1/providers/{slug}/verify-connection", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
  },
});

const data = await res.json();
const result = await cs.api.post("/v1/providers/{slug}/verify-connection", {
  path: {
    slug: "example"
  }
});
Example response 200
application/json
{
  "ok": true,
  "provider": "string",
  "account": {
    "id": "obj_0000000000000000",
    "name": "Example",
    "extras": {}
  },
  "latency_ms": 0
}
Providers | CodeSpar