REST API
The raw HTTP surface behind the whole UseThatApp offering — selling from your own website with UseThatApp as merchant of record (public app-profile and pricing endpoints feeding hosted buy links), licensing (the Bearer-authed entitlement endpoint and the client-credentialed license key endpoints), and authentication ("Sign in with UseThatApp" over OIDC). If you run Python or JavaScript, the usethatapp SDK wraps all of it; this page is for every other stack — and for calling the public endpoints straight from a browser.
Conventions
- Base URL:
https://www.usethatapp.com - Format: JSON requests and responses.
- Versioning: the REST API lives under
/api/v1/. The OIDC endpoints (under/o/) and the canonical entitlement path (/licensing/entitlement/) are frozen, unversioned surfaces the SDKs pin. - Live schema: an OpenAPI 3 schema at /api/v1/schema/ and Swagger UI at /api/v1/docs/.
Authentication models
| Surface | Auth |
|---|---|
/api/v1/public/… | None — anonymous, CORS-enabled (Access-Control-Allow-Origin: *), cacheable public reads. |
/api/v1/entitlement/ | OAuth 2.0 Bearer access token with the entitlements scope, obtained via the OIDC login flow. Requires the Hosted sign-in add-on (otherwise 403 service_not_enabled). |
/api/v1/licenses/…, /api/v1/orders/… | HTTP Basic with your app's OAuth client id and secret — server-to-server, no end-user token. Included in the base per-sale fee. |
Public endpoints
Two anonymous read endpoints power selling from your own website — app profile and live prices, addressed by your app's public OAuth client_id. Full field-by-field reference with example responses: Pricing API.
GET /api/v1/public/apps/{client_id}/ → app profile (name, tagline, listing_mode, url, marketplace_url)
GET /api/v1/public/apps/{client_id}/prices/ → live prices, each with a ready-made buy_urlBoth only resolve for published apps with external sales enabled (404 otherwise) and send Cache-Control: public, max-age=60.
Entitlement endpoint
The live source of truth for a user's plan in your app. Two paths, identical behavior:
GET /licensing/entitlement/— the canonical path, which the SDKs call.GET /api/v1/entitlement/— an alias mounted so the v1 API surface is complete. Behaves identically.
Send the OAuth access token from the login flow as a Bearer token. The token must carry the entitlements scope (part of the default openid entitlements scopes).
curl -H "Authorization: Bearer <access-token>" \
https://www.usethatapp.com/api/v1/entitlement/{
"entitled": true,
"version": "Pro",
"product_id": "6f0e8f9a-2b1c-4d5e-8f7a-9b0c1d2e3f4a",
"product_public_id": "prod_9XkKe2QGxN4Ttz1Rw7Ya",
"status": "active",
"is_free": false,
"period_end": "2026-08-27"
}| Field | Meaning |
|---|---|
entitled | True if the user may use the app (an active license, or a free tier). |
version | Plan display name (e.g. "Pro"). Mutable — for display only. |
product_public_id | Opaque product identifier (prod_…). Gate features on this. Matches product_id in the Pricing API, and is kept permanently. |
product_id | Equal-valued alias of product_public_id — both carry the same prod_… value. Gate on either. |
status | active, trialing, one_time_active, free, preview, or none. |
is_free | Whether the entitlement is the app's free tier. |
period_end | ISO date the current period ends, or null. |
Errors
| Status | Body | Meaning |
|---|---|---|
401 | {"error": "invalid_token"} | Missing, expired, or revoked token (a confirmed logout lands here). Refresh, or send the user back through login. |
403 | {"error": "insufficient_scope", "scope": "entitlements"} | A valid token that lacks the entitlements scope. |
400 | {"error": "Client is not linked to an app"} | The OAuth client isn't linked to an app — a configuration problem, not a user problem. |
The SDKs map 401 to UtaTokenError and 403 to UtaPermissionError — see Error Handling. For what the fields mean for feature gating, see Licensing.
License key endpoints
Server-to-server verification for apps that keep their own authentication. All three take HTTP Basic client credentials; none involves an end-user token. Field meanings, statuses, and the integration walkthrough are on License Keys.
POST /api/v1/licenses/validate → {entitled, status, license_id,
product_id, product_public_id,
period_end, canceled_at}
body: {"key": "key_…"} 120/min per client
GET /api/v1/orders/<ref> → the same fields + license_key
202 {"status":"processing"} if the
license has not landed yet
120/min per client
POST /api/v1/licenses/<license_id>/regenerate-key → the same fields + license_key,
rotated_at. Old key dies at once.
30/min per clientErrors: 401 invalid_client (bad credentials), 404 unknown_key / unknown_order / unknown_license (never issued, or belonging to another app — the endpoints are not cross-app oracles), and 409 license_canceled on rotation of a dead license. A key whose license was canceled or refunded is not "unknown": it validates with status: "canceled".
OIDC / OAuth endpoints
UseThatApp is a standard OpenID Provider. Rather than hardcoding endpoint paths, fetch the discovery document — it enumerates the authorization, token, userinfo, JWKS, and end-session endpoints, plus supported scopes and signing algorithms:
GET https://www.usethatapp.com/o/.well-known/openid-configuration- authorization_endpoint — start the authorization-code + PKCE flow.
- token_endpoint — exchange the code for tokens (confidential client: send your client secret).
- userinfo_endpoint — the pairwise pseudonymous
subclaim (no PII — see Authentication). - jwks_uri — public keys for validating ID tokens.
- end_session_endpoint — RP-initiated logout.
If you're integrating by hand on a stack without an SDK, any certified OIDC client library pointed at this issuer (https://www.usethatapp.com/o) will do the protocol work.
Rate limits
| Surface | Limit |
|---|---|
| Entitlement endpoint | 60 requests/minute per token |
Public API (/api/v1/public/…) | 120 requests/minute per IP |
Machine-readable schema
The versioned API is described by a live OpenAPI 3 schema at https://www.usethatapp.com/api/v1/schema/ with interactive Swagger UI at https://www.usethatapp.com/api/v1/docs/. AI agents can also use llms.txt and the MCP server.
