Protected animal deposits
Animal listings in Reserve mode take a deposit through United Pet Club and hold it until the handover is
confirmed (buyer photo + seller confirmation + microchip match). The balance is never on the platform. Everything
below rides the gateway under /api/animallisting/v1.
Reading a listing
Section titled “Reading a listing”GET public/listings/{shortCode} — the card carries protectedDeposit: true and deposit carries reserveMode
(NONE | RESERVE), pickupWindowEnd and depositClause (REFUND | RELEASE | ARBITRATE). Render the badge and the
“no buyer protection beyond the held deposit” statement whenever reserveMode = RESERVE; contact-only listings
(NONE) show the deposit as informational.
Opening a deposit (signed-in buyer)
Section titled “Opening a deposit (signed-in buyer)”- Single-animal listing:
POST public/listings/{shortCode}/deposits - Litter item:
POST public/listings/{shortCode}/items/{itemId}/deposits
Both return { depositId, amount, checkout: { psp, pspIntentReference, clientSecret, publishableKey }, expiresAtUtc } —
host Stripe’s Payment Element on clientSecret. Payment success is applied from the provider webhook only: poll
GET deposits/{depositId} until status = PAID. Refusals: AnimalListing_AnimalListingDeposit_AD_Cap_RJ (422, the
deposit exceeds min($500, 25 %)), …_AD_NotEligible_RJ (409, the listing does not meet the Reserve conditions),
…_HD_Fee_FL (502, retry — the fee could not be resolved), AnimalListing_AnimalListingEngagement_VL_Deposit_RJ
(409, no deposit can be taken — e.g. a contact-only listing).
The deposit surface
Section titled “The deposit surface”GET deposits/{id} (buyer, seller or admin) → ProtectedDepositDto: amounts and the fee snapshot, status
(PAID = captured and held, RELEASED, REFUNDED, IN_ARBITRATION, …), pickupWindowEnd, holdExpiresAtUtc,
depositClause, buyer / seller confirmation blocks, handoverConfirmedAtUtc, matchedChipId,
ownershipTransferredAtUtc, releasedToSellerAtUtc + releaseReason, arbitration, viewerRole.
Seller lists: GET me/listings/deposits; buyer lists: GET me/deposits.
Confirming the handover
Section titled “Confirming the handover”- Buyer:
POST deposits/{id}/handover/buyer { photoMediaShortCode, scannedChipId? }— the photo is a Media asset short code you uploaded on the buyer’s behalf (required on the first confirmation). - Seller:
POST deposits/{id}/handover/seller { scannedChipId }.
When both exist and a scan equals the animal record’s microchip the response shows status = RELEASED; a mismatch
answers AnimalListing_AnimalListingHandover_UP_ChipMismatch_RJ (409) and opens arbitration; a chip-pending animal
answers …_UP_ChipNotRegistered_RJ (422) until the seller registers the chip. Other codes: …_UP_NotParty_RJ (403),
…_UP_PhotoRequired_RJ (400), …_UP_State_RJ (409). Sellers may still POST deposits/{id}/refund; forfeit on a
protected deposit opens a dispute case instead of releasing the money.
The ownership transfer
Section titled “The ownership transfer”A confirmed handover is recorded as a completed ownership transfer in the AnimalTransfer service (since
2026-09-23): type SALE, method MARKETPLACE, sourceKind = ANIMAL_LISTING_DEPOSIT and sourceRefId = the deposit
id. The buyer’s photo confirmation is the acceptance, and nobody has to start or accept anything. Read it with
GET /api/animal-transfer/v1/animals/{animalId}/transfers (or the buyer’s
GET /api/animal-transfer/v1/recipients/{profileId}/transfers), signed in as a party. The animal’s owner of record
then changes within seconds.
ownershipTransferredAtUtc on the deposit stamps the recorded transfer. It stays empty while the transfer cannot be
recorded, for example because the seller no longer owns the animal of record or another transfer of it is open.
United Pet Club retries daily; the deposit money is released to the seller regardless.
While a deposit is held (PAID or IN_ARBITRATION), a member-started transfer of that animal is refused:
POST /api/animal-transfer/v1/transfers answers AnimalTransfer_AnimalTransferOrchestration_AD_Reserved_RJ (409).
Seller-side eligibility
Section titled “Seller-side eligibility”GET listings/{id}/reserve-eligibility (owner) → the checklist (RESERVE_ANIMAL_RECORD, RESERVE_MICROCHIP,
RESERVE_BORN, RESERVE_PICKUP_WINDOW, RESERVE_PRICE, RESERVE_DEPOSIT_CAP, RESERVE_PAYOUT_ACTIVE), the cap and a
fee preview (5 % min $5 seller-paid plus processing; 0 for verified nonprofits). Set the terms on
PUT listings/{id} → deposit: { required, amount, refundable, policyText, reserveMode, pickupWindowEnd, depositClause, contractTemplateId }.
Deposits on a member’s direct sale
Section titled “Deposits on a member’s direct sale”Since 2026-09-25 a protected deposit can also come from a sale between two members with no listing: an ownership transfer whose seller chose a deposit held by United Pet Club. The buyer pays it in the United Pet Club Dashboard, on the transfer’s page, once they have accepted the offer and the owners have approved it. What an integration sees:
- On
GET deposits/{id}and in theme/depositsandme/listings/depositslists such a deposit haslistingId: null, an emptylistingShortCode, alistingTitlethat is the deposit’s description, and atransferIdnaming the transfer.pickupWindowEndis the transfer’s planned handover date. - The handover confirmations, their codes and the arbitration above work the same way. When the chip matches, the
deposit is released and the members’ own transfer completes with
sourceKind = TRANSFER_DEPOSITandsourceRefId= the deposit id; no second transfer is recorded. - The transfer settles its deposit when it ends before the handover: the seller cancelling (or the offer expiring)
refunds the buyer, the buyer withdrawing applies the seller’s
depositClause, and a dispute opens an arbitration. - A
REFUNDorRELEASEclause, on any protected deposit, counts only against an agreement both parties signed in the app, each acting as their own profile; a signature made through a contract’s signing link doesn’t count. Without one, the case goes to arbitration.
A litter with a Litter Pass takes deposits on any plan
Section titled “A litter with a Litter Pass takes deposits on any plan”Since 2026-09-25 a litter listing whose Breeding litter holds a Litter Pass may take protected deposits
whatever the seller’s plan — the LISTING_DEPOSITS entitlement is not required for it — and the listing
does not count against the seller’s litter-listing slots. The Reserve checklist
(GET listings/{id}/reserve-eligibility: a born, chipped litter, a price, the deposit cap, an active payout
account) still applies, and the buyer-side routes above are unchanged. Taking a deposit is never behind the
pass on a listing whose plan already grants deposits.
What the pass does gate is the waitlist: POST public/listings/{shortCode}/waitlist, an owner’s offer
and POST me/waitlist/{entryId}/accept on a listing created from a litter answer
AnimalListing_Waitlist_AD_LitterPass_RJ (402) with data.purchase = { "featureCode": "LITTER_PASSES", "quantity": 1 } while the litter has no live pass, and the public detail reports the waitlist closed. This
refusal applies whether or not metering is on. The owner stamps the pass in Breeding
(POST /api/breeding/v1/births/{id}/pass), which draws nothing while metering is off; offer the purchase
only if the stamp itself answers 402. See
Entitlements → Litter Passes.