Products
A product is anything a clinic sells or charges for: vaccines, exams, surgical procedures, food, retail items, and any other line that can appear on an invoice. Products are organized into categories, can be grouped into bundles for common visit types, and are linked to tax rates that drive the line-item math on invoices.
Products are the catalog. Inventory tracks the physical stock of products; invoice items reference products to record what was sold.
Schema
Section titled “Schema”Authoritative schemas live in the live Swagger UI. The fields below are a representative subset for orientation.
| Field | Type | Description |
|---|---|---|
id | string | Server-assigned identifier. |
name | string | Human-readable name (e.g. “Canine DA2PP vaccine”). |
customProductId | string | Clinic’s own product code, used for easier search. |
price | number | Default sale price. |
productCategoryId | string | Category this product belongs to. productCategory expands it. |
productTypeId | string | Product type reference. productType expands it. |
isTaxable | boolean | Whether tax applies. There is no tax-rate reference on the product itself; see the product tax-rate sub-resources. |
isDiscountable | boolean | Whether discounts may be applied. |
isControlled | boolean | Whether this is a controlled substance. |
isActive | boolean | Whether the product is currently sellable. |
dispensingFeeId / customDispensingFee | string / number | Dispensing fee reference and per-product override. |
medicationProduct / vaccineProduct / serviceProduct / injectionProduct | object | Type-specific detail blocks; only the one matching the product type is populated. |
inventoryProductSetting | object | Stock-tracking settings for this product. |
includedOnInvoice / includedInMedicalRecord | boolean | Whether the product appears on the invoice and in the medical record. |
dateCreated | string | ISO 8601 UTC timestamp of creation. |
dateUpdated | string | ISO 8601 UTC timestamp of last update. |
isDeleted | boolean | Soft-delete flag. Note this is isDeleted, not deleted. |
List / search
Section titled “List / search”/pav2/open-api-products Standard collection envelope. Filter and page through the catalog with the usual pagination, sort, and embed conventions.
Example
Section titled “Example”curl -X POST https://open-api.shepherd.vet/pav2/open-api-products \ -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 '{ "page": 1, "rpp": 100, "sort": "name|asc" }'import os, requests
resp = requests.post( "https://open-api.shepherd.vet/pav2/open-api-products", 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={"page": 1, "rpp": 100, "sort": "name|asc"}, timeout=30,)resp.raise_for_status()page = resp.json()for p in page["item"]: print(p["name"], p["price"])using System.Net.Http.Json;
using var http = new HttpClient();http.DefaultRequestHeaders.Add("X-Integration-Public-Key", publicKey);http.DefaultRequestHeaders.Add("X-Integration-Private-Key", privateKey);http.DefaultRequestHeaders.Add("X-Clinic-Id", clinicId);
var body = JsonContent.Create(new { page = 1, rpp = 100, sort = "name|asc",});var resp = await http.PostAsync( "https://open-api.shepherd.vet/pav2/open-api-products", body);resp.EnsureSuccessStatusCode();Console.WriteLine(await resp.Content.ReadAsStringAsync());<?php$ch = curl_init('https://open-api.shepherd.vet/pav2/open-api-products');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([ 'page' => 1, 'rpp' => 100, 'sort' => 'name|asc', ]),]);$response = curl_exec($ch);curl_close($ch);echo $response;const resp = await fetch('https://open-api.shepherd.vet/pav2/open-api-products', { 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({ page: 1, rpp: 100, sort: 'name|asc' }),});if (!resp.ok) throw new Error(`Shepherd ${resp.status}: ${await resp.text()}`);const page = await resp.json();for (const p of page.item) console.log(p.name, p.price);Response
Section titled “Response”{ "item": [ { "id": "44de1c2e-18b4-4a91-a5c9-abc123456789", "name": "Canine DA2PP vaccine", "customProductId": "VAC-DA2PP", "price": 38.00, "productCategoryId": "c1aa3c4d-5e6f-789a-bcde-0123456789ab", "productTypeId": "7b2e4f10-9c3a-4d51-8e6f-2a1b3c4d5e6f", "productCategory": null, "productType": null, "isTaxable": true, "isDiscountable": true, "isControlled": false, "isActive": true, "includedOnInvoice": true, "includedInMedicalRecord": true, "vaccineProduct": null, "medicationProduct": null, "serviceProduct": null, "dateCreated": "2025-09-10T16:55:01Z", "dateUpdated": "2026-02-14T08:09:33Z", "isDeleted": false } ], "totalRecords": 1284, "page": 1, "recordsPerPage": 100, "sort": "name|asc", "searchQuery": null, "embed": null, "links": []}Writing products
Section titled “Writing products”The product catalog is read only through the public API. There is no create, update, or delete endpoint for products; the catalog is maintained by the clinic inside Shepherd, and integrations read it to resolve product IDs, pricing, and tax treatment.
Cache the catalog rather than re-reading it per transaction; it changes rarely. See Lookups for the caching approach.
Sub-resources
Section titled “Sub-resources”The product catalog has several adjacent endpoints. Each follows the same request shape: POST, JSON body, the three auth headers, the standard pagination conventions.
| Endpoint | Purpose |
|---|---|
POST /pav2/open-api-product-budle | Bundles: pre-built groups of products for common visit types. |
POST /pav2/open-api-product-bundle-items | Line items that make up each bundle. |
POST /pav2/open-api-product-categories | Catalog categories used to organize products. |
POST /pav2/open-api-product-tax-rates | Tax rates referenced by products and invoice items. |
See also
Section titled “See also”- Invoices for how products end up on a bill.
- Inventory for stock tracking against the catalog.
- Conventions for pagination, embedding, sorting, and date filters.