HomeGuidesAPI ReferenceChangelogDiscussions
Hire a ReactVision Expert 🛠️Launch Studio 🖥️Sponsor on GitHub ❤️Log In
API Reference

Mint a user token

Secret key only.

Mints a user token for one signed-in user of your app. Call it from your backend, never from the app: it needs a secret key.

A user token reaches one project and one app user, and lasts at most one hour. Its scp claim lists what it may be used for, and is ["vps"] today: user tokens are accepted by the VPS operations only, once VPS launches, and every other operation refuses them. Treat the token as opaque.

Deleting or expiring the secret key that minted a token revokes the token too. There is no other way to revoke one.

Mint only for a user your backend has signed in. If you need more protection against automated clients, your endpoint can first check App Attest (iOS), Play Integrity (Android) or Meta's attestation (Quest).

Limits, always enforced: 1,200 tokens a minute per key, and 60 an hour per key and app user. A backend can cache a token per app user until shortly before expires_at.

Your backend

Mint a token for the signed-in user and return it to the app:

const r = await fetch("https://platform.reactvision.xyz/functions/v1/app-user-tokens", {
  method: "POST",
  headers: { "x-api-key": process.env.RV_SECRET_KEY!, "content-type": "application/json" },
  body: JSON.stringify({ project_id: PROJECT_ID, app_user_id: user.id }),
})
const { token, expires_at } = await r.json() // hand both to the signed-in app user

Your app (Viro, once VPS launches)

Viro calls credentials before every VPS request, so cache the token until it is close to expiring:

let cached: { token: string; exp: number } | null = null
const credentials = async () => {
  if (!cached || cached.exp - Date.now() < 120_000) {
    const r = await fetch("https://api.example.com/rv-token", { headers: { Authorization: `Bearer ${session}` } })
    const { token, expires_at } = await r.json()
    cached = { token, exp: Date.parse(expires_at) }
  }
  return { projectId: PROJECT_ID, accessToken: cached.token }
}
<ViroVPS arSceneNavigator={nav} credentials={credentials} />

Not counted against the monthly API request quota.

Recent Requests
Log in to see full request history
TimeStatusUser Agent
Retrieving recent requests…
LoadingLoading…
Body Params
uuid
required

The project the token reaches. A bound key may only name its own.

string
required
length between 1 and 255

Your id for the signed-in app user: 1 to 255 printable ASCII characters, no spaces, not . or ...

integer
60 to 3600

Lifetime, 60 to 3600 seconds. Default 3600.

Responses

INVALID_BODY: the body is not a JSON object, project_id is not a UUID, or ttl_seconds is not an integer from 60 to 3600.

INVALID_APP_USER_ID: app_user_id is not 1 to 255 printable ASCII characters without spaces, or is . or ...

MISSING_API_KEY: no x-api-key header.

INVALID_API_KEY_FORMAT: the key is not rv_live_, rv_client_ or rv_secret_ followed by 64 lowercase hexadecimal characters.

INVALID_API_KEY: no such key, or a key sent with the wrong prefix for its type.

API_KEY_EXPIRED: the key is past its expiry date.

API_KEY_TYPE_NOT_ALLOWED: a client key or a legacy key. This operation needs a secret key.

PROJECT_NOT_FOUND: project_id does not exist, belongs to another team, or is not the project the key is bound to.

RATE_LIMITED: over 1,200 tokens a minute for the key, or 60 an hour for the key and app user. Wait the number of seconds in Retry-After.

INTERNAL_ERROR: an unexpected error. Retry, and quote meta.request_id to support if it persists.

API_KEY_LOOKUP_FAILED: the key could not be checked. Retry; this does not mean the key is invalid.

PROJECT_LOOKUP_FAILED: the project could not be checked. Retry.

APP_USER_TOKEN_SIGNING_UNAVAILABLE: tokens cannot be signed right now. Retry.

Language
Credentials
Header
LoadingLoading…
Response
Choose an example:
application/json