TIAN Points API

Integrate TIAN Points into your application to verify balances, deduct points for usage, and set up recurring monthly subscriptions. All endpoints are authenticated via API keys.

OAuth 2.0 — Privy-Free Partner Auth

External partners can let users authorise TIAN Points deductions without sharing the askTIAN Privy App ID. Users log in on the askTIAN Wallet domain (your Privy cost, your UX) and grant a scoped access token to the partner. The partner stores the token server-side and calls /api/v1/points/deduct with it — no Privy SDK, no session tokens.

When to use OAuth 2.0: Your app is outside the askTIAN ecosystem (you don't already embed the Privy App ID). Use the legacy Auth flow if you already have Privy integrated.
Flow overview
  1. Partner registers with askTIAN and receives a client_id and client_secret.
  2. Partner redirects the user to GET /api/oauth2/authorize with client_id, redirect_uri, scope, and state.
  3. User logs into askTIAN Wallet (Privy on askTIAN's domain) and sees a consent screen listing the requested permissions.
  4. On approval, askTIAN redirects to the partner's redirect_uri with a short-lived code.
  5. Partner exchanges the code server-to-server: POST /api/oauth2/token → receives a long-lived access_token (prefix tcat_).
  6. Partner calls POST /api/v1/points/deduct with Authorization: Bearer tcat_…. No privyId or sessionToken needed.
Step 1 — Redirect user to consent screen

Send the user to this URL. All params must be URL-encoded.

bash
GET https://wallet.asktian.com/api/oauth2/authorize
  ?client_id=tc_live_xxx
  &redirect_uri=https%3A%2F%2Fyourapp.com%2Fcallback
  &scope=deduct
  &state=RANDOM_CSRF_TOKEN
client_id — your partner API key (tc_live_xxx)
redirect_uri — must exactly match one of your registered redirect URIs
scope — space-separated: deduct and/or balance
state — random CSRF token; you must verify this in step 3
Step 2 — User approves on askTIAN Wallet

The user sees a consent screen on wallet.asktian.com showing your app name and the requested permissions. On approval, they are redirected to your redirect_uri:

bash
https://yourapp.com/callback?code=AUTH_CODE&state=RANDOM_CSRF_TOKEN

The code is single-use and expires in 10 minutes.

Step 3 — Exchange code for access token (server-side)
bash
POST https://wallet.asktian.com/api/oauth2/token
Content-Type: application/json

{
  "grant_type": "authorization_code",
  "code": "AUTH_CODE",
  "redirect_uri": "https://yourapp.com/callback",
  "client_id": "tc_live_xxx",
  "client_secret": "YOUR_CLIENT_SECRET"
}
json
{
  "access_token": "tcat_xxxxxxxxxxxxxxxxxxxx",
  "token_type": "Bearer",
  "scope": "deduct",
  "expires_in": 2592000,
  "refresh_token": "tcrt_xxxxxxxxxxxxxxxxxxxx",
  "refresh_token_expires_in": 7776000
}

Store both tokens securely server-side. The access_token is valid for 30 days; the refresh_token is valid for 90 days and can be used to silently issue a new access token without requiring the user to re-authorise.

Step 4 — Deduct points using the access token
bash
POST https://wallet.asktian.com/api/v1/points/deduct
Authorization: Bearer tcat_xxxxxxxxxxxxxxxxxxxx
Content-Type: application/json

{
  "amount": 10,
  "description": "Feature usage",
  "idempotencyKey": "order_abc123"
}
json
{
  "success": true,
  "creditsDeducted": 10,
  "newBalance": 490,
  "transactionId": "txn_123",
  "webhookDelivered": true
}

No privyId or sessionToken required — user identity is derived from the access token.

Step 5 — Silently refresh an expiring access token

When an access token is about to expire (or has expired), exchange the refresh token for a new access token + refresh token pair. This is a server-to-server call — the user is never redirected.

bash
POST https://wallet.asktian.com/api/oauth2/token
Content-Type: application/json

{
  "grant_type": "refresh_token",
  "refresh_token": "tcrt_xxxxxxxxxxxxxxxxxxxx",
  "client_id": "tc_live_xxx",
  "client_secret": "YOUR_CLIENT_SECRET"
}
json
{
  "access_token": "tcat_NEW_TOKEN",
  "token_type": "Bearer",
  "expires_in": 2592000,
  "refresh_token": "tcrt_NEW_REFRESH_TOKEN",
  "refresh_token_expires_in": 7776000,
  "scope": "deduct"
}

Token rotation: each refresh token is single-use. After exchange, both the old access token and old refresh token are immediately invalidated. Always store the new pair.

Replay detection: if a refresh token is submitted a second time, all tokens for that user + partner are revoked as a security measure.

Expiry warning: the askTIAN wallet sends an owner notification 7 days before a token expires. If the user's refresh token also expires (90 days of inactivity), they will need to re-authorise via the full OAuth flow.

Revoking access

Partners can revoke a token at any time. Users can also revoke access from their wallet settings.

bash
POST https://wallet.asktian.com/api/oauth2/revoke
Content-Type: application/json

{ "token": "tcat_xxxxxxxxxxxxxxxxxxxx" }
Token introspection

Inspect the current state of any access token without making a deduction. Useful for debugging expired or revoked tokens.

bash
GET https://wallet.asktian.com/api/oauth2/token/info
Authorization: Bearer tcat_xxxxxxxxxxxxxxxxxxxx

Returns a JSON object describing the token:

FieldTypeDescription
activebooleanWhether the token is currently valid and not revoked
scopestringGranted scope (e.g. deduct)
userIdnumberaskTIAN user ID of the token owner
partnerIdnumberID of the partner integration that issued the token
expiresAtISO 8601When the access token expires (30 days from issuance)
revokedAtISO 8601 | nullSet if the token has been revoked; null otherwise
json
{
  "active": true,
  "scope": "deduct",
  "userId": 1001,
  "partnerId": 7,
  "expiresAt": "2026-05-10T08:00:00.000Z",
  "revokedAt": null
}

Returns HTTP 401 if the token is missing or malformed. Returns the object with active: false if the token is expired or revoked.

Webhook events

When a user revokes an OAuth token from their wallet, askTIAN fires a token.revoked event to your registered webhook endpoint. Subscribe to it in the Partners dashboard under Event Filter.

FieldTypeDescription
eventstringtoken.revoked
timestampISO 8601When the revocation occurred
tokenIdnumberInternal ID of the revoked access token
userIdnumberaskTIAN user ID of the token owner
scopestringScope that was granted (e.g. deduct)
revokedAtISO 8601Timestamp of revocation
revokedBystringuser (wallet self-service) or partner (API revoke endpoint)
json
{
  "event": "token.revoked",
  "timestamp": "2026-04-10T07:00:00.000Z",
  "tokenId": 42,
  "userId": 1001,
  "scope": "deduct",
  "revokedAt": "2026-04-10T07:00:00.000Z",
  "revokedBy": "user"
}

On receiving this event, clear the stored access and refresh tokens for this user and prompt re-authorisation if needed. The webhook is delivered with HMAC-SHA256 signature verification (same as all other events).

Supported scopes
ScopePermission
deductDeduct TIAN Points from the user's balance
balanceRead the user's current TIAN Points balance (read-only)