Getting Started
Versioning & stability
What counts as a breaking change, what you can rely on, and how to write a client that survives the API evolving.
Every response carries a version in its metadata:
{
"metadata": {
"timestamp": "2026-07-30T12:00:00.000Z",
"version": "1.0",
"path": "/assistants"
}
}Endpoints are served at the root path with no version prefix. The one exception is the organization-level audit endpoints, which sit under /v1:
POST /v1/retention/audits
POST /v1/churn/auditsTODO — verify before release
The change policy below describes the contract this documentation is written against. It has not yet been ratified by the API team — confirm the deprecation window, the announcement channel, and whether metadata.version will ever be incremented before this page ships.
Write a client that tolerates change#
Regardless of policy, these four habits make an integration durable. They cost nothing today and save a rewrite later.
Ignore unknown fields. Responses are open — new keys can appear on any object. Deserialize permissively; do not fail on a field you have not seen.
Branch on error.code, never on error.message. Codes are the stable contract. Messages are written for humans and may be reworded at any time.
Do not depend on key order or array order unless an endpoint explicitly documents an ordering. Task groups are ordered because order is the feature; assistant lists are not.
Treat IDs as opaque. They are UUIDs today. Parsing them, sorting by them, or inferring creation time from them will break.
interface Envelope<T> {
success: boolean;
data?: T;
error?: { code: string; message: string; details?: unknown };
}
// Narrow to what you use; ignore everything else the API sends.
interface AssistantSummary {
id: string;
name: string;
}
async function listAssistants(): Promise<AssistantSummary[]> {
const res = await fetch("https://core-api.heysadie.ai/assistants", {
headers: { Authorization: `ApiKey ${process.env.ONCORE_API_KEY}` },
});
const body = (await res.json()) as Envelope<{ assistants: AssistantSummary[] }>;
if (!body.success) {
// Branch on the code — the message is not a contract.
switch (body.error?.code) {
case "UNAUTHORIZED":
throw new Error("Check ONCORE_API_KEY");
default:
throw new Error(body.error?.code ?? "UNKNOWN");
}
}
return body.data?.assistants ?? [];
}What is expected to change#
Some parts of the platform move faster than others. Plan accordingly:
| Area | Stability | Why |
|---|---|---|
| Response envelope, error codes | Stable | The contract every endpoint shares |
| Resource IDs and core fields | Stable | Breaking these breaks stored references |
| Voices, models, transcribers | Expected to grow | New providers and voices are added regularly — reference by id, never by display name |
Assistant settings sub-objects | Expected to grow | New tuning options land here first |
| WhatsApp onboarding states | Expected to change | Driven by Meta's platform, not solely by OnCore |
Voice names are not unique
The same voice can appear once per underlying model, so two rows can share a display name. Always store and send voiceId.
Staying current#
- The interactive API explorer is generated from the running API, so it is always current — treat it as the source of truth if this reference ever disagrees.
/llms.txt— a machine-readable index of these docs, for agents and AI tooling.
Next#
- API conventions — pagination, identifiers, idempotency.
- Rate limits — retry and backoff.