Skip to main content

Integrate Apollia via the driving contract

This guide is for teams embedding an Apollia runtime in a product. It covers the contract your host application drives: the HTTP API, its stability guarantee, authentication, and the generated clients. It assumes you can run an Apollia daemon and want to wire it into an application.

If you just want to see the flow work once, follow Drive Apollia from your product first.

The contract​

An Apollia runtime exposes its capabilities over an HTTP API under /api/v1: submit tasks, open chat sessions, stream results, inspect the audit trail, and manage the runtime.

The daemon started by apollia-os start listens on a Unix socket and on 127.0.0.1:7771. TCP is bound on every start; --port only chooses the number.

The API is described by an OpenAPI 3.1 specification that is generated from the runtime source, so it cannot drift from the code. The runtime serves it at:

GET /api/v1/openapi.json

A committed copy also lives at clients/openapi.json. For a browsable view of every operation, see the HTTP API reference, which is generated from that same specification.

Stability guarantee​

/api/v1 is the versioned contract, and the OpenAPI document of each release describes it exactly. Before 1.0, a release can still remove or change an operation of v1, and each such change is listed with its migration under "Breaking changes and migration" in the changelog. 0.3.0 removed the project routes (among them PUT /api/v1/sessions/{id}/project and GET /api/v1/projects/{id}/sessions, and the project field of POST /api/v1/sessions), the route of the resolved approvals and PUT /api/v1/onboarding/profile. Read that section before you upgrade the runtime under an integration.

Authentication​

Choose the surface that fits your deployment:

  • Unix socket: local-trust. Access is governed by filesystem permissions and no token is required. Use this when the host and the runtime share a machine and a trust boundary.
  • TCP on 127.0.0.1: token-authenticated. Every request must carry Authorization: Bearer <token>. On first boot, when [api] require_token is enabled (the default), the daemon generates a token and writes it to ~/.apollia/api-token with owner-only permissions. Your host reads that file and sends the token.

The runtime the desktop application embeds binds both surfaces too: the Unix socket, and a TCP port on 127.0.0.1 ([api] port, 7771 by default) where the token is enforced.

Use a generated client​

Two host-driving clients are generated from the OpenAPI specification and live under clients/. Both are generated, not hand-written, so they stay in sync with the contract. Do not hand-edit generated files; change the runtime and regenerate.

ClientPathToolchain
TypeScriptclients/tsopenapi-typescript types plus openapi-fetch
Pythonclients/pythonopenapi-python-client

Neither is published to a registry, so there is no install command for either. Both are consumed from a checkout of the repository. @apollia/runtime-client declares its entry point as its own TypeScript sources, so depend on clients/ts by path (a file: dependency, or a workspace entry) and let your bundler compile it; it pulls openapi-fetch. clients/python is generated with --meta none, so it carries no pyproject.toml and pip install has nothing to read: put its parent directory on PYTHONPATH or copy clients/python/apollia_runtime_client into your project, and install httpx and attrs yourself.

TypeScript​

import { readFileSync } from "node:fs";
import { homedir } from "node:os";
import { createApolliaClient } from "@apollia/runtime-client";

const token = readFileSync(`${homedir()}/.apollia/api-token`, "utf8").trim();
const apollia = createApolliaClient({ token }); // baseUrl defaults to 127.0.0.1:7771

const health = await apollia.GET("/api/v1/health");

const submit = await apollia.POST("/api/v1/tasks", {
body: {
agent_id: "coach",
input: { parts: [{ type: "text", text: "hello from the host" }] },
},
});

Every operation is typed against the contract, so request and response shapes are checked at compile time. agent_id names an installed agent; coach is the one Your first agent builds.

Python​

import pathlib
from apollia_runtime_client import AuthenticatedClient
from apollia_runtime_client.api.tasks import submit_task, get_task
from apollia_runtime_client.models import SubmitTaskRequest, SubmitTaskRequestInput

token = (pathlib.Path.home() / ".apollia" / "api-token").read_text().strip()
client = AuthenticatedClient(base_url="http://127.0.0.1:7771", token=token)

resp = submit_task.sync(client=client, body=SubmitTaskRequest(
agent_id="coach",
input_=SubmitTaskRequestInput.from_dict(
{"parts": [{"type": "text", "text": "hello"}]}
),
))
task = get_task.sync(id=resp.task_id, client=client)

Streaming results​

Beyond submit-and-poll, the contract exposes server-sent event streams for long-running work: task output at GET /api/v1/tasks/{id}/stream and chat session output at GET /api/v1/sessions/{id}/stream. GET /api/v1/events streams the event bus of the runtime as a whole, for a host that follows every run rather than one. Consult the HTTP API reference for their event shapes.

Regenerating the clients​

When the runtime contract changes, refresh the clients from the spec:

# From the committed spec:
bash clients/regen.sh

# Or refresh the spec from a running daemon first:
bash clients/regen.sh --from-daemon