Reference

REST API Reference

The Ava-Twin REST API lets you manage apps, API keys, customizer settings, and mint session tokens from your own backend or any HTTP client.

Authentication

Most endpoints require a Bearer token in the Authorization header. This is the JWT access token you receive after signing in via Supabase Auth.

Authorization header
Authorization: Bearer your-access-token-here

The customizer-token-mint endpoint also supports API key authentication via custom headers, which is the recommended approach for server-to-server calls from your game backend:

API key authentication
x-app-id: your-app-id-here
x-api-key: ava_sk_your-api-key-here

For multi-organization accounts, pass the optional x-org-id header to specify which organization context to use.

Base URL

All endpoints are Supabase Edge Functions served at:

https://api.ava-twin.me/functions/v1/

Each endpoint is a separate function. For example, to list your apps, send a request to: https://api.ava-twin.me/functions/v1/apps-list

Error Handling

All errors return a JSON object with an error field and an appropriate HTTP status code.

Error response
{
  "error": "Unauthorized"
}
StatusMeaning
400Bad request — missing or invalid parameters
401Unauthorized — invalid or missing token
403Forbidden — insufficient permissions (e.g. developer role)
404Not found — resource does not exist or you lack access
405Method not allowed — wrong HTTP method
429Too many requests — rate limit exceeded
500Server error — unexpected failure

Response formats vary by endpoint. See individual endpoint documentation below.

Identifier-related 403 errors

Customizer and Category C runtime endpoints validate the calling build's platform identifier against what is registered for the app in Console. When the check fails, the response is a 403 with a stable code field in addition to error:

Error response (identifier check)
{
  "error": "Origin not registered for this app",
  "code": "ORIGIN_NOT_REGISTERED"
}
CodeDescriptionReturned by
ORIGIN_NOT_REGISTEREDWebGL request, no origin registered for this appcustomizer-token-mint, avatar-resolve, player-*
ORIGIN_NOT_ALLOWEDBrowser's parent origin doesn't match registeredcustomizer-token-mint, avatar-resolve, player-*
BUNDLE_NOT_REGISTEREDNative request, no bundle ID registered for this platformcustomizer-token-mint, avatar-resolve, player-*
BUNDLE_NOT_ALLOWEDSDK-reported bundle doesn't match registeredcustomizer-token-mint, avatar-resolve, player-*
PARENT_ORIGIN_REQUIREDCustomizer couldn't determine parent origin (referrer policy)customizer-token-mint
PARENT_ORIGIN_INVALIDparent_origin not a valid URLcustomizer-token-mint
PLAN_PLATFORM_NOT_ALLOWEDTrying to register a platform your plan doesn't includeapps-update
INVALID_ORIGIN_WILDCARDWildcard origin too broad (e.g., *.com)apps-update

For full troubleshooting, accepted formats, and per-platform setup, see Registering Build Identifiers.

Authorization & Identifier Checks

Beyond API key authentication, every customizer and Category C runtime endpoint validates your build's platform identifier against what is registered for the app in Console. WebGL requests are matched against the registered origin (browser-set Origin header); native requests are matched against the SDK-reported bundle_id. Editor mode (mode=editor) bypasses these checks for development. See Registering Build Identifiers for setup, formats, and full error reference.

Rate Limiting

Rate limits apply per endpoint. If exceeded, you'll receive a 429 response with a Retry-After header indicating how many seconds to wait before retrying.

Apps

Apps represent your game projects. Each app has its own API keys, customizer settings, and usage tracking. Create a separate app for each game or environment (development / production).

POST/apps-list

List all apps in your organization.

Request body

No body required. Send an empty POST request.

Example
Request
curl -X POST \
  https://api.ava-twin.me/functions/v1/apps-list \
  -H "Authorization: Bearer your-access-token-here" \
  -H "Content-Type: application/json"
Response
{
  "success": true,
  "count": 2,
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "user_id": "user-uuid",
      "org_id": "org-uuid",
      "name": "My Game",
      "description": "Production build",
      "created_at": "2026-01-15T10:30:00Z"
    }
  ]
}
POST/apps-create

Create a new app. Requires owner or admin role.

Request body
namestringrequired
App name (max 100 characters).
descriptionstring
Optional description (max 500 characters).
Example
Request
curl -X POST \
  https://api.ava-twin.me/functions/v1/apps-create \
  -H "Authorization: Bearer your-access-token-here" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My Unity Game",
    "description": "Production environment"
  }'
Response
{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "user_id": "user-uuid",
    "org_id": "org-uuid",
    "name": "My Unity Game",
    "description": "Production environment",
    "created_at": "2026-01-15T10:30:00Z"
  }
}
POST/apps-get

Retrieve a single app by ID.

Request body
idstringrequired
The app ID (UUID).
Example
Request
curl -X POST \
  https://api.ava-twin.me/functions/v1/apps-get \
  -H "Authorization: Bearer your-access-token-here" \
  -H "Content-Type: application/json" \
  -d '{ "id": "550e8400-e29b-41d4-a716-446655440000" }'
Response
{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "user_id": "user-uuid",
    "org_id": "org-uuid",
    "name": "My Unity Game",
    "description": "Production environment",
    "created_at": "2026-01-15T10:30:00Z"
  }
}
PATCH/apps-update

Update an app's name or description. Requires owner or admin role.

Request body
idstringrequired
The app ID (UUID).
namestringrequired
New app name.
descriptionstring | null
New description, or null to clear.
platform_identifiersobject
Map of Unity platform name to identifier string. Used by the runtime identifier check to authorize this app on each platform. Only platforms in your plan's allowed list can be registered — passing a disallowed platform returns PLAN_PLATFORM_NOT_ALLOWED. Wildcard origins that are too broad (e.g. *.com) return INVALID_ORIGIN_WILDCARD. See Registering Build Identifiers.
platform_identifiers (example)
{
  "WebGLPlayer": "mygame.com",
  "Android": "com.example.mygame",
  "IPhonePlayer": "com.example.mygame"
}
Example
Request
curl -X PATCH \
  https://api.ava-twin.me/functions/v1/apps-update \
  -H "Authorization: Bearer your-access-token-here" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "My Game (Prod)",
    "description": "Updated description",
    "platform_identifiers": {
      "WebGLPlayer": "mygame.com",
      "Android": "com.example.mygame",
      "IPhonePlayer": "com.example.mygame"
    }
  }'
Response
{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "user_id": "user-uuid",
    "org_id": "org-uuid",
    "name": "My Game (Prod)",
    "description": "Updated description",
    "created_at": "2026-01-15T10:30:00Z"
  }
}
DELETE/apps-delete

Permanently delete an app and all associated data. Requires owner or admin role.

Request body
idstringrequired
The app ID (UUID).
Example
Request
curl -X DELETE \
  https://api.ava-twin.me/functions/v1/apps-delete \
  -H "Authorization: Bearer your-access-token-here" \
  -H "Content-Type: application/json" \
  -d '{ "id": "550e8400-e29b-41d4-a716-446655440000" }'
Response
{
  "success": true
}

API Keys

API keys authenticate server-to-server requests (e.g. minting customizer tokens from your game backend). Keys are prefixed with ava_sk_ and are shown only once at creation.

POST/api-keys-list

List all API keys for an app. Key values are redacted — only the last 4 characters are shown.

Request body
app_idstringrequired
The app ID (UUID).
Example
Request
curl -X POST \
  https://api.ava-twin.me/functions/v1/api-keys-list \
  -H "Authorization: Bearer your-access-token-here" \
  -H "Content-Type: application/json" \
  -d '{ "app_id": "550e8400-e29b-41d4-a716-446655440000" }'
Response
{
  "success": true,
  "count": 1,
  "data": [
    {
      "id": "key-uuid",
      "app_id": "app-uuid",
      "name": "Production",
      "created_at": "2026-01-15T10:30:00Z",
      "key_last4": "Ab3x",
      "key_prefix": "ava_sk_"
    }
  ]
}
POST/api-keys-create

Generate a new API key for an app. Requires owner or admin role.

Request body
app_idstringrequired
The app ID (UUID).
namestringrequired
A label for this key (e.g. "Production", "Dev").
Example
Request
curl -X POST \
  https://api.ava-twin.me/functions/v1/api-keys-create \
  -H "Authorization: Bearer your-access-token-here" \
  -H "Content-Type: application/json" \
  -d '{
    "app_id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Production"
  }'
Response
{
  "success": true,
  "data": {
    "id": "key-uuid",
    "app_id": "app-uuid",
    "name": "Production",
    "created_at": "2026-01-15T10:30:00Z",
    "key_last4": "Ab3x",
    "key_prefix": "ava_sk_",
    "key": "ava_sk_dGhpcyBpcyBhIHBsYWNlaG9sZGVyIGtleQ"
  }
}

The full key value is returned only in this response. Store it securely — it cannot be retrieved again. If lost, delete the key and create a new one.

POST/api-keys-delete

Revoke and permanently delete an API key. Requires owner or admin role. Also accepts DELETE method.

Request body
idstringrequired
The API key ID (UUID) — not the key value itself.
Example
Request
curl -X POST \
  https://api.ava-twin.me/functions/v1/api-keys-delete \
  -H "Authorization: Bearer your-access-token-here" \
  -H "Content-Type: application/json" \
  -d '{ "id": "key-uuid" }'
Response
{
  "success": true
}

Customizer

The customizer is the 3D avatar editor your players use to create their characters. These endpoints manage default settings and mint short-lived session tokens for embedding the customizer in your game.

POST/customizer-defaults-get

Get the default customizer configuration for an app.

Request body
app_idstringrequired
The app ID (UUID).
Example
Response
{
  "success": true,
  "data": {
    "defaults": {
      "head": "1Y7tsN8a",
      "top": "kR3vPm2x",
      "bottom": "Qw9nLp4j",
      "shoes": "Xt6bHc7d"
    }
  }
}
POST/customizer-defaults-upsert

Create or update the default customizer configuration for an app. Requires owner or admin role.

Request body
app_idstringrequired
The app ID (UUID).
defaultsobjectrequired
A JSON object containing the customizer configuration (colors, slots, presets, etc.).
Example
Request
curl -X POST \
  https://api.ava-twin.me/functions/v1/customizer-defaults-upsert \
  -H "Authorization: Bearer your-access-token-here" \
  -H "Content-Type: application/json" \
  -d '{
    "app_id": "app-uuid",
    "defaults": {
      "theme": "dark",
      "primaryColor": "#BC75FF",
      "slots": ["hair", "face", "body"]
    }
  }'
Response
{
  "success": true,
  "data": {
    "app_id": "app-uuid",
    "defaults": { ... },
    "updated_at": "2026-01-15T10:30:00Z"
  }
}
POST/customizer-token-mint

Mint a short-lived JWT for embedding the avatar customizer. This is the primary endpoint your game backend calls.

Authentication

Supports two authentication modes:

  • Bearer token — for console users (playground mode)
  • API key headers — for server-to-server calls (embed mode). Pass x-app-id and x-api-key.
Request body
app_idstringrequired
The app ID. Can also be passed via x-app-id header.
mode"embed" | "editor" | "playground"
Token mode. Defaults to "embed" when using API key auth, "playground" when using Bearer token. Use "editor" to bypass identifier enforcement during Unity Editor development.
player_idstring
Your game's unique player identifier. Required for MAU tracking in embed mode.
platformstring
Unity Application.platform.ToString() value (e.g., "WebGLPlayer", "Android", "IPhonePlayer"). Required for embed mode so the server can pick the right registered identifier to validate against.
parent_originstring
The URL of the page hosting the customizer iframe. Required for WebGL embed mode — the SDK's customizer iframe JS reads this from document.referrer and passes it through. Missing or empty values return PARENT_ORIGIN_REQUIRED; non-URL values return PARENT_ORIGIN_INVALID.
bundle_idstring
The SDK-reported Application.identifier for the build (sent on all platforms; required for native enforcement). Validated against the registered bundle ID for the platform — mismatches return BUNDLE_NOT_ALLOWED.
Example (API key auth)
Request
curl -X POST \
  https://api.ava-twin.me/functions/v1/customizer-token-mint \
  -H "x-app-id: your-app-id-here" \
  -H "x-api-key: ava_sk_your-api-key-here" \
  -H "Content-Type: application/json" \
  -d '{
    "player_id": "player-12345",
    "mode": "embed"
  }'
Response
{
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "expiresIn": 86400
}

Tokens expire in 24 hours (86400 seconds). Mint a new token for each customizer session. MAU usage is tracked per unique player_id per calendar month.

POST/avatar-resolve

Resolves an avatar ID to a signed GLB download URL. Use this from your game backend to fetch the 3D model file.

Authentication

Accepts either a Bearer token or, as an alternative for Category C runtime calls from a game build, API key authentication via custom headers:

  • x-app-id — your app ID
  • x-api-key — your API key (ava_sk_...)

When called via the API key path, requests are subject to the identifier checks described in Authorization & Identifier Checks.

Request body
avatar_idstringrequired
A short opaque alphanumeric identifier for the avatar (e.g. NWtPd1zoxDE). The same combination of pieces always returns the same ID.
bundle_idstring
The SDK-reported Application.identifier for the build. Sent by SDK v1.0.0+ on Category C calls and validated against the registered bundle ID for the platform.
platformstring
Unity Application.platform.ToString() value (e.g., "Android", "IPhonePlayer"). Sent by SDK v1.0.0+ on Category C calls so the server can select the right registered identifier to validate against.
Example
Request
curl -X POST \
  https://api.ava-twin.me/functions/v1/avatar-resolve \
  -H "x-app-id: your-app-id-here" \
  -H "x-api-key: ava_sk_your-api-key-here" \
  -H "Content-Type: application/json" \
  -d '{
    "avatar_id": "NWtPd1zoxDE"
  }'
Response
{
  "avatar_id": "NWtPd1zoxDE",
  "url": "https://...signed-url...",
  "expires_in": 300
}

The signed URL expires in 5 minutes (300 seconds). Request a new URL each time you need to download the GLB file. The avatar_id is a short opaque alphanumeric string. The same combination of pieces always returns the same ID.

Plans

Public endpoint for retrieving available plans and pricing. No authentication required.

GET/plans-public-get

Retrieve all active plans and any current promotions. No authentication required.

Authentication

None required. This is a public endpoint.

Example
Request
curl https://api.ava-twin.me/functions/v1/plans-public-get
Response
{
  "success": true,
  "plans": [
    {
      "name": "free",
      "displayName": "Free",
      "price": "$0",
      "priceYearly": null,
      "cadence": "",
      "highlight": "Start building for free",
      "bullets": ["1 app", "100 sessions/mo", "2 API keys per app", "1 member", "Community support"],
      "comparisonFeatures": [...],
      "featured": false,
      "trialEligible": false,
      "trialDays": 0
    },
    {
      "name": "indie",
      "displayName": "Indie",
      "price": "$29",
      "priceYearly": 290,
      "cadence": "/ month",
      "highlight": "For solo creators shipping their first game",
      "bullets": ["2 apps", "1,000 sessions/mo", "3 API keys per app", "1 member", "Email support"],
      "comparisonFeatures": [...],
      "featured": false,
      "trialEligible": true,
      "trialDays": 30
    }
  ],
  "promotion": null
}
Looking to integrate avatars into your Unity game?

The Unity SDK handles avatar loading, caching, animation, and character controls. For most use cases, the SDK is all you need — the REST API is for server-side management and automation.