Clients
A client represents a pet owner: the human responsible for one or more patients (pets) at a clinic. Clients carry the billing relationship, the contact details, and the household-level preferences. Every patient in the Shepherd API is linked to at least one client, and most clinical and financial workflows start from a client lookup.
Clients have several related sub-resources (phones, notes, info, discount) that are managed through their own endpoints rather than as deep writes on the client object itself.
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 | Unique identifier for the client. |
identifier | string | Short human-facing identifier shown in the Shepherd UI. |
firstName / middleName / lastName | string | Name parts. |
email | string | Primary email address. |
dateOfBirth | string | Client’s date of birth. |
clientPhones | array | Phone numbers. Populated with embed=clientPhones. |
clientInfo | object | The contact block, including the postal address. Populated with embed=clientInfo; there are no address fields on the client itself. |
clientCoOwner | object | Co-owner, with their own phone list. Populated with embed=clientCoOwner. |
clientStatusId / clientTypeId / genderId | string | Lookup references. |
clientDiscountId | string | Discount program applied to this client. |
authorizedAgents | array | People authorised to act for the client. |
isTaxExempt | boolean | Whether the client is exempt from taxes. |
clinicId | string | The owning clinic. |
legacyId | string | Identifier carried over from a previous practice-management system. |
dateCreated | string | ISO 8601 UTC timestamp of creation. |
dateUpdated | string | ISO 8601 UTC timestamp of last update. |
deleted | boolean | Soft-delete flag; filter on this in queries. |
List / search
Section titled “List / search”/pav2/open-api-clients Returns a paginated collection of clients for the clinic identified by X-Clinic-Id. Accepts the standard collection parameters: page, rpp, sort, searchQuery, embed, and the dateCreatedFrom / dateCreatedTo date-range pair. See Conventions for the full parameter rules.
Example
Section titled “Example”curl -X POST https://open-api.shepherd.vet/pav2/open-api-clients \ -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": 25, "sort": "lastName|asc", "searchQuery": "smith", "embed": "clientPhones" }'import osimport requests
resp = requests.post( "https://open-api.shepherd.vet/pav2/open-api-clients", 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": 25, "sort": "lastName|asc", "searchQuery": "smith", "embed": "clientPhones", }, timeout=30,)resp.raise_for_status()payload = resp.json()for client in payload["item"]: print(client["id"], client["firstName"], client["lastName"])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-clients", new { page = 1, rpp = 25, sort = "lastName|asc", searchQuery = "smith", embed = "clientPhones", });response.EnsureSuccessStatusCode();var payload = await response.Content.ReadFromJsonAsync<JsonElement>();<?php$ch = curl_init('https://open-api.shepherd.vet/pav2/open-api-clients');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' => 25, 'sort' => 'lastName|asc', 'searchQuery' => 'smith', 'embed' => 'clientPhones', ]),]);$body = curl_exec($ch);$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);curl_close($ch);if ($status >= 400) { throw new RuntimeException("Shepherd API $status: $body");}$payload = json_decode($body, true);const response = await fetch('https://open-api.shepherd.vet/pav2/open-api-clients', { 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: 25, sort: 'lastName|asc', searchQuery: 'smith', embed: 'clientPhones', }),});if (!response.ok) { throw new Error(`Shepherd API ${response.status}: ${await response.text()}`);}const payload = await response.json();Response
Section titled “Response”{ "item": [ { "id": "e45a6e12-3a1c-4e7d-9fd2-3473a4d8a7a6", "firstName": "Jane", "lastName": "Smith", "email": "jane.smith@example.com", "clientPhones": [ { "id": "7d7ea1ee-3329-4b06-9966-b2f800c16f53", "countryId": "158d44c8-d163-4c27-8e77-b2b300f2522f", "phoneNumber": "5555550123", "phoneTypeId": "534494bb-d287-eb09-e90b-8a2b4e5b9398", "isPrimary": true } ], "dateCreated": "2026-03-14T18:22:11Z", "dateUpdated": "2026-05-01T09:14:00Z", "deleted": false } ], "totalRecords": 184, "page": 1, "recordsPerPage": 25, "sort": "lastName|asc", "searchQuery": "smith", "embed": "clientPhones", "links": []}Create
Section titled “Create”/pav2/open-api-clients/write Creates a new client record for the clinic.
Required: firstName, lastName, and clientPhones. The phone array is required by the model, so send it even if the client has no number on file; pass [] in that case.
Each entry in clientPhones requires countryId, phoneNumber, and phoneTypeId, all resolved from Lookups (open-api-countries and open-api-phone-types). isPrimary and phoneNumberName are optional.
A co-owner, the contact block, and authorized agents can be created in the same call via the clientCoOwner, clientInfo, and authorizedAgents objects. Notes and discounts are separate sub-resources.
Example
Section titled “Example”curl -X POST https://open-api.shepherd.vet/pav2/open-api-clients/write \ -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 '{ "firstName": "Jane", "lastName": "Smith", "email": "jane.smith@example.com", "clientPhones": [ { "countryId": "158d44c8-d163-4c27-8e77-b2b300f2522f", "phoneNumber": "5555550123", "phoneTypeId": "534494bb-d287-eb09-e90b-8a2b4e5b9398", "isPrimary": true } ] }'import osimport requests
resp = requests.post( "https://open-api.shepherd.vet/pav2/open-api-clients/write", 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={ "firstName": "Jane", "lastName": "Smith", "email": "jane.smith@example.com", "clientPhones": [ { "countryId": "158d44c8-d163-4c27-8e77-b2b300f2522f", "phoneNumber": "5555550123", "phoneTypeId": "534494bb-d287-eb09-e90b-8a2b4e5b9398", "isPrimary": True, } ], }, timeout=30,)resp.raise_for_status()client = resp.json()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-clients/write", new { firstName = "Jane", lastName = "Smith", email = "jane.smith@example.com", clientPhones = new[] { new { countryId = "158d44c8-d163-4c27-8e77-b2b300f2522f", phoneNumber = "5555550123", phoneTypeId = "534494bb-d287-eb09-e90b-8a2b4e5b9398", isPrimary = true, }, }, });response.EnsureSuccessStatusCode();<?php$ch = curl_init('https://open-api.shepherd.vet/pav2/open-api-clients/write');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([ 'firstName' => 'Jane', 'lastName' => 'Smith', 'email' => 'jane.smith@example.com', 'clientPhones' => [[ 'countryId' => '158d44c8-d163-4c27-8e77-b2b300f2522f', 'phoneNumber' => '5555550123', 'phoneTypeId' => '534494bb-d287-eb09-e90b-8a2b4e5b9398', 'isPrimary' => true, ]], ]),]);$body = curl_exec($ch);curl_close($ch);$client = json_decode($body, true);const response = await fetch('https://open-api.shepherd.vet/pav2/open-api-clients/write', { 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({ firstName: 'Jane', lastName: 'Smith', email: 'jane.smith@example.com', clientPhones: [ { countryId: '158d44c8-d163-4c27-8e77-b2b300f2522f', phoneNumber: '5555550123', phoneTypeId: '534494bb-d287-eb09-e90b-8a2b4e5b9398', isPrimary: true, }, ], }),});if (!response.ok) { throw new Error(`Shepherd API ${response.status}: ${await response.text()}`);}const client = await response.json();Update
Section titled “Update”/pav2/open-api-clients/{id}/update Updates an existing client. This is a PUT, so it is a full replacement: send the complete client representation, not just the fields you changed. Any editable field omitted from the body is cleared. See Updating records for the general rule.
Required on every update: firstName, lastName, and clientPhones. Omitting any of them is rejected.
What happens to each field you leave out of the body:
| Kept when omitted | Cleared or reset when omitted |
|---|---|
clientStatusId, the clientInfo contact block, authorizedAgents (an edit list, below) | middleName, email, dateOfBirth, clientTypeId, genderId, clientDiscountId, alerts, additionalText, referralSourceIds, clientCoOwner (and its phones), isTaxExempt (resets to false, so the client is charged tax), and every notification preference |
The home clinic, system identifiers, and deleted status cannot be changed here. authorizedAgents is an edit list rather than a replacement: each entry adds an agent (omit id), edits one in place (include id), or removes one (set authorizedAgentIdForRemove with a removalNote); agents you do not reference are left alone.
Returns 200 OK with the updated client. A 404 means the {id} does not resolve within the clinic named by X-Clinic-Id.
Preserving co-owners and their phone numbers
Section titled “Preserving co-owners and their phone numbers”The most common data-loss report against this endpoint involves co-owners. clientCoOwner is a nested object that carries its own clientCoOwnerPhones array, so a body that includes the co-owner but not their phones removes those phone numbers.
Fetch the full co-owner record before you merge. There are two ways to get it:
Embed it in the client read. Ask for the co-owner and their phones by name, using the dot notation described in Embedding related entities:
{ "ids": "e45a6e12-3a1c-4e7d-9fd2-3473a4d8a7a6", "embed": "clientCoOwner,clientCoOwner.clientCoOwnerPhones" }Or query the co-owner endpoint directly. POST /pav2/open-api-client-co-owners returns co-owner records with their phone information attached. Co-owners are keyed by the owning client’s ID, so passing that ID in ids returns the co-owner for that client:
{ "ids": "e45a6e12-3a1c-4e7d-9fd2-3473a4d8a7a6", "embed": "client" }Either way you end up with the complete co-owner object to resubmit.
Example: read, merge, write
Section titled “Example: read, merge, write”The example changes one field, the email address, and carries every other editable field through from the current record. clientInfo and clientStatusId are left out because they are kept when omitted.
# 1. Read the current client, embedding the nested collections you will resend.curl -X POST https://open-api.shepherd.vet/pav2/open-api-clients \ -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 '{ "ids": "e45a6e12-3a1c-4e7d-9fd2-3473a4d8a7a6", "rpp": 1, "embed": "clientPhones,clientCoOwner,clientCoOwner.clientCoOwnerPhones" }' > current-client.json
# 2. Build merged-client.json from item[0]: every update field copied from the# record (renaming the two mass-communication flags), with your change applied.# 3. PUT the complete object back.curl -X PUT https://open-api.shepherd.vet/pav2/open-api-clients/e45a6e12-3a1c-4e7d-9fd2-3473a4d8a7a6/update \ -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 @merged-client.jsonimport osimport requests
BASE = "https://open-api.shepherd.vet/pav2"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"],}client_id = "e45a6e12-3a1c-4e7d-9fd2-3473a4d8a7a6"
# Update fields with the same name on the record you read.SAME_NAME = ( "firstName", "middleName", "lastName", "email", "dateOfBirth", "clientTypeId", "genderId", "clientDiscountId", "isTaxExempt", "alerts", "additionalText", "referralSourceIds", "clientPhones", "clientCoOwner", "emailNotification", "smsNotification", "smsDirectMessagingNotification", "reminderEmailNotification", "reminderPostcardNotification",)# Update field -> field on the record you read.RENAMED = { "emailMassCommuncationNotification": "emailMassCommunicationNotification", "smsMassCommuncationNotification": "smsMassCommunicationNotification",}
# 1. Read, embedding every nested collection you intend to resend.read = requests.post( f"{BASE}/open-api-clients", headers=HEADERS, json={ "ids": client_id, "rpp": 1, "embed": "clientPhones,clientCoOwner,clientCoOwner.clientCoOwnerPhones", }, timeout=30,)read.raise_for_status()current = read.json()["item"][0]
# 2. Merge: copy every update field from the record, then apply the change.payload = {f: current[f] for f in SAME_NAME if f in current}payload.update({f: current[r] for f, r in RENAMED.items() if r in current})payload["email"] = "jane.newaddress@example.com" # the actual change
# 3. Write the complete object back.write = requests.put( f"{BASE}/open-api-clients/{client_id}/update", headers=HEADERS, json=payload, timeout=30,)write.raise_for_status()updated = write.json()using System.Net.Http.Json;using System.Text.Json.Nodes;
const string Base = "https://open-api.shepherd.vet/pav2";var clientId = "e45a6e12-3a1c-4e7d-9fd2-3473a4d8a7a6";
// Update fields with the same name on the record you read.string[] sameName =[ "firstName", "middleName", "lastName", "email", "dateOfBirth", "clientTypeId", "genderId", "clientDiscountId", "isTaxExempt", "alerts", "additionalText", "referralSourceIds", "clientPhones", "clientCoOwner", "emailNotification", "smsNotification", "smsDirectMessagingNotification", "reminderEmailNotification", "reminderPostcardNotification",];// Update field -> field on the record you read.var renamed = new Dictionary<string, string>{ ["emailMassCommuncationNotification"] = "emailMassCommunicationNotification", ["smsMassCommuncationNotification"] = "smsMassCommunicationNotification",};
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };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"));
// 1. Read with embeds.var read = await http.PostAsJsonAsync($"{Base}/open-api-clients", new{ ids = clientId, rpp = 1, embed = "clientPhones,clientCoOwner,clientCoOwner.clientCoOwnerPhones",});read.EnsureSuccessStatusCode();var current = (await read.Content.ReadFromJsonAsync<JsonObject>())!["item"]![0]!.AsObject();
// 2. Merge: copy every update field from the record, then apply the change.var payload = new JsonObject();foreach (var field in sameName) if (current.TryGetPropertyValue(field, out var value)) payload[field] = value?.DeepClone();foreach (var (field, readName) in renamed) if (current.TryGetPropertyValue(readName, out var value)) payload[field] = value?.DeepClone();payload["email"] = "jane.newaddress@example.com"; // the actual change
// 3. Write it back.var write = await http.PutAsJsonAsync($"{Base}/open-api-clients/{clientId}/update", payload);write.EnsureSuccessStatusCode();<?php$base = 'https://open-api.shepherd.vet/pav2';$clientId = 'e45a6e12-3a1c-4e7d-9fd2-3473a4d8a7a6';$headers = [ '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'),];
// Update fields with the same name on the record you read.$sameName = [ 'firstName', 'middleName', 'lastName', 'email', 'dateOfBirth', 'clientTypeId', 'genderId', 'clientDiscountId', 'isTaxExempt', 'alerts', 'additionalText', 'referralSourceIds', 'clientPhones', 'clientCoOwner', 'emailNotification', 'smsNotification', 'smsDirectMessagingNotification', 'reminderEmailNotification', 'reminderPostcardNotification',];// Update field => field on the record you read.$renamed = [ 'emailMassCommuncationNotification' => 'emailMassCommunicationNotification', 'smsMassCommuncationNotification' => 'smsMassCommunicationNotification',];
// 1. Read with embeds.$ch = curl_init("$base/open-api-clients");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_HTTPHEADER => $headers, CURLOPT_POSTFIELDS => json_encode([ 'ids' => $clientId, 'rpp' => 1, 'embed' => 'clientPhones,clientCoOwner,clientCoOwner.clientCoOwnerPhones', ]),]);$current = json_decode(curl_exec($ch), true)['item'][0];curl_close($ch);
// 2. Merge: copy every update field from the record, then apply the change.$payload = [];foreach ($sameName as $field) { if (array_key_exists($field, $current)) { $payload[$field] = $current[$field]; }}foreach ($renamed as $field => $readName) { if (array_key_exists($readName, $current)) { $payload[$field] = $current[$readName]; }}$payload['email'] = 'jane.newaddress@example.com'; // the actual change
// 3. Write it back.$ch = curl_init("$base/open-api-clients/$clientId/update");curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => 'PUT', CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_HTTPHEADER => $headers, CURLOPT_POSTFIELDS => json_encode($payload),]);$updated = json_decode(curl_exec($ch), true);curl_close($ch);const BASE = 'https://open-api.shepherd.vet/pav2';const clientId = 'e45a6e12-3a1c-4e7d-9fd2-3473a4d8a7a6';const 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,};
// Update fields with the same name on the record you read.const SAME_NAME = [ 'firstName', 'middleName', 'lastName', 'email', 'dateOfBirth', 'clientTypeId', 'genderId', 'clientDiscountId', 'isTaxExempt', 'alerts', 'additionalText', 'referralSourceIds', 'clientPhones', 'clientCoOwner', 'emailNotification', 'smsNotification', 'smsDirectMessagingNotification', 'reminderEmailNotification', 'reminderPostcardNotification',];// Update field -> field on the record you read.const RENAMED = { emailMassCommuncationNotification: 'emailMassCommunicationNotification', smsMassCommuncationNotification: 'smsMassCommunicationNotification',};
// 1. Read, embedding every nested collection you intend to resend.const read = await fetch(`${BASE}/open-api-clients`, { method: 'POST', headers, body: JSON.stringify({ ids: clientId, rpp: 1, embed: 'clientPhones,clientCoOwner,clientCoOwner.clientCoOwnerPhones', }), signal: AbortSignal.timeout(30_000),});if (!read.ok) throw new Error(`Shepherd API ${read.status}: ${await read.text()}`);const [current] = (await read.json()).item;
// 2. Merge: copy every update field from the record, then apply the change.const payload = {};for (const field of SAME_NAME) { if (field in current) payload[field] = current[field];}for (const [field, readName] of Object.entries(RENAMED)) { if (readName in current) payload[field] = current[readName];}payload.email = 'jane.newaddress@example.com'; // the actual change
// 3. Write the complete object back.const write = await fetch(`${BASE}/open-api-clients/${clientId}/update`, { method: 'PUT', headers, body: JSON.stringify(payload), signal: AbortSignal.timeout(30_000),});if (!write.ok) throw new Error(`Shepherd API ${write.status}: ${await write.text()}`);const updated = await write.json();Sub-resources
Section titled “Sub-resources”Client-adjacent data lives on its own endpoints. Each follows the same list / write conventions as the parent resource.
| Endpoint | Purpose |
|---|---|
/pav2/open-api-client-phones | Phone numbers attached to a client (mobile, home, work). |
/pav2/open-api-client-infos | Extended profile fields and household metadata. |
/pav2/open-api-client-notes | Free-form notes recorded by clinic staff against the client. |
/pav2/open-api-client-discounts | Discount programs and percentage adjustments applied to the client’s invoices. |
/pav2/open-api-client-co-owners | Co-owners attached to a client, with their phone numbers. Keyed by the owning client’s ID. |
See also
Section titled “See also”- Conventions: pagination, embed, sort, and date-range rules.
- Authentication: the three required headers.
- Errors: HTTP status codes and the error body shapes.
- Patients: the pets attached to each client.