PawPlacer
PawPlacer
  • Home
  • Compare
  • Migrating
  • Security
  • FAQs
  • Documentation
  • About
Sign InDemoStart Free

Calendar

Website WidgetsDeveloper API
JavaScript SDKGet DataCreate and Update Records
Petfinder IntegrationAdopt-a-Pet IntegrationPetLink Microchip Registry Integration
Match Lab

Documentation

Create and Update Records

Create pet profiles, update existing pets, and create people records with the PawPlacer API

In this guide

Key sections from this documentation page.

Request OverviewRequired FieldsOptional Fields

Use POST /api/pets to create pet profiles, PATCH /api/pets/{petIdentifier} to update existing pet profiles, and POST /api/people to create adopter, foster, surrender, or volunteer 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
PropertyRequirement
Rate limit250 requests/hour per write endpoint
Authenticationx-api-key header
Body formatJSON

Required Fields

FieldAccepted values
nameAny non-empty string
speciesdog, cat, rabbit, horse, small_furry, bird, barnyard, scales_fins_other, reptile, invertebrate
age_categoryyoungest, young, adult, senior
sexmale, female, unknown
sizexSmall, small, medium, large, xLarge
statusYour custom status name or label (case-insensitive)

If the provided status label does not exist, PawPlacer falls back to your default “available” status.

Optional Fields

CategoryFields
Identificationcustom_id, microchip_id
Visualsimage_urls (array of URLs), show_public (boolean)
Demographicsbreed (array), color (array), weight (string), coat_length
Datesintake_date, outcome_date, age_birthday (ISO 8601 strings)
Compatibilitygood_with, bad_with (values listed below)
Behaviortemperaments (values listed below), special_needs (array)
Healthhealth (unknown, poor, good, great), spayed (boolean)
Notesdescription, status_change_notes
Administrationprimary_veterinarian_id (UUID), template_id (UUID)
Intake metadatacustom_status_id (UUID), location_found, reason_for_surrender
Financialadoption_fee (string or number)
Custom datacustom_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

  1. Call GET /api/pets/custom-fields.
  2. Use the field_key values returned (not the human-readable label).
  3. Provide values that match each field’s type (text, number, select, etc.) in create or update payloads.
  4. 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

StatusReasonExample Payload
400Validation failure{ "error": "Validation failed: name - Required", "code": "validation_failed", "request_id": "..." }
400Malformed JSON{ "error": "Malformed JSON body", "code": "malformed_json", "request_id": "...", "hint": "Ensure body is valid JSON and header is application/json" }
400Unsupported 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" }
401Missing or invalid API key{ "error": "API key required", "code": "api_key_required", "request_id": "..." }
403Read-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": "..." }
400Empty body{ "error": "Empty request body. To list pets, use GET /api/pets. POST creates new pets.", "code": "bad_request", "request_id": "..." }
404Pet not found on update{ "error": "Pet not found", "code": "pet_not_found", "request_id": "..." }
409Reused Idempotency-Key with different payload{ "error": "This Idempotency-Key was already used with a different request payload.", "code": "idempotency_conflict", "request_id": "..." }
429Rate limit exceeded{ "error": "Rate limit exceeded. Maximum 250 writes per hour for this key.", "code": "rate_limited", "request_id": "..." }
500Unexpected 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_fee to strings if your language does not preserve large numbers automatically.
  • Provide show_public: true when you want the pet to be eligible for PawPlacer's public profile after the organization, status, and image requirements are also satisfied.
  • Keep the PawPlacer id returned from create responses when possible; updates accept either that UUID or an assigned custom_id in PATCH /api/pets/{petIdentifier}.
  • Use custom_id for external de-duplication and website-to-PawPlacer record matching. Duplicate custom_id values are rejected on create and update.
  • Call GET /api/pets after creation or update if you rely on the list endpoint for displays. It returns every non-deleted pet in the key's account, so filter show_public, status, media, and any other publishing rules before rendering records to an unauthenticated audience.

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:

CategoryFields
Profilename, description, custom_id
Status and visibilitystatus, custom_status_id, show_public, status_change_notes
Photosimage_urls
Age and demographicsage_category, age_years, age_months, age_birthday, sex, size, weight
Health and compatibilityhealth, spayed, good_with, bad_with, temperaments, special_needs
Custom datacustom_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 a Person Record

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
PropertyRequirement
Rate limit250 requests/hour
Authenticationx-api-key header (write scope)
Body formatJSON

Required Fields

FieldAccepted values
typeadopter, foster, surrender, or volunteer
nameAny non-empty string

Optional Fields

FieldTypeNotes
emailstringValidated email address
phonestringPhone number
addressstringMailing address
statusstringpending, active, training, inactive, denied, suspended, blocked. Defaults to pending.
status_change_notesstringNotes about the status
custom_field_dataobjectKeyed by field_key from GET /api/people/custom-fields
capacitynumberFoster capacity (integer, min 0)
applicationobjectAdopter/foster-only application with one to five unique pet_ids, terms_accepted: true, and the optional rendered template_id
petsarraySurrender pet details or an existing pet_id; intended for surrender records

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"
}

Adopter and Foster Applications

Use the application form contract exposed by PawPlacer instead of hardcoding rescue-specific questions:

  1. Call GET /api/people/custom-fields?type=adopter (or foster). Fields are already ordered by section_order and field_order.
  2. Skip fields where hidden or internal_only is true. Render the remaining field_type, options, placeholder, help_text, and section metadata.
  3. Call GET /api/contracts?type=adopter (or foster) and render the returned Markdown.
  4. Require an explicit checkbox accepting that contract.
  5. Send the standard answers and custom_field_data to POST /api/people with application. When the rendered field metadata includes a template_id, echo that value in application.template_id so PawPlacer can reject a stale template.

Application submissions must use a UUID Idempotency-Key. Generate it once for the browser submission and reuse it if the browser or trusted server retries that same submission.

// This UUID was generated once for the form submission and survives retries.
const submissionId = '550e8400-e29b-41d4-a716-446655440002';

const response = await fetch('https://pawplacer.com/api/people', {
  method: 'POST',
  headers: {
    'x-api-key': process.env.PAWPLACER_API_KEY,
    'Content-Type': 'application/json',
    'Idempotency-Key': submissionId
  },
  body: JSON.stringify({
    type: 'adopter',
    name: 'Jane Smith',
    email: 'jane@example.com',
    custom_field_data: {
      housing_type: 'house',
      has_yard: true
    },
    application: {
      pet_ids: ['550e8400-e29b-41d4-a716-446655440001'],
      template_id: '550e8400-e29b-41d4-a716-446655440009',
      terms_accepted: true
    }
  })
});

if (!response.ok) {
  throw await response.json();
}

const applicant = await response.json();

The operation is atomic: PawPlacer creates the pending person, application submission, and selected-pet associations together. The response includes application metadata:

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "type": "adopter",
  "name": "Jane Smith",
  "status": "pending",
  "application": {
    "id": "550e8400-e29b-41d4-a716-446655440003",
    "pet_ids": ["550e8400-e29b-41d4-a716-446655440001"],
    "submitted_at": "2026-08-11T16:00:00.000Z"
  }
}

Idempotency

Use an Idempotency-Key header for safe retries, same behavior as POST /api/pets. Application submissions require the key to be a UUID.

Error Responses

StatusReason
400Missing type or name, invalid email, malformed body, unaccepted terms, or a missing/non-UUID application idempotency key
401Missing or invalid API key
403Read-only key used on a write endpoint
409Idempotency key conflict
422invalid_pets — an application pet is not selectable for this rescue, or a bonded pair was split
429Rate limit exceeded

Keep reading

Move through the documentation in order.

PreviousGet DataNextPetfinder Integration
PawPlacer

© Copyright 2026 PawPlacer. All Rights Reserved.

Contact
  • Email
About
  • About Us
  • Migration Help
  • Funding Philosophy
  • Careers
  • FAQ
  • Blog
  • Changelog
  • Roadmap
Legal
  • Terms of Service
  • Privacy Policy
  • Security
  • Cookie Policy