co/core
Developer docsDocs
Jump to
API reference

Inference API

OpenAI-compatible HTTP endpoints at https://cocore.dev/api/v1. Authenticated routes accept a co/core API key as Authorization: Bearer ….

Base URLhttps://cocore.dev/api/v1
/chat/completionspostauth: required

OpenAI-compatible chat completion. Routes to an attested provider serving the requested model. Set country to an ISO 3166-1 alpha-2 code (e.g. US) to route only to providers advertising that region — an advisory provider self-claim — failing closed with no_providers_for_country when none match. Set min_provider_version (e.g. 0.9.32) to require a minimum provider release, failing closed with no_providers_for_version when none qualify (see Provider version below).

ParamType
model*string
messages*array
streamboolean
max_tokensinteger
countrystring
min_provider_versionstring
curl
API key
model
user message
max_tokens
country
min_provider_version
curl -sS -X POST -H 'Authorization: Bearer $COCORE_API_KEY' 'https://cocore.dev/api/v1/chat/completions' -H 'Content-Type: application/json' -d '{"model":"stub","messages":[{"role":"user","content":"Hello"}],"stream":false,"max_tokens":256}'
response

Paste an API key above, then run the example.

/modelsgetauth: none

Public model directory. Default response matches OpenAI's list shape; use view for co/core-specific detail.

ParamType
viewstring
curl
view
curl -sS 'https://cocore.dev/api/v1/models'
response

Click Run example to fetch a live response.

/private/chat/completionspostauth: required

Same request shape as chat/completions, but routing is limited to providers run by DIDs on your friends list. country still narrows by region.

ParamType
model*string
messages*array
streamboolean
max_tokensinteger
countrystring
min_provider_versionstring
curl
API key
model
user message
max_tokens
country
min_provider_version
curl -sS -X POST -H 'Authorization: Bearer $COCORE_API_KEY' 'https://cocore.dev/api/v1/private/chat/completions' -H 'Content-Type: application/json' -d '{"model":"stub","messages":[{"role":"user","content":"Hello"}],"stream":false,"max_tokens":256}'
response

Paste an API key above, then run the example.

/verified/chat/completionspostauth: required

Same request shape as chat/completions, but routing is limited to providers whose attestation is cryptographically verified (recomputed from the signed Apple-rooted attestation, not the self-asserted label). Set min_trust to "hardware-attested" (default) or "confidential" to pick the floor. Fails closed with no_verified_providers when none qualify. country still narrows by region.

ParamType
model*string
messages*array
min_truststring
streamboolean
max_tokensinteger
countrystring
min_provider_versionstring
curl
API key
model
user message
min_trust
max_tokens
country
min_provider_version
curl -sS -X POST -H 'Authorization: Bearer $COCORE_API_KEY' 'https://cocore.dev/api/v1/verified/chat/completions' -H 'Content-Type: application/json' -d '{"model":"stub","messages":[{"role":"user","content":"Hello"}],"stream":false,"max_tokens":256}'
response

Paste an API key above, then run the example.

/probono/chat/completionspostauth: required

Same request shape as chat/completions, but routing is limited to providers whose proBono policy elects to serve YOU for free (mode any, or mode direct with your DID listed). A matched job is unmetered, zero-price, and takes no exchange cut, so a balance-less requester can still get a completion. Fails closed with no_pro_bono_providers (503) when no connected provider currently offers you pro bono, or pro_bono_lookup_failed (502) when the provider lookup itself fails. country still narrows by region.

ParamType
model*string
messages*array
streamboolean
max_tokensinteger
countrystring
min_provider_versionstring
curl
API key
model
user message
max_tokens
country
min_provider_version
curl -sS -X POST -H 'Authorization: Bearer $COCORE_API_KEY' 'https://cocore.dev/api/v1/probono/chat/completions' -H 'Content-Type: application/json' -d '{"model":"stub","messages":[{"role":"user","content":"Hello"}],"stream":false,"max_tokens":256}'
response

Paste an API key above, then run the example.

Image input

Vision-capable models accept images alongside text using OpenAI's multimodal content parts. Carried inline as base64 or fetched from a URL; no separate upload step.

Any chat/completions route (open, private, and verified) accepts the OpenAI array-of-parts content shape. A message is either a plain string (text only) or an ordered list of text and image_url parts. There is no separate upload endpoint — images travel inside the request body.

{
  "model": "your-vision-model",
  "messages": [
    {
      "role": "user",
      "content": [
        { "type": "text", "text": "What's in this image?" },
        {
          "type": "image_url",
          "image_url": {
            "url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg..."
          }
        }
      ]
    }
  ]
}

The image_url.url field takes one of two forms:

  • Inline base64 data URI data:<mime>;base64,<payload>. The MIME type must be image/* (e.g. image/png, image/jpeg). The bytes are committed directly into the signed job, so the receipt verifies offline with no extra fetch.
  • Remote URL — an http(s):// link. co/core fetches it server-side, verifies the response is image/*, and inlines it as base64 before sealing, so the input stays self-contained.

Limits

  • Up to 20 MiB of decoded image bytes per request, budgeted separately from the 1 MiB text limit.
  • Up to 256 messages per request; images may be spread across them.
  • Images only. Non-image MIME types and arbitrary file attachments (PDFs, documents, audio) are rejected — there is no general file-upload part today.

Model support

Image input is not gated per model: any model id you can route to will accept an image-bearing request. Whether the image is actually understood depends on the model — send images to a vision/multimodal model (the models directory flags vision-capable models). A bad or unparseable image_url with no accompanying text returns 400 invalid_request_error.

Tool calling

Send standard OpenAI tools and receive structured tool-call intents from canary-verified provider/model pairs; requester clients execute every tool.

Pass OpenAI-compatible tools and optional tool_choice. A capable model may return tool_calls with finish_reason: "tool_calls". The provider generates only the structured intent; it never executes a tool. Your client executes it and sends the result back as a tool-role message. Upgraded eligible machines with no stored preference attempt this by default; owners can opt out per machine without disabling ordinary inference.

{
  "model": "mlx-community/Qwen3.5-4B-MLX-4bit",
  "messages": [{ "role": "user", "content": "Check Amsterdam weather" }],
  "tools": [{
    "type": "function",
    "function": {
      "name": "get_weather",
      "parameters": {
        "type": "object",
        "properties": { "city": { "type": "string" } },
        "required": ["city"]
      }
    }
  }]
}

Support is live and per model, not inferred from catalog metadata. The models directory reports tools: true only when a connected machine passed a forced-tool startup canary for that exact model. If none did, the request fails with 400 tool_calls_not_supported; ordinary requests still route normally.

Provider version

Pin a minimum tray-provider release so your request only reaches machines new enough to honor a feature, failing closed when none qualify.

Every chat/completions route (open, private, verified, and probono) accepts an optional min_provider_version body field. Set it to a dotted-numeric release (e.g. 0.9.32; an optional leading v is tolerated) to route only to providers running that release or newer. Each tray provider reports its binaryVersion when it registers with the matchmaker; only machines reporting a version greater than or equal to your floor are eligible.

{
  "model": "your-model",
  "messages": [{ "role": "user", "content": "Hello" }],
  "min_provider_version": "0.9.32"
}

Automatic floor for multimodal requests

A request that carries images or tool messages (the messages-v1 envelope) already derives a floor on its own — the first release that both reports its version and parses that envelope (currently 0.9.32) — so an image request never reaches a provider that can't read it. When you also pass min_provider_version, the effective floor is whichever is higher: your explicit pin is never relaxed below the multimodal floor, and the multimodal floor is never relaxed below your pin.

When none qualify

If the model is served but no connected provider runs a new-enough binary, the request fails closed with 503 no_providers_for_version (see dispatch errors). It is capacity-shaped and retryable — capable machines may come online as the fleet updates. A provider that reports no version is treated as below every floor and excluded.

Dispatch errors

Returned when the exchange cannot place your request with a provider.

// 404 — no provider is serving this model
{ "error": { "type": "invalid_request_error", "code": "model_not_found", "message": "..." } }

// 503 — no providers are connected
{ "error": { "type": "service_unavailable_error", "code": "no_providers_connected", "message": "..." } }

// 503 — friends-only, but no friends are online
{ "error": { "type": "service_unavailable_error", "code": "no_friends_available", "message": "..." } }

// 404 — friends-only, but no friend serves this model
{ "error": { "type": "invalid_request_error", "code": "no_friends_for_model", "message": "..." } }

// 503 — country set, but no provider in that region serves this model
{ "error": { "type": "service_unavailable_error", "code": "no_providers_for_country", "message": "..." } }

// 503 — min_provider_version (or a multimodal floor) set, but no connected
//        provider runs a new-enough binary. Retryable as the fleet updates.
{ "error": { "type": "service_unavailable_error", "code": "no_providers_for_version", "message": "..." } }

// 503 — pro-bono route, but no connected provider currently serves you free
{ "error": { "type": "service_unavailable_error", "code": "no_pro_bono_providers", "message": "..." } }

// 502 — pro-bono route, the provider lookup itself failed (try again)
{ "error": { "type": "server_error", "code": "pro_bono_lookup_failed", "message": "..." } }

HTTP errors

Authentication, validation, and upstream failure responses.

{
  "error": {
    "message": "Missing Authorization: Bearer header",
    "type": "authentication_error",
    "code": null,
    "param": null
  }
}
  • 401 authentication_error — missing or invalid API key. Create a new key on /account.
  • 400 invalid_request_error — malformed body (missing model, messages, etc.).
  • 502 server_error — provider disconnected mid-stream. Retrying usually succeeds.