{
  "info": {
    "name": "Doppl API",
    "_postman_id": "d0ppl-api-collection-v1",
    "description": "Generate AI-powered avatars from photos with the Doppl API. Import this collection, set your api_key in the environment, and walk through authentication, avatar generation, webhook delivery, and status polling in one click. Documentation: https://doppl.polsia.app/docs",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "item": [
    {
      "name": "1. Authentication",
      "description": "Get a demo API key (no signup required) or use OAuth 2.0 for production integrations. All other endpoints in this collection read the `api_key` variable from the imported environment.",
      "item": [
        {
          "name": "Generate Demo API Key",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const data = pm.response.json();",
                  "if (data.api_key) {",
                  "  pm.environment.set('api_key', data.api_key);",
                  "  pm.environment.set('key_name', data.name);",
                  "  console.log('Saved demo key:', data.api_key);",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"{{key_name}}\",\n  \"email\": \"{{user_email}}\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/keys",
              "host": ["{{base_url}}"],
              "path": ["keys"]
            },
            "description": "Mint a free demo API key (5 requests/minute). The test script auto-saves the key to the `api_key` environment variable so the rest of the collection picks it up automatically. No authentication required."
          },
          "response": []
        },
        {
          "name": "OAuth - Authorize",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{auth_url}}/authorize?client_id={{oauth_client_id}}&redirect_uri={{oauth_redirect_uri}}&response_type=code&scope={{oauth_scope}}",
              "host": ["{{auth_url}}"],
              "path": ["authorize"],
              "query": [
                { "key": "client_id", "value": "{{oauth_client_id}}" },
                { "key": "redirect_uri", "value": "{{oauth_redirect_uri}}" },
                { "key": "response_type", "value": "code" },
                { "key": "scope", "value": "{{oauth_scope}}" }
              ]
            },
            "description": "OAuth 2.0 authorization-code flow. Open this URL in a browser to grant access; Doppl redirects to `redirect_uri` with a `?code=...` parameter you exchange in the next call. Use this for server-to-server integrations where embedding an API key in client code is undesirable."
          },
          "response": []
        },
        {
          "name": "OAuth - Exchange Code for Token",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const data = pm.response.json();",
                  "if (data.access_token) {",
                  "  pm.environment.set('oauth_access_token', data.access_token);",
                  "  console.log('Saved OAuth access token.');",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"grant_type\": \"authorization_code\",\n  \"code\": \"{{oauth_auth_code}}\",\n  \"client_id\": \"{{oauth_client_id}}\",\n  \"client_secret\": \"{{oauth_client_secret}}\",\n  \"redirect_uri\": \"{{oauth_redirect_uri}}\"\n}"
            },
            "url": {
              "raw": "{{auth_url}}/token",
              "host": ["{{auth_url}}"],
              "path": ["token"]
            },
            "description": "Exchange the authorization code returned by the /authorize redirect for an OAuth access token. The test script auto-saves the token to the `oauth_access_token` environment variable."
          },
          "response": []
        },
        {
          "name": "OAuth - Refresh Token",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"grant_type\": \"refresh_token\",\n  \"refresh_token\": \"{{oauth_refresh_token}}\",\n  \"client_id\": \"{{oauth_client_id}}\",\n  \"client_secret\": \"{{oauth_client_secret}}\"\n}"
            },
            "url": {
              "raw": "{{auth_url}}/token",
              "host": ["{{auth_url}}"],
              "path": ["token"]
            },
            "description": "Refresh an expired OAuth access token. Requires the `refresh_token` returned alongside the original access token."
          },
          "response": []
        }
      ]
    },
    {
      "name": "2. Avatar Generation",
      "description": "Generate an avatar from a photo URL. Submit a photo_url plus a style and receive a new avatar ID you can poll (or wait for via webhook).",
      "item": [
        {
          "name": "List Available Styles",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/styles",
              "host": ["{{base_url}}"],
              "path": ["styles"]
            },
            "description": "Returns the six supported styles (`stylized`, `realistic`, `anime`, `pixel`, `clay`, `minimal`) with brief descriptions. Unauthenticated."
          },
          "response": []
        },
        {
          "name": "Create Avatar (Synchronous)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const data = pm.response.json();",
                  "if (data.id) {",
                  "  pm.environment.set('last_avatar_id', data.id);",
                  "  console.log('Saved avatar id:', data.id);",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-API-Key", "value": "{{api_key}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"photo_url\": \"{{photo_url}}\",\n  \"style\": \"{{avatar_style}}\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/avatars",
              "host": ["{{base_url}}"],
              "path": ["avatars"]
            },
            "description": "Submit a photo URL and pick a style. The API returns within ~15-30 seconds with `status: \"completed\"` and the final `avatar_url`. The test script saves the new avatar id to `last_avatar_id` for the polling step. Pass `photo_url` OR `photo` (third-party URL); must be HTTPS."
          },
          "response": []
        },
        {
          "name": "Create Avatar with Webhook",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const data = pm.response.json();",
                  "if (data.id) {",
                  "  pm.environment.set('last_avatar_id', data.id);",
                  "  console.log('Saved avatar id:', data.id);",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-API-Key", "value": "{{api_key}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"photo_url\": \"{{photo_url}}\",\n  \"style\": \"{{avatar_style}}\",\n  \"webhook_url\": \"{{webhook_url}}\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/avatars",
              "host": ["{{base_url}}"],
              "path": ["avatars"]
            },
            "description": "Same as the synchronous create, plus a `webhook_url`. Doppl POSTs the finished avatar (or failure) to that URL with `X-Doppl-Signature: sha256=<hmac>` so your handler can verify. No need to poll this one — see section 3."
          },
          "response": []
        },
        {
          "name": "API Root",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}",
              "host": ["{{base_url}}"],
              "path": [""]
            },
            "description": "Hit the API root for a live list of available endpoints and the docs URL. Unauthenticated."
          },
          "response": []
        }
      ]
    },
    {
      "name": "3. Status Polling",
      "description": "Once you've kicked off an avatar, poll until the status flips to `completed` (or `failed`). Demo keys are rate-limited to 5/min — keep the delay reasonable.",
      "item": [
        {
          "name": "Get Avatar by ID",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const data = pm.response.json();",
                  "console.log('Avatar status:', data.status);",
                  "if (data.status === 'completed' && data.avatar_url && !pm.environment.get('completed_avatar_url')) {",
                  "  pm.environment.set('completed_avatar_url', data.avatar_url);",
                  "  console.log('Saved completed_avatar_url.');",
                  "}",
                  "if (data.status === 'completed' || data.status === 'failed') {",
                  "  pm.test.stop();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [
              { "key": "X-API-Key", "value": "{{api_key}}" }
            ],
            "url": {
              "raw": "{{base_url}}/avatars/{{last_avatar_id}}",
              "host": ["{{base_url}}"],
              "path": ["avatars", "{{last_avatar_id}}"]
            },
            "description": "Look up a single avatar by ID. Use the `last_avatar_id` saved by the Create Avatar requests. The test script prints the current status — re-run until it shows `completed` or `failed`."
          },
          "response": []
        },
        {
          "name": "List My Avatars",
          "request": {
            "method": "GET",
            "header": [
              { "key": "X-API-Key", "value": "{{api_key}}" }
            ],
            "url": {
              "raw": "{{base_url}}/avatars?limit=20&offset=0&status=completed",
              "host": ["{{base_url}}"],
              "path": ["avatars"],
              "query": [
                { "key": "limit", "value": "20" },
                { "key": "offset", "value": "0" },
                { "key": "status", "value": "completed", "description": "Filter by status: processing | completed | failed. Omit for all." }
              ]
            },
            "description": "List recent avatars owned by your API key. Supports `limit` (max 100), `offset`, and an optional `status` filter (`processing`, `completed`, `failed`)."
          },
          "response": []
        },
        {
          "name": "API Stats",
          "request": {
            "method": "GET",
            "header": [
              { "key": "X-API-Key", "value": "{{api_key}}" }
            ],
            "url": {
              "raw": "{{base_url}}/stats",
              "host": ["{{base_url}}"],
              "path": ["stats"]
            },
            "description": "Aggregated usage stats for the current API key: total avatars, completed/failed/processing counts, average processing time, and key metadata."
          },
          "response": []
        }
      ]
    }
  ],
  "variable": [
    {
      "key": "base_url",
      "value": "https://doppl.polsia.app/api/v1",
      "type": "string"
    },
    {
      "key": "auth_url",
      "value": "https://doppl.polsia.app/oauth",
      "type": "string"
    }
  ]
}
