Use POST /api/pets to create pet profiles, PATCH /api/pets/{petIdentifier} to update existing pet profiles, and POST /api/people to create adopter or foster records from your own systems. These endpoints require a Write + read key from Settings > SDK & API, validate fields, enforce rate limits, and support idempotency keys for safe retries.
Request Overview
POST https://pawplacer.com/api/pets
PATCH https://pawplacer.com/api/pets/{petIdentifier}
POST https://pawplacer.com/api/people
x-api-key: YOUR_API_KEY
Content-Type: application/json
| Property | Requirement |
|---|---|
| Rate limit | 10 requests/hour |
| Authentication | x-api-key header |
| Body format | JSON |
Required Fields
| Field | Accepted values |
|---|---|
name | Any non-empty string |
species | dog, cat, rabbit |
age_category | youngest, young, adult, senior |
sex | male, female, unknown |
size | xSmall, small, medium, large, xLarge |
status | Your custom status name or label (case-insensitive) |
health | unknown, poor, good, great |
If the provided status label does not exist, PawPlacer falls back to your default “available” status.
Optional Fields
| Category | Fields |
|---|---|
| Identification | custom_id, microchip_id |
| Visuals | image_urls (array of URLs), show_public (boolean) |
| Demographics | breed (array), color (array), weight (string), coat_length |
| Dates | intake_date, outcome_date, age_birthday (ISO 8601 strings) |
| Compatibility | good_with, bad_with (values listed below) |
| Behavior | temperaments (values listed below), special_needs (array) |
| Notes | description, status_change_notes |
| Administration | primary_veterinarian_id (UUID), template_id (UUID) |
| Intake metadata | custom_status_id (UUID), location_found, reason_for_surrender |
| Financial | adoption_fee (string or number) |
| Custom data | custom_field_data object keyed by field_key |
Allowed Compatibility Values
activeLifestyle, cats, disabledMental, disabledPhysical, dogs, families, firstTimeOwners, frequentTravelers, kids, otherPets, outdoorsLiving, sedentaryLifestyle, seniors, smallApartments
Allowed Temperament Values
affectionate, aggressive, cuddly, curious, docile, energetic, fearful, gentle, independent, loyal, mischievous, moody, playful, protective, quiet, rough, shy, smart, social, stubborn, vocal
Custom Fields
- Call
GET /api/pets/custom-fields. - Use the
field_keyvalues returned (not the human-readable label). - Provide values that match each field’s type (text, number, select, etc.) in create or update payloads.
- Invalid keys trigger a 400 response that includes a list of available fields.
Example Request
const response = await fetch('https://pawplacer.com/api/pets', {
method: 'POST',
headers: {
'x-api-key': process.env.PAWPLACER_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Max',
species: 'dog',
age_category: 'young',
sex: 'male',
size: 'medium',
status: 'Available',
health: 'good',
show_public: true,
breed: ['Labrador Retriever'],
good_with: ['families', 'kids'],
temperaments: ['playful', 'loyal'],
adoption_fee: 250,
custom_field_data: {
favorite_toy: 'Tennis ball'
}
})
});
if (!response.ok) {
throw await response.json();
}
const pet = await response.json();
Success Response
The endpoint returns the newly created pet with normalized data. Status values are resolved to their display label, and arrays are always present even when empty.
{
"id": "d4738cf8-f31f-453d-a6c1-ba321a845c55",
"name": "Max",
"species": "dog",
"breed": ["Labrador Retriever"],
"color": [],
"age_years": null,
"age_months": null,
"age_category": "young",
"sex": "male",
"size": "medium",
"status": "Available",
"health": "good",
"spayed": false,
"adoption_fee": "250",
"microchip_id": null,
"good_with": ["families", "kids"],
"bad_with": [],
"temperaments": ["playful", "loyal"],
"image_urls": [],
"coat_length": null,
"custom_field_data": {
"favorite_toy": "Tennis ball"
},
"custom_id": null,
"intake_date": null,
"outcome_date": null,
"show_public": true,
"special_needs": [],
"status_change_notes": null,
"weight": null,
"created_at": "2025-07-29T13:34:20.242277+00:00",
"updated_at": "2025-07-29T13:34:20.242277+00:00"
}
Idempotency
Use an Idempotency-Key header when your integration may retry the same create
request. PawPlacer will replay the original successful response for the same key
and payload, and it will reject the request with 409 idempotency_conflict if
the same key is reused with a different body.
Error Responses
| Status | Reason | Example Payload |
|---|---|---|
| 400 | Validation failure | { "error": "Validation failed: name - Required", "code": "validation_failed", "request_id": "..." } |
| 400 | Malformed JSON | { "error": "Malformed JSON body", "code": "malformed_json", "request_id": "...", "hint": "Ensure body is valid JSON and header is application/json" } |
| 400 | Unsupported content type | { "error": "Unsupported Content-Type. Use application/json for POST /api/pets.", "code": "unsupported_content_type", "request_id": "...", "hint": "Set header: Content-Type: application/json" } |
| 401 | Missing or invalid API key | { "error": "API key required", "code": "api_key_required", "request_id": "..." } |
| 403 | Read-only key used on a write endpoint | { "error": "This API key does not have write access. Use a write-enabled key for this operation.", "code": "insufficient_scope", "request_id": "..." } |
| 400 | Empty body | { "error": "Empty request body. To list pets, use GET /api/pets. POST creates new pets.", "code": "bad_request", "request_id": "..." } |
| 404 | Pet not found on update | { "error": "Pet not found", "code": "pet_not_found", "request_id": "..." } |
| 409 | Reused Idempotency-Key with different payload | { "error": "This Idempotency-Key was already used with a different request payload.", "code": "idempotency_conflict", "request_id": "..." } |
| 429 | Rate limit exceeded | { "error": "Rate limit exceeded. Maximum 10 writes per hour for this key.", "code": "rate_limited", "request_id": "..." } |
| 500 | Unexpected server issue | { "error": "Something went wrong", "code": "internal_error", "request_id": "..." } |
Retry only after resolving validation errors or waiting for the rate limit window to reset.
Best Practices
- Convert numeric fields like
adoption_feeto strings if your language does not preserve large numbers automatically. - Provide
show_public: truewhen you want the pet to appear in public listings immediately. - Keep the PawPlacer
idreturned from create responses when possible; updates accept either that UUID or an assignedcustom_idinPATCH /api/pets/{petIdentifier}. - Use
custom_idfor external de-duplication and website-to-PawPlacer record matching. Duplicatecustom_idvalues are rejected on create and update. - Call
GET /api/petsafter creation or update if you rely on the list endpoint for displays. The list only includes non-deleted pets withshow_public: trueand a public or unset custom status.
PATCH /api/pets/{petIdentifier} – Update an Existing Pet
PATCH https://pawplacer.com/api/pets/{petIdentifier}
x-api-key: YOUR_API_KEY
Content-Type: application/json
Idempotency-Key: website-pet-sync:DOG-2026-001:2026-06-10
Use either the PawPlacer pet UUID returned from POST /api/pets or an assigned custom_id. If a route value could match both a UUID and a custom_id, PawPlacer treats it as a UUID first.
Send only the fields that should change. At least one field is required. Common supported fields include:
| Category | Fields |
|---|---|
| Profile | name, description, custom_id |
| Status and visibility | status, custom_status_id, show_public, status_change_notes |
| Photos | image_urls |
| Age and demographics | age_category, age_years, age_months, age_birthday, sex, size, weight |
| Health and compatibility | health, spayed, good_with, bad_with, temperaments, special_needs |
| Custom data | custom_field_data object keyed by field_key |
const response = await fetch(`https://pawplacer.com/api/pets/${externalDogId}`, {
method: 'PATCH',
headers: {
'x-api-key': process.env.PAWPLACER_API_KEY,
'Content-Type': 'application/json',
'Idempotency-Key': `website-pet-sync:${externalDogId}:${updatedAt}`
},
body: JSON.stringify({
description: 'Updated bio from the website CMS.',
status: 'Available',
image_urls: ['https://example.org/dogs/max-1.jpg'],
age_years: '4',
show_public: true,
custom_id: externalDogId,
custom_field_data: {
favorite_toy: 'Tennis ball'
}
})
});
if (!response.ok) {
throw await response.json();
}
const pet = await response.json();
The response is the updated pet payload. The same Idempotency-Key and identical payload replay the original successful response. Reusing the key with a different payload returns 409 idempotency_conflict.
POST /api/people – Create Adopter or Foster
Use this endpoint from a trusted server when your existing website forms should create applicant records in PawPlacer instead of embedding PawPlacer's built-in forms.
POST https://pawplacer.com/api/people
x-api-key: YOUR_API_KEY
Content-Type: application/json
| Property | Requirement |
|---|---|
| Rate limit | 10 requests/hour |
| Authentication | x-api-key header (write scope) |
| Body format | JSON |
Required Fields
| Field | Accepted values |
|---|---|
type | adopter or foster |
name | Any non-empty string |
Optional Fields
| Field | Type | Notes |
|---|---|---|
email | string | Validated email address |
phone | string | Phone number |
address | string | Mailing address |
status | string | pending, active, training, inactive, denied, suspended, blocked. Defaults to pending. |
status_change_notes | string | Notes about the status |
custom_field_data | object | Keyed by field_key from GET /api/people/custom-fields |
capacity | number | Foster capacity (integer, min 0) |
Example Request
const response = await fetch('https://pawplacer.com/api/people', {
method: 'POST',
headers: {
'x-api-key': process.env.PAWPLACER_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
type: 'adopter',
name: 'Jane Smith',
email: 'jane@example.com',
phone: '555-0100',
address: '123 Main St',
custom_field_data: {
has_yard: true
}
})
});
const adopter = await response.json();
Success Response
New records are created with status: "pending" by default. The response includes all person fields:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"type": "adopter",
"name": "Jane Smith",
"email": "jane@example.com",
"phone": "555-0100",
"address": "123 Main St",
"status": "pending",
"status_change_notes": null,
"custom_field_data": { "has_yard": true },
"tags": [],
"capacity": null,
"created_at": "2026-04-10T10:00:00.000000+00:00",
"updated_at": "2026-04-10T10:00:00.000000+00:00"
}
Idempotency
Use an Idempotency-Key header for safe retries, same behavior as POST /api/pets.
Error Responses
| Status | Reason |
|---|---|
| 400 | Missing type or name, invalid email, or malformed body |
| 401 | Missing or invalid API key |
| 403 | Read-only key used on a write endpoint |
| 409 | Idempotency key conflict |
| 429 | Rate limit exceeded |