# API keys and customer tokens

CherrySub separates the hosted person's session, the application's server authority, and the purchasing customer's short-lived client authority. Choose the correct credential for each boundary.

## Credential reference

| Credential | Where it belongs | What it authorizes |
| --- | --- | --- |
| `JSESSIONID` | Dashboard browser or a private CLI profile | Hosted account/project operations, subject to membership and role |
| `iap_pk_` | Mobile/web client configuration | Identifies the app; public runtime reads/submissions also require a customer token |
| `iap_ct_` | Client memory, supplied by your authenticated backend | The specific app and App User ID until expiry/revocation |
| `iap_sk_` | Your trusted application backend | App-scoped server operations and customer-token mint/revoke |
| Root admin bearer key | Service operator environment | `/admin/v1`; not a hosted customer's project credential |
| Apple/Google store key | Managed encrypted storage or operator credential configuration | Calls to the respective store; not a CherrySub API bearer key |
| Webhook signing secret | Your webhook receiver | Verifies outbound delivery signatures |

The CLI uses hosted account sessions. It does not require root admin keys. A publishable key is intentionally distributable, but it does not let a client mint tokens, edit the catalog, or manage webhooks.

## Create and rotate app keys

Open **API keys** for the selected app. Create a key, choose Secret or Publishable, and label its purpose. Copy the one-time plaintext result into the correct deployment configuration. Lists show only safe hints and metadata.

For rotation, create the replacement first, update the consuming server or application, verify it works, then revoke the old key. A revoked key stops authenticating. Do not delete the last working key before clients have a replacement. A lost secret cannot be read back from CherrySub.

## Implement an authenticated token broker

Your backend obtains the App User ID from its verified session or JWT middleware. Never let a client request a token for an arbitrary `app_user_id` merely by placing it in JSON.

On the trusted backend, call:

```sh
curl -fsS "$CHERRYSUB_API_URL/v1/client-tokens" \
  -H "Authorization: Bearer $CHERRYSUB_SECRET_KEY" \
  -H 'Content-Type: application/json' \
  --data '{"app_user_id":"user-123","ttl_seconds":3600}'
```

The result includes `id`, `app_user_id`, `hint`, `expires_at`, and one-time `token`. Return only `{ "token": "iap_ct_...", "expires_at": "..." }` to the client. Keep the token ID server-side if logout should revoke it with `DELETE /v1/client-tokens/{token_id}`. The default TTL is one hour, with an allowed range of 300–86400 seconds.

Use `Cache-Control: no-store` on your broker response. The [Go SDK](/docs/go) implements this trusted API handoff; you can also use the HTTP contract from another backend language.

## Send a public runtime request

```sh
curl -fsS "$CHERRYSUB_API_URL/v1/subscribers/user-123" \
  -H "Authorization: Bearer $CHERRYSUB_PUBLISHABLE_KEY" \
  -H "X-IAP-Customer-Token: $CHERRYSUB_CUSTOMER_TOKEN"
```

The customer token must belong to that app. Its subject must match the subscriber path or receipt's `app_user_id`. These checks run before store I/O or ownership changes. URL-encode user IDs when constructing paths. Keep `event_id` strings as strings in clients; do not round them through JavaScript numbers.

## Refresh and logout

The SDKs' optional in-memory token providers cache by user and expiry, refresh 30 seconds early by default, coalesce concurrent refreshes, and prevent an old in-flight refresh from repopulating an invalidated user's cache. On a 401, a client forces token refresh and retries the request once.

On application logout, invalidate that user's provider cache and clear application-owned access/purchase UI state. If your policy requires server-side invalidation, revoke the minted customer token as well. Local cache invalidation alone is not server revocation.

Do not fall back to a server secret in the client after a token error. For repeated 401s, check token expiry, app/deployment mismatch, subject mismatch, key revocation, and the broker's authenticated identity. For 403, verify that the chosen API operation is permitted by the key kind.
