Doppl API Docs

Generate AI-powered avatars from photos. One API call, six styles, every game engine. No 3D pipeline required.

Base URL https://doppl.polsia.app/api/v1

The Doppl API is a REST API that accepts JSON request bodies and returns JSON responses. All endpoints are accessed via https://doppl.polsia.app/api/v1.

Quick Start

From zero to avatar in 3 steps. No signup, no credit card.

1

Generate an API Key

Get a free demo key instantly. No authentication required.

Terminal
curl -X POST https://doppl.polsia.app/api/v1/keys \
  -H "Content-Type: application/json" \
  -d '{"name": "My Game Studio"}'
2

Upload a Photo

Send a photo URL and pick a style. The API handles the rest.

Terminal
curl -X POST https://doppl.polsia.app/api/v1/avatars \
  -H "Content-Type: application/json" \
  -H "X-API-Key: dppl_YOUR_KEY_HERE" \
  -d '{"photo": "https://example.com/selfie.jpg", "style": "stylized"}'
3

Get Your Avatar

The response includes the generated avatar URL immediately. Use it in your game, app, or wherever you need it.

Response
{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "completed",
  "style": "stylized",
  "avatar_url": "https://...",
  "format": "png",
  "size": "1024x1024",
  "processing_time_ms": 2340
}
Want to try it interactively? The API Playground on this page lets you generate keys and avatars directly in the browser.

Code Examples

Copy-pasteable examples in your language of choice. Each snippet covers key generation, avatar generation, response parsing, and error handling.

Get an API Key

curl
curl -X POST https://doppl.polsia.app/api/v1/keys \
  -H "Content-Type: application/json" \
  -d '{"name": "My App"}'

Generate an Avatar

curl
curl -X POST https://doppl.polsia.app/api/v1/avatars \
  -H "Content-Type: application/json" \
  -H "X-API-Key: dppl_YOUR_KEY_HERE" \
  -d '{"photo": "https://example.com/selfie.jpg", "style": "stylized"}'

# On success, the response JSON contains "avatar_url"

Authentication

All avatar endpoints require an API key. Pass it via the X-API-Key header or as a Bearer token.

Header Format

Option 1: X-API-Key (recommended)
X-API-Key: dppl_your_api_key_here
Option 2: Authorization Bearer
Authorization: Bearer dppl_your_api_key_here

API keys are prefixed with dppl_. Demo keys are rate-limited to 5 requests/minute. Missing or invalid keys return a 401 error.

Keep your API key secret. Don't expose it in client-side code or public repositories. Treat it like a password.

OAuth 2.0

For server-to-server integrations, Doppl supports OAuth 2.0 with the authorization code flow. This is recommended when you need to act on behalf of users or want to avoid embedding API keys in client code.

Authorization Code Flow

The authorization code flow involves three steps: redirect the user to Doppl's authorization endpoint, exchange the authorization code for tokens, and use the access token for API requests.

Step 1: Redirect to authorization
# Redirect user to Doppl authorization endpoint
curl -X GET "https://doppl.polsia.app/oauth/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=https://yourapp.com/callback&response_type=code&scope=avatars:read avatars:write"
Step 2: Exchange code for tokens
curl -X POST https://doppl.polsia.app/oauth/token \n  # Replace CODE with the authorization code from the redirect
  -H "Content-Type: application/json" \n  -d ‘{
    "grant_type": "authorization_code",
    "code": "AUTHORIZATION_CODE",
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET",
    "redirect_uri": "https://yourapp.com/callback"
  ‘}
Step 3: Use access token
curl -X POST https://doppl.polsia.app/api/v1/avatars \n  -H "Authorization: Bearer ACCESS_TOKEN"
  -H "Content-Type: application/json" \n  -d ‘{"photo": "https://example.com/photo.jpg", "style": "stylized"}‘

Token Refresh

Access tokens expire after 1 hour. Use the refresh token to obtain a new access token without re-authenticating the user.

Refresh access token
curl -X POST https://doppl.polsia.app/oauth/token \n  -H "Content-Type: application/json"
  -d '{
    "grant_type": "refresh_token",
    "refresh_token": "YOUR_REFRESH_TOKEN",
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET"
  }'

Webhooks

Receive a POST notification the moment an avatar generation completes — no polling, no waiting on long responses. Pair this with the OAuth guide above for a complete automation story.

Registering a Webhook

Pass a webhook_url on the POST /api/v1/avatars request body and Doppl will fire a single POST to that URL when the avatar finishes generating — successfully or otherwise.

Request
curl -X POST https://doppl.polsia.app/api/v1/avatars \
  -H "Content-Type: application/json" \
  -H "X-API-Key: dppl_YOUR_KEY_HERE" \
  -d '{
    "photo": "https://example.com/selfie.jpg",
    "style": "stylized",
    "webhook_url": "https://yourapp.com/webhooks/doppl"
  }'
Endpoint requirements. Your webhook URL must use https://. Doppl enforces a 10-second timeout and treats any 2xx response (within the timeout) as a successful delivery. Anything else will trigger a retry (see below).

Payload Format

The webhook body is a JSON document mirroring the shape of the POST /api/v1/avatars response, plus a couple of envelope fields:

Payload
{
  "event": "avatar.completed",
  "api_key_id": "key_abc123",
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "completed",
  "style": "stylized",
  "avatar_url": "https://oaidalleapiprodscus.blob.core.windows.net/...",
  "source_photo_url": "https://example.com/selfie.jpg",
  "format": "png",
  "size": "1024x1024",
  "processing_time_ms": 2340,
  "created_at": "2026-04-05T12:00:00.000Z",
  "completed_at": "2026-04-05T12:00:02.340Z"
}
FieldTypeDescription
event string Event type. Currently always avatar.completed
api_key_id string ID of the API key that triggered the generation
id string Avatar ID — same value across all retries; dedupe on this
status string completed on success, failed if generation errored
style string Style ID used for generation (e.g. stylized, anime)
avatar_url string URL of the generated avatar image. null when status is failed
source_photo_url string The original photo URL you submitted
format string Image format of the avatar (e.g. png)
size string Image dimensions (e.g. 1024x1024)
processing_time_ms number Time taken to generate, in milliseconds
created_at string ISO-8601 timestamp when the request was received
completed_at string ISO-8601 timestamp when generation finished

Signature Verification

Every webhook is delivered with an X-Doppl-Signature header of the form sha256=<hex>, where the hex digest is HMAC-SHA256 of the raw request body, keyed by your API key. Verify it before trusting the payload:

Node.js
const crypto = require("crypto");

function verifyDopplSignature(rawBody, signatureHeader, apiKey) {
  const expected = "sha256=" + crypto
    .createHmac("sha256", apiKey)
    .update(rawBody)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signatureHeader)
  );
}
Sign against the raw body. The signature is only valid if computed against the request body before JSON parsing — byte-for-byte. Most frameworks expose this via a rawBody/verify option on the body parser. If the signature header is missing, malformed, or fails to match, respond with 401 and drop the request.

Retry Behavior

If your handler does not return 2xx within the 10-second window, Doppl automatically retries the delivery. Receivers must be idempotent.

BehaviorValue
Max attempts 4 (1 initial + 3 retries)
Retried on 5xx response, 429 response, network timeout, connection error
Backoff 1s, 4s, 16s (between attempts)
Idempotency Same id across every attempt — dedupe on it

For client-side retry strategy against 429s when calling Doppl, see Rate Limits & Best Practices.

Minimal Handler

A complete webhook receiver that verifies the signature and echoes the parsed avatar back:

simulate-local
# Forward a sample Doppl payload to your local handler
curl -X POST http://localhost:3000/webhooks/doppl \
  -H "Content-Type: application/json" \
  -H "X-Doppl-Signature: sha256=<computed-hex>" \
  -d '{
    "event": "avatar.completed",
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "status": "completed",
    "avatar_url": "https://example.com/avatar.png"
  }'

Rate Limits

Rate limits are enforced per API key on a 60-second rolling window.

Key Type Limit Window
Demo 5 requests/minute 60 seconds
Standard 10 requests/minute 60 seconds

Response Headers

Every authenticated response includes rate limit headers:

Header Description
X-RateLimit-Limit Maximum requests allowed per window
X-RateLimit-Remaining Requests remaining in current window
X-RateLimit-Reset Unix timestamp when the window resets

When the limit is exceeded, the API returns 429 Too Many Requests with a retry_after_seconds field.

Rate Limits & Best Practices

The limits in the previous section are enforced per API key across all endpoints. The table below breaks down the effective limit per endpoint.

Per-Endpoint Limits

Endpoint Limit Window
POST /api/v1/keys 5 requests/minute 60 seconds
POST /api/v1/avatars 10 requests/minute 60 seconds
GET /api/v1/avatars/:id Shared with the per-key limit 60 seconds
GET /api/v1/avatars Shared with the per-key limit 60 seconds
GET /api/v1/styles Shared with the per-key limit 60 seconds
GET /api/v1/stats Shared with the per-key limit 60 seconds

Handling 429 Responses

When Doppl returns a 429 Too Many Requests, the response body includes a retry_after_seconds field. Standard practice: respect the server-supplied value before retrying, fall back to exponential backoff with jitter when the field is absent, and add a small random delay before sleeping so concurrent callers don't all retry in lockstep.

Condition Action
retry_after_seconds present Sleep for that long, then retry once
retry_after_seconds absent Exponential backoff with jitter (see snippet below)
Before any sleep Add random jitter of 0–500 ms to spread out colliding retries

Idempotency Keys

The POST /api/v1/avatars and POST /api/v1/keys endpoints accept an optional Idempotency-Key header. Submitting the same request body with the same key returns the cached response without re-running generation — safe to use when retrying after a network error or a 5xx response.

Request
curl -X POST https://doppl.polsia.app/api/v1/avatars \
  -H "Content-Type: application/json" \
  -H "X-API-Key: dppl_YOUR_KEY_HERE" \
  -H "Idempotency-Key: 8f4e9c2a-1b7d-4e3f-9a2c-6d5b8e1f0a3b" \
  -d '{"photo": "https://example.com/selfie.jpg", "style": "stylized"}'

Exponential Backoff (Node.js)

Reference helper for client-side retries against Doppl's POST endpoints:

Node.js
async function fetchWithRetry(url, opts = {}) {
  const maxAttempts = 5;
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    const res = await fetch(url, opts);
    if (res.status !== 429) {
      if (res.ok) return res;
      throw new Error("HTTP " + res.status);
    }
    if (attempt === maxAttempts - 1) return res;

    let delayMs;
    try {
      const body = await res.clone().json();
      if (typeof body.retry_after_seconds === "number") {
        delayMs = body.retry_after_seconds * 1000;
      }
    } catch (_) { /* fall through */ }
    if (delayMs == null) {
      delayMs = (2 ** attempt) * 1000;
    }
    const jitter = Math.floor(Math.random() * 500);
    await new Promise(r => setTimeout(r, delayMs + jitter));
  }
  throw new Error("unreachable");
}

POST /api/v1/keys

Generate a new demo API key. No authentication required.

Request Body

ParameterTypeRequiredDescription
name string optional Label for your API key (default: "Demo Key")
email string optional Email to associate with the key for account linking

Example

Request
curl -X POST https://doppl.polsia.app/api/v1/keys \
  -H "Content-Type: application/json" \
  -d '{"name": "My Game Studio", "email": "dev@studio.com"}'
201 Created
Response
{
  "api_key": "dppl_a1b2c3d4e5f6...",
  "name": "My Game Studio",
  "rate_limit": "5 requests/minute",
  "is_demo": true,
  "created_at": "2026-04-05T12:00:00.000Z",
  "usage": "Pass via X-API-Key header: curl -H \"X-API-Key: dppl_...\" ..."
}

POST /api/v1/avatars Requires API Key

Create an avatar from a photo. The API analyzes the photo using computer vision and generates a styled avatar image.

Request Body

ParameterTypeRequiredDescription
photo string required* Public URL to a photo of a person (HTTP or HTTPS). Alias: photo_url
style string optional Avatar style. Default: stylized. See Avatar Styles
webhook_url string optional URL to receive a webhook when generation completes

Example

Request
curl -X POST https://doppl.polsia.app/api/v1/avatars \
  -H "Content-Type: application/json" \
  -H "X-API-Key: dppl_YOUR_KEY_HERE" \
  -d '{
    "photo": "https://example.com/photo.jpg",
    "style": "anime"
  }'
201 Created
Response
{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "completed",
  "style": "anime",
  "avatar_url": "https://oaidalleapiprodscus.blob.core.windows.net/...",
  "source_photo_url": "https://example.com/photo.jpg",
  "format": "png",
  "size": "1024x1024",
  "processing_time_ms": 2340,
  "created_at": "2026-04-05T12:00:00.000Z",
  "_links": {
    "self": "/api/v1/avatars/a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "styles": "/api/v1/styles"
  }
}
Processing time. Avatar generation typically takes 2-8 seconds depending on the style. The response is returned synchronously once generation is complete.

GET /api/v1/avatars/:id Requires API Key

Retrieve a specific avatar by its ID. Only returns avatars created with your API key.

Path Parameters

ParameterTypeDescription
id string (UUID) The avatar's unique identifier returned from the create endpoint

Example

Request
curl https://doppl.polsia.app/api/v1/avatars/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
  -H "X-API-Key: dppl_YOUR_KEY_HERE"
200 OK
Response
{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "completed",
  "style": "stylized",
  "avatar_url": "https://...",
  "source_photo_url": "https://example.com/photo.jpg",
  "format": "png",
  "size": "1024x1024",
  "processing_time_ms": 2340,
  "created_at": "2026-04-05T12:00:00.000Z",
  "completed_at": "2026-04-05T12:00:02.340Z",
  "_links": {
    "self": "/api/v1/avatars/a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "styles": "/api/v1/styles"
  }
}

GET /api/v1/avatars Requires API Key

List all avatars created with your API key. Supports pagination and status filtering.

Query Parameters

ParameterTypeDefaultDescription
limit integer 20 Results per page (max: 100)
offset integer 0 Number of results to skip
status string Filter by status: processing, completed, or failed

Example

Request
curl "https://doppl.polsia.app/api/v1/avatars?limit=10&status=completed" \
  -H "X-API-Key: dppl_YOUR_KEY_HERE"
200 OK
Response
{
  "avatars": [
    {
      "id": "a1b2c3d4-...",
      "status": "completed",
      "style": "stylized",
      "avatar_url": "https://...",
      "format": "png",
      "processing_time_ms": 2340,
      "created_at": "2026-04-05T12:00:00.000Z",
      "completed_at": "2026-04-05T12:00:02.340Z"
    }
  ],
  "total": 25,
  "limit": 10,
  "offset": 0,
  "has_more": true
}

GET /api/v1/styles

List all available avatar styles with their descriptions. No authentication required.

Example

Request
curl https://doppl.polsia.app/api/v1/styles
200 OK
Response
{
  "styles": [
    { "id": "stylized", "name": "Stylized", "description": "..." },
    { "id": "realistic", "name": "Realistic", "description": "..." },
    { "id": "anime", "name": "Anime", "description": "..." },
    { "id": "pixel", "name": "Pixel", "description": "..." },
    { "id": "clay", "name": "Clay", "description": "..." },
    { "id": "minimal", "name": "Minimal", "description": "..." }
  ]
}

GET /api/v1/stats Requires API Key

Get usage statistics for your API key, including avatar counts, processing times, and request totals.

Example

Request
curl https://doppl.polsia.app/api/v1/stats \
  -H "X-API-Key: dppl_YOUR_KEY_HERE"
200 OK
Response
{
  "total_avatars": 25,
  "completed": 22,
  "failed": 2,
  "processing": 1,
  "avg_processing_time_ms": 2340,
  "first_avatar_at": "2026-04-01T10:00:00.000Z",
  "last_avatar_at": "2026-04-05T12:00:00.000Z",
  "api_key": {
    "name": "My Game Studio",
    "total_requests": 450,
    "rate_limit": "10/minute",
    "created_at": "2026-03-15T08:00:00.000Z"
  }
}

Avatar Styles

Doppl supports 6 distinct avatar styles. Pass the style id when creating an avatar.

Stylized
stylized
Modern game art style. Clean, slightly exaggerated proportions with smooth shading. Vibrant but not cartoonish. Think Fortnite meets Pixar.
Realistic
realistic
Photorealistic 3D-rendered portrait. Highly detailed skin texture, accurate lighting, subtle subsurface scattering. Professional headshot quality.
Anime
anime
Large expressive eyes, clean line art, cel-shaded coloring. Japanese animation aesthetic with a modern polish.
Pixel Art
pixel
64x64 pixel art scaled up cleanly. Retro game aesthetic with careful dithering and a limited color palette. Charming and recognizable.
Clay
clay
Claymation-style 3D portrait. Soft, rounded features with visible material texture. Warm lighting, slightly whimsical. Think Aardman animations.
Minimal
minimal
Flat-design portrait. Simple geometric shapes, limited color palette (max 5 colors), clean edges. Modern tech company avatar style.

Error Codes

All errors follow a consistent format with an error code and human-readable message.

Error Response Format
{
  "error": "error_code",
  "message": "Human-readable description"
}
StatusError CodeDescription
400 validation_error Invalid request body — missing required fields, invalid URL, or unsupported style
401 authentication_required No API key provided in the request headers
401 invalid_api_key The API key is invalid or has been deactivated
404 not_found The requested avatar or endpoint does not exist
429 rate_limit_exceeded Too many requests. Includes retry_after_seconds field — see Rate Limits & Best Practices for retry strategy
500 generation_failed Avatar generation failed — photo may be inaccessible or not contain a person
500 internal_error Unexpected server error. Retry the request

API Playground

Send a live avatar-generation request directly from this page.

1. API Key

Paste an existing key, or click Get demo key to generate one now.

2. Request

or
curl preview
# Fill in an API key and photo URL to see the curl command