Skip to main content

codai-sdk — Python SDK

PyPI version Python versions license

Official Python client for the codai AI gateway. Zero dependencies (stdlib only), Python 3.9+.

pip install codai-sdk

The package is codai-sdk. pip install codai installs an unrelated project from another author that uses the same import name and will shadow this SDK. If from codai import Codai fails, run pip uninstall codai and pip install codai-sdk.

You need a codai API key — keys start with codai_. Create one at hub.codai.ro and export it as CODAI_API_KEY. The import name is codai:

import os

from codai import Codai

client = Codai(api_key=os.environ["CODAI_API_KEY"], session_id="my-project")

# Chat (OpenAI-compatible, smart-routed)
result = client.chat([{"role": "user", "content": "Explain asyncio.gather"}])
print(result.content)
print(result.routed_to)   # which model actually served

# Streaming
for delta in client.chat_stream([{"role": "user", "content": "hi"}]):
    print(delta, end="", flush=True)

# Server-side agent loop
run = client.agents_run("Find and summarize the TODOs in this codebase")
print(run.result)

# Feedback (improves routing for everyone)
client.feedback(result.request_id, 1)

codai extensions

Option Effect
session_id Session memory + routing stickiness
agent_mode=True Plan-and-execute loop (Pro+)
compact="auto" Server-side context compaction
best_of=0/3 Disable / force best-of-N ensembling

Resource groups (0.2.0)

Every gateway operation is a method on the client, grouped exactly like the TypeScript SDK (camelCase → snake_case). The 0.1.x helpers above still work — client.chat([...]) and client.chat.completions.create({...}) are the same call. Every method takes an optional ext={...} bag of X-Codai-* headers (effort, thinking, thinking_budget, cache, no_task, task_id, device, compact, best_of, share_token, … plus raw headers).

Attribute Methods Gateway
chat.completions create, stream POST /v1/chat/completions
messages create, stream POST /v1/messages (Anthropic wire)
responses create, stream POST /v1/responses (OpenAI Responses wire)
embeddings create POST /v1/embeddings
audio transcribe, transcribe_detailed, speech, speech_detailed /v1/audio/*
tokens create POST /v1/tokens
models list GET /v1/models
health get, ready, status /health, /health/ready, /status
agents run; runs.create/get/steps/stats/cancel/stream /v1/agents/*
tools search, fetch /v1/tools/*
tasks list, pending, stats, get, confirm /v1/tasks/*
sessions create, list, get, update, delete, dispatch, stream; events.*, controls.*, lease.*, shares.* /v1/sessions/*
devices list, update, delete, dispatch_inbox /v1/devices/*
hosts list, exec, post_result, stream /v1/hosts/*
orgs create, list; members.list/add/remove /v1/orgs/*
account get, update /v1/account
receipt get GET /v1/receipt
feedback submit POST /v1/feedback
phone_models list GET /v1/phone/models
billing get, invoices, watch_balance (read-only) /v1/billing, /v1/billing/invoices
referrals get, redeem, set_consent GET /v1/referrals, PATCH /v1/account
resolve submit, get, list, iterate, accept, balance, attestation, keys, wait_for https://resolve.codai.ro/v1/resolve/*
from codai import Codai, CodaiError

client = Codai(api_key="codai_...", device="5dc0de00-0000-4000-8000-00000000c0da")

# Chat with extension headers; raw chunks + aggregate on streams
r = client.chat.completions.create(
    {"model": "codai", "messages": [{"role": "user", "content": "hi"}]},
    ext={"effort": "high", "thinking": True, "thinking_budget": 8192},
)
print(r.content, r.routed_to, r.usage)

stream = client.chat.completions.stream({"messages": [{"role": "user", "content": "hi"}]})
for delta in stream:            # text deltas; stream.chunks() for raw chunk dicts
    print(delta, end="")
print(stream.final.usage, stream.final.tool_calls)

# Anthropic / OpenAI Responses wires
client.messages.create({"messages": [{"role": "user", "content": "hi"}], "max_tokens": 200}).text
client.responses.create({"input": "hi"}).output_text

# Async agent run + SSE progress
run = client.agents.runs.create({"task": "Summarise the repo README."})
for ev in client.agents.runs.stream(run["id"]):
    if ev["event"] == "done":
        print(ev["data"]["status"], ev["data"]["result"])

# Tasks, account, receipt
page = client.tasks.list(limit=20)
print(client.account.get()["plan"], client.receipt.get(since="2026-09-01T00:00:00Z"))

# Shared sessions (needs a device id on the client) and hosts
s = client.sessions.create({"title": "pairing"})
for ev in client.sessions.stream(s["id"], after=0):
    print(ev["event"], ev["data"].get("seq"))
print(client.hosts.list())

try:
    client.orgs.members.add("org-1", {"user_id": "u", "role": "member"})
except CodaiError as e:
    print(e.status, e.code, e.request_id, e.retry_after)

Typed request/response shapes live in codai._types (generated from the OpenAPI spec — TypedDicts such as ChatCompletionRequest, Task, SessionEvent, CreateSessionShareResponse).

Lists and the share sheet

client.lists covers /v1/lists/* and POST /v1/share: list, create, reorder, update, delete, items, add, update_item, delete_item, image (ListItemImage with data + content_type), export_markdown (str), share and stream (live list events). add and share are idempotent on client_id / the canonical URL: a duplicate answers 200 with created: False.

shared = client.lists.share({"url": "https://example.com", "auto": True})
md = client.lists.export_markdown(shared["list"]["id"])
for ev in client.lists.stream():
    print(ev["op"], ev.get("listId"), ev.get("itemId"))

client.schedules.audit(schedule_id=None, limit=None) returns the scheduler audit log (GET /v1/schedules/audit), newest first.

codai Resolve (0.4.0)

Pay per verified fix: submit a public GitHub issue, get a flat quote, accept it (held on your wallet), and pay only when the fix passes your tests. First 3 fixes are free. Lives on https://resolve.codai.ro (same key; resolve_base_url= to override).

from codai import Codai, ResolveInsufficientFundsError

client = Codai(api_key="codai_...")
job = client.resolve.submit({
    "repo_url": "https://github.com/owner/repo",
    "issue_url": "https://github.com/owner/repo/issues/42",
})
if job["status"] == "quoted":
    try:
        client.resolve.accept(job["id"])
    except ResolveInsufficientFundsError as e:
        print("top up first:", e.topup_url, e.bundle_url or "")  # bundle_url: t7 only
        raise
done = client.resolve.wait_for(job["id"])
print(done["status"], done.get("attestation_url"))

Failed attempts and infrastructure errors refund the hold in full. Types: codai._resolve_types (generated from resolve.yaml).

Pricing (0.7.0). Your first paid accept holds €1 instead of the list price (the quote then carries intro_price_micro_eur / list_price_micro_eur); it is used up only by a verified fix — a failed intro job is refunded and the intro is offered again. A fix bundle (€49 = 10 credits, https://pay.codai.ro/checkout?bundle=resolve10) pays for t7 jobs only: a credit is taken at accept instead of a wallet hold and returned on failure. Precedence: free > intro > credit (t7) > wallet; accept() reports it in paid_with, a t7 402 sets bundle_url, and client.resolve.balance() returns fix_credits, intro_available and wallet_micro_eur.

Webhooks instead of polling (0.5.0)

Pass callback_url (https, public host) on submit; the response carries callback_secret once — store it. Resolve POSTs job.quoted, job.declined, job.resolved, job.failed and job.error, signed with x-codai-signature: t=<unix>,v1=<hex HMAC-SHA256>. Verify the raw body with verify_resolve_webhook (constant-time, 300 s tolerance by default). Up to 3 attempts on 5xx/429/network errors; deduplicate on x-codai-delivery; delivery never changes job state — get(id) stays the source of truth.

import json
from codai import verify_resolve_webhook

job = client.resolve.submit({
    "repo_url": "https://github.com/owner/repo",
    "issue_url": "https://github.com/owner/repo/issues/42",
    "callback_url": "https://ci.example.com/hooks/codai-resolve",
})
save_secret(job["id"], job["callback_secret"])  # shown once

# in your webhook handler (FastAPI: raw = await request.body())
if not verify_resolve_webhook(secret, raw, request.headers.get("x-codai-signature")):
    raise HTTPException(401, "bad signature")
event = json.loads(raw)  # event["event"], event["job"]["attestation_url"]

Verifying a signed attestation (0.6.0)

Attestations created from 2026-09-27 carry signature: a DSSE envelope (ECDSA P-256/SHA-256) over a canonical statement of sha256 hashes of the patch, repro test and test outputs, plus the commands, base commit, runner digest and timestamps. verify_attestation checks that the signed statement matches the fields you were served (stdlib) and the signature against GET /v1/resolve/keys — the signature step needs pip install cryptography (the SDK stays dependency-free; without it an ImportError explains what to install). Older attestations have signature: None → {"ok": False, "reason": "UNSIGNED"}.

proof = client.resolve.attestation(job["attestation_url"])
r = client.resolve.verify_attestation(proof)  # or keys=pinned_keys
if not r["ok"]:
    raise SystemExit(f"not verified: {r['reason']}")  # BAD_SIGNATURE | STATEMENT_MISMATCH | ...
print("signed by", r["keyid"])

Development

python -m venv .venv && .venv/Scripts/python -m pip install -e ".[dev]"
.venv/Scripts/python -m pytest -q             # unit + OpenAPI parity tests, no network
.venv/Scripts/python scripts/gen-types.py     # regenerate src/codai/_types.py

MIT © codai

Metadata

Release files for codai-sdk 0.67.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for codai-sdk 0.67.0
File Size Uploaded Provenance
codai_sdk-0.67.0.tar.gz 240.5 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for codai-sdk 0.67.0
File Interpreter ABI Platform
codai_sdk-0.67.0-py3-none-any.whl Python 3 none any Details

Total release size: 411.7 kB

Release files / codai_sdk-0.67.0.tar.gz

Download URL codai_sdk-0.67.0.tar.gz
Size 240.5 kB
Tags Source
SHA-256 checksum
How to use checksums
9d0b77604ebf3ec418f08d0ad5b8312fa384ba9c8da6873c5fcd51cd9d549cad
BLAKE2b-256 checksum
How to use checksums
f32c4020710051e6f99e76044fcc396011a67715868b75eeafdbaaaa4d7636bd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.

Transparency log

Release files / codai_sdk-0.67.0-py3-none-any.whl

Download URL codai_sdk-0.67.0-py3-none-any.whl
Size 171.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
09d3a08684e7b1bdaffca95c91b5649a6c59193420a7f1312d81429fda826512
BLAKE2b-256 checksum
How to use checksums
a1e9554e18c54809c698b9f50cb1855ed1b11712eeec49c7f28b9d2cb8b983d7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.

Transparency log

Security for codai-sdk 0.67.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for codai_sdk-0.67.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page