REST API

API Documentation.

Integrate matter plans, their field sets and the court, council and agency forms they use directly into your practice-management system. Pull a plan with its documents in one call, fill templates against the canonical matter.* vocabulary, and assemble everything locally.

Base URL
https://www.openmatterplans.com/api/v1

Authentication

Four access modes. Registry and template metadata are open; PMS integrations send an API key, and full plan bodies need a per-account one; community contributions use a browser session. Keyed endpoints fail closed in production.

Public

Registry and template metadata. No credentials.

/councils, /forms/:id/latest, /templates, /templates/:id, /matter-types (teaser)
API key

PMS integration. A per-account key, or the transitional shared key.

/matter-plans, /matter-plans/:id/latest, /forms/:id/download, /councils/:id/report, /field-sets/*, /pms-fields
Account key

Full plan bodies. A per-account key only: every call is logged to your account and counts towards a daily cap of 2,000.

/matter-plans/:id/download, /matter-plans/:id/kit
Session

Website sign-in. Create and revoke keys under Profile → API Access; verified practitioners suggest and vote.

/users/api-keys, /suggestions, /votes, /matter-plans/:id
Send your key as a header
curl "https://www.openmatterplans.com/api/v1/field-sets/resolve?practiceArea=conveyancing&jurisdiction=AU-QLD" \
  -H "x-omn-api-key: omn_live_xxxxxxxxxxxxxxxx"

# or, equivalently:
  -H "Authorization: Bearer omn_live_xxxxxxxxxxxxxxxx"

Endpoints

Endpoints

Matter Plans (PMS integration)
Templates
Authorities and Forms
Field Standard
Account
Community (website)
GET

/api/v1/matter-plans

API key

The plan catalogue, one summary per plan (forks included). Send your key as x-omn-api-key; a signed-in browser session gets the same envelope. Each row carries contentHash and fieldSetSlug for cheap diffing, plus the canonical jurisdiction, practiceAreaKey and partyRole codes. Filters: page, limit (<=100), jurisdiction (AU-QLD), practiceAreaKey (conveyancing), practiceArea (website category, e.g. property), saliAreaOfLaw, countryCode, jurisdictionSub, search, updatedSince (ISO).

// Response Body

GET /api/v1/matter-plans?jurisdiction=AU-QLD&practiceAreaKey=conveyancing

{
  "planSchemaVersion": 1,
  "total": 2, "page": 1, "limit": 20, "pages": 1,
  "data": [
    {
      "id": "94169c3a-a04c-4304-bedb-454bf212ba6d",
      "slug": "au/qld/property/property-purchase-buyer/standard-residential-purchase",
      "title": "Property Purchase (Buyer)",
      "forkLabel": "QLD - Standard Residential Purchase",
      "forkedFromId": null, "forkCount": 0,
      "saliAreaOfLaw": "Real Estate", "saliProcess": "TRANSACTIONAL",
      "practiceArea": "property", "category": "property",
      "practiceAreaKey": "conveyancing",
      "partyRole": "BUYER",
      "countryCode": "AU", "jurisdictionSub": "QLD", "jurisdiction": "AU-QLD",
      "documentLinkCount": 60,
      "contentHash": "b2ad8ecdaa11...",
      "planSchemaVersion": 1,
      "updatedAt": "2026-10-14T06:38:51.605Z",
      "fieldSetSlug": "plan-property_purchase-buyer-94169c3a",
      "fieldsNormalized": true,
      "netVotes": 0
    }
  ]
}

Matter field vocabulary

Templates fill against canonical matter.* tokens that resolve on your side, against your own matter data (matter data never leaves the PMS). Parties are addressed by role (matter.party.purchaser.fullName), never by position. Field sets form a hierarchy of up to five levels, base > area core > country > state > plan; resolve the one for a matter by context rather than hard-coding a token list.

Pull the always-current vocabulary:GET /api/v1/field-sets/resolve?practiceArea=conveyancing&jurisdiction=AU-QLD
Manifest field rules (PDF and DOCX templates)
{ "token": "matter.ourFirmName" }Fill from one canonical token, resolved against your own matter data.
{ "token": "matter.fieldValues.settlement_date", "format": "DD/MM/YYYY" }Fill a value, formatted. DD/MM/YYYY is the only format in v1.
{ "token": "matter.fieldValues.settlement_date", "part": "DD" }Fill one component of a value a form splits across boxes: DD / MM / YYYY, address parts (unitAndStreetNo, suburb, postcode ...), parcel parts (lot, planNo ...). Not implemented yet? Leave the field blank, never the whole value.
{ "compose": ["matter.party.purchaser.email", "matter.party.purchaser.address"], "separator": "\n" }Fill one field from several tokens: drop the empty ones and join the rest with the separator (a new line by default).
{ "constant": "Search request" }Literal text, the same on every assembly.
{ "constant": true }On a checkbox: tick it unconditionally.
{ "rule": "checkIf", "expression": "matter.matterType == 'conveyancing_purchase'" }Tick only when the expression is true (== / != against a single-quoted value).
{ "ai": "the searched property’s nearest cross street" }No exact token: your PMS fills it with AI from its own matter context. Not supported yet? Leave it blank.
{ "image": "matter.user.signature", "fit": "contain" }Draw an image your PMS holds (the operator’s signature PNG) into the field’s rectangle. The image never leaves the PMS.
{ "signature": true }Prompt the signer to draw a signature at assembly and stamp it into the field.

A pdf_form template embeds manifest.json (under /Names → /EmbeddedFiles); a docx_precedent carries the same JSON as the omn/manifest.json part and {placeholder} tags in the body. Each field maps to one rule above, and may also carry label, suggest and describe hints. A rule your engine does not know yet is a no-op: leave the field blank, never error.

Streaming matter plans into your PMS

Plans evolve as practitioners refine them. Rather than re-pull every plan, track freshness off the contentHash and only download what changed. Each plan is bound to a resolved field set from the Matter Field Standard, and every form it uses is filled from that set, so the tokens a PMS pre-fills against travel with the plan.

Step 1

Find the plan

GET /api/v1/matter-plans (key), filtered by jurisdiction and practiceAreaKey. Store each plan’s id, contentHash and fieldSetSlug. Page with updatedSince to poll cheaply.

Step 2

Take the kit

GET /api/v1/matter-plans/:id/kit (per-account key): the plan, its resolved field set and every document it uses, with field coverage. Or /download, plus GET /templates/:id for each document.

Step 3

Stay fresh

GET /api/v1/matter-plans/:id/latest. A matching contentHash means you are current. A changed one: take the kit again (send If-None-Match) and re-read the stage and task ids.

Tasks carry dates, fields and documents

Each task names the field that holds its anchor date (baseKeyToken), the matter.* tokens it touches (pmsFieldRefs) and the templates it uses (documents). Your PMS turns those into the task card: a due date computed from the matter, the input to fill right on the card, and the forms to open, without leaving the checklist.

{
  "id": "a862cc54-b90f-451a-bca3-341bc4e0ff51",
  "title": "Open or Join the Electronic Workspace and Prepare the Transfer",
  "relativeDays": -14,
  "baseKeyDate": "Settlement Date",
  "baseKeyToken": "matter.fieldValues.settlement_date",
  "pmsFieldRefs": ["matter.fieldValues.elno_workspace_id",
                   "matter.fieldValues.settlement_date", "..."],
  "documents": [ { "templateId": "24b2d696-9873-436f-bdaf-fc59334122d8",
                   "formName": "Transfer (Form 1)", "formNumber": "Form 1",
                   "templateType": "docx_precedent", "via": "own",
                   "downloadUrl": "..." } ]
}
// -> due date = the matter's settlement_date minus 14 days;
//    inputs for the workspace id and settlement date on the card,
//    typed from the field set (settlement_date: dataType "date", format "DD/MM/YYYY");
//    the Transfer one click away, filled from the same set.
Every plan response carries planSchemaVersion, and the kit adds kitSchemaVersion. Both are 1 today. Gate your parser on them: if a response reports a higher version than your client understands, skip the plan and prompt for an update rather than mis-reading a changed shape. New keys are additive and never move a version, so ignore keys you do not recognise.
Limits: 100 requests a minute per IP across /api, and 2,000 plan bodies (/download and /kit) per key in any rolling 24 hours; both answer 429 with Retry-After. /councils, /templates, /field-sets/:slug, /download and /kit send an ETag: send If-None-Match and an unchanged resource costs a 304 (on /download and /kit it still counts towards the cap). Errors are { "error": "..." } on the original endpoints and { "error": { "code", "message" } } on the newer ones; read both.