{"openapi":"3.1.0","info":{"title":"Reslide API","description":"\nConvert PDF pages or slide images into **fully editable PowerPoint (.pptx)**\nfiles — real text boxes, vector shapes and cleanly separated pictures, not a\nscreenshot pasted on a slide.\n\n**Getting started**\n\n1. Create an API key in the [dashboard](https://pptx-web-pptxapi.up.railway.app/keys).\n2. `POST /v1/uploads` your PDF (or host images/PDF yourself).\n3. `POST /v1/tasks` to start a conversion, then poll `GET /v1/tasks/{task_id}`\n   or receive a signed webhook via `callback_url`.\n4. Download `result.pptx_url` when `status` is `succeeded`.\n\n**Billing** — 1 credit = 1 page. Failed or canceled tasks have zero net\nbillable task credits. Their reservations are reconciled against the original\npaid and bonus pools subject to those pools' lifecycle rules. Web workspace and\nAPI share the same account balance.\n\n**Errors** — every response uses the `{code, message, data}` envelope: `code`\nis `0` on success, otherwise it mirrors the HTTP status (`401` bad key, `402`\nout of credits, `429` rate limited with `Retry-After`, `5xx` retry with\nbackoff). `message` is human-readable and not a stable contract.\n\n**Code samples** — every endpoint ships ready-to-run cURL / Python / Node.js\nexamples; the client picker on each snippet can also generate code for 20+\nother languages and HTTP clients.\n\n**Webhooks** — the signed `task.finished` callback (payload schema and HMAC\nverification snippets) is documented in the *Webhooks* section below.\n","version":"1.0.0","x-scalar-sdk-installation":[{"lang":"Shell","description":"No install needed — `curl` ships with macOS, Linux and Windows 10+.\nGrab an API key from the [dashboard](https://pptx-web-pptxapi.up.railway.app/keys):\n\n```sh\nexport TOSEA_API_KEY=\"tpx_live_...\"\n\ncurl https://pptx-api.tosea.ai/v1/credits \\\n  -H \"Authorization: Bearer $TOSEA_API_KEY\"\n```"},{"lang":"Python","description":"Works with plain [`requests`](https://requests.readthedocs.io) —\nno SDK required:\n\n```sh\npip install requests\n```\n\n```python\nimport requests\n\nr = requests.get(\n    \"https://pptx-api.tosea.ai/v1/credits\",\n    headers={\"Authorization\": \"Bearer tpx_live_...\"},\n)\nprint(r.json())  # {\"code\": 0, \"message\": \"ok\", \"data\": {...}}\n```"},{"lang":"Node.js","description":"Zero dependencies — Node.js 18+ ships `fetch` and `FormData`\nbuilt in:\n\n```javascript\nconst res = await fetch(\"https://pptx-api.tosea.ai/v1/credits\", {\n  headers: { Authorization: \"Bearer tpx_live_...\" },\n});\nconsole.log(await res.json()); // { code: 0, message: \"ok\", data: {...} }\n```"}]},"paths":{"/v1/uploads":{"post":{"tags":["PPTX Conversion"],"summary":"Upload a PDF or page image","description":"Upload a local/private PDF **or a single page image** (PNG/JPG/WebP).\nPass the returned `upload_id` (PDF) or a list of `upload_ids` (images) to\n`POST /v1/tasks`. The upload record and API access expire after 24 hours;\nthis does not promise physical deletion of the underlying storage object at\nthat exact time. Max 100MB.\n\nImage uploads additionally return a signed `preview_url` (24h) you can\nrender immediately; PDFs don't.","operationId":"create_upload_v1_uploads_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_create_upload_v1_uploads_post"}}},"required":true},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"integer","description":"0 on success; otherwise mirrors the HTTP status"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/Upload"}}},"example":{"code":0,"message":"ok","data":{"upload_id":"9b2f6c1e-8d43-4a5b-9f0e-2f1c3d4e5a6b","filename":"slides.pdf","size":2148332,"content_type":"application/pdf","page_count":12,"expires_at":"2026-07-15T09:28:00+00:00"}}}}},"400":{"description":"Not a valid PDF / PNG / JPG / WebP, or over the size limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"},"example":{"code":400,"message":"File too large (max 100MB)","data":null}}}},"401":{"description":"Missing, malformed or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"},"example":{"code":401,"message":"Invalid or revoked API key","data":null}}}},"429":{"description":"Over the per-key rate limit (10 req/s, burst 30) or the plan's concurrent-task cap — retry after `Retry-After` seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"},"example":{"code":429,"message":"Rate limited","data":null}}}}},"x-codeSamples":[{"lang":"shell","label":"cURL","source":"curl -X POST \"https://pptx-api.tosea.ai/v1/uploads\" \\\n  -H \"Authorization: Bearer tpx_live_YOUR_KEY\" \\\n  -F \"file=@slides.pdf\""},{"lang":"python","label":"Python","source":"import requests\n\nAPI = \"https://pptx-api.tosea.ai\"\nHEADERS = {\"Authorization\": \"Bearer tpx_live_YOUR_KEY\"}\n\nwith open(\"slides.pdf\", \"rb\") as f:\n    r = requests.post(f\"{API}/v1/uploads\", headers=HEADERS,\n                      files={\"file\": (\"slides.pdf\", f, \"application/pdf\")})\nbody = r.json()\nassert body[\"code\"] == 0, body[\"message\"]\nupload = body[\"data\"]\nprint(upload[\"upload_id\"], upload[\"page_count\"])"},{"lang":"javascript","label":"Node.js","source":"import { readFile } from \"node:fs/promises\";\n\nconst API = \"https://pptx-api.tosea.ai\";\nconst headers = { Authorization: \"Bearer tpx_live_YOUR_KEY\" };\n\nconst form = new FormData();\nform.append(\"file\", new Blob([await readFile(\"slides.pdf\")],\n  { type: \"application/pdf\" }), \"slides.pdf\");\n\nconst res = await fetch(`${API}/v1/uploads`, { method: \"POST\", headers, body: form });\nconst { code, message, data } = await res.json();\nif (code !== 0) throw new Error(message);\nconsole.log(data.upload_id, data.page_count);"}]}},"/v1/tasks":{"post":{"tags":["PPTX Conversion"],"summary":"Create a conversion task","description":"Convert a PDF or a list of page images into an **editable PowerPoint**.\n\nProvide exactly **one** input: `upload_id` (PDF via /v1/uploads),\n`upload_ids` (images via /v1/uploads), `pdf_url`, or `image_urls`.\n\n1 credit = 1 page, reserved at creation. A failed or canceled task has\n**zero net billable task credits**; its reservation is reconciled against\nthe original paid and bonus pools subject to their lifecycle rules.\nTrack progress by polling `GET /v1/tasks/{task_id}` or via `callback_url`\n(see the *Webhooks* section for the payload and signature).","operationId":"create_task_v1_tasks_post","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Any unique string (e.g. your order ID). Retrying with the same key returns the already-created task instead of charging again.","title":"Idempotency-Key"},"description":"Any unique string (e.g. your order ID). Retrying with the same key returns the already-created task instead of charging again."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTaskBody"}}}},"responses":{"200":{"description":"Task created — credits reserved (or the existing task replayed when the same `Idempotency-Key` is retried)","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"integer","description":"0 on success; otherwise mirrors the HTTP status"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/Task"}}},"example":{"code":0,"message":"ok","data":{"task_id":"7c9e6679-7425-40de-944b-e07fc1f90ae7","operation":"pdf_to_pptx","status":"pending","progress":0,"title":"Q3 Strategy Deck","page_count":12,"credits_charged":12,"created_at":"2026-07-14T09:30:12+00:00","finished_at":null,"error":null}}}}},"400":{"description":"Invalid input combination, unreachable/private URL, page out of range, or over the 100-page limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"},"example":{"code":400,"message":"Provide exactly one of: upload_id, upload_ids, pdf_url, image_urls","data":null}}}},"401":{"description":"Missing, malformed or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"},"example":{"code":401,"message":"Invalid or revoked API key","data":null}}}},"402":{"description":"Not enough credits for the page count — reserved nothing, safe to retry after topping up","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"},"example":{"code":402,"message":"Insufficient credits: need 12 pages. Top up at https://pptx-web-pptxapi.up.railway.app/billing","data":null}}}},"429":{"description":"Over the per-key rate limit (10 req/s, burst 30) or the plan's concurrent-task cap — retry after `Retry-After` seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"},"example":{"code":429,"message":"Rate limited","data":null}}}}},"x-codeSamples":[{"lang":"shell","label":"cURL (PDF)","source":"curl -X POST \"https://pptx-api.tosea.ai/v1/tasks\" \\\n  -H \"Authorization: Bearer tpx_live_YOUR_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Idempotency-Key: order-2026-0714-001\" \\\n  -d '{\n    \"operation\": \"pdf_to_pptx\",\n    \"input\": {\n      \"upload_id\": \"9b2f6c1e-8d43-4a5b-9f0e-2f1c3d4e5a6b\",\n      \"pages\": [1, 2, 3],\n      \"title\": \"Q3 Strategy Deck\"\n    },\n    \"callback_url\": \"https://example.com/webhooks/tosea\"\n  }'"},{"lang":"shell","label":"cURL (images)","source":"curl -X POST \"https://pptx-api.tosea.ai/v1/tasks\" \\\n  -H \"Authorization: Bearer tpx_live_YOUR_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"operation\": \"images_to_pptx\",\n    \"input\": {\n      \"image_urls\": [\n        \"https://example.com/slides/page-1.png\",\n        \"https://example.com/slides/page-2.png\"\n      ],\n      \"title\": \"Pitch Deck\"\n    }\n  }'"},{"lang":"python","label":"Python","source":"import requests\n\nAPI = \"https://pptx-api.tosea.ai\"\nHEADERS = {\"Authorization\": \"Bearer tpx_live_YOUR_KEY\"}\n\nr = requests.post(\n    f\"{API}/v1/tasks\",\n    headers={**HEADERS, \"Idempotency-Key\": \"order-2026-0714-001\"},\n    json={\n        \"operation\": \"pdf_to_pptx\",\n        \"input\": {\n            \"upload_id\": \"9b2f6c1e-8d43-4a5b-9f0e-2f1c3d4e5a6b\",\n            \"pages\": [1, 2, 3],          # optional: convert a subset\n            \"title\": \"Q3 Strategy Deck\",\n        },\n        \"callback_url\": \"https://example.com/webhooks/tosea\",\n    },\n)\nbody = r.json()\nassert body[\"code\"] == 0, body[\"message\"]\ntask = body[\"data\"]\nprint(task[\"task_id\"], task[\"status\"], task[\"credits_charged\"])"},{"lang":"javascript","label":"Node.js","source":"const API = \"https://pptx-api.tosea.ai\";\n\nconst res = await fetch(`${API}/v1/tasks`, {\n  method: \"POST\",\n  headers: {\n    Authorization: \"Bearer tpx_live_YOUR_KEY\",\n    \"Content-Type\": \"application/json\",\n    \"Idempotency-Key\": \"order-2026-0714-001\",\n  },\n  body: JSON.stringify({\n    operation: \"pdf_to_pptx\",\n    input: {\n      upload_id: \"9b2f6c1e-8d43-4a5b-9f0e-2f1c3d4e5a6b\",\n      pages: [1, 2, 3], // optional: convert a subset\n      title: \"Q3 Strategy Deck\",\n    },\n    callback_url: \"https://example.com/webhooks/tosea\",\n  }),\n});\nconst { code, message, data: task } = await res.json();\nif (code !== 0) throw new Error(message);\nconsole.log(task.task_id, task.status, task.credits_charged);"}]},"get":{"tags":["PPTX Conversion"],"summary":"List tasks","description":"Newest first. Cursor pagination: pass the last item's `task_id` as\n`after` to fetch the next page; stop when `has_more` is `false`.\nList items omit `result` — fetch a single task for the download URL.","operationId":"list_tasks_v1_tasks_get","parameters":[{"name":"operation","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter: `pdf_to_pptx` or `images_to_pptx`","title":"Operation"},"description":"Filter: `pdf_to_pptx` or `images_to_pptx`"},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter, comma-separated: `pending,processing,succeeded,failed,canceled`","title":"Status"},"description":"Filter, comma-separated: `pending,processing,succeeded,failed,canceled`"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","description":"Page size, 1–100","default":20,"title":"Limit"},"description":"Page size, 1–100"},{"name":"after","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Cursor: the last item's `task_id` from the previous page","title":"After"},"description":"Cursor: the last item's `task_id` from the previous page"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"integer","description":"0 on success; otherwise mirrors the HTTP status"},"message":{"type":"string"},"data":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Task"},"description":"Newest first; `result` is omitted here — fetch a single task for the download URL"},"has_more":{"type":"boolean"}}}}},"example":{"code":0,"message":"ok","data":{"items":[{"task_id":"7c9e6679-7425-40de-944b-e07fc1f90ae7","operation":"pdf_to_pptx","status":"pending","progress":0,"title":"Q3 Strategy Deck","page_count":12,"credits_charged":12,"created_at":"2026-07-14T09:30:12+00:00","finished_at":null,"error":null}],"has_more":false}}}}},"401":{"description":"Missing, malformed or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"},"example":{"code":401,"message":"Invalid or revoked API key","data":null}}}},"429":{"description":"Over the per-key rate limit (10 req/s, burst 30) or the plan's concurrent-task cap — retry after `Retry-After` seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"},"example":{"code":429,"message":"Rate limited","data":null}}}}},"x-codeSamples":[{"lang":"shell","label":"cURL","source":"curl \"https://pptx-api.tosea.ai/v1/tasks?status=processing,succeeded&limit=20\" \\\n  -H \"Authorization: Bearer tpx_live_YOUR_KEY\"\n# next page: append &after=<task_id of the last item>"},{"lang":"python","label":"Python","source":"import requests\n\nAPI = \"https://pptx-api.tosea.ai\"\nHEADERS = {\"Authorization\": \"Bearer tpx_live_YOUR_KEY\"}\n\nparams = {\"limit\": 50}\nwhile True:\n    page = requests.get(f\"{API}/v1/tasks\", headers=HEADERS, params=params).json()[\"data\"]\n    for task in page[\"items\"]:\n        print(task[\"task_id\"], task[\"status\"], task[\"title\"])\n    if not page[\"has_more\"]:\n        break\n    params[\"after\"] = page[\"items\"][-1][\"task_id\"]  # cursor pagination"},{"lang":"javascript","label":"Node.js","source":"const API = \"https://pptx-api.tosea.ai\";\nconst headers = { Authorization: \"Bearer tpx_live_YOUR_KEY\" };\n\nlet after;\ndo {\n  const qs = new URLSearchParams({ limit: \"50\", ...(after && { after }) });\n  const page = (await (await fetch(`${API}/v1/tasks?${qs}`, { headers })).json()).data;\n  for (const task of page.items) console.log(task.task_id, task.status, task.title);\n  after = page.has_more ? page.items.at(-1).task_id : undefined;\n} while (after);"}]}},"/v1/tasks/{task_id}":{"get":{"tags":["PPTX Conversion"],"summary":"Get task status & result","description":"Poll every few seconds until `status` reaches a terminal state\n(`succeeded`, `failed` or `canceled`). When `status` is `succeeded`,\n`data.result.pptx_url` is a signed download URL valid for 24h — download\npromptly and store on your side.","operationId":"get_task_v1_tasks__task_id__get","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","description":"Task ID returned by `POST /v1/tasks`","title":"Task Id"},"description":"Task ID returned by `POST /v1/tasks`"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"integer","description":"0 on success; otherwise mirrors the HTTP status"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/Task"}}},"example":{"code":0,"message":"ok","data":{"task_id":"7c9e6679-7425-40de-944b-e07fc1f90ae7","operation":"pdf_to_pptx","status":"succeeded","progress":100,"title":"Q3 Strategy Deck","page_count":12,"credits_charged":12,"created_at":"2026-07-14T09:30:12+00:00","finished_at":"2026-07-14T09:31:40+00:00","error":null,"result":{"pptx_url":"https://xyz.supabase.co/storage/v1/object/sign/results/7c9e6679/deck.pptx?token=eyJhbGciOi..."}}}}}},"401":{"description":"Missing, malformed or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"},"example":{"code":401,"message":"Invalid or revoked API key","data":null}}}},"404":{"description":"Unknown (or deleted) `task_id`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"},"example":{"code":404,"message":"Task not found","data":null}}}},"429":{"description":"Over the per-key rate limit (10 req/s, burst 30) or the plan's concurrent-task cap — retry after `Retry-After` seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"},"example":{"code":429,"message":"Rate limited","data":null}}}}},"x-codeSamples":[{"lang":"shell","label":"cURL","source":"curl \"https://pptx-api.tosea.ai/v1/tasks/7c9e6679-7425-40de-944b-e07fc1f90ae7\" \\\n  -H \"Authorization: Bearer tpx_live_YOUR_KEY\""},{"lang":"python","label":"Python (poll + download)","source":"import time\n\nimport requests\n\nAPI = \"https://pptx-api.tosea.ai\"\nHEADERS = {\"Authorization\": \"Bearer tpx_live_YOUR_KEY\"}\ntask_id = \"7c9e6679-7425-40de-944b-e07fc1f90ae7\"\n\nwhile True:\n    task = requests.get(f\"{API}/v1/tasks/{task_id}\", headers=HEADERS).json()[\"data\"]\n    if task[\"status\"] in (\"succeeded\", \"failed\", \"canceled\"):\n        break\n    time.sleep(3)\n\nif task[\"status\"] == \"succeeded\":\n    pptx = requests.get(task[\"result\"][\"pptx_url\"]).content  # signed URL, 24h\n    with open(\"deck.pptx\", \"wb\") as f:\n        f.write(pptx)\nelse:\n    print(\"Conversion did not succeed:\", task[\"error\"])"},{"lang":"javascript","label":"Node.js (poll + download)","source":"import { writeFile } from \"node:fs/promises\";\n\nconst API = \"https://pptx-api.tosea.ai\";\nconst headers = { Authorization: \"Bearer tpx_live_YOUR_KEY\" };\nconst taskId = \"7c9e6679-7425-40de-944b-e07fc1f90ae7\";\n\nlet task;\nfor (;;) {\n  task = (await (await fetch(`${API}/v1/tasks/${taskId}`, { headers })).json()).data;\n  if ([\"succeeded\", \"failed\", \"canceled\"].includes(task.status)) break;\n  await new Promise((r) => setTimeout(r, 3000));\n}\n\nif (task.status === \"succeeded\") {\n  const pptx = await (await fetch(task.result.pptx_url)).arrayBuffer(); // 24h URL\n  await writeFile(\"deck.pptx\", Buffer.from(pptx));\n} else {\n  console.error(\"Conversion did not succeed:\", task.error);\n}"}]}},"/v1/tasks/{task_id}/cancel":{"post":{"tags":["PPTX Conversion"],"summary":"Cancel a pending task","description":"Cancels a `pending` or `processing` task and refunds the reserved\ncredits. Tasks already in a terminal state cannot be canceled.","operationId":"cancel_task_v1_tasks__task_id__cancel_post","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","description":"Task ID returned by `POST /v1/tasks`","title":"Task Id"},"description":"Task ID returned by `POST /v1/tasks`"}],"responses":{"200":{"description":"Task canceled — zero net billable task credits; reservation reconciled against its original pools","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"integer","description":"0 on success; otherwise mirrors the HTTP status"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/Task"}}},"example":{"code":0,"message":"ok","data":{"task_id":"7c9e6679-7425-40de-944b-e07fc1f90ae7","operation":"pdf_to_pptx","status":"canceled","progress":0,"title":"Q3 Strategy Deck","page_count":12,"credits_charged":0,"created_at":"2026-07-14T09:30:12+00:00","finished_at":"2026-07-14T09:30:45+00:00","error":null}}}}},"400":{"description":"Task already reached a terminal state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"},"example":{"code":400,"message":"Task is succeeded, only pending or processing tasks can be canceled","data":null}}}},"401":{"description":"Missing, malformed or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"},"example":{"code":401,"message":"Invalid or revoked API key","data":null}}}},"404":{"description":"Unknown (or deleted) `task_id`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"},"example":{"code":404,"message":"Task not found","data":null}}}},"429":{"description":"Over the per-key rate limit (10 req/s, burst 30) or the plan's concurrent-task cap — retry after `Retry-After` seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"},"example":{"code":429,"message":"Rate limited","data":null}}}}},"x-codeSamples":[{"lang":"shell","label":"cURL","source":"curl -X POST \"https://pptx-api.tosea.ai/v1/tasks/7c9e6679-7425-40de-944b-e07fc1f90ae7/cancel\" \\\n  -H \"Authorization: Bearer tpx_live_YOUR_KEY\""},{"lang":"python","label":"Python","source":"import requests\n\nAPI = \"https://pptx-api.tosea.ai\"\nHEADERS = {\"Authorization\": \"Bearer tpx_live_YOUR_KEY\"}\ntask_id = \"7c9e6679-7425-40de-944b-e07fc1f90ae7\"\n\nbody = requests.post(f\"{API}/v1/tasks/{task_id}/cancel\", headers=HEADERS).json()\nassert body[\"code\"] == 0, body[\"message\"]\nprint(body[\"data\"][\"status\"])  # canceled — zero net billable task credits"},{"lang":"javascript","label":"Node.js","source":"const API = \"https://pptx-api.tosea.ai\";\nconst headers = { Authorization: \"Bearer tpx_live_YOUR_KEY\" };\nconst taskId = \"7c9e6679-7425-40de-944b-e07fc1f90ae7\";\n\nconst res = await fetch(`${API}/v1/tasks/${taskId}/cancel`, { method: \"POST\", headers });\nconst { code, message, data: task } = await res.json();\nif (code !== 0) throw new Error(message);\nconsole.log(task.status); // canceled — zero net billable task credits"}]}},"/v1/credits":{"get":{"tags":["PPTX Conversion"],"summary":"Check credit balance","description":"1 credit = 1 page. The balance is shared between the web workspace and\nthe API.\n\n`balance` is the combined spendable total: `paid_balance` (subscription\ncredits, valid within the current billing period) plus `bonus_balance`\n(reward credits from referrals or redemption codes — these never expire).","operationId":"get_credits_v1_credits_get","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"integer","description":"0 on success; otherwise mirrors the HTTP status"},"message":{"type":"string"},"data":{"type":"object","properties":{"balance":{"type":"integer","description":"Credits left (1 = 1 page)"},"paid_balance":{"type":"integer","description":"Spendable credits in the base plan pool. For a paid plan this is 0 while the subscription is inactive or its current credit window has expired; disabled accounts report 0."},"bonus_balance":{"type":"integer","description":"Spendable non-expiring reward credits; disabled accounts report 0"},"plan":{"type":"string","enum":["free","starter","pro","ultra"]}}}}},"example":{"code":0,"message":"ok","data":{"balance":286,"paid_balance":280,"bonus_balance":6,"plan":"starter"}}}}},"401":{"description":"Missing, malformed or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"},"example":{"code":401,"message":"Invalid or revoked API key","data":null}}}},"429":{"description":"Over the per-key rate limit (10 req/s, burst 30) or the plan's concurrent-task cap — retry after `Retry-After` seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"},"example":{"code":429,"message":"Rate limited","data":null}}}}},"x-codeSamples":[{"lang":"shell","label":"cURL","source":"curl \"https://pptx-api.tosea.ai/v1/credits\" \\\n  -H \"Authorization: Bearer tpx_live_YOUR_KEY\""},{"lang":"python","label":"Python","source":"import requests\n\nAPI = \"https://pptx-api.tosea.ai\"\nHEADERS = {\"Authorization\": \"Bearer tpx_live_YOUR_KEY\"}\n\ndata = requests.get(f\"{API}/v1/credits\", headers=HEADERS).json()[\"data\"]\nprint(data[\"balance\"], data[\"plan\"])"},{"lang":"javascript","label":"Node.js","source":"const API = \"https://pptx-api.tosea.ai\";\nconst headers = { Authorization: \"Bearer tpx_live_YOUR_KEY\" };\n\nconst { data } = await (await fetch(`${API}/v1/credits`, { headers })).json();\nconsole.log(data.balance, data.plan);"}]}},"/v1/usage":{"get":{"tags":["PPTX Conversion"],"summary":"Credit usage history","description":"Full credit ledger, newest first: charges (`task_charge`), automatic\nrefunds (`task_refund`), grants (`free_grant`, `subscription_grant`), …\n\n`pool` tells which balance the entry moved: `paid` (subscription credits)\nor `bonus` (reward credits). A charge that spans both pools produces two\nentries for the same task; `balance_after` is per-pool.","operationId":"get_usage_v1_usage_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","description":"1-based page number","default":1,"title":"Page"},"description":"1-based page number"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","description":"Items per page, 1–100","default":50,"title":"Page Size"},"description":"Items per page, 1–100"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"integer","description":"0 on success; otherwise mirrors the HTTP status"},"message":{"type":"string"},"data":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/UsageEntry"}},"total":{"type":"integer"},"page":{"type":"integer"},"page_size":{"type":"integer"}}}}},"example":{"code":0,"message":"ok","data":{"items":[{"delta":-12,"reason":"task_charge","task_id":"7c9e6679-7425-40de-944b-e07fc1f90ae7","balance_after":286,"created_at":"2026-07-14T09:30:12+00:00"},{"delta":300,"reason":"subscription_grant","task_id":null,"balance_after":298,"created_at":"2026-07-01T00:00:03+00:00"}],"total":2,"page":1,"page_size":50}}}}},"401":{"description":"Missing, malformed or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"},"example":{"code":401,"message":"Invalid or revoked API key","data":null}}}},"429":{"description":"Over the per-key rate limit (10 req/s, burst 30) or the plan's concurrent-task cap — retry after `Retry-After` seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"},"example":{"code":429,"message":"Rate limited","data":null}}}}},"x-codeSamples":[{"lang":"shell","label":"cURL","source":"curl \"https://pptx-api.tosea.ai/v1/usage?page=1&page_size=50\" \\\n  -H \"Authorization: Bearer tpx_live_YOUR_KEY\""},{"lang":"python","label":"Python","source":"import requests\n\nAPI = \"https://pptx-api.tosea.ai\"\nHEADERS = {\"Authorization\": \"Bearer tpx_live_YOUR_KEY\"}\n\ndata = requests.get(f\"{API}/v1/usage\", headers=HEADERS,\n                    params={\"page\": 1, \"page_size\": 50}).json()[\"data\"]\nfor entry in data[\"items\"]:\n    print(entry[\"created_at\"], entry[\"reason\"], entry[\"delta\"],\n          entry[\"balance_after\"])"},{"lang":"javascript","label":"Node.js","source":"const API = \"https://pptx-api.tosea.ai\";\nconst headers = { Authorization: \"Bearer tpx_live_YOUR_KEY\" };\n\nconst { data } = await (\n  await fetch(`${API}/v1/usage?page=1&page_size=50`, { headers })\n).json();\nfor (const e of data.items)\n  console.log(e.created_at, e.reason, e.delta, e.balance_after);"}]}},"/v1/limits":{"get":{"tags":["PPTX Conversion"],"summary":"Plan limits & pricing table","description":"Effective limits for your plan: max pages per task, max upload size,\nand how many tasks may run concurrently.","operationId":"get_limits_v1_limits_get","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"integer","description":"0 on success; otherwise mirrors the HTTP status"},"message":{"type":"string"},"data":{"type":"object","properties":{"plan":{"type":"string"},"max_pages_per_task":{"type":"integer"},"max_upload_mb":{"type":"integer"},"concurrent_tasks":{"type":"integer","description":"Max active (pending + processing) tasks for your plan"},"credits_per_page":{"type":"integer"}}}}},"example":{"code":0,"message":"ok","data":{"plan":"starter","max_pages_per_task":100,"max_upload_mb":100,"concurrent_tasks":2,"credits_per_page":1}}}}},"401":{"description":"Missing, malformed or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"},"example":{"code":401,"message":"Invalid or revoked API key","data":null}}}},"429":{"description":"Over the per-key rate limit (10 req/s, burst 30) or the plan's concurrent-task cap — retry after `Retry-After` seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"},"example":{"code":429,"message":"Rate limited","data":null}}}}},"x-codeSamples":[{"lang":"shell","label":"cURL","source":"curl \"https://pptx-api.tosea.ai/v1/limits\" \\\n  -H \"Authorization: Bearer tpx_live_YOUR_KEY\""},{"lang":"python","label":"Python","source":"import requests\n\nAPI = \"https://pptx-api.tosea.ai\"\nHEADERS = {\"Authorization\": \"Bearer tpx_live_YOUR_KEY\"}\n\nprint(requests.get(f\"{API}/v1/limits\", headers=HEADERS).json()[\"data\"])"},{"lang":"javascript","label":"Node.js","source":"const API = \"https://pptx-api.tosea.ai\";\nconst headers = { Authorization: \"Bearer tpx_live_YOUR_KEY\" };\n\nconsole.log((await (await fetch(`${API}/v1/limits`, { headers })).json()).data);"}]}}},"webhooks":{"task.finished":{"post":{"summary":"Task finished (sent to callback_url)","description":"Sent to the `callback_url` you supplied on `POST /v1/tasks` once the task\nreaches a terminal state (`succeeded`, `failed` or `canceled`).\n\nDelivery is **at-least-once** with exponential-backoff retries for about a\nday — handle it idempotently by `task_id`. Respond with any 2xx within 15\nseconds; anything else (or a timeout) counts as a failed attempt and is\nretried. Polling `GET /v1/tasks/{task_id}` always works as a fallback.\n\nEvery delivery is signed: the `X-Tosea-Signature: sha256=<hex>` header is\nthe HMAC-SHA256 of the **raw request body**, keyed with the webhook secret\nfrom your [dashboard](https://pptx-web-pptxapi.up.railway.app/keys). Verify before trusting:\n\n```python\nimport hashlib, hmac\n\ndef verify(secret: str, raw_body: bytes, signature_header: str) -> bool:\n    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()\n    return hmac.compare_digest(f\"sha256={expected}\", signature_header)\n```\n\n```javascript\nimport { createHmac, timingSafeEqual } from \"node:crypto\";\n\nfunction verify(secret, rawBody, signatureHeader) {\n  const expected =\n    `sha256=${createHmac(\"sha256\", secret).update(rawBody).digest(\"hex\")}`;\n  return timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader));\n}\n```","operationId":"task_finished_webhooktask_finished_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskWebhookPayload"}}},"required":true},"responses":{"200":{"description":"Return any 2xx within 15 seconds to acknowledge receipt; anything else (or a timeout) is retried with backoff."}},"security":[]}}},"components":{"schemas":{"Body_create_upload_v1_uploads_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File","description":"The PDF or image (PNG/JPG/WebP) to convert"}},"type":"object","required":["file"],"title":"Body_create_upload_v1_uploads_post"},"CreateTaskBody":{"properties":{"operation":{"type":"string","enum":["pdf_to_pptx","images_to_pptx"],"title":"Operation","description":"`pdf_to_pptx` (PDF input) or `images_to_pptx` (image-list input)","default":"pdf_to_pptx"},"input":{"$ref":"#/components/schemas/TaskInput"},"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url","description":"HTTPS webhook called once the task reaches a terminal state (`succeeded`, `failed` or `canceled`). Delivered at-least-once with exponential-backoff retries for about a day, so handle it idempotently by `task_id`. Verify the `X-Tosea-Signature: sha256=<hmac>` header with your webhook secret."}},"type":"object","required":["input"],"title":"CreateTaskBody"},"TaskInput":{"properties":{"upload_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Upload Id","description":"A PDF upload ID from POST /v1/uploads"},"upload_ids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Upload Ids","description":"Image upload IDs from POST /v1/uploads, one per slide, in order"},"pdf_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pdf Url","description":"Public URL of a PDF hosted by your system"},"image_urls":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Image Urls","description":"List of page-image URLs (PNG/JPG/JPEG/WebP), one per slide, in order"},"pages":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Pages","description":"Optional 1-based page numbers to convert (PDF input only). Billing equals the number of selected pages; omit to convert every page."},"title":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Title","description":"Optional deck title"}},"type":"object","title":"TaskInput"},"TaskWebhookPayload":{"properties":{"task_id":{"type":"string","title":"Task Id","description":"Deduplicate deliveries by this ID"},"operation":{"type":"string","title":"Operation","description":"`pdf_to_pptx` or `images_to_pptx`"},"status":{"type":"string","title":"Status","description":"Terminal state: `succeeded`, `failed` or `canceled`"},"progress":{"type":"integer","title":"Progress","description":"100 when succeeded"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"page_count":{"type":"integer","title":"Page Count"},"credits_charged":{"type":"integer","title":"Credits Charged","description":"0 for failed/canceled tasks; the reservation is reconciled against its original pools under their lifecycle rules"},"created_at":{"type":"string","title":"Created At"},"finished_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Finished At"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Failure reason when status is `failed`"},"result":{"anyOf":[{"$ref":"#/components/schemas/TaskWebhookResult"},{"type":"null"}]}},"type":"object","required":["task_id","operation","status","progress","title","page_count","credits_charged","created_at","finished_at","result"],"title":"TaskWebhookPayload","description":"Body POSTed to your `callback_url` when a task reaches a terminal state."},"TaskWebhookResult":{"properties":{"pptx_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pptx Url","description":"Signed download URL (valid 24h) — present when status is `succeeded`"}},"type":"object","title":"TaskWebhookResult"},"ErrorEnvelope":{"type":"object","description":"Every non-2xx response uses this envelope; `data` is always null.","properties":{"code":{"type":"integer","description":"Mirrors the HTTP status code"},"message":{"type":"string","description":"Human-readable reason — not a stable contract, do not parse"},"data":{"type":"null"}}},"Task":{"type":"object","description":"A conversion task. Terminal states: `succeeded`, `failed`, `canceled`.","properties":{"task_id":{"type":"string","format":"uuid"},"operation":{"type":"string","enum":["pdf_to_pptx","images_to_pptx"]},"status":{"type":"string","enum":["pending","processing","succeeded","failed","canceled"],"description":"`pending` → `processing` → `succeeded` | `failed`; `canceled` via POST /v1/tasks/{task_id}/cancel"},"progress":{"type":"integer","minimum":0,"maximum":100},"title":{"type":["string","null"]},"page_count":{"type":"integer","description":"Pages converted — equals the credits reserved"},"credits_charged":{"type":"integer","description":"0 for a failed or canceled task. Its reservation is reconciled against the original pools under their lifecycle rules."},"created_at":{"type":"string","format":"date-time"},"finished_at":{"type":["string","null"],"format":"date-time"},"error":{"type":["string","null"],"description":"Failure reason when `status` is `failed`"},"result":{"type":["object","null"],"description":"Present only when `status` is `succeeded`","properties":{"pptx_url":{"type":"string","description":"Signed .pptx download URL, valid for 24 hours — download promptly and store on your side"}}}}},"Upload":{"type":"object","description":"A temporary upload whose record and API access expire 24 hours after creation. The underlying storage object may be deleted later.","properties":{"upload_id":{"type":"string","format":"uuid","description":"Pass as `input.upload_id` (PDF) or in `input.upload_ids` (images) on POST /v1/tasks"},"filename":{"type":["string","null"]},"size":{"type":"integer","description":"Bytes"},"content_type":{"type":["string","null"]},"page_count":{"type":"integer","description":"PDF page count (1 for images)"},"expires_at":{"type":"string","format":"date-time"},"preview_url":{"type":"string","description":"Signed preview URL — image uploads only, omitted for PDFs"}}},"UsageEntry":{"type":"object","description":"One credit-ledger entry.","properties":{"delta":{"type":"integer","description":"Positive = credits granted, negative = credits spent"},"reason":{"type":"string","description":"`task_charge` · `task_refund` · `free_grant` · `subscription_grant` · …"},"task_id":{"type":["string","null"],"format":"uuid"},"balance_after":{"type":"integer"},"created_at":{"type":"string","format":"date-time"}}}},"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"tpx_live_…","description":"API key from the [dashboard](https://pptx-web-pptxapi.up.railway.app/keys). Send it as `Authorization: Bearer tpx_live_…` on every request."}}},"security":[{"bearerAuth":[]}]}