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/<product>/… — see Product namespaces
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.

Product namespaces

Every endpoint lives under exactly one product namespace, so the path alone tells you which product you are calling:

Namespace Product Reference
/api/v1/bevm/ EVM auditing tools: sessions and forks, targets, scans, Composer, the contract sandbox, EVM analyses Sessions and forks and the other EVM pages
/api/v1/bsvm/ the same auditing tools for Solana (SVM) programs Solana sessions and programs
/api/v1/workbench/ what both auditing workbenches share: the audit evidence plane, long-running operations, artifacts, your Auditing IDE view Workbench
/api/v1/radar/ chain intelligence: contract code search Radar
/api/v1/workspaces/ workspaces, projects, activity, the contract and audit indexes, invitations, notes, Audit Passport attestations Workspaces and the pages after it
/api/v1/ide/ the Contract IDE Workspaces and files
/api/v1/arch/ the Architecture Explorer Architecture
/api/v1/public/ attestation verification and shared reports, no credential needed Public

A namespace names a product, never the service behind it: bevm, bsvm and workbench are served by the same backend, and that is an implementation detail you never address. Each operation has exactly one path; when one moves, the old path is removed rather than kept as an alias. See Deprecations.

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 :verb — POST /api/v1/bevm/sessions/{uid}/snapshots:restore, POST /api/v1/workbench/operations/{op_id}:cancel, POST /api/v1/public/attestations/verify. The colon may be sent raw or percent-encoded as %3A; both resolve identically.

Beyond that:

  • Every list is paginated the same way. A collection takes page_size (default 50, at most 200 — larger values are clamped, never refused) and page_token, and answers {"data": [...], "has_more", "next_page_token", "page_size"} plus an RFC 8288 Link: rel="next" header. Follow next_page_token until it is null. See Pagination.
  • 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.

It expands every product namespace above: the EVM and Solana auditing tools, the shared workbench, radar contract search, the workspace plane, the Contract IDE, architecture and the public verification endpoints. It lists the canonical path of every operation and nothing else. Operations that require an Idempotency-Key declare it, and each session-scoped operation declares the trilocore-session-tag header.

Prefixes whose contract belongs entirely to the service behind them are listed under x-forwarded-prefixes rather than expanded: sign-in and account management under /api/v1/auth/*, and the AI planes, which are parked.

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 endpoints that require Idempotency-Key, and replay semantics
Errors RFC 9457 problem documents and the full code catalogue
Deprecations What happens when an operation moves, and porting older integrations
Rate limits and quota x-ratelimit-* headers, 429 handling, daily quota
Pagination page_size / page_token, the list envelope, and which lists are not paginated
Endpoint reference Every live endpoint, by product namespace