Skip to content

Custom Software

API services: a promise about who may ask and what they get

An API is how your other software, your partners and your customers' tools reach your records. Each of them relies on it behaving the same way tomorrow. So every API we build starts as a contract: what each caller may ask for, exactly what comes back, what a refusal looks like, and how much notice comes before anything changes. So partners and your own apps can build on your records without breaking when you change something.
GET /v2/jobs/{id}needsjobs:readreturnsfields for the caller's scopeerrorsplain words and a codev1served until Mar 1Customer portaljobs:read · own jobs200status, arrival window,technician codeTechnician appjobs:read · assigned jobs200site, equipment,access notesPartner integrationno scope403missing scope: jobs:readOld appv1200deprecatedv1 retires Mar 1

What the contract says

Written before the code, and published for everyone who calls it.
Endpoint and versionGET /v2/jobs/{id}Callers know exactly what they're relying on
Scopejobs:readAccess is decided by the caller's permission, not by hiding the endpoint
Fields by scopePortal: status, window; technician: site, access notesNobody gets more than their job needs
Errors403 · “missing scope: jobs:read”The caller can fix the problem without phoning you
Limits60 requests a minute per callerOne busy partner can't slow everyone else down
Change policyv1 served for 90 days after v2Nobody is broken by surprise

Three endpoints

One read, one write and one event. Each has its own contract.

Read a job

Request
GET /v2/jobs/{id}
Needs
jobs:read
Returns
the job, with fields for the caller's scope
Refuses
jobs outside the caller's customer or assignment, as 404
Limit
60 a minute per caller

How APIs break the people using them

Most API problems aren't outages. They're surprises.
  1. 01

    Silent changes

    A field renamed overnight

    Every change that could break a caller goes into a new version, with the old one served until a published date.

  2. 02

    Errors that leak

    A stack trace in the reply

    Errors explain what the caller can fix and never expose internals or other customers' data.

  3. 03

    No limits

    One caller slows everyone

    Limits per caller, with a clear message and a time to retry.

  4. 04

    Data across customers

    Ask for job 1041, get someone else's

    Every request is checked against the caller's customer or assignment before anything is read.

  5. 05

    Undocumented behaviour

    It works, nobody knows why

    The contract is published, examples included, and requests are logged so a caller's question can be answered from the record.

What API services hand off

In

Requests
portal, apps, partners
Callers
identity and scopes
Records
jobs, payments, customers
API services
check the contract → read or write

Out

Responses
only what the scope allows
Events
to subscribers, signed
Request log
refusals included

Worth building when other software needs your records

Good fit

  • A portal, an app or a partner needs to read or change your records.
  • More than one system relies on the same logic.
  • You want to add new tools later without rewiring the old ones.

Not the right fit

  • One screen talks to one database and nothing else will. The application can do it directly.
  • A partner only needs a nightly file. Send the file.

Questions about API services

REST or GraphQL?

Usually REST, because most callers expect it. GraphQL when many different screens need different shapes of the same data. The contract matters more than the style.

How do partners get access?

Each gets its own credentials and only the scopes it needs. Access can be changed or withdrawn without affecting anyone else.

What does versioning cost us?

Running two versions for a while. It's cheaper than breaking a partner's integration without warning.

Is it documented?

Yes. The contract is published with examples and kept in step with the code, because it's generated from the same definitions.