Start your 7-day trial No card. Workspace opens immediately.
Start free
Developers

The AttorneyOS API

A plain REST API and signed webhooks, so your accounting package, intranet, intake form or reporting tool can read and write the same clients, matters, time, invoices and payments your firm works with every day. Tokens are minted in Settings; no sales call, no partner programme.

Authentication

Every request carries a bearer token. A firm admin creates tokens under Settings → API and webhooks, choosing read or read and write and an optional expiry. A token acts as the person who created it, inside that firm only, and can be revoked at any time.

curl https://attorneyos.co.za/api/v1/me \
  -H "Authorization: Bearer aos_your_token_here" \
  -H "Accept: application/json"

Base URL https://attorneyos.co.za/api/v1. Responses are JSON. Lists are paginated with ?page and ?per_page (default 25, maximum 100) and return data, meta and links. Dates are ISO 8601 in South African time; money is a decimal string in rand.

Rate limits and errors

120 requests a minute per token. Beyond that you receive 429 with a Retry-After header. Validation problems come back as 422 with an errors object keyed by field; a missing or revoked token is 401; a token without the needed ability, or an inactive firm, is 403; a record outside your firm is simply 404.

Endpoints

Method and pathWhat it doesNeeds
GET /meThe firm, the user the token acts as, and the token's abilities.read
GET /clientsList clients. Filters: search, updated_since.read
POST /clientsCreate a client. Fields: name (required), client_type, email, phone, id_number, company_number, address, popia_consent, fica_status, notes, assigned_user_id.write
GET /clients/{id}One client.read
PATCH /clients/{id}Update any of the fields above.write
GET /mattersList matters. Filters: status, client_id, search, updated_since.read
POST /mattersOpen a matter. Fields: title (required), client_id, assigned_user_id, practice_area, matter_type, status, court, case_number, opposing_party, opposing_attorney, next_action, next_deadline, prescription_date, opened_at, notes. The matter number is allocated by the firm's numbering.write
GET /matters/{id}One matter.read
PATCH /matters/{id}Update a matter, including its status.write
GET /time-entriesList time. Filters: matter_id, from, to, billed.read
POST /time-entriesRecord time. Fields: matter_id, work_date, description, hours (required), rate (defaults to the matter or user rate), billable, user_id.write
GET /invoicesList invoices. Filters: status, client_id, matter_id.read
GET /invoices/{id}One invoice with its payments.read
GET /paymentsList payments. Filters: invoice_id, from.read
GET /documentsDocument metadata (never file contents). Filters: matter_id, client_id.read
GET /enquiriesEnquiries that arrived through your directory listings. Filter: status.read
GET /eventsThe webhook event names and what they mean.read
GET, POST /webhooksList or register endpoints. POST returns the signing secret once.read / write
PATCH, DELETE /webhooks/{id}Change events, pause, or remove an endpoint.write
POST /webhooks/{id}/testQueue a ping delivery.write
GET /webhooks/{id}/deliveriesThe delivery log with status codes and attempts.read

Example: open a matter for a new client

curl -X POST https://attorneyos.co.za/api/v1/clients \
  -H "Authorization: Bearer aos_..." -H "Content-Type: application/json" \
  -d '{"name":"Thandi Nkosi","client_type":"Individual","email":"thandi@example.co.za","phone":"082 123 4567"}'

curl -X POST https://attorneyos.co.za/api/v1/matters \
  -H "Authorization: Bearer aos_..." -H "Content-Type: application/json" \
  -d '{"title":"Transfer of Erf 5 Monavoni","client_id":123,"practice_area":"Conveyancing"}'

Webhooks

Register an https URL and the events you want. Each delivery is a JSON POST with the headers X-AttorneyOS-Event, X-AttorneyOS-Delivery and X-AttorneyOS-Signature. Reply with any 2xx within ten seconds; anything else is retried after one minute, five minutes, thirty minutes, two hours and twelve hours, then marked failed. Deliveries can arrive out of order and, rarely, twice: use the id to ignore repeats.

EventWhen
client.createdA client was added
client.updatedA client was edited
matter.createdA matter was opened
matter.updatedA matter was edited or its status changed
time_entry.createdTime was recorded
invoice.createdAn invoice was created
invoice.paidAn invoice was paid in full
payment.receivedA payment was recorded
document.uploadedA document was added
document.signedA document was signed electronically
enquiry.receivedA directory enquiry arrived for one of your listings
pingA test delivery you trigger yourself
{
  "id": "evt_1042",
  "event": "matter.created",
  "created_at": "2026-09-04T09:12:33+02:00",
  "data": { "id": 87, "matter_number": "AOS-2026-00087", "title": "Transfer of Erf 5 Monavoni", "status": "Open", "client_id": 123, ... }
}

Verifying a signature

The header is t=<unix time>,v1=<hex> where the hex is HMAC-SHA256 of "<t>.<raw body>" with your endpoint's secret. Reject anything older than five minutes.

// PHP
[$t, $v1] = [null, null];
foreach (explode(',', $_SERVER['HTTP_X_ATTORNEYOS_SIGNATURE']) as $part) { [$k, $v] = explode('=', $part, 2); if ($k === 't') $t = $v; if ($k === 'v1') $v1 = $v; }
$body = file_get_contents('php://input');
$ok = abs(time() - (int) $t) < 300 && hash_equals(hash_hmac('sha256', $t.'.'.$body, $secret), (string) $v1);

// Node
const [t, v1] = sig.split(',').map(p => p.split('=')[1]);
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
const ok = Math.abs(Date.now() / 1000 - Number(t)) < 300 && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));

Good practice

Changelog

2026-09-04 v1 released: clients, matters, time entries, invoices, payments, documents, directory enquiries, webhooks with signed deliveries and retries.

Need an attorney for this?

Not a developer? Ask your IT partner to connect what you already use.

Most firms connect their accounting package or an intake form. Point your IT provider at this page; a read token takes a minute to create.

Read the help centre
Are you the attorney?

Everything the API reads is already in one workspace.

Matters, trust, billing, documents and the diary, with an API on top when you need it.