Lookups
Most resources in the Shepherd API carry foreign keys to small enumerated sets: species, breeds, sexes, statuses, phone types, weight and temperature units, and so on. Writing a patient or a client means resolving those IDs first.
Each set has its own endpoint. There is no single lookup endpoint and no type parameter; you call the endpoint for the set you want. Every one is a POST that takes a JSON filter body and returns the standard collection envelope, so page, rpp, sort, searchQuery, ids, and the date-range filters all behave exactly as they do elsewhere. See Conventions.
Available lookups
Section titled “Available lookups”| Endpoint | Returns |
|---|---|
/pav2/open-api-species | Species (canine, feline, and so on). |
/pav2/open-api-breeds | Breeds. Each belongs to a species. |
/pav2/open-api-patient-sexes | Patient sex values (male, female, neutered, spayed, unknown). |
/pav2/open-api-patient-statuses | Patient status values (active, inactive, deceased, and so on). |
/pav2/open-api-patient-cpr-statuses | Patient CPR / resuscitation-status values. |
/pav2/open-api-appointment-statuses | Appointment status values. |
/pav2/open-api-payment-statuses | Payment status values. |
/pav2/open-api-inventory-status | Inventory status values. |
/pav2/open-api-task-entry-priority-statuses | Task priority values. |
/pav2/open-api-phone-types | Phone types (mobile, home, work). |
/pav2/open-api-countries | Countries, used by countryId on phone numbers. |
/pav2/open-api-weight-units | Weight units, used by weightUnitId on weight entries. |
/pav2/open-api-temperature-units | Temperature units. |
/pav2/open-api-time-units | Time units. |
/pav2/open-api-uom-units | Units of measure, used by inventory and products. |
/pav2/open-api-client-discounts | Client discount programs. |
/pav2/open-api-referral-source | Referral sources. |
/pav2/open-api-client-referral-source | Referral sources linked to a client. |
New sets are added periodically; the live Swagger UI has the current list and the exact response schema for each.
Scoping and legacy entries
Section titled “Scoping and legacy entries”Most lookups are global to an environment: every clinic sees the same list, and the filter body accepts only the standard collection parameters. Two are different, because clinics curate their own lists and retire old entries:
| Endpoint | Extra filters |
|---|---|
/pav2/open-api-breeds | getBreedsByClinicId, getBreedsByGroupId, speciesId, isLegacy, isDeleted |
/pav2/open-api-species | getSpeciesByClinicId, getSpeciesByGroupId, isLegacy, isDeleted |
When you are populating a picker for a specific clinic, ask for that clinic’s own non-legacy list:
{ "getBreedsByClinicId": true, "isLegacy": false, "rpp": 1000 }Leaving getBreedsByClinicId unset returns all breeds in the environment, including entries the clinic does not use and retired legacy rows. Writing a patient with one of those is a common source of rejected creates.
Patient sexes and statuses have no clinic scoping and no legacy flag, so no extra filtering is needed there.
Species and breed are one relationship
Section titled “Species and breed are one relationship”There is no speciesId on a patient. Species is a property of the breed, so choosing a breed determines the species. To offer a species-then-breed picker, read /pav2/open-api-species, then filter breeds by the chosen speciesId:
{ "speciesId": "9c1f...", "getBreedsByClinicId": true, "isLegacy": false, "rpp": 1000 }Example: fetch a clinic’s breeds
Section titled “Example: fetch a clinic’s breeds”curl -X POST https://open-api.shepherd.vet/pav2/open-api-breeds \ -H "Content-Type: application/json" \ -H "X-Integration-Public-Key: $SHEPHERD_PUBLIC_KEY" \ -H "X-Integration-Private-Key: $SHEPHERD_PRIVATE_KEY" \ -H "X-Clinic-Id: $SHEPHERD_CLINIC_ID" \ -d '{"getBreedsByClinicId":true,"isLegacy":false,"page":1,"rpp":1000}'import osimport requests
resp = requests.post( "https://open-api.shepherd.vet/pav2/open-api-breeds", headers={ "Content-Type": "application/json", "X-Integration-Public-Key": os.environ["SHEPHERD_PUBLIC_KEY"], "X-Integration-Private-Key": os.environ["SHEPHERD_PRIVATE_KEY"], "X-Clinic-Id": os.environ["SHEPHERD_CLINIC_ID"], }, json={"getBreedsByClinicId": True, "isLegacy": False, "page": 1, "rpp": 1000}, timeout=30,)resp.raise_for_status()breeds = {row["name"]: row["id"] for row in resp.json()["item"]}using System.Net.Http;using System.Net.Http.Json;
using var http = new HttpClient();http.DefaultRequestHeaders.Add("X-Integration-Public-Key", Environment.GetEnvironmentVariable("SHEPHERD_PUBLIC_KEY"));http.DefaultRequestHeaders.Add("X-Integration-Private-Key", Environment.GetEnvironmentVariable("SHEPHERD_PRIVATE_KEY"));http.DefaultRequestHeaders.Add("X-Clinic-Id", Environment.GetEnvironmentVariable("SHEPHERD_CLINIC_ID"));
var response = await http.PostAsJsonAsync( "https://open-api.shepherd.vet/pav2/open-api-breeds", new { getBreedsByClinicId = true, isLegacy = false, page = 1, rpp = 1000 });response.EnsureSuccessStatusCode();<?php$ch = curl_init('https://open-api.shepherd.vet/pav2/open-api-breeds');curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'X-Integration-Public-Key: ' . getenv('SHEPHERD_PUBLIC_KEY'), 'X-Integration-Private-Key: ' . getenv('SHEPHERD_PRIVATE_KEY'), 'X-Clinic-Id: ' . getenv('SHEPHERD_CLINIC_ID'), ], CURLOPT_POSTFIELDS => json_encode([ 'getBreedsByClinicId' => true, 'isLegacy' => false, 'page' => 1, 'rpp' => 1000, ]),]);$breeds = json_decode(curl_exec($ch), true)['item'];curl_close($ch);const response = await fetch('https://open-api.shepherd.vet/pav2/open-api-breeds', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Integration-Public-Key': process.env.SHEPHERD_PUBLIC_KEY, 'X-Integration-Private-Key': process.env.SHEPHERD_PRIVATE_KEY, 'X-Clinic-Id': process.env.SHEPHERD_CLINIC_ID, }, body: JSON.stringify({ getBreedsByClinicId: true, isLegacy: false, page: 1, rpp: 1000, }),});if (!response.ok) { throw new Error(`Shepherd API ${response.status}: ${await response.text()}`);}const { item: breeds } = await response.json();Response
Section titled “Response”Lookup responses use the standard collection envelope. The fields on each row vary by set; every set carries at least id and name.
{ "item": [ { "id": "6178ed48-7c31-282b-b87d-eccc6151437e", "name": "Labrador Retriever", "isLegacy": false }, { "id": "1a2b3c4d-5e6f-789a-bcde-0123456789ab", "name": "Border Collie", "isLegacy": false } ], "totalRecords": 412, "page": 1, "recordsPerPage": 1000, "sort": null, "searchQuery": null, "embed": null, "links": []}Caching guidance
Section titled “Caching guidance”Lookup data changes rarely and is read on almost every write you make against other resources. A few patterns that work well:
- Cache by
(environment, clinicId, lookup)and refresh once a day, or on a manual “reload” hook in your admin UI. The environment belongs in the key: sandbox and production IDs are not interchangeable. - Treat the
idvalues as opaque strings; do not parse them or infer meaning from their shape. - Page with a large
rpp(the maximum is 1000) rather than many small pages. Most sets fit in one page; breeds may not. - If a foreign key in another resource doesn’t resolve against your cached set, refresh the cache before treating it as an error. A clinic may have added a breed since you last synced.
See also
Section titled “See also”- Conventions for the collection envelope and pagination.
- Patients for the write endpoints that consume
breedId,sexId, andstatusId. - Clients for the ones that consume
countryIdandphoneTypeId. - Errors for the two JSON error body shapes.