Skip to content

API overview

Every Trilocore API call is an HTTPS request to https://api.trilocore.ai, carrying one Authorization: Bearer header. There is no separate API host, no per-service hostname, and no SDK requirement — the API is plain HTTP and JSON.

curl https://api.trilocore.ai/api/v1/workspaces \
  -H "Authorization: Bearer $TRILOCORE_API_KEY"
import os
import httpx

r = httpx.get(
    "https://api.trilocore.ai/api/v1/workspaces",
    headers={"Authorization": f"Bearer {os.environ['TRILOCORE_API_KEY']}"},
)
r.raise_for_status()
print(r.json())
const r = await fetch("https://api.trilocore.ai/api/v1/workspaces", {
  headers: { Authorization: `Bearer ${process.env.TRILOCORE_API_KEY}` },
});
if (!r.ok) throw new Error(`${r.status} ${await r.text()}`);
console.log(await r.json());

See Authentication for how to obtain a token.

Base URL and versioning

Base URL https://api.trilocore.ai
Product surface /api/v1/<group>/…
Transport HTTPS only, HTTP/1.1 and HTTP/2
Request and response bodies application/json (errors use application/problem+json)

The version lives in the path, not in a header. v1 is the only version currently served, and it is additive: new endpoints, new optional request fields and new response fields may appear at any time. Treat unknown response fields as forgiving — do not fail a parse on a field you have not seen before.

A single front door serves the whole product. It authenticates the caller once, applies rate limits, quota and idempotency, and forwards the request to the service that owns the resource. Which service that is shows up in the reference as the endpoint's upstream; it is an implementation detail, not something you address directly.

The resource model

The API is a resource API, not an RPC API. Three consequences are worth internalising before you write a client:

Identifiers live in the path. A call names the thing it acts on — POST /api/v1/workspaces/{workspace_id}/attestations — rather than passing an operation name and an id in a body. On the auditing surface the front door additionally validates each path parameter's shape before forwarding, so a malformed address or transaction hash is rejected with 400 validation_failed instead of reaching a service.

Most creates answer 201; exactly one adds a Location. Many POSTs answer 201 Created, and the per-endpoint tables in the endpoint reference state which. Only POST /api/v1/bevm/scans is promoted by the front door to 201 with a Location header and an X-Resource-Id, because that is the one route where the gateway itself derives the identifier. Everywhere else, read the id out of the response body — do not write a client that depends on Location, because it will be absent.

Some operations are custom methods. Where an action does not map onto a noun, the path ends in :verbPOST /api/v1/bevm/snapshots:restore, POST /api/v1/bevm/storage-slots:write, POST /api/v1/public/attestations/verify. The colon may be sent raw or percent-encoded as %3A; both resolve identically.

Beyond that:

  • Pagination is not uniform, so check per endpoint. The auditing surface (/api/v1/bevm/*) emits an RFC 8288 Link header — follow rel="next" rather than constructing offsets — and spells its cursor page_token, with page_size alongside it. The workspace and Contract IDE surfaces do not emit Link; the workspace collections take a plain limit. Treat "no Link header" as "this collection does not paginate that way", never as "there are no more results".
  • Conditional requests work end to end. If-None-Match and If-Match are forwarded to the owning service, and ETag, Cache-Control and Vary come back. Use them for polling: a 304 still counts as a request, but it saves you transferring and re-parsing an unchanged body.
  • Some auditing singletons accept current. Several /api/v1/bevm/* resources are singletons keyed by the caller, and there the literal current may stand in for the id to mean "mine" — which is how a first-time caller addresses a resource that has no id yet. This is a property of the auditing surface only: on /api/v1/workspaces/* and the other prefixes an id must be a real UUID, and current fails the matcher.
  • Unknown resources answer 404, never 403. An identifier that exists but is not yours is indistinguishable from one that does not exist. This is deliberate: it stops the API confirming that another tenant's resource exists.

The machine-readable specification

An OpenAPI 3.1.0 document is served publicly and needs no authentication:

curl https://api.trilocore.ai/api/v1/meta/openapi.json

It is generated from the same routing table that serves traffic, so it cannot drift from the endpoints it covers. It is cacheable (Cache-Control: public, max-age=300) and CORS-open, so a browser tool can fetch it directly.

The served specification is partial — do not treat it as the whole API

The document expands one product group only: the auditing endpoints under /api/v1/bevm/sessions and its siblings. That is 137 paths and 153 operations out of the 363 the API actually serves.

Architecture, Contract IDE, workspaces, projects, activity, contracts, audits, invitations, notes and the public verification endpoints are not in its paths object — several appear only as one-line prefix annotations, and the rest are absent entirely.

Generating a client from this file therefore gives you a client for less than half the API. The endpoint reference covers all of it, and is the source to work from if you need the full surface.

What this API does not serve

Documenting an endpoint that does not answer is worse than omitting it, so two absences are stated plainly rather than left for you to discover:

  • The model plane at /v1/ is mounted but not in service. Unlike the surfaces below, this prefix is routed — an unauthenticated request to it answers 401 rather than 404, which can easily read as "this exists and my token is wrong". It does not currently serve traffic: an authenticated request fails at the upstream. Do not build against it.
  • The fork platform, billing and webhook-receiver surfaces are not mounted at all. They return 404 because no route exists. They are built but not deployed, and there is no behaviour to integrate with today.

Where to go next

Authentication Bearer keys, browser sessions, CSRF, and the headers the front door strips
Idempotency The 13 endpoints that require Idempotency-Key, and replay semantics
Errors RFC 9457 problem documents and the full code catalogue
Rate limits and quota x-ratelimit-* headers, 429 handling, daily quota
Endpoint reference Every live endpoint, by group