How we verify this reference
We publish the Swagger UI, and it is generated from the API’s own code, so it is the authority on the shape of every request and response: routes, fields, and types. What a schema cannot tell you is how the API behaves at the edges. Which values are rejected and which are quietly adjusted, which filters an endpoint actually applies, what a mistake costs, and which failures are worth retrying.
That behavior is what breaks integrations in production, so we don’t document it from the schema alone. We test it against the real API and write down what we observe.
The test harness
Section titled “The test harness”We built a test harness, written in Python, that exercises the Shepherd API the way real integrations do, including the ways they commonly go wrong. It runs against the live production API, inside a dedicated test clinic that holds no real client or patient data.
Each run:
- Calls every public read endpoint with a minimal valid request and records the shape of the response.
- Probes the edges of every request convention. Page sizes and page numbers at and past their limits, sort fields and directions that don’t exist, date windows that are malformed or run backwards, unknown fields and embeds, and request bodies that are empty,
null, or not valid JSON. - Tests authentication and clinic scoping. Missing, wrong, and swapped keys; missing, malformed, and unlinked clinic ids; and whether any request can return records from a clinic other than the one it names.
- Creates and updates clearly marked test records through the write endpoints that are safe to exercise, then reads each record back. Writes that could reach a real person, such as messages or appointment notifications, are never sent.
- Watches its own impact. Requests are spaced out and stay far below the rate limit, and the run stops itself at the first sign of strain on the API.
The harness records only the shape of each response (status, timing, size, field names and types), never the field values, so results can be kept and compared from one API release to the next.
Every claim on these pages, and every rule in the AI agent skill, is checked against those results and against the API’s source code.
What the evidence changed
Section titled “What the evidence changed”The September 2026 runs covered API version 2.246.0.33 with about 4,500 requests. Where the results disagreed with what we had written, we corrected the pages:
rppandpagebelow 1 are rejected with a400, as is a date window that ends before it starts. We had said they were not rejected.- A clinic your keys are not linked to returns
401(Integration not enabled for the provided clinic), not403. A403means the key’s permissions don’t cover that endpoint or record. See Errors. - An unsupported sort is usually ignored, not rejected. Most endpoints return
200in their default order; only a few return400, and two return501. See Sorting. - Some endpoints accept filters they never apply. For example, the inventory transaction journal filters on
dateFrom/dateToand ignores the standarddateCreatedpair. See Fields an endpoint does not apply. - A
500is almost always caused by the request, typically an empty ornullbody or a malformed id. We now advise against retrying it at all. - A few endpoints return very large records, so we now recommend pages of 100 or less on them.
- The unfiltered breed list is far larger than a lookup should be, so always filter it to the clinic. See Lookups.
The runs also confirmed much of what these pages already said: unknown fields and embeds are ignored rather than rejected, an rpp above 1000 is reduced to 1000, rejected requests count against your rate limit while the usage endpoint does not, a malformed clinic id fails with a 500, and no request returned another clinic’s records.
When we run it
Section titled “When we run it”We run the harness after API releases and before we publish changes to the API reference or the agent skill. Findings that are defects in the API, rather than in these pages, go to the engineering team; until a fix ships, these pages describe the API as it behaves today.
If you see the API behave differently from what these pages say, tell us. Include the endpoint, the request body with credentials and clinic data removed, the status you got, and the time in UTC.