Python Documentation
Sell your web app from your own website with UseThatApp as the merchant of record, sign buyers in with their UseThatApp account over standard OAuth 2.0 / OpenID Connect, and gate features on their live entitlement — a buyer who completes checkout lands back in your app already signed in, entitlement live. The usethatapp Python SDK is framework-agnostic — it takes and returns plain strings and one JSON-able flow_state dict, so it works with Flask, Django, FastAPI, Dash, Streamlit, plain WSGI, or a CLI. Each framework guide below is copied from a tested example in the SDK.
This guide covers the Hosted sign-in path
It has UseThatApp run login, so buyers return signed in and the same session answers what they bought — the Hosted sign-in add-on. If your app already has its own accounts, you do not need any of the sign-in wiring below: keep your login and verify purchases with a license key your server validates in one call, included in the per-sale fee.
What the SDK does
A single OIDC login gives you identity and entitlement at once, and the purchase helpers close the loop:
- Identity.
complete_login()returns aUtaSessioncarrying the user'ssub— a pairwise pseudonymous id that is stable for a user within your app, different in every other app, and carries no PII. Use it as your local user key. - Entitlement.
get_entitlement(access_token)returns the user's current, live plan so you can gate features. A canceled license stops being entitled immediately. - Purchasing.
purchase_url(price_id)andmanage_url()are pure URL builders — no network call — for hosted checkout and the buyer's subscription-management page. UseThatApp is the merchant of record (payment, tax, refunds), and the license is created synchronously at checkout, so no webhooks.get_prices()/get_prices_async()return a liveAppPriceswith a tuple ofPriceentries, each carrying a ready-madebuy_urland theproduct_idthat matches the entitlement’sproduct_public_id;get_app_info()/get_app_info_async()return your publicAppInfolisting. See Sell from your own website and the Pricing API.
The SDK is a confidential, server-side client: it holds your client secret and validates ID tokens. Keep it on your server, never in the browser. For the conceptual deep-dive, see OpenID Connect.
Install
pip install usethatappPython 3.9+. Runtime dependencies are httpx and joserfc — no web framework required. These docs describe 2.2.0 or newer; if you are upgrading from an earlier release, run pip install --upgrade usethatapp first — the License Key API, product_id, and UtaServiceNotEnabledError do not exist before it.
Configure
The SDK reads its config from django.conf.settings when Django is installed and configured, otherwise from environment variables — or set anything in code with configure(...), which wins over both. Only the client id and secret are required; the rest default to production.
UTA_CLIENT_ID=... # required
UTA_CLIENT_SECRET=... # required (server-side only)
# or read the secret from a mounted file (Render / k8s / Fly):
# UTA_CLIENT_SECRET_PATH=/run/secrets/uta_client_secret
UTA_REDIRECT_URI=https://yourapp.com/callback # required
# optional — default to production:
UTA_ISSUER=https://www.usethatapp.com/o
UTA_API_URL=https://www.usethatapp.com
UTA_SCOPES="openid entitlements"In code — same names, snake_case keywords (the JavaScript SDK's configure({...}) works the same way):
import usethatapp
# e.g. point a dev machine at a local platform instance:
usethatapp.configure(
api_url="http://localhost:8000",
issuer="http://localhost:8000/o",
)
usethatapp.load_config() # the resolved UtaConfig (cached)
usethatapp.reset_config() # drop overrides; back to settings/env
usethatapp.DEFAULT_API_URL # "https://www.usethatapp.com"The shape of an integration
Every framework follows the same three routes plus a gated view. The SDK does the crypto; you wire the framework-specific bits (read callback query params, store flow_state in your session, issue redirects).
from usethatapp import begin_login, complete_login, get_entitlement
# /login — start the auth-code flow:
auth_url, flow_state = begin_login()
save_to_session("uta_flow", flow_state) # JSON-able dict
return redirect(auth_url)
# /callback — reads ?code=&state=, or ?error= on cancel:
if read_query("error"):
return redirect("/") # login canceled
session = complete_login(
code=read_query("code"),
state=read_query("state"),
flow_state=load_from_session("uta_flow"),
)
save_to_session("uta_sub", session.sub)
save_to_session("uta_access_token", session.access_token)
# anywhere you gate features:
ent = get_entitlement(load_from_session("uta_access_token"))
if ent.entitled and ent.product_public_id == "prod_<your-pro-id>":
unlock_pro_features()Framework guides
- Flask — confidential server app,
/login·/callback·/logout. - Django — views and URL patterns; config read from
settings. - FastAPI — async hot path with
get_entitlement_asyncand Starlette sessions. - Dash — routes on the underlying Flask server, entitlement in a callback.
- Streamlit — a small BFF companion server handles OIDC; Streamlit reads the result.
Next steps
Read OpenID Connect for the full model, then Error Handling and Gotchas.
