Studio Tank API and MCP

Create characters from the catalog, preview them and download rigged, animated files straight from your code, your game pipeline or your AI assistant. Same rules as the studio: quota and license follow your account plan.

Get started in 3 steps

  1. Sign in to the studio, open the account menu and create an API key. It is shown only once: keep it in a vault or an environment variable. Create a key in the studio →
  2. Roll a random character (or build the recipe with catalog ids):
    curl -s https://avatar.tankpvp.com/api/v1/avatars/random \
      -H "Authorization: Bearer $AVATAR_KEY" \
      -H "Content-Type: application/json" \
      -d '{"style": "mago"}'
  3. Request the file and wait for it; the response carries a download link valid for 15 minutes:
    curl -s https://avatar.tankpvp.com/api/v1/exports \
      -H "Authorization: Bearer $AVATAR_KEY" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: meu-pedido-001" \
      -d '{"code": "<code>", "format": "glb", "fps": 24, "clips": ["Walk","Run","Attack"]}'
    
    curl -s "https://avatar.tankpvp.com/api/v1/exports/<id>?wait=20" -H "Authorization: Bearer $AVATAR_KEY"
    # → download.url (15 min)

OpenAPI 3.1 specification for Postman, Insomnia or SDK generators: https://avatar.tankpvp.com/api/v1/openapi.json

Authentication

Send the key in the Authorization: Bearer avs_… header on every call. The API ignores the website cookie and has no CORS: keep the key on your server, never in page JavaScript. Each account can have up to 5 active keys with scopes, and revoking one cuts access immediately.

ScopeAllows
catalog:readcatalog and recipe validation
avatars:writerandom characters and image previews
exports:writecreate files (uses quota), status, download and history

Limits and quota

The quota belongs to the account: downloads from the studio, the API and MCP add up in the same counter. RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset headers show the per-minute limit; when exceeded you get 429 with Retry-After.

FreePro
Downloads per month5unlimited
File licensepersonal, non-commercialcommercial
Requests per minute (per key)60300
Image previews per hour30300
Random rolls per day20unlimited
Spritesup to 128 px, 4 directions256 px, 8 directions
Character batchesnoup to 50 GLB in a .zip
Exports per hour / at once60 / 160 / 1

Files, idempotency and license

  • Queue: POST /exports answers 202 with the id. Ask GET /exports/{id}?wait=20: the response waits up to 25 s, so you poll less.
  • Idempotency: send Idempotency-Key on POST /exports. Repeating with the same key and body within 24 h returns the same file without using quota again.
  • Download: files stay on the server for 15 minutes. Download with your key or via the signed download.url (no key needed; ideal to hand over to whoever asked).
  • License: every file carries a number AS-XXXXX-XXXXX (in the GLB, the PNG and LICENCA.txt inside zips). Anyone can check it at GET /licenses/{id} without seeing the owner.
  • Recipes: send config (parts and colors) or code (the short code from the studio link). Wrong ids fall back to defaults; /avatars/validate explains what changed.

Errors

Every error has the same shape: { "error": { "code", "message", "docs" } }. Branch on code (it is stable); the message is for humans.

HTTPcodemessage
401missing_keySend your API key in the header: Authorization: Bearer avs_...
401invalid_keyAPI key not found or revoked.
403not_verifiedConfirm the account e-mail before using the API.
403scopeThis API key does not have the scope this endpoint needs.
429rate_limitedToo many requests. Retry after the seconds in the Retry-After header.
400invalid_jsonThe request body is not valid JSON.
413too_largeThe request body is too large.
400config_requiredSend the avatar as `config` (object) or `code` (recipe code).
400invalid_codeThe recipe code could not be decoded.
400invalid_styleUnknown style. See `styles` in GET /api/v1/catalog.
400invalid_familyUnknown family. See `families` in GET /api/v1/catalog (chibi, voxel).
400invalid_count`count` must be an integer in the allowed range.
400invalid_seed`seed` must be an integer from 0 to 4294967295.
400invalid_size`size` must be one of the allowed values.
400format`format` must be glb, obj, sprites or png.
400fps`fps` must be one of 12, 24, 30, 36 or 60.
400clipsGLB and sprites need at least one valid clip id in `clips`.
400sprites`dirs` must be 1, 4 or 8 and `size` 64, 128 or 256.
402pro_onlyThis option is available on the Pro plan.
400too_bigToo many sprite frames. Use fewer clips, directions, a lower fps or a smaller size.
400batch`batch.count` must be 1 to 50 and batches are GLB only.
429busyAn export is already running for this account. Wait for it to finish.
402quotaMonthly download quota reached. The Pro plan has unlimited downloads.
402random_limitDaily random limit reached on the free plan.
402quota_outThe free plan's monthly downloads are used up, so random avatars are paused until next month. The Pro plan has no limits.
503queue_fullThe export queue is full. Try again in a minute.
404not_foundNot found (or it belongs to another account, or it already expired).
403expired_linkThis download link is invalid or expired. Ask GET /api/v1/exports/{id} for a new one.
400idempotency_key`Idempotency-Key` must have 1 to 255 visible ASCII characters.
422idempotency_mismatchThis Idempotency-Key was already used with a different request body.
409idempotency_in_progressA request with this Idempotency-Key is still being processed.
402creditsNot enough AI credits. Buy a credit pack in the studio.
503ai_offAI generation is not available right now.
429ai_busyAn AI generation is already running for this account.
429ai_dailyDaily AI generation limit reached for this account.
503ai_pausedAI generation is paused for today. Try again tomorrow.
400ai_prompt`prompt` must have 3 to 1000 characters (or send an image).
400ai_image`image` must be a data URL of a JPEG, PNG or WebP image.
413ai_image_sizeThe image is too large (max ~1.2 MB). Resize it and try again.
200ai_refusedThe AI declined this request. Describe the character differently.
200ai_incompleteThe AI could not finish this character. Try a simpler description.
200ai_bad_outputThe AI answer was invalid. Your credits were returned; try again.
200ai_failedThe AI service failed. Your credits were returned; try again.
500genericSomething went wrong on our side. Try again.

MCP: connect Claude and other assistants

The MCP server at /mcp lets an AI assistant build characters with you: it reads the catalog, shows an image preview and, once you approve, exports and hands you the link. Same key, quota and license as the API.

POSThttps://avatar.tankpvp.com/mcp

Claude Code (terminal):

claude mcp add --transport http avatar-studio https://avatar.tankpvp.com/mcp \
  --header "Authorization: Bearer $AVATAR_KEY"

Cursor, VS Code, Windsurf and other clients with HTTP MCP (config file):

{
  "mcpServers": {
    "avatar-studio": {
      "type": "http",
      "url": "https://avatar.tankpvp.com/mcp",
      "headers": {
        "Authorization": "Bearer avs_..."
      }
    }
  }
}

Claude Desktop (through mcp-remote, needs Node installed):

{
  "mcpServers": {
    "avatar-studio": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://avatar.tankpvp.com/mcp",
        "--header",
        "Authorization:Bearer avs_..."
      ]
    }
  }
}

Available tools

get_catalogPart ids by category (with styles and rules), styles, color zones, palettes, style presets, animation clips and export options. Call this first.
validate_avatarNormalizes a recipe and explains problems (unknown ids, wrong category, invalid colors), defaults and hidden categories. Free.
random_avatar1 to 5 random recipes, optionally of one style. A seed makes it reproducible. Counts toward the daily random limit on the free plan.
preview_avatarRenders a PNG portrait (128 or 256 px) of the recipe so you and the user can see it. Does not use the download quota (hourly limit).
export_avatarCreates the downloadable file: glb (rigged + animations), obj (zip), sprites (zip of sheets) or png (1024 px portrait). Uses 1 download from the monthly quota. Waits up to ~25 s and returns a download link valid for 15 minutes plus the license. Ask the user before calling.
get_exportStatus of an export by id; waits up to `wait` seconds (max 25). When done, returns a fresh 15-minute download link.
account_statusPlan, license kind, downloads used and left this month, random rolls left today and limits.

MCP console (try it here)

Reference

Account, plan and quota

GET/me

Account, plan and quota

Returns the plan, downloads used this month, random rolls left today, limits and the key in use.

  • 200 OK
  • 401 Missing or invalid API key.
  • 403 The key lacks the scope, or the account e-mail is not confirmed.
  • 429 Rate limited (see Retry-After and RateLimit-* headers).

Parts, styles, clips and colors

GET/catalogcatalog:read

Parts, styles and clips catalog

Everything a recipe can use: part ids per category, styles, color zones, palettes, clips, presets and export options. Names in the requested language.

langquerypt | en | es
categoryqueryhead | hair | eyes | mouth | beard | helmet | body | legs | boots | gloves | back | handR | handLOnly parts of this category.
familyquerychibi | voxelOnly parts of this family (chibi or voxel).
  • 200 OK
  • 401 Missing or invalid API key.
  • 403 The key lacks the scope, or the account e-mail is not confirmed.
  • 429 Rate limited (see Retry-After and RateLimit-* headers).

Validate, randomize and preview

POST/avatars/validatecatalog:read

Validate a recipe

Normalizes the recipe (ids and colors checked), returns the short code and explains what was replaced, what was defaulted and what ends up hidden (e.g. a helmet hides hair). Free.

{
  "config": {
    "name": "Ana",
    "parts": {
      "helmet": "helm_wizard",
      "handR": "r_staff"
    },
    "palette": {
      "primary": "#5b3fc4"
    }
  }
}
  • 200 OK
  • 400 Neither config nor code, or an invalid code.
  • 401 Missing or invalid API key.
  • 403 The key lacks the scope, or the account e-mail is not confirmed.
  • 429 Rate limited (see Retry-After and RateLimit-* headers).
POST/avatars/randomavatars:write

Random characters

Generates 1 to 10 recipes consistent with a style (or any style). With `seed` the result is reproducible. On the free plan it counts toward the daily random limit.

{
  "style": "mago",
  "count": 2,
  "seed": 42
}
  • 200 OK
  • 400 Invalid style, count or seed.
  • 401 Missing or invalid API key.
  • 402 Daily random limit reached (free plan).
  • 403 The key lacks the scope, or the account e-mail is not confirmed.
  • 429 Rate limited (see Retry-After and RateLimit-* headers).
POST/avatars/previewavatars:write

Image preview

Server-rendered PNG portrait, 128 or 256 px. Does not use a download; limited per hour (free 30, Pro 300).

{
  "config": {
    "name": "Bomber",
    "parts": {
      "head": "head_round",
      "hair": "hair_short",
      "eyes": "eyes_big",
      "mouth": "mouth_open",
      "beard": "beard_none",
      "helmet": "helm_bomber",
      "body": "body_bomber",
      "legs": "legs_cargo",
      "boots": "boots_sneakers",
      "gloves": "gloves_cartoon",
      "back": "back_bombs",
      "handR": "r_bomb",
      "handL": "l_none"
    },
    "palette": {
      "skin": "#f2c39b",
      "hair": "#5a3a22",
      "eyes": "#2fb3c4",
      "primary": "#f07f2a",
      "secondary": "#2f4f8f",
      "detail": "#f2c534",
      "metal": "#b9c2cf",
      "leather": "#5a3a22",
      "glow": "#ff6b3d",
      "dark": "#1d1b26",
      "white": "#f7f7fb",
      "tongue": "#e2697c",
      "flash": "#ffd24a"
    }
  },
  "size": 256
}
  • 200 PNG image
  • 400 Invalid avatar or size.
  • 401 Missing or invalid API key.
  • 403 The key lacks the scope, or the account e-mail is not confirmed.
  • 429 Rate limited (see Retry-After and RateLimit-* headers).
  • 503 Render queue full.

Files (use the download quota)

POST/exportsexports:write

Create a file (GLB, OBJ, sprites, PNG)

Validates, takes 1 download from the quota (batch: N) and queues the file. Answers 202 with the id; follow it with GET /exports/{id}. Send an Idempotency-Key header to retry safely.

Idempotency-KeyheaderstringSame key + same body within 24 h returns the same export without using quota again.
{
  "config": {
    "name": "Bomber",
    "parts": {
      "head": "head_round",
      "hair": "hair_short",
      "eyes": "eyes_big",
      "mouth": "mouth_open",
      "beard": "beard_none",
      "helmet": "helm_bomber",
      "body": "body_bomber",
      "legs": "legs_cargo",
      "boots": "boots_sneakers",
      "gloves": "gloves_cartoon",
      "back": "back_bombs",
      "handR": "r_bomb",
      "handL": "l_none"
    },
    "palette": {
      "skin": "#f2c39b",
      "hair": "#5a3a22",
      "eyes": "#2fb3c4",
      "primary": "#f07f2a",
      "secondary": "#2f4f8f",
      "detail": "#f2c534",
      "metal": "#b9c2cf",
      "leather": "#5a3a22",
      "glow": "#ff6b3d",
      "dark": "#1d1b26",
      "white": "#f7f7fb",
      "tongue": "#e2697c",
      "flash": "#ffd24a"
    }
  },
  "format": "glb",
  "fps": 24,
  "clips": [
    "Walk",
    "Run",
    "Attack"
  ]
}
  • 202 Queued
  • 400 Invalid request (format, fps, clips, sprites, batch).
  • 401 Missing or invalid API key.
  • 402 Quota reached or Pro-only option.
  • 403 The key lacks the scope, or the account e-mail is not confirmed.
  • 409 Idempotency-Key still in progress.
  • 422 Idempotency-Key reused with another body.
  • 429 Rate limited (see Retry-After and RateLimit-* headers).
  • 503 Export queue full (quota returned).
GET/exports/{id}exports:write

Export status

Status, progress, license and, when ready, a signed download link (15 min). With `wait` (up to 25 s) the response waits for completion.

id *pathstring
waitqueryinteger
  • 200 OK
  • 401 Missing or invalid API key.
  • 403 The key lacks the scope, or the account e-mail is not confirmed.
  • 404 Unknown, expired or not yours.
  • 429 Rate limited (see Retry-After and RateLimit-* headers).
GET/exports/{id}/fileexports:write

Download the file

The finished file (kept for 15 min). With the key in the header, or without a key using the signed link `token`.

id *pathstring
tokenquerystringFrom `download.url` (no API key needed).
  • 200 File (model/gltf-binary, application/zip or image/png)
  • 403 Invalid or expired signed link.
  • 404 Unknown, expired or not yours.
GET/downloadsexports:write

Download history

The account's downloads (studio, API and MCP) with license number and kind, newest first. Paginate with `before`.

limitqueryinteger
beforequeryintegerReturn downloads with id lower than this (use `next`).
  • 200 OK
  • 401 Missing or invalid API key.
  • 403 The key lacks the scope, or the account e-mail is not confirmed.
  • 429 Rate limited (see Retry-After and RateLimit-* headers).

AI character design (uses AI credits)

GET/creditsavatars:write

AI credit balance

Balance (purchased + this month's Pro credits), cost of each generation kind and whether AI is available.

  • 200 OK
  • 401 Missing or invalid API key.
  • 403 The key lacks the scope, or the account e-mail is not confirmed.
  • 429 Rate limited (see Retry-After and RateLimit-* headers).
POST/ai/generateavatars:write

Design a character with AI

The AI (Claude) picks catalog parts and colors from a description, a reference photo (data URL JPEG/PNG/WebP, up to ~1.2 MB) or adjusts a recipe (mode: refine). Costs 2, 3 or 1 credit, charged up front and refunded on failure. Waits up to 20 s; if not done, answers 202 with the id for GET /ai/generations/{id}. Real people and brands become generic traits.

{
  "prompt": "a tall rapper with braids, dark sunglasses and a blue jacket",
  "lang": "en"
}
  • 200 Done (status: done) or failed (status: error, credits refunded)
  • 202 Still running: poll GET /ai/generations/{id}
  • 400 Invalid prompt or image.
  • 401 Missing or invalid API key.
  • 402 Not enough AI credits.
  • 403 The key lacks the scope, or the account e-mail is not confirmed.
  • 429 Rate limited (see Retry-After and RateLimit-* headers).
  • 503 AI unavailable or paused for today.
GET/ai/historyavatars:write

AI generation history

Everything the account asked the AI and what came out (recipe code to reopen for free). Reference photos are never stored.

limitqueryinteger
beforequeryinteger
  • 200 OK
  • 401 Missing or invalid API key.
  • 403 The key lacks the scope, or the account e-mail is not confirmed.
  • 429 Rate limited (see Retry-After and RateLimit-* headers).
GET/ai/generations/{id}avatars:write

AI generation result

Status of a generation (owner only); with `wait` (up to 25 s) it waits for completion. Kept for 15 minutes.

id *pathinteger
waitqueryinteger
  • 200 OK
  • 401 Missing or invalid API key.
  • 403 The key lacks the scope, or the account e-mail is not confirmed.
  • 404 Unknown, expired or not yours.
  • 429 Rate limited (see Retry-After and RateLimit-* headers).

Public license check

GET/licenses/{id}Public (no key)

Check a license

Public (no key): tells whether the license exists, its kind (personal or commercial), date and format. Never reveals the owner.

id *pathstring
  • 200 OK