Skip to content

Changelog

Notable API changes over time.

2026-10-19 — Accounting file formats (Preview)

Section titled “2026-10-19 — Accounting file formats (Preview)”

New section: Accounting files. UPC Accounting, in Preview, exchanges data with other software through files only: no live bank feed, no accounting-software API sync, and no partner API for the books. The section documents the file contracts for vendors, banks and bookkeepers:

  • Imports: bank statements (OFX, QFX, QBO, bank CSV and Excel, camt.052 / 053 / 054, MT940 / MT942, BAI2), open-banking JSON exports (FDX, UK Open Banking v3.1, Berlin Group NextGenPSD2, Australia CDR), and books from QuickBooks Online and Desktop, Xero, Sage, Wave, FreshBooks, Zoho Books, FEC, DATEV, SAF-T and JPK_KR. See Bank statements, Open-banking exports and Accounting software imports.
  • Exports: QuickBooks Online journal CSV, IIF, Xero manual journal CSV, FEC, DATEV Buchungsstapel, SAF-T 2.00, JPK_KR, the UPC templates and a full-entity archive. See Exports and UPC templates.
  • Rules: 10 MB per statement file, 25 MB per accounting-software file, XML with a DOCTYPE refused, transaction ids and entry numbers used to skip what was imported before, and a formula guard on exported text. See the overview.
  • Not available: payroll register files, fixed-asset and employee templates, and some list and journal exports; each page says which.

2026-10-18 — Business resources act for the connected business

Section titled “2026-10-18 — Business resources act for the connected business”

Breaking for any integration built against the earlier contract of these resources.

  • A delegated token is required. Animals, invoices, breeding records, messages, content items, inventory items, marketplace offers, sales, business profiles, and the social channel, post and engagement reads act for the business that connected your app. They need a token from the authorization-code grant and a business that keeps a valid payment card on file. A client-credentials token answers 403 0010010000406; no card, 403 0010010000407. A resource of another business answers 404. A business grants their scopes on the consent page, from the ones your app declared, once United Pet Club approved a review of your app’s scopes; a self-registered app can declare only the booking scopes. See Business resources and Scopes.
  • Social reads are the business’s. Channels, posts and engagement are those the connected business owns, no longer those of the app’s publisher profile.
  • Marketplace: new GET /api/marketplace/v1/offers and /offers/{id}; /listings and /listings/{id} stay as aliases. The resource gains offerCode, storefrontId, kind, catalogItemId, catalogCategoryId, pricingMode, listPriceMinorUnits, priceMinorUnits, priceStatus and updatedAt. price (a decimal) and category are gone: prices are integer minor units and may be null (see priceStatus), and the kind filter replaces category (still accepted as a filter name). Drafts are listed. See Marketplace offers.
  • Order: new GET /api/order/v1/sales and /sales/{id}; /orders and /orders/{id} stay as aliases. The id is the sale (the business’s part of an order), not the order. Gains orderId, orderStatus, subtotal, discount, tax, feesAndLevies, deposit, shipping; totalAmount is what the buyer pays for the sale, not the payout. See Sales.
  • Billing: new GET /api/billing/v1/platform-invoices (+ /{id}), what United Pet Club charged the business, with kind (PLAN / PLATFORM_FEES), minor-unit amounts, period and plan fields; /invoices (+ /{id}) is now its alias. New GET /api/billing/v1/client-invoices (+ /{id}), the invoices the business issued to its clients, with isOverdue, dueAt, clientName, reference and lines (on a get); paidAt is always null for now. A platform-invoice page may reach 1,000 invoices deep. See Invoices.
  • Communication: POST /api/communication/v1/messages takes { clientId, subject?, body, idempotencyKey? } and sends an in-app message to one of the business’s clients; recipient is gone and channel accepts only IN_APP. The answer gains conversationId, clientId, postedAt (replacing queuedAt), conversationStarted and replayed (200 on a replay, 201 otherwise). New refusals: 409 for a guest client, 429 past the daily caps (50 clients per business, 5 messages per client). See Messages to clients.
  • Animals: the list holds the animals the business owns of record; a get, update or archive also reaches one it co-owns. DELETE archives the animal. Create gains confirmedNotDuplicate; the resource gains upcCode, registeredName, lifeStatus and isOwnerOfRecord. See Animals.
  • Breeding records: sireAnimalId is replaced by sireAnimalIds (every sire) and servicings; gains matingMethod, isArtificialInsemination, matingStartedAt, matingEndedAt, expectedBirthLatestDate, createdAt and updatedAt; matingDate and damAnimalId may now be null. See Breeding records.
  • Business profiles: the list is the business and its brand locations. The resource gains handle, purpose, avatarPath, relation (SELF / BRAND_LOCATION) and verifiedTier (the verified badge: NONE, PERSON, OWNER or BUSINESS). See Business profiles.
  • Content items: publishedAt may be null (never published); gains titleLocale, createdAt and updatedAt. See Content items.
  • Inventory items: quantityOnHand is a decimal in base units (for example 2.5 g); gains isActive. See Inventory items.
  • Errors: a business resource relays its service’s 429 as well as 400, 403, 404, 409 and 503. See Errors.

2026-10-17 — Plan switch credit by whole months; refunds keep usage back, once per client

Section titled “2026-10-17 — Plan switch credit by whole months; refunds keep usage back, once per client”
  • Plan switch: an upgrade’s credit is now the whole months of the current plan after the month in progress (a monthly plan gets none; gifts are credited too). The checkout and preview switch object gains unusedMonths and termMonths; unusedDays / termDays stay.
  • Refunds: GET /api/sub/v1/subscriptions/{id}/refund-eligibility gains grossRefundableAmountMinor, usageChargeMinor and usageLines (definitionSysName, quantity, packPriceMinor, packUnits, amountMinor): texts / WhatsApp, e-mail sends and AI credits used since the charge are kept back from the refund, so refundableAmountMinor is net of them. New decision USAGE_COVERS_CHARGE. LIMIT_REACHED now means the client’s one self-serve refund, ever, and comes with canRequestRefund: false. Refund rows gain usageChargeMinor and usageLines.
  • New: GET /api/sub/v1/refund-policy — the signed-in caller’s refund terms (selfServeRefundAvailable, windowDays, usageChargedDefinitions), to show before a purchase.

2026-10-17 — Reference prices on booking fees

Section titled “2026-10-17 — Reference prices on booking fees”
  • No request or response changes. The Commission of a booking recorded below its service’s reference price is worked out on the reference price (Platform Agreement 13.5), a booking pays one Commission however it is paid, and a completed booking never settled is charged after 14 days. See Reference prices.

2026-10-15 — Promo codes on carts; seller promotions

Section titled “2026-10-15 — Promo codes on carts; seller promotions”
  • Promo codes: POST /api/cart/v1/carts/{cartId}/promo-codes { code } and DELETE /api/cart/v1/carts/{cartId}/promo-codes/{code} answer the cart plus the refused codes with a reasonCode. Promo discounts in the cart detail carry couponCodeSnapshot, cartLineId, fundedBy (SELLER / UPC) and fundedByProfileIdLogical. POST …/discounts no longer accepts a promotion or coupon from a member (403).
  • Seller promotions: /api/promotion/v1/seller/… lets an approved seller or a verified organization or shelter create, publish, pause, resume and end its own promotions, add codes, and read results and the per-claim fees it was charged. GET /api/promotion/v1/me/reward-codes lists referral reward codes.
  • Membership dues: a seller promotion can name listingKindCode: "MEMBERSHIP_TIER" and duesTermsCount (1–36); POST /api/membership/v1/applications and …/memberships/{id}/change-requests take promoCode, and the application / change request reads gain promoCode, promoDiscountAmount, promoDuesTermsCount. Claims gain claimSourceKindCode and termNumber.
  • See Promo codes and seller promotions.

2026-10-14 — A brand names the company that owns it

Section titled “2026-10-14 — A brand names the company that owns it”
  • The profile detail (GET /api/profile/v1/{locale}/Profile/profiles/{profileId} and …/by-handle/{handle}) gains brandOwner on a brand profile (the owning company, or null) and ownedBrands on any other profile (the brands it owns; empty when none), each { profileId, handleText, displayName }. See Brands and locations.

2026-10-12 — Pet food statistics from opted-in owners

Section titled “2026-10-12 — Pet food statistics from opted-in owners”
  • Two anonymous reads: GET /api/diet/v1/public/research/foods (the foods pets of a species, size and age eat most) and GET /api/diet/v1/public/research/products/{productId} (one food’s counts, outcome mix, breakdowns and reformulation comparisons). Groups of 10 or more pets only, counts rounded to 5, a null count is withheld. See Pet diet (public) → Food statistics.

2026-10-12 — Limits by profile, not by plan: ceiling refusals with data.ceiling

Section titled “2026-10-12 — Limits by profile, not by plan: ceiling refusals with data.ceiling”
  • Litter registration (POST /api/breeding/v1/births): the fair-use rate of five litters a day (Breeding_Litter_AD_FairUse_RJ, 429) is replaced by a ceiling on litters per rolling 24 hours and on open litters, set by the owner profile’s kind, verification, profile age and trust tier. Past it: 409 Breeding_Litter_AD_Ceiling_RJ with data.ceiling = { featureCode, window, limit, used, band, resetsAtUtc, raisedBy }.
  • Animal listings (/api/animallisting/v1): saved searches, showcase slots and the listing allowances are limits of the same kind, no longer plan entitlements. Past one: 409 AnimalListing_AnimalListingListing_AD_Ceiling_RJ with the same data.ceiling.
  • No entitlement, plan or purchase raises these limits; raisedBy lists what does (VERIFY, ACCOUNT_AGE). See Entitlements → the litter limit.

2026-10-12 — Retired plans leave the catalogue; contract packs; Audience Insights Pro

Section titled “2026-10-12 — Retired plans leave the catalogue; contract packs; Audience Insights Pro”
  • GET /api/plan/v1/plans lists only plans on sale. The Family (family-*), Breeder Pro (petpro-*) and Rescue (rescue-*) plans were retired and are no longer listed, whatever status you pass. A retired plan is still readable by its code, so stored references resolve; it is never returned by recommended-plans.
  • Their prices are withdrawn. GET /api/price/v1/platform-prices answers their plan: keys, and feature:APPOINTMENT_SCHEDULING, with an empty points array.
  • Contracts sell in three packs. feature:CONTRACTS returns three UNIT_PRICE points, unitsPerPack 10, 20 and 50 (4000, 7000 and 15000 USD minor units), each with expiresAfterMonths 24; the former pack of 10 at 3000 is gone. No plan includes contracts: every CONTRACTS unit is bought. Choose the pack with pricePointCode on POST /api/sub/v1/checkout/feature-credits.
  • New price subject feature:AUDIENCE_PRO (Audience Insights Pro): one SUBSCRIPTION_RECURRING point, ANNUALLY, 6000 USD minor units; no monthly point.
  • POST /api/sub/v1/checkout/plan-refill is removed with Breeder Pro.

See Platform prices and Entitlements.

2026-10-11 — Filter a media list by several kinds or by labels

Section titled “2026-10-11 — Filter a media list by several kinds or by labels”
  • kind on GET /api/media/v1/assets?ownerProfileId= takes a comma-separated list of asset-kind codes, for example kind=VIDEO,EXTERNAL_VIDEO, and keeps assets of any of them. A single code works as before. Unknown codes are skipped; a list of only unknown codes returns no items.
  • New labelIds: a comma-separated list of label ids keeps assets carrying any of those labels.
  • With either filter, total counts the matching assets, so you can page with skip / take.
Section titled “2026-10-11 — Change an external video’s link; covers in the media list”
  • New: PUT /api/media/v1/external-videos/{shortCode}/link with { "url": "…", "fetchMetadata": true } points an external video (YouTube, Vimeo, Dailymotion, Twitch, or any web address) the member owns at a new link. The asset keeps its short code, title, caption and every place it is used. The response is the new external source (providerCode, externalId, canonicalUrl, embedUrl, thumbnailUrl, …). The same video in another URL shape changes nothing. Refusals: 400 Media_Assets_VL_Request_RJ (not an http(s) URL, or not an external video), 403, 404, 409 Media_Assets_AD_Duplicate_RJ (another asset already uses that video), Media_Assets_UP_State_RJ (in the trash) or Media_Assets_UP_SharedInUse_RJ (approved for the shared library). The asset goes back to moderation. Member action only; the Partner API scopes do not cover it.
  • GET /api/media/v1/assets?ownerProfileId= items of kind EXTERNAL_VIDEO carry a new external object: { providerCode, url, embedUrl, thumbnailUrl } (embedUrl / thumbnailUrl absent when unknown). Other kinds are unchanged.
  • YouTube and Dailymotion videos always have a thumbnailUrl now: when the provider’s oEmbed answers none, the provider’s standard cover address is used.

2026-10-11 — A profile’s animal list with a very large owner

Section titled “2026-10-11 — A profile’s animal list with a very large owner”
  • GET /api/animal/v1/profiles/{owningProfileId}/animals answers quickly for every owner. For the platform profile, which owns the imported catalogue, the list used to fail with a 500 after about 30 seconds.
  • New response field totalCapped (boolean, false unless stated below). When a profile owns more than 10,000 live animals and more than 10,000 match the request, total is 10,000 and totalCapped is true: show “10,000+” and page with skip until a page returns fewer than take items.
  • Such a profile’s list comes in created order. sort=created keeps the direction you ask for; name, species, sex, age and code fall back to newest-first.
  • Its search matches the start of a call or registered name, or of the UPC code, as GET /api/animal/v1/animals/search does without a breed, from 3 characters on; total is then an estimate until a page comes back short. A shorter term is ignored, and the list comes back as if no search had been sent.
  • Every other profile’s list is unchanged: exact total, every sort, and search anywhere in the names or code.

2026-10-10 — Media search matches more fields; replace an asset’s file

Section titled “2026-10-10 — Media search matches more fields; replace an asset’s file”
  • q on GET /api/media/v1/assets?ownerProfileId= and GET /api/media/v1/shared-media/items searches more. It now matches the original filename, the short code, the slug, and the title, alt text and caption of the default locale and of every translation. The query is split into words (at most 8, each up to 100 characters) and every word must appear, anywhere, case-insensitively; before, the whole string had to appear in one field. A q that is a media id (any GUID format) returns exactly that asset. % and _ in a word act as wildcards.
  • + in q is a space. Send q=groomer+website (what URLSearchParams produces) or q=groomer%20website; a literal plus is %2B. Until now a + was searched for literally.
  • New: POST /api/media/v1/assets/{shortCode}/file?filename=&kind= replaces the file of an asset the member owns, with the raw bytes as the body and the file’s Content-Type. The asset keeps its id, short code, title, alt text, caption, folder and labels; the response is { mediaAssetId, shortCode, slug, versionNumber, sizeBytes }. The slug changes (to {slug}-v{N}), so rebuild public file URLs from the returned slug; the old URL redirects (301) to the new one. Refusals: 400 Media_Assets_VL_Request_RJ (no filename, a file of another kind, an external video), 403, 404, 409 Media_Assets_UP_State_RJ (in the trash) or Media_Assets_UP_SharedInUse_RJ (approved for the shared library), 402 Media_Assets_AD_Quota_RJ (the new file does not fit the profile’s storage; nothing changed), 503 Platform_Entitlement_HD_MeteringUnavailable_FL (retry). Member action only; the Partner API scopes do not cover it.
  • Quote ads. Marketplace offers carry a new isQuoteAd flag. A quote ad is priced QUOTE_REQUIRED and takes quote requests with a deposit: show Request a quote, never a price. Paused, suspended and ended ads are not returned by the public offer read. See Quote sales.
  • The quote flow under /api/price/v1/quotes: requests, {id}/send, /revise, /withdraw, /accept (now requires revisionNumber, the revision the buyer read), /reject. Member actions only; the Partner API scopes do not cover them. GET quotes/{id} adds depositStatus, depositRefundedMinorUnits, acceptedRevisionNumber and activity[].
  • New response codes: Price_PriceQuote_VL_State_RJ, …_VL_Revision_RJ, …_VL_Validity_RJ, …_VL_Expired_RJ, …_VL_Deposit_RJ, …_VL_Offer_RJ, …_HD_Offer_FL, …_VL_OwnOffer_RJ, …_VL_AdInactive_RJ, …_VL_PayoutReadiness_RJ, …_HD_PayoutReadiness_FL.
  • Seller fees on a quote sale: Commission 1.25 % of the accepted total (from the deposit, the rest on the seller’s card), Platform Processing Fee on the deposit, Stripe Payout Fees; US$50 a month per active quote ad.

2026-10-09 — Awards are confirmed by the other side

Section titled “2026-10-09 — Awards are confirmed by the other side”
  • New fields on awards: statusCode and sourceCode, in the career portfolio’s awards and in GET /api/profile/v1/{locale}/Profile/profiles/{profileId}/awards. sourceCode is SELF (recorded by the profile) or ISSUER (given by a business). Public reads return only CONFIRMED (show a “Confirmed” mark) and SELF_REPORTED (label it “Not confirmed”) awards; PENDING, DECLINED and WITHDRAWN ones are visible to the profile itself only. Existing awards read SELF / SELF_REPORTED. See Award statuses.
  • Awards on every profile kind. Business and place profiles can now hold awards, not only person profiles.
  • Eleven new routes for confirming, accepting, declining, giving and withdrawing awards (…/awards/received, …/awards/{awardId}/request-confirmation | accept | decline, …/award-confirmations[…], …/given-awards[…]). They act as the profile and need permission to edit it. See Managing awards.
  • Changed: award writes return { id, statusCode }. POST and PUT …/profiles/{profileId}/awards[/{awardId}] now answer the award’s id and its status (a linked issuer makes a new award PENDING). A profile can hold 30 awards; declined and withdrawn ones no longer count.

2026-10-09 — Days per type of service; businesses finish their booking setup; seller fees

Section titled “2026-10-09 — Days per type of service; businesses finish their booking setup; seller fees”
  • Seller fees have three names, and classes their own rates. What a provider pays is now called Commission (1.25 % on service bookings; classes 7 % in person, 17 % online; minimum 0.50; the Pro plan’s 0 % covers service bookings only, never classes), Platform Processing Fee and Stripe Payout Fees (0.25 % + 0.25 per payout, plus 2.00 in each month with a payout, at cost). Nothing changes in the booking APIs. See Fees on bookings.

  • New anonymous read: GET /api/commission/v1/payout-fee-rates/current?country=CA, and a payoutFees object on GET /api/commission/v1/public/fee-sheet. See Published fee schedules.

  • Slots honour a business’s days per type of service. A business can now offer a type of service only on some days and times (grooming Monday to Wednesday, training at weekends). GET /api/scheduling/v1/services/{serviceId}/slots returns only starts that fit both a team member’s hours and those windows, and a booking or reschedule outside them answers 409 Scheduling_SchedulingPartner_AD_Slot_RJ. Search each service separately. See Search availability.

  • Grooming questions apply to grooming services only. A booking for a service the business does not class as grooming ignores the pets’ grooming answers and raises no clip or corded-coat advisory. Agreements still apply.

  • New refusal: 409 Scheduling_Provider_AD_NotReady_RJ. From a date the platform announces, a business that has not connected payouts, accepted the service fees and added a payment card cannot take new bookings. data.reasons[] lists the missing steps (PAYOUT_NOT_CONNECTED, FEE_TERMS_NOT_ACCEPTED, NO_CARD_ON_FILE). Booked appointments, slot search and reads are unaffected. A check that cannot be made answers 503 Scheduling_Provider_HD_Readiness_FL (retry). See A business that is not ready for bookings.

Section titled “2026-10-08 — Consent requests for several scopes”
  • Fixed: asking for two or more scopes on the consent page. An authorize request whose scope lists several scopes (scope=scheduling:read appointment:write, sent as + or %20 between them) showed the business This link can’t be used. The consent page now accepts both encodings. Requests with no scope (every scope your app declared) or a single scope were not affected.
  • Businesses can also disconnect your app from their account settings (Account → Security → Connected Apps), as well as from Dashboard → Profile → Connected Apps. The effect on your tokens and webhooks is the same; see When the business disconnects.

2026-10-08 — Career portfolios: public reads

Section titled “2026-10-08 — Career portfolios: public reads”
  • New: a person’s Career Portfolio. GET /api/profile/v1/{locale}/Profile/profiles/{profileId}/career-portfolio returns the headline, About, skills with a 1–5 comfort level, breeds, experience, education, awards and the open-to-work flag, as the caller may see them. An anonymous call receives the public view; sections it may not see come back empty and listed in lockedSections. See Career portfolios.
  • New: the skill catalogue and lookups. GET …/Profile/skills/catalog (24 categories, 493 skills, localized) and GET …/Profile/career-portfolio/lookups (employment types, degree types, award kinds, work settings, job titles).
  • Changed: GET …/Profile/profiles/{profileId}/portfolio-items on a person profile now follows the person’s choice of who can see their work samples: an anonymous caller gets an empty list when they are not public. Organization profiles are unchanged.

2026-10-07 — Animal search needs a breed or a 3-character term

Section titled “2026-10-07 — Animal search needs a breed or a 3-character term”
  • GET /api/animal/v1/animals/search must be narrowed. Send a breedId, or a search term of at least 3 characters. Any other request is refused at once with Animal_AnimalRegistry_VL_SearchScope_RJ (400). That includes no filter at all, or only sex, speciesId, lifeStatus or labelIds. Before this change, such a request could run for up to a minute and then fail with a 500.
  • With breedId, search matches anywhere in the call name, registered name or UPC code, as before. speciesId is optional; it is taken from the breed.
  • Without breedId, search matches the start of an animal’s call or registered name, or of its UPC code, in any letter case. max finds “Max” and “Maxwell’s Midnight Sun”, but not “Ch. Max of Oakridge”.
  • Without breedId, results come in name order (code order for a code prefix). sort and descending are ignored. total is an estimate, at most 2,000, until the last page: page with skip until a page returns fewer than take items. That page’s total is exact.
  • Response shapes, paging (skip, take up to 200) and the public fields returned are unchanged. To list one profile’s animals, use GET /api/animal/v1/profiles/{profileId}/animals, which needs no extra filter.

2026-09-29 — Audience upc.js version 2: referrers of in-app navigations, shorter cache for the bare URL

Section titled “2026-09-29 — Audience upc.js version 2: referrers of in-app navigations, shorter cache for the bare URL”
  • Referrer after a client-side navigation. On pushState / popstate page views, upc.js now sends the previous page as the referrer instead of repeating document.referrer, which stays fixed at the first load. Single-page apps no longer count every in-app page as a new search, social or AI-assistant referral. Earlier data is unchanged.
  • Caching. upc.{version}.js (now upc.2.js) stays cached for a year; the bare upc.js is now cached for one hour instead of a year. Pin upc.2.js in your tag and change the number when a new version is announced. See The browser script.
  • No change to the collect endpoint, the hit fields or the responses.

2026-09-29 — Storage is metered: an upload can answer 402

Section titled “2026-09-29 — Storage is metered: an upload can answer 402”
  • Media uploads count against the profile’s storage. Every profile has a STORAGE_GB allowance: 200 MB free, plus any storage add-on it holds (25 GB or 100 GB). POST /api/media/v1/upload-sessions/{id}/commit draws the stored size of the uploaded file from the profile the upload belongs to. Thumbnails and other generated sizes draw nothing, files committed before 2026-09-29 were not counted, and deleting an asset gives its space back.
  • Out of storage, the commit answers Media_Assets_AD_Quota_RJ (402) and stores nothing, with data = { "purchase": { "featureCode": "STORAGE_ADDON_25GB", "quantity": 1 } }. Treat it like the other 402 quota refusals: tell the member they are out of storage (they can delete files to free space) and do not retry the same file until the balance has changed. A file larger than the space left is refused even when some space remains.
  • Only storage is metered. Litter Passes, contracts and every other entitlement are still not metered on the live platform (see the note under the next entry). No route changed, and a successful commit answers as before.

2026-09-25 — Litter records are free; the Litter Pass, contract packs and Breeder Pro

Section titled “2026-09-25 — Litter records are free; the Litter Pass, contract packs and Breeder Pro”
  • Registering a litter draws no entitlement on any plan. POST /api/breeding/v1/births no longer answers Breeding_Litter_AD_AddonRequired_RJ or Breeding_Registry_AD_PlanLimit_RJ; the only limit is a fair-use ceiling, Breeding_Litter_AD_FairUse_RJ (429), five litters per profile per 24 hours.
  • The Litter Pass. POST /api/breeding/v1/births/{id}/pass stamps a Litter Pass on a litter, and DELETE …/pass undoes it within 24 hours (409 Breeding_Litter_AD_PassLocked_RJ after). Litter reads carry hasLitterPass and litterPass. While metering is off, the stamp draws nothing (litterPass.sourceCode ADMIN). Once it is on, the stamp draws one LITTER_PASSES unit (sourceCode PLAN) and answers 402 Breeding_Litter_AD_PassRequired_RJ with data.purchase when the balance is out. GET /api/breeding/v1/me/litter-meter now names LITTER_PASSES for every profile, with purchase always present. KENNEL_LITTERS is renamed LITTER_PASSES (same definition). See Entitlements.
  • Waitlists need the pass. Join, offer and accept on a listing created from a litter answer AnimalListing_Waitlist_AD_LitterPass_RJ (402) without one; a passed litter’s listing takes protected deposits on any plan. This refusal applies today, whether or not metering is on: it checks that the litter holds a pass, which its owner’s stamp gives it. See Protected deposits.
  • Contracts are metered once metering is on. POST /api/contract/v1/contracts then draws one CONTRACTS unit and answers Contract_Signing_AD_Quota_RJ (402) with data.purchase when none is left; packs of 10 through checkout/feature-credits. Until then, a create draws nothing.
  • Breeder Pro. The Pet Pro plans are Breeder Pro 2 / 6 / 15 / 50 (slugs unchanged, petpro-max new), yearly only: GET /api/price/v1/platform-prices returns one ANNUALLY point per plan and the new feature:LITTER_PASSES / feature:CONTRACTS keys. POST /api/sub/v1/checkout/plan-refill sells an active subscriber the same tier’s passes and contracts again for twelve months. See Platform prices.

2026-09-25 — The Partner API is free; apps can register themselves

Section titled “2026-09-25 — The Partner API is free; apps can register themselves”
  • Free, with a payment card on file. The Partner API costs nothing, for your app and for the business. The Partner API add-on announced on 2026-09-24 was withdrawn before it was sold. Instead, a business must keep a valid payment card on file (Dashboard → Finances → Billing) to allow an app on the consent page, and its apps work only while it keeps one. 403 0010010000407 now means payment-card-required (title “Payment card required”); a 503 0010010000503 with Retry-After: 5 means the card could not be checked. See Booking → Partner API.
  • Webhooks of a business with no valid card are cancelled (the subscription stays ACTIVE); while the card cannot be checked they wait. See Webhooks.
  • Self-registration. POST https://auth.unitedpetclub.com/connect/register (RFC 7591) registers an app without signing in — for WordPress plugins, AI-built websites and AI agents. A self-registered app has no publisher, is shown to businesses as unverified, may ask only for the Partner API scopes, and must use PKCE. At most 20 registrations per address in 24 hours. See Self-registration and the new WordPress plugins and AI agents guide.
  • Public clients. An app registered with token_endpoint_auth_method none has no secret: it sends client_id without client_secret on authorization_code and refresh_token, must use PKCE S256 (invalid_grant without it), cannot use client_credentials (400 unauthorized_client), and a secret it sends is refused (401 invalid_client). See Public clients and PKCE.
  • Discovery. GET https://auth.unitedpetclub.com/.well-known/oauth-authorization-server (RFC 8414) and GET https://api.unitedpetclub.com/.well-known/oauth-protected-resource (RFC 9728); a 401 for a missing, invalid or expired token now carries WWW-Authenticate: Bearer resource_metadata="…". See Discovery metadata.
  • Fix: token responses use the RFC names. /connect/token answered camelCase JSON (accessToken, tokenType, errorDescription, …) although these pages showed access_token. It now answers the RFC 6749 names (access_token, token_type, expires_in, scope, refresh_token; error, error_description) with Cache-Control: no-store.
  • Apps a business connects. A business can connect your app on the United Pet Club consent page (https://auth.unitedpetclub.com/connect/authorize, OAuth 2.0 authorization code with PKCE). POST /connect/token now also accepts authorization_code and refresh_token (refresh tokens rotate on every use and last 90 days) and HTTP Basic client authentication. See Connecting a business.
  • /api/scheduling/v1 on api.unitedpetclub.com acts for the connected business: the business, services, open slots, booking options, estimates, appointments (book with Idempotency-Key, reschedule, cancel), clients and pets, and appointment webhooks signed with HMAC-SHA256. See the Partner API and Webhooks.
  • Self-service registration: register the app, its redirect URIs and scopes, and rotate its secret under Account → Developer → Applications. Businesses see and disconnect connected apps under Dashboard → Profile → Connected Apps.
  • Scopes: new scheduling:read and client:read; appointment:read, appointment:write and webhook:manage are now the Partner API’s; booking:read / booking:write are retired. The unreleased /api/appointment/v1, /api/booking/v1 and /api/webhook/v1 families were removed in favour of /api/scheduling/v1.
  • New edge codes: 0010010000400 (validation), 0010010000406 (a client-credentials token on a route that acts for a business), 0010010000407 (the business has no valid payment card on file — this entry first said “lacks the Partner API add-on”; see 2026-09-25). See Errors.
  • Subscribers can now get refunds for plans, memberships and bought credit packs (a pack only while none of it is used). A refund ends the subscription at once (support may choose to keep it running), so its SUBSCRIPTION grants are revoked at that moment, exactly as for an immediate cancellation. /check reflects it within seconds.

2026-09-23 — Grants end when the plan ends

Section titled “2026-09-23 — Grants end when the plan ends”
  • Grants with source SUBSCRIPTION are now revoked whenever the plan behind them ends: cancelled right away, cancelled at the end of its paid period (revoked on that date), or not renewed at the end of its paid period (for example because no card is saved). Before this, a plan cancelled at the end of its period, or one that never renewed, could keep its grants. Expect a subject’s quantity to drop to zero at those moments. /check reflects it within seconds.
  • Every marketplace offer has a 10-character offerCode (immutable, never reused), returned right after id on the offer reads. GET /api/marketplace/v1/codes/{code} (anonymous) resolves a code — body or the UPC-OF- form, any case — to the offer, its storefront and the product’s item code; isLiveFlag is false for a withdrawn offer, an unknown code is 404. See Offer codes.

2026-09-20 — Booking references, preferred groom style, estimate wording

Section titled “2026-09-20 — Booking references, preferred groom style, estimate wording”
  • POST scheduling/v1/public/bookings accepts references[] (LINK url / MEDIA mediaId, ≤ 5; MEDIA needs a session); GET scheduling/v1/appointments/{id} returns references[].
  • PUT animal/v1/animals/{id}/coat/groom-style sets a pet’s preferred groom style (text, referenceUrl); GET animal/v1/animals/{id} exposes groomStylePreference*; the appointment’s animalDetails[] carries preferredStyleText / preferredStyleReferenceUrl as snapshotted at booking. New code Animal_AnimalRegistry_VL_GroomStyleUrl_RJ (400).
  • Grooming prices are estimates until the pet is seen — the hosted booking page now says so; partner UIs should present them the same way.

2026-09-09 — Brand profiles and locations

Section titled “2026-09-09 — Brand profiles and locations”
  • New anonymous Profile reads under /profile/v1/{locale}/Profile/profiles/{profileId}: /brand (the brand a location belongs to, 404 when none), /locations (a brand’s locations, each with its own rating, plus a review-count-weighted roll-up), /carriers (profiles that stock or service a brand) and /carried-brands. The profile detail now embeds brand and, for brand profiles, brandRollup. Brand profiles (purpose BRAND) are not returned by the professional directory.
  • Price: GET /price/v1/pricelists/by-brand/{brandProfileId} and GET /price/v1/pricepoints/by-brand/{brandProfileId}?subjectKey=; POST /pricepoints/resolve and /preview accept brandProfileId and answer resolvedFromBrandList. A location’s own price always ranks above the brand’s. See Brands and locations.

2026-09-09 — Localized forms (Form V2.0)

Section titled “2026-09-09 — Localized forms (Form V2.0)”
  • The form reads GET /form/v1/public/deployments/{id}/render, /form/v1/deployments/{id}/render, /form/v1/templates/{id}/preview, /form/v1/templates and /form/v1/definitions/{id} accept ?locale=. With it, every sysName / sysTitle / sysPrompt / sysHelp / sysLabel in the payload is resolved for that locale (requested → language default → en-US → authored text, per row); displayLocaleCode and availableLocales are new on the render payload. Without it the payload is unchanged.
  • New owner routes to translate a version: GET /form/v1/versions/{id}/locales, GET|PUT|DELETE /form/v1/versions/{id}/locales/{locale}. Allowed on published versions.
  • The 15 platform templates are available in all 72 platform locales.

2026-09-08 — Corded coats + localized booking questions (Scheduling)

Section titled “2026-09-08 — Corded coats + localized booking questions (Scheduling)”
  • coatConditionCode (estimate, booking animals[], visit assessment) accepts CORDED — the coat is kept in cords. Not matting; hours of drying. Raises the new CORDED_COAT advisory (regionCode: null) under the provider’s CORDED_COAT_MODE; two new booking rejections: 409 Scheduling_Booking_VL_CordedCoat_RJ (acknowledgement required) and 409 Scheduling_Booking_VL_CordedRefused_RJ.
  • advisories[].regionCode is now nullable (null = the whole coat).
  • New lookups: GET /scheduling/v1/lookups/intake-questions?locale=&audience= (every booking / visit question with localized prompt, help and fixed answers) and GET /scheduling/v1/lookups/coat-advisories?locale= (advisory texts + textHash). GET /providers/{id}/grooming-options gained intakeQuestions[] (the same rows, filtered to what the provider asks). bookingQuestions gained cordedCoatMode / cordedCoatText.
  • Price COAT_CONDITION vocabulary gained CORDED; the estimate also adds CORDED to the pet’s COAT_TEXTURE when the coat is corded.
  • The intake-question and coat-advisory lookups answer in all 72 platform locales (?locale=), with the usual fallback to the language default and en-US.

2026-09-07 — Double-coat declaration on mixed-breed dogs

Section titled “2026-09-07 — Double-coat declaration on mixed-breed dogs”

The animal read (GET /animal/v1/animals/{id}) gained three optional fields:

  • hasDoubleCoat — true / false, or null when nobody has declared it. Only ever set on mixed-breed dogs; purebreds and other species always read null, because their coat is a breed fact served by the breed guide instead.
  • doubleCoatDeclaredByRole — OWNER or CARE_CIRCLE (a groomer, vet or trainer holding an access grant on the animal); null while undeclared.
  • doubleCoatDeclaredAtUtc.

Two new authenticated routes:

  • PUT /animal/v1/animals/{id}/coat/double-coat with { "doubleCoat": "UNKNOWN" | "YES" | "NO" } — accepted from the owner or any active care-circle grantee; UNKNOWN clears the declaration. Rejected with Animal_AnimalRegistry_VL_DoubleCoat_RJ (400) when the animal is not a mixed-breed dog. Outsiders receive the same not found answer as for any other animal they cannot see.
  • GET /animal/v1/animals/{id}/access/me — the calling profile’s own standing on the animal (isOwner, grantScope, inCareCircle). Reveals nothing about other profiles.

Treat an absent field as unknown: older responses do not carry it.

2026-08-11 — Self-serve plan & credit purchases

Section titled “2026-08-11 — Self-serve plan & credit purchases”

Customers can now buy platform plans and one-time feature-credit packs directly in the United Pet Club apps. No partner API surface changed, but integrations reading entitlements will observe the effects:

  • New grants with source SUBSCRIPTION appear in subject grant ledgers within seconds of a purchase, and /check availability can increase between calls.
  • A plan upgrade supersedes the previous plan’s grants (they are revoked, not stacked) — treat stored grant ids as ephemeral and re-read rather than assuming an old grant is still active.

See the Entitlements guide for the recommended check/consume pattern.