Entitlements
Entitlements are what a subject (a profile, account, animal, etc.) has the right to use
right now — a feature flag (BOOLEAN), a counted quota (QUOTA / CREDIT), a measured
capacity (STORAGE), a rate limit, or a time-bounded window (DURATION). They are granted
from many sources (subscriptions, promotions, referrals, memberships) and your integration
reads and draws them down through one consistent surface.
All routes are under /api/entitlement/v1/ and require your bearer token. The subject is
polymorphic — { "kindCode": "PROFILE", "id": "<guid>" } (or ACCOUNT, ANIMAL, …).
Check before you act
Section titled “Check before you act”POST /check is a side-effect-free probe — call it to decide whether to show or allow a
feature.
POST /api/entitlement/v1/check{ "subject": { "kindCode": "PROFILE", "id": "…" }, "definitionSysName": "televet.session.monthly", "requestedQuantity": 1 }{ "isAvailable": true, "availableQuantity": 7, "reservedQuantity": 0, "hasActiveBoolean": false, "effectiveExpiresAtUtc": "…", "nextResetAtUtc": "…", "blockingReasonCode": null }/check is authoritative but advisory — always follow it with /consume for the actual draw.
Consume (draw down)
Section titled “Consume (draw down)”POST /consume is the authoritative, atomic draw-down. An Idempotency-Key header is
required — retries with the same key never double-charge and return the original result
(idempotentReplay: true).
POST /api/entitlement/v1/consumeIdempotency-Key: <stable-key-for-this-action>{ "subject": { … }, "definitionSysName": "televet.session.monthly", "quantity": 1, "consumedByBcCode": "TELEVET", "consumedByEntityId": "<appointmentId>" }{ "usageId": "…", "grantIdDrawnFrom": "…", "remainingQuantity": 6, "idempotentReplay": false }Use a deterministic Idempotency-Key derived from the thing being consumed (e.g. the
appointment id), so a retried request is recognised as the same operation.
To refund: POST /usage/{usageId}/reverse with a reversalReasonCode restores the quantity.
Reserve → confirm/release (multi-step flows)
Section titled “Reserve → confirm/release (multi-step flows)”For flows where you hold capacity while the user completes a step (picking a slot, paying), use a reservation. Scarce/expensive definitions require it.
POST /reserve(Idempotency-Key) →{ reservationId, expiresAtUtc, reservedQuantity }— holds the quantity.POST /reservation/{id}/confirm→ converts the hold into usage.POST /reservation/{id}/release→ frees the hold (idempotent). Held reservations also auto-expire at their TTL.
Reading state
Section titled “Reading state”| Method | Route | Returns |
|---|---|---|
GET | subject/{kind}/{id}/balances | All current balances for a subject. |
GET | subject/{kind}/{id}/definition/{sysName}/balance | One balance. |
GET | subject/{kind}/{id}/grants · /usage | Paged grant / usage history. |
GET | definitions/{sysName} | What an entitlement is (kind, unit, label). |
Partner tier definitions
Section titled “Partner tier definitions”A service provider’s Partner status (a rolling three-month average of on-platform booking volume at
or above the threshold) is expressed as grants with source PARTNER_QUALIFICATION, minted and revoked by
the platform — never by your integration. Read them like any other entitlement:
| SysName | Kind | Subject | Meaning |
|---|---|---|---|
PARTNER_STATUS | BOOLEAN | the brand profile | The provider is a Partner (or in its grace period). |
PARTNER_CAMPAIGNS | BOOLEAN | brand | Marketing email campaigns are available. |
PARTNER_PRIORITY_SUPPORT | BOOLEAN | brand | Priority support (before 2026-10-11; counts as the Guided support plan). |
SUPPORT_GUIDED / SUPPORT_ASSISTED / SUPPORT_HANDS_ON | BOOLEAN | profile | The support plan held (bought, or included with Partner / Pro). |
SUPPORT_HOURS | QUOTA (minutes) | profile | Bought hands-on support time, drawn after the plan’s included time. |
SUPPORT_SESSIONS | QUOTA | profile | One-off 30-minute live support sessions. |
SCHEDULING_LEAD_TRACKING | BOOLEAN | brand | Lead / abandoned-booking tracking. |
PARTNER_HOSTED_SITE | BOOLEAN | each location profile | That location’s website carries no United Pet Club branding and may serve pages the business writes itself. The website itself is free for every verified business (since 2026-09-22) and needs no grant. |
PARTNER_CUSTOM_DOMAIN | BOOLEAN | each location profile | That site may use the provider’s own domain. |
CAMPAIGN_EMAIL_SENDS | QUOTA, resets monthly | brand | Marketing emails the brand may send this month. |
A location under a brand reads the brand-wide rights through its brand; the site and domain rights are granted per location. The same definitions may also be granted by a paid plan; they stack by the larger grant, so check the balance, not the grant list.
Litter Passes (LITTER_PASSES) and contracts (CONTRACTS)
Section titled “Litter Passes (LITTER_PASSES) and contracts (CONTRACTS)”Since 2026-09-25 registering a litter is free on every plan: POST /api/breeding/v1/births draws no
entitlement, for breeders and kennels alike. (KENNEL_LITTERS, LITTER_REGISTRATIONS and the
Breeding_Litter_AD_AddonRequired_RJ / Breeding_Registry_AD_PlanLimit_RJ refusals are gone.)
The litter limit. Registration is limited only to stop abuse, and no entitlement or purchase changes the limit.
Until 2026-10-12 it was a fair-use rate of five litters per profile per 24 hours (Breeding_Litter_AD_FairUse_RJ, 429).
Since 2026-10-12 it is a ceiling on litters registered in a rolling
24 hours and on open litters (expected or born), set by the owner profile’s kind, verification, profile age and trust
tier. Past it, the registration answers 409 Breeding_Litter_AD_Ceiling_RJ and nothing is written. The response’s
data.ceiling says which limit was reached and how to get past it:
{ "ceiling": { "featureCode": "LITTERS", "window": "PER_DAY", "limit": 1, "used": 1, "band": { "profileKindCode": "PERSON", "businessTypeCode": null, "verificationCode": "UNVERIFIED", "ageBandCode": "LT_30D" }, "resetsAtUtc": "2026-10-13T14:05:00+00:00", "raisedBy": [ { "code": "VERIFY", "limit": 2, "allowed": null, "ageBandCode": null, "atUtc": null } ] }}| Field | Meaning |
|---|---|
featureCode | the limited feature (LITTERS here) |
window | STANDING (held at once), PER_DAY (rolling 24 h) or FLAG (a yes/no feature; limit is null) |
limit, used | the limit that applies to this profile now (null = none) and how much is used |
band | what set it: profileKindCode PERSON / BUSINESS / ORGANIZATION, businessTypeCode, verificationCode UNVERIFIED / VERIFIED, ageBandCode LT_30D / D30_90 / GTE_90D (profile age) |
resetsAtUtc | for PER_DAY, when a slot frees again |
raisedBy | what would raise it: VERIFY (verifying the profile) and / or ACCOUNT_AGE (ageBandCode, atUtc = when the profile reaches that age), each with the limit or allowed it would give |
Do not retry a ceiling refusal unchanged: wait for resetsAtUtc, or let the member free a place (close a litter) or
verify the profile. Breeding_Litter_AD_FairUse_RJ is no longer sent once this ships; treat it like a PER_DAY ceiling
without detail if an older environment still answers it.
What is metered is the Litter Pass: one LITTER_PASSES unit, stamped on one litter by its owner,
for that litter’s life. It unlocks the litter’s reservation queue, protected deposits on any plan, a
contract per reservation, whole-litter sharing and full audience detail. Pro for kennels includes six
passes a year; every other pass is bought, US$29 each. (Until 2026-10-12 the Breeder Pro and Rescue plans
included passes too; they were retired.)
GET /api/breeding/v1/me/litter-meter{ "model": "BREEDER", "definitionSysName": "LITTER_PASSES", "purchase": { "featureCode": "LITTER_PASSES", "quantity": 1 }, "mistakeRefundHours": 24}model is BREEDER or KENNEL_CATTERY and changes only wording; every other field is the same for
every profile. To show the balance, read it for definitionSysName
(GET /api/entitlement/v1/subject/PROFILE/{id}/definition/{sysName}/balance); then stamp the litter.
The stamp, not the balance, decides:
POST /api/breeding/v1/births/{id}/pass → 200, the litter with litterPass { grantedAtUtc, sourceCode, revocableUntilUtc } and hasLitterPass: trueDELETE /api/breeding/v1/births/{id}/pass → 200 within 24 h of the grant (a unit the stamp drew returns to the balance); 409 Breeding_Litter_AD_PassLocked_RJ afterlitterPass.sourceCode is PLAN when the stamp drew a unit and ADMIN when it drew none (metering
off). A stamp on a litter that already holds a live pass answers 200 and draws nothing. With metering on
and no pass left, the stamp answers Breeding_Litter_AD_PassRequired_RJ (402) with
data.purchase = { "featureCode": "LITTER_PASSES", "quantity": 1 } — buy one with
POST /api/sub/v1/checkout/feature-credits { "buyerProfileId", "featureCode": "LITTER_PASSES", "quantity": 1 }
(the token acting as that profile; the purchase bills the account the profile belongs to) and retry once
the payment settles. Every litter read carries hasLitterPass. Do not consume LITTER_PASSES yourself —
the stamp does, keyed to the litter. Plan passes and bought passes add (MAX_PLUS_ADDONS, the plan’s
drawn first); bought ones never expire or refill. Cancelling a litter within mistakeRefundHours of the
grant returns the unit its stamp drew; a later cancel loses it.
Other services read the same pass, whether or not metering is on: joining, offering or accepting on a
litter listing’s waitlist without one answers AnimalListing_Waitlist_AD_LitterPass_RJ (402, same
purchase shape) today. The owner’s stamp clears it, so try the stamp before offering the purchase. A
passed litter’s listing may take protected deposits on any plan (Protected deposits).
Contracts, once metering is on, are metered once, at create: POST /api/contract/v1/contracts
reserves and confirms one CONTRACTS unit of the owning profile, from its bought packs, the soonest-expiring
first. No plan includes contracts since 2026-10-12. Out of units it answers Contract_Signing_AD_Quota_RJ (402)
with data.purchase = { "featureCode": "CONTRACTS", "quantity": 1 }: one purchase is one pack. Contracts sell in
three packs, 10, 20 or 50 contracts (unitsPerPack on the price point), each lapsing 24 months after purchase
(expiresAfterMonths 24). Name the pack with pricePointCode on
POST /api/sub/v1/checkout/feature-credits; without it the cheapest pack (10) is sold. Packs stack. Prices:
Platform prices. Updating, sending and signing draw
nothing.
Breeder Pro was retired on 2026-10-12, and POST /api/sub/v1/checkout/plan-refill (its yearly bundle bought
again) was removed with it: the route no longer exists. Grants a refill minted before then keep their
effectiveExpiresAtUtc.
Errors
Section titled “Errors”Mutations return a stable ENT-500xx code: ENT-50001 insufficient quantity, ENT-50003
definition inactive, ENT-50004 reservation required, ENT-50005 quantity out of range,
ENT-50006 definition not found, ENT-50008 reservation expired. See
Error handling for the envelope shape.