Skip to content

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.

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.

  • 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).

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.

  • 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.

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).

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

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 the me/deposits and me/listings/deposits lists such a deposit has listingId: null, an empty listingShortCode, a listingTitle that is the deposit’s description, and a transferId naming the transfer. pickupWindowEnd is 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_DEPOSIT and sourceRefId = 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 REFUND or RELEASE clause, 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.