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.
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.
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.
| Method and path | What it does | Needs |
|---|---|---|
| GET /me | The firm, the user the token acts as, and the token's abilities. | read |
| GET /clients | List clients. Filters: search, updated_since. | read |
| POST /clients | Create 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 /matters | List matters. Filters: status, client_id, search, updated_since. | read |
| POST /matters | Open 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-entries | List time. Filters: matter_id, from, to, billed. | read |
| POST /time-entries | Record time. Fields: matter_id, work_date, description, hours (required), rate (defaults to the matter or user rate), billable, user_id. | write |
| GET /invoices | List invoices. Filters: status, client_id, matter_id. | read |
| GET /invoices/{id} | One invoice with its payments. | read |
| GET /payments | List payments. Filters: invoice_id, from. | read |
| GET /documents | Document metadata (never file contents). Filters: matter_id, client_id. | read |
| GET /enquiries | Enquiries that arrived through your directory listings. Filter: status. | read |
| GET /events | The webhook event names and what they mean. | read |
| GET, POST /webhooks | List 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}/test | Queue a ping delivery. | write |
| GET /webhooks/{id}/deliveries | The delivery log with status codes and attempts. | read |
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"}'
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.
| Event | When |
|---|---|
| client.created | A client was added |
| client.updated | A client was edited |
| matter.created | A matter was opened |
| matter.updated | A matter was edited or its status changed |
| time_entry.created | Time was recorded |
| invoice.created | An invoice was created |
| invoice.paid | An invoice was paid in full |
| payment.received | A payment was recorded |
| document.uploaded | A document was added |
| document.signed | A document was signed electronically |
| enquiry.received | A directory enquiry arrived for one of your listings |
| ping | A 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, ... }
}
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));
updated_since rather than fetching everything; better still, subscribe to webhooks and fetch on demand.2026-09-04 v1 released: clients, matters, time entries, invoices, payments, documents, directory enquiries, webhooks with signed deliveries and retries.
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 centreMatters, trust, billing, documents and the diary, with an API on top when you need it.