Skip to main content

Professional API: Getting Started

Read your own practice data (profile, appointments, approaches, matters and engagement letters) from your own systems, and create will drafts for your clients.

What the Professional API Is

A view of your practice data, and one write surface for will drafts, for your own systems.

Scope of the API

The Professional API lets your practice management software, CRM or internal dashboard read the data Orchard72 already holds for your practice. Every endpoint lives under /api/v1/pro/ext/ and returns JSON.

The read collections are read-only: they have no create, update or delete endpoints. The one write surface is will drafts for your own clients at /api/v1/pro/ext/wills/, which needs its own scope and a plan that includes it (see Creating Will Drafts below). Anything else that changes data is still done in the professional dashboard.

Who Can Use It

API access is part of the Pro Growth and Pro Enterprise plans. On Pro Free and Pro Starter the endpoints are present but refuse every request, because those plans include no API allowance.

Note

Nothing needs to be activated separately. As soon as your plan includes API access, the Developer section of your professional dashboard shows your key management tools and your current allowance.

Authentication

Creating a key and sending it with every request.

Creating a Key

  1. Open the Developer section of your professional dashboard
  2. Create a key and give it a descriptive name, so you can tell your integrations apart later
  3. Choose the pro:read scope for the read collections. If your plan includes will creation you can also include pro:wills:write. Scopes never imply one another, so a key needs pro:read to read and pro:wills:write to create will drafts
  4. Copy the key straight away and store it in a secrets manager or environment variable

Important

The key is shown once, at creation. Only a one-way hash is stored, so it cannot be recovered afterwards. If you lose a key, revoke it and create a replacement.

Sending the Key

Professional keys carry the same twai_ prefix as consumer keys (older keys beginning pk_ keep working) and travel in the Authorization header using the Bearer scheme:

Authorization: Bearer twai_your_api_key_here

  • A missing or malformed key returns 401
  • A valid key without the scope an endpoint needs (pro:read, or pro:wills:write for will drafts) returns 403
  • A valid, correctly scoped key on a plan without API access also returns 403, rather than an upgrade prompt dressed up as a success

Your First Call

Confirm the key works before you build anything on it.

Fetch Your Profile

  1. Make a GET request to /api/v1/pro/ext/me/ with your key in the Authorization header
  2. A working key returns your professional profile. If the account has no professional profile attached, the response is a 404 carrying {"detail": "No professional profile found for this account."}
  3. A 401 means the key is wrong, revoked or expired; a 403 means the key lacks pro:read or the plan has no API access

Tip

Start with /api/v1/pro/ext/me/ rather than a collection. It is the cheapest call that proves the key, the scope and the plan entitlement are all in order.

The Read Collections

What the API exposes, and what it deliberately does not.

Endpoints

  • /api/v1/pro/ext/me/: your professional profile
  • /api/v1/pro/ext/appointments/: list, plus a detail view per appointment
  • /api/v1/pro/ext/approaches/: list, plus a detail view per approach
  • /api/v1/pro/ext/matters/: list only. There is no bare matter detail endpoint; the matter brief is fetched from /api/v1/pro/ext/matters/{id}/brief/
  • /api/v1/pro/ext/engagement-letters/: list, plus a detail view per engagement letter

List responses are paginated, 100 records per page by default.

Matter Briefs Are Consent-Gated

A matter brief leaves the platform only while the client’s consent for that matter is live. The consent is re-checked on every single call, never cached, and each fetch is written to the matter’s disclosure log, recording the key prefix used but never the key itself.

Note

The brief endpoint also carries its own hourly cap, separate from your plan allowance, and it is shared with the signed bundle download. It is there to keep bulk extraction of client material off the table, so design your integration to fetch a brief when you need it rather than sweeping them on a schedule.

Creating Will Drafts

Create a will draft for one of your own clients, poll it, and retry safely.

Who Can Create Will Drafts

Will creation is part of the Pro Growth and Pro Enterprise plans (and the founding-member Pro Growth plan). It needs a key carrying the pro:wills:write scope, which the Developer section offers only while your plan includes will creation.

Pro Free and Pro Starter do not include will creation. The hourly will-draft budget for each plan is listed under Partner will creation in the Rate Limits section below.

That hourly budget applies on top of your ordinary plan allowance, and each draft also uses one will-generation credit from your plan; when the credits are used up, a create returns 402. Polling and cancelling use no credits. Your subscription record reports partner_will_api_enabled and partner_will_create_rate_limit_per_hour, so an integration can read its own entitlement.

Note

Every draft is generated on our own servers. A will created through the API is never sent to an external AI provider, and a request that names one is refused.

Endpoints

  • POST /api/v1/pro/ext/wills/: create a will draft and queue its generation
  • GET /api/v1/pro/ext/wills/{id}/: one will you created, and its generation status
  • POST /api/v1/pro/ext/wills/{id}/cancel/: stop a generation that has not finished

The Request

  • professional_client_id: the identifier of one of your own active clients. Any other identifier returns 404
  • client_reference (optional): your own reference for the will, up to 255 characters
  • will: an object of named sections. jurisdiction and testator are required; residence, family, assets, gifts, funeral, executors and witnesses are optional

Each section is checked against the same rules as the will interface in the dashboard, and the whole request is checked before anything is created, so a 400 means nothing was stored. An unknown field or section is a 400, never silently dropped: a refused section comes back under will, keyed by section name. Replace BASE_URL with the address you sign in at:

curl -X POST "BASE_URL/api/v1/pro/ext/wills/" \
  -H "Authorization: Bearer twai_your_api_key_here" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 7d3f9a52-client-4471-will-1" \
  -d '{
    "professional_client_id": "YOUR_CLIENT_ID",
    "client_reference": "MATTER-4471",
    "will": {
      "jurisdiction": {
        "will_country": "GB",
        "will_state": "ENG",
        "will_state_name": "England",
        "jurisdiction": "GB-ENG",
        "user_country": "GB",
        "date_of_birth": "1970-05-14",
        "will_type": "solo"
      },
      "testator": {
        "first_name": "Harriet",
        "last_name": "Blackwood"
      }
    }
  }'

The Response and Its Status

A create answers 202 with a Location header to poll and a body carrying identifiers and status only, never the client’s details or the will’s content:

{
  "id": "3f2b6c1e-8d4a-4b7e-9c21-5a0e7d9f1b44",
  "status": "pending",
  "professional_client_id": "YOUR_CLIENT_ID",
  "client_reference": "MATTER-4471",
  "created_at": "2026-09-24T10:15:00Z",
  "updated_at": "2026-09-24T10:15:00Z"
}

status is one of pending, processing, completed, failed or cancelled. Poll GET /api/v1/pro/ext/wills/{id}/ to follow it. A failed generation is reported in status, not as an HTTP error; create a new draft to try again. Cancelling a will with nothing generating returns 400.

Retrying Safely With Idempotency-Key

  • Every create must carry an Idempotency-Key header, up to 255 characters and unique to that will; without one the request returns 400
  • Sending the same key and the same body again within 24 hours returns the will the first request created, with an Idempotent-Replayed: true header, and does not create a second will
  • Reusing a key with a different body returns 422; use a new key for a new will
  • A refused request (400, 402, 404 or 422) stores nothing, so you can correct it and send it again with the same key

Tip

Generate the key once per will in your own system and store it with the request. After a timeout or a dropped connection, resend the same request with the same key rather than a fresh one.

Rate Limits

How much you may call, and where to read your own allowance.

Limits are counted on a sliding window. When you go over one, the API answers 429 Too Many Requests with a Retry-After header telling you how long to wait.

Professional API

Every request to /api/v1/pro/ext/.

  • Pro Growth: 2,000 requests per hour
  • Support Service Provider Growth: 2,000 requests per hour
  • Pro Enterprise: unlimited
  • Support Service Provider Enterprise: unlimited
  • Founding Member Pro Growth: 2,000 requests per hour

Shared by every key you hold, so a new key never adds to it. Plans not listed here have no access to this API. Scopes: pro:read, pro:org:read, pro:wills:write.

Partner will creation

POST /api/v1/pro/ext/wills/ only, on top of the Professional API budget.

  • Pro Growth: 30 requests per hour
  • Pro Enterprise: 300 requests per hour
  • Founding Member Pro Growth: 30 requests per hour

Shared by every key you hold, so a new key never adds to it. Plans not listed here have no access to this API. Scopes: pro:wills:write.

Matter briefs and will documents

GET /api/v1/pro/ext/matters/{id}/brief/, GET /api/v1/pro/ext/wills/{id}/document/ and one-off brief bundles, sharing one budget, on top of the Professional API budget.

  • Every plan: 30 requests per hour

Shared by every key you hold, so a new key never adds to it. Scopes: pro:read.

Professional MCP agent tools

MCP tool calls, counted separately from your REST budget.

  • Pro Growth: 2,000 requests per hour
  • Support Service Provider Growth: 2,000 requests per hour
  • Pro Enterprise: unlimited
  • Support Service Provider Enterprise: unlimited
  • Founding Member Pro Growth: 2,000 requests per hour

Shared by every key you hold, so a new key never adds to it. Plans not listed here have no access to this API. Scopes: pro:read, pro:org:read.

These figures come from the limits the API enforces, also served as JSON at /api/v1/portfolio/rate-limits/.

Seeing Your Own Limit

The Developer section of your professional dashboard shows the allowance your plan currently grants, the number of keys you may hold, and the scope your keys are issued with. The same figures come back on your subscription record as rate_limit_per_hour (an integer, or null where the plan is unlimited), so an integration can read its own ceiling rather than hard-coding one.

Tip

Every response reports your allowance in the X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset headers. The allowance is per user, shared by all of your keys, and a per-minute burst cap derived from your hourly rate stops it being spent in a few seconds; pace your calls from the headers and back off when you are told to.

When You Exceed It

An over-limit request returns 429 with a Retry-After header and a body carrying error_code of RATE_LIMITED plus a wait_time in seconds. Honour whichever you read; do not retry immediately.

If you need to know about changes as they happen, subscribe to webhooks instead of polling.

Errors and Scoping

The response shapes to code against, and why a 404 is not always a missing record.

Error Shapes

Two shapes are in use on this surface, so handle both rather than assuming one:

  • The scoping and profile refusals carry a detail key, for example {"detail": "Not found."}
  • Authentication, permission and throttling refusals carry error and status_code, and the 429 additionally carries message, error_code and wait_time

404 Rather Than 403

Asking for a record that belongs to another practice returns 404, not 403, and it is the identical body you get for an identifier that does not exist anywhere. The queryset behind each collection is filtered to your own practice before the lookup happens, so a record outside it is simply not there to be found.

This is deliberate. A 403 would confirm that the identifier exists and belongs to someone, which is exactly the signal an identifier-guessing attack needs. The matter brief goes further still: "not yours" and "consent is not live" return byte-identical responses, so the presence or absence of a client’s consent cannot be probed from outside.

Note

When you get a 404 on an identifier you believe is yours, check the account the key belongs to before assuming the record was deleted.

Related Topics

Can’t find what you’re looking for?

Our support team is here to help. Contact us and we’ll get back to you as soon as possible.

Contact Support

We use cookies to improve your experience. See our Cookie Policy (opens in a new tab) for details.