REST API reference

Base URL https://entitlehub.com/v1. JSON in, JSON out. Authenticate every request with Authorization: Bearer <key> — see API keys. Reads accept a pk_ key; writes require an sk_ key.

POST /v1/check

Authoritative single-entitlement check. Any key.

http
POST /v1/check
Authorization: Bearer pk_live_…
{ "app_user_id": "user_123", "entitlement": "pro", "sandbox": false }

200 OK
{
  "app_user_id": "user_123", "entitlement": "pro", "active": true,
  "status": "active", "store": "play", "product": "app_pro_monthly",
  "expires_at": "2026-08-01T00:00:00Z", "will_renew": true, "since": "2026-07-01T00:00:00Z"
}
// not entitled → { "active": false, "reason": "no_active_grant" }

GET /v1/subscribers/{app_user_id}

A subscriber's active entitlements (the "customer info"). Any key.

http
GET /v1/subscribers/user_123
Authorization: Bearer pk_live_…

200 OK
{
  "app_user_id": "user_123",
  "active_entitlements": [
    { "entitlement": "premium", "status": "active", "store": "play",
      "product": "app_premium_lifetime", "expires_at": null, "will_renew": false, "since": "…" },
    { "entitlement": "pro", "status": "active", "store": "play",
      "product": "app_pro_monthly", "expires_at": "2026-08-01T00:00:00Z", "will_renew": true, "since": "…" }
  ]
}

Only currently-active entitlements are returned; an unknown user returns an empty list (not an error).

POST /v1/subscribers/{app_user_id}/purchases

Record a store purchase → maps product to entitlement(s) and writes a grant. Secret key required. Three input modes:

Google Play (validated)

http
POST /v1/subscribers/user_123/purchases
Authorization: Bearer sk_live_…
{ "store": "play", "store_product_id": "app_pro_monthly",
  "purchase_token": "…", "is_subscription": true }

Apple StoreKit 2 (validated)

http
POST /v1/subscribers/user_123/purchases
Authorization: Bearer sk_live_…
{ "signed_transaction": "<StoreKit2 JWS>" }
// store + product are read from the verified JWS

Stripe (validated)

http
POST /v1/subscribers/user_123/purchases
Authorization: Bearer sk_live_…
{ "store": "stripe", "stripe_subscription_id": "sub_1P…" }
// validated via the project's Stripe secret key; store_product_id ← the Stripe Price id

Trusted server-report (no store validation)

http
POST /v1/subscribers/user_123/purchases
Authorization: Bearer sk_live_…
{ "store": "play", "store_product_id": "app_premium_lifetime" }
// only when you've already validated the receipt elsewhere — call server-side only

All three return the updated customer-info object (same shape as the subscribers endpoint).

POST /v1/subscribers/{app_user_id}/grant

Grant an entitlement directly — promos, comps, redemption codes. Secret key required.

http
POST /v1/subscribers/user_123/grant
Authorization: Bearer sk_live_…
{ "entitlement": "premium", "duration_days": 0 }
// duration_days: 0 = lifetime, N = expires in N days. is_sandbox optional.

GET /v1/offerings

Your catalog — entitlements and products — for building a paywall. Any key.

http
GET /v1/offerings
Authorization: Bearer pk_live_…

200 OK
{ "entitlements": [ { "key": "pro", "name": "Pro", ... } ],
  "products": [ { "store_product_id": "app_pro_monthly", "type": "subscription",
                  "price_micros": 2990000, "currency": "USD", "entitlements": ["pro"] } ] }

Errors

StatusMeaning
401Missing / invalid / revoked key.
403Used a pk_ key on a write endpoint (needs sk_).
400Invalid token, unknown/unmapped product, or no Play credential configured.
Reads never 404 on unknown users

A check or subscriber read for a user with no grants returns an inactive/empty result, not an error.