Skip to content

Skill for AI coding agents

If you use an AI coding assistant such as Claude Code to build your application that’s integrated with Shepherd, install this skill first. It gives the agent the Shepherd API’s endpoints, schemas, and rules, plus a tool that checks every request before your code sends it, so the code it writes is correct the first time and easy on the servers every clinic shares.

Shepherd OpenAPI skill

  • Version 1.1.0
  • 267 KB zip, 42 files
  • MIT license
Download the skill

SHA-256 18a6df23bf162bd798b68270f172b70bafb4830f23cf23ec9badda76d617046f

The skill is free and MIT licensed: use it, change it, and ship code written with it. It follows the open Agent Skills format, so it is plain Markdown, JSON, and one Python script that you can read before you install it.

AI assistants write API clients quickly, and they fill gaps in what they know with confident guesses. Against the Shepherd API, the usual guesses go wrong in the same few ways:

  • The wrong host or header. Calls go to a host that does not serve the API, or the clinic id goes in the JSON body instead of the X-Clinic-Id header.
  • Unbounded reads. A sync asks for a clinic’s entire history in one query, or a misspelled filter is silently ignored and the query runs unfiltered.
  • Retry loops. A slow request times out and the code sends it again, and again, multiplying the work on the server; or a rejected request is retried until the rate limit locks the integration out.
  • Destructive updates. An update sends only the changed field, and the API, which treats updates as full replacements, clears everything else on the record.
  • Duplicate records. A create is retried after a timeout and the clinic gets two appointments.

Each of these fails, loses clinic data, or slows the API for other clinics. With the skill installed, the agent knows the rules behind them and checks its requests against them before anything is sent.

  • The endpoint reference, offline. Every public endpoint, with its request fields, required fields, and response shape, generated from the live API description. The agent looks endpoints up instead of guessing, and does not need to browse.
  • The rules the schema cannot express. Which lists need a date window, page-size and embed limits, which filter fields an endpoint accepts but never applies, how rate limits are counted, which failures to retry and which never to retry, and exactly which fields each update keeps or clears. These rules are checked against the live API with a test harness, not only read from the schema; see How we verify.
  • A request linter. scripts/shepherd_api.py lint checks a request against those rules and the schema before any code sends it: host, verb, route, field names, fields the endpoint does not apply, sort fields, id and date formats, date windows, page sizes, embeds, and update completeness.
  • A budgeted development runner. scripts/shepherd_api.py call sends one request at a time for exploration, against the sandbox by default, under a local rate budget and circuit breaker.
  • Starter clients and tests in Python, TypeScript, C#, and PHP that already handle timeouts, rate limits, paging, error bodies, and safe updates.
  • Directoryshepherd-openapi/
    • SKILL.md the rules and workflow the agent follows
    • LICENSE.txt
    • Directoryreferences/
      • api-essentials.md hosts, headers, paging, filters, embeds
      • limits-and-sync.md rate limits, retries, incremental sync
      • writes.md creates, updates, read-merge-write
      • errors.md status codes, error bodies, fixes
      • resources.md, client-patterns.md, testing.md, production-checklist.md
      • Directoryendpoints/ per-area endpoint reference
        • …
    • Directoryassets/
      • openapi.json machine-readable endpoint description, with request rules
      • Directorytemplates/ starter clients and tests for four languages
        • …
    • Directoryscripts/
      • shepherd_api.py endpoint lookup, linter, and development runner

Claude Code loads skills from a folder on disk and uses them automatically when a task involves the Shepherd API.

  1. Download the zip above.

  2. Unzip it into one of the skills folders:

    Terminal window
    # For you, in every project
    unzip shepherd-openapi-skill.zip -d ~/.claude/skills/
    # Or for one repository, shared with everyone who clones it
    unzip shepherd-openapi-skill.zip -d .claude/skills/

    Either way you end up with skills/shepherd-openapi/SKILL.md. Committing the project copy gives your whole team the same rules.

  3. Start a new Claude Code session. Ask it to work on your integration, or invoke the skill directly with /shepherd-openapi.

The agent never needs your keys in the conversation. Set them in the environment the agent runs in, and use sandbox keys while you develop:

Terminal window
export SHEPHERD_ENV=sandbox # or production
export SHEPHERD_PUBLIC_KEY=...
export SHEPHERD_PRIVATE_KEY=...
export SHEPHERD_CLINIC_ID=...
export SHEPHERD_APP_NAME=acme-lab-sync # optional: identifies your integration

Sandbox keys work against https://demo-open-api.shepherd.vet/pav2/. If you don’t have them yet, see Authentication.

Ask your agent for real work. For example:

  • “Write a job that syncs appointments changed since the last run into our database.”
  • “Add a function that updates a patient’s alerts.”
  • “Our appointment import keeps timing out in production. Fix it.”
  • “Add tests for our Shepherd client that cover rate limits and timeouts.”
  • “Show me the first page of sandbox clients created last month.”

The agent runs scripts/shepherd_api.py from the skill folder, and you can too. It uses only the Python standard library.

Terminal window
python3 scripts/shepherd_api.py endpoints --search invoice
python3 scripts/shepherd_api.py schema "POST /pav2/open-api-invoices"
python3 scripts/shepherd_api.py lint "POST /pav2/open-api-appointments" --body '{"rpp":100,"dateFrom":"2026-05-01T00:00:00Z","dateTo":"2026-06-01T00:00:00Z"}'
python3 scripts/shepherd_api.py call "POST /pav2/open-api-clinics" --body '{"page":1,"rpp":1}'
python3 scripts/shepherd_api.py status

Here is the linter reviewing a request an agent might write from memory:

$ python3 scripts/shepherd_api.py lint "POST /pav2/open-api-appointments" \
--body '{"page":1,"rpp":1000,"dateCreatedFrom":"2026-01-01T00:00:00Z","embed":"appointmentPatients.patient","clinicId":"9890662f-52db-4a41-a5c1-b2e300d400b4"}'
ERROR [clinic-in-body] The clinic id is the X-Clinic-Id HEADER. A clinicId in the body is ignored.
ERROR [date-pair] `dateCreatedFrom` without `dateCreatedTo` spans from then until now. Send both ends, one month at most.
ERROR [unbounded] This list has no bound: add a date window (one month at most) or an id filter (`appointmentStatusIds`, `cancellationReasonIds`, `clientIds`, `patientIds`, `providerIds`, `ids`). Enumerating a whole collection is not a supported access pattern.
WARN [embed-rpp] `rpp` 1000 with embeds multiplies the response size. Keep embedded pages at 250 or fewer.
INFO [is-deleted] Deleted records are included unless you send `isDeleted: false`. Either filter them out or handle the soft-delete flag.
ERROR [heavy-embed] Embedding `appointmentPatients.patient` is expensive on large pages. Use `rpp` of 50 or less with it, and an id filter.

For updates, pass the current record with --current and the linter lists every field the update would clear.

The call runner is for exploration during development, not for production traffic. It is deliberately conservative:

  • It targets the sandbox unless SHEPHERD_ENV=production is set, and refuses creates and updates against production without --allow-production-write.
  • It lints first and does not send a request with errors.
  • It sends one request at a time, within a local budget of 30 requests a minute and 600 an hour per key.
  • It never retries a timeout or a create. After a 429 it refuses further requests until the next UTC minute; after repeated timeouts or server errors it stops sending for five minutes.
  • --dry-run prints the equivalent curl command, with keys left as environment variables, instead of sending.

The templates in assets/templates/ are starting points to copy into your project and own, not published packages. Each client implements the same behaviors, and each test suite exercises them against a mocked transport, so the tests run offline and instantly.

LanguageClientTestsRuntime
Pythonshepherd_client.pytest_shepherd_client.py (pytest)Python 3.11+, httpx
TypeScriptshepherd-client.tsshepherd-client.test.ts (node:test)Node.js 24+, no dependencies
C#ShepherdClient.csShepherdClientTests.cs (xUnit v3).NET 10
PHPsrc/ShepherdClient.phptests/ShepherdClientTest.php (PHPUnit 13)PHP 8.4+, Guzzle 8

The tests cover the cases that matter in production: 429 handling, 502 and 503 backoff, timeouts, 500s, and creates that are never retried, malformed clinic ids refused before sending, the circuit breaker, JSON string, JSON object, and plain-text error bodies, refusing unbounded lists, and read-merge-write updates that keep untouched fields.

The rules in SKILL.md match the API reference on this site. In short, the agent will:

  • Use the right environment. The sandbox for development and tests, https only, the three headers with X-Clinic-Id as a header. See Authentication.
  • Bound every read. A date window of one calendar month or less, or an id filter; explicit page sizes; shallow embeds. See Conventions.
  • Respect the limits. One shared budget per key, sequential requests by default, a wait for the next UTC minute after a 429, and no retries of timeouts, 4xx responses, 500s, or creates. See Rate limits and Errors.
  • Write safely. Read, merge, and write every update; look for an existing record before creating one after an ambiguous failure; get a human’s approval before the first production write. See Updating records.
  • Test. Unit tests for the failure cases above, and a few opt-in contract tests against the sandbox.
  • The skill runs entirely inside your agent. It has no telemetry and sends nothing to Shepherd or anyone else; the only network requests are the API calls you or your agent run with call.
  • Keys are read from environment variables and are never written to disk or printed. The runner keeps its budget and breaker state in ~/.cache/shepherd-openapi-skill/, under a hash of the public key.
  • Requests sent by the runner carry a User-Agent naming the skill version and your SHEPHERD_APP_NAME, so Shepherd can tell development traffic apart if you ask for help.
  • As with any skill, review what you install. Everything in the zip is readable text, and the SHA-256 above lets you confirm you have the file published here.

This is version 1.1.0, built from the public API as of September 2026 (API version 2.246.0.33). When the API changes in a way that affects integrations, a new version is published here. Download it again and replace the shepherd-openapi folder (or upload it again in the Claude apps).

VersionDateChanges
1.1.0September 2026Rules corrected and extended from live test-harness evidence: filter fields an endpoint accepts but does not apply are flagged, unknown sort fields are errors, rpp and page below 1 or null are rejected, smaller pages on large-record endpoints, a bound required on breeds, a date window required on every patient-notes read, 401 (not 403) for an unlinked clinic, and 500s are never retried. The starter clients refuse a malformed clinic id and no longer retry a 500.
1.0.0September 2026First release.

The agent isn’t using the skill. Mention the Shepherd API in your request, or in Claude Code invoke it with /shepherd-openapi. Check that the folder is named shepherd-openapi and contains SKILL.md directly.

The helper script won’t run. It needs Python 3.11 or newer on the path as python3. Without it, the agent still has the reference and rules, but cannot lint or make live calls.

Live calls fail with a network error. Your agent’s environment may block outbound requests; the Claude API always does, and the Claude apps can, depending on settings. Run the calls from your own machine instead.

Something in the skill is wrong or missing. Tell us. Include the skill version, the endpoint, and what you expected.