Browse the docs
API & SDKs

API reference

A region-scoped REST API and a matching gRPC service. Authenticate with a secret key, send and receive JSON, and page through everything the SDKs expose.

Every OrthID capability is reachable over HTTP. The REST API is the primary surface and is what the typed SDKs wrap; the gRPC service mirrors it for low-latency, server-to-server callers. Both speak the same resources, scopes and error model, so what you learn here applies to the SDKs too.

Base URL

There is no global endpoint. Each region is addressed directly, which is how OrthID keeps requests inside their sovereign boundary:

Base URL
https://api.<region>.orthid.com

# for example
https://api.au-syd-1.orthid.com
https://api.eu-fra-1.orthid.com

A key issued in one region is only valid against that region’s host. Sending it elsewhere returns region_mismatch. See Regions for the full list.

Authentication

Authenticate server-to-server with your secret key as a Bearer token. Keep ORTHID_SECRET_KEY on the server only - it must never reach the browser, where you use the publishable key instead.

Terminal
curl https://api.au-syd-1.orthid.com/v1/users/user_5f3a \
  -H "Authorization: Bearer $ORTHID_SECRET_KEY"

The typed SDK reads the same key and region from the environment:

users.ts
import { orthid } from "@orthid/sdk";

// Reads ORTHID_SECRET_KEY and ORTHID_REGION from the environment.
const user = await orthid.users.get("user_5f3a");

console.log(user.id, user.email);

Core endpoints

Resources are organised around the three actors - users, organisations and agents - plus sessions and audit. These are the most-used routes; the SDKs cover the full set.

MethodPathDescription
GET/v1/usersList users in the tenant.
GET/v1/users/:idRetrieve a single user.
POST/v1/organizationsCreate an organisation.
POST/v1/organizations/:id/membersAdd a member to an organisation.
POST/v1/sessions/verifyVerify a session token and return its actor.
POST/v1/agentsIssue a scoped, expiring agent credential.
GET/v1/auditPage through the immutable audit log.

Pagination

List endpoints are cursor-paginated. Pass limit (default 25, max 100) and follow next_cursor until it is null. Cursors are opaque and stable; do not construct them yourself.

Response
{
  "data": [ { "id": "user_5f3a", "email": "ada@example.org" } ],
  "has_more": true,
  "next_cursor": "eyJpZCI6InVzZXJfNWYzYSJ9"
}

Errors

Errors use standard HTTP status codes with a typed JSON body. The code field is stable and safe to branch on; the message is for humans and may change.

Error response
HTTP/1.1 403 Forbidden

{
  "error": {
    "code": "insufficient_scope",
    "message": "Token is missing the required 'users:read' scope.",
    "request_id": "req_8b21d0"
  }
}

Common codes:

  • 401 invalid_key - the secret key is missing, malformed or revoked.
  • 403 insufficient_scope - the actor lacks a required scope.
  • 404 not_found - no such resource in this tenant.
  • 409 region_mismatch - the key does not belong to this region.
  • 429 rate_limited - too many requests; back off.

Always log request_id - quoting it lets support trace a single call through the audit log.

Rate limits

Limits are applied per secret key. Each response carries your current budget so you can throttle yourself before you are throttled:

Response headers
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 597
X-RateLimit-Reset: 1750550400
Handle 429 with backoff
On a 429, read Retry-After and retry with exponential backoff and jitter. The SDKs do this for you; if you call the REST API directly, build it in from the start.

Next steps