{"activeVersionTag":"latest","latestAvailableVersionTag":"latest","collection":{"info":{"_postman_id":"f6131e9e-a28d-4093-bc6e-71175c10ec49","name":"Skavio Processing API 1.9.0","description":"# Skavio Processing API\n\nOne company-scoped API for document extraction, PDF and image tools, media processing, transcription and meetings.\n\n- [Product and pricing](https://www.skavio.eu/api/)\n- [Live developer documentation](https://www.skavio.eu/api/docs/)\n- [Company workspace: register and create an API key](https://www.skavio.eu/dashboard/)\n- [OpenAPI v1](openapi-v1.json) · [Developer guide](developer-guide.md)\n- [Python quickstart](quickstart.py) · [Node.js quickstart](quickstart.mjs) · [Bulk guide](bulk-guide.md)\n- [Postman public documentation](https://documenter.getpostman.com/view/58677341/2sBYHNWi4n) · [Collection](Skavio-Processing-API.postman_collection.json)\n- [DEV.to integration article](https://dev.to/skavioeu/building-a-retry-safe-file-processing-api-estimates-idempotency-and-signed-webhooks-32p6)\n- [Webhook verification](verify_webhook.py)\n\n**API route version:** /v1. **Contract snapshot:** 1.9.0 + Pricing V2, captured 2026-10-04.\n\n## What is new in API 1.7.0\n\n- Monthly charged/reserved usage and separate payment history, bounded CSV and key attribution.\n- Atomic company/key budgets for API, batch, bulk and paid web admission.\n- Expiring API keys with granular permissions and administrator-only policy writes.\n- Queue visibility, persisted alerts, cancellation of unstarted standalone jobs and governance audit.\n- Idempotent replay of failed job/batch/bulk webhook deliveries using the frozen original event.\n- Structured errors and X-Request-Id correlation; Czech/German/English dashboard additions.\n\nRead the [Usage, budgets and recovery guide](governance-guide.md) for authorization, UTC month rules, limits and compatibility.\n\n## Storage Fabric introduced in API 1.6.0\n\n- Private S3 sources/results and compatible legacy local files.\n- Verified direct uploads and resumable imports from approved customer cloud URLs.\n- Company-scoped retention, archive, deletion hold and opt-in separate-bucket backups.\n- Manual verified backup/restore, signed result downloads and encrypted import credentials.\n- Existing Pricing V2, jobs, batches and bulk contracts remain supported.\n\nRead the [Storage Fabric guide](storage-guide.md) for endpoints, production limits and browser upload handling.\n\n## Bulk features introduced in API 1.5.0\n\n- **Bulk manifests up to 100,000 items** with immutable manifest pages of 1–100 items and bounded incremental child admission.\n- **Storage-aware admission** through `GET /v1/storage`, declared input/output/scratch envelopes and durable storage reservations.\n- **Resume and failed-item retry** for large workloads without resubmitting successful items.\n- **Crash/restart recovery** for scheduler state, reservations, checkpoints and provider-intent reconciliation.\n- **Fair scheduling across companies** while preserving the current ceiling of 4 active platform jobs globally.\n- Existing **100-item batches** remain available for simpler workloads.\n\nStart with [Bulk processing guide](bulk-guide.md) or the [Python bulk quickstart](bulk_quickstart.py).\nThis repository contains the public developer kit. The hosted processing service requires a Skavio account and credits.\n\n## First result\n\n1. Register, create a company profile and generate a read/write key in the company workspace. Copy it once and keep it server-side.\n2. Upload a source using multipart field `files`.\n3. Call `POST /v1/estimate` without starting processing.\n4. Set `max_credits` to the returned `estimated_credits`.\n5. Submit `POST /v1/jobs` with a stable `Idempotency-Key`.\n6. Poll the returned job ID until completed or failed; download through the authenticated result paths.\n\nThe included invoice is **synthetic**, contains no customer data, and can be used for an extraction test.\n\n### Python 3.10+\n\n```bash\npython -m venv .venv\n. .venv/bin/activate\npip install -r requirements.txt\n# Set SKAVIO_API_KEY securely in your local environment.\nexport SKAVIO_IDEMPOTENCY_KEY=\"your-persisted-invoice-request-001\"\npython quickstart.py\n```\n\n### Node.js 22+\n\n```bash\n# Set SKAVIO_API_KEY securely in your local environment.\nexport SKAVIO_IDEMPOTENCY_KEY=\"your-persisted-invoice-request-002\"\nnode quickstart.mjs\n```\n\nBoth examples use `sample-invoice.txt`; set `SKAVIO_SOURCE` to a PDF/image/text source to change it. They estimate cost before submission and write JSON output into `results/`.\nFor intentional processing of a different source or changed payload, use a **new** idempotency key.\nThe examples are server-side starters, not a full SDK or a durable workflow engine.\n\n### Postman\n\nImport the collection or fork its public workspace copy. Set **your own local/private environment** variable `api_key`; never save a real key in the public collection. Set `source_file` to a local source. Run Upload → Estimate → Submit → Job status → Download. Poll every 3–10 seconds until the job is terminal before downloading.\nRequests cover the API-key processing endpoints; administration, key creation, billing and webhook registration use an interactive company administrator session in the dashboard.\nBefore a new business request, clear `upload_id`, `job_id`, `download_path`, `max_credits` and `idempotency_key` in your private environment. Preserve the idempotency key when retrying the same submission.\n\n## What can I process?\n\n| Workflow | Operations |\n| --- | --- |\n| Documents | `flow-extract` → XLSX, CSV, JSON; chosen fields, evidence, review flags |\n| PDFs | merge, split, extract/delete/rotate pages, compression, watermark, page numbering, protect/unlock with your password, OCR |\n| Images | resize/crop/rotate/flip, compression, format conversion, OCR |\n| Audio/video | conversion, trimming, compression; video rotation/resolution/mute |\n| Speech | `transcribe`, `meeting` |\n\n[Capabilities snapshot](capabilities.json) lists exact operation names. Query `GET /v1/capabilities` for current prices and limits.\nNormal file jobs accept one source; PDF merge accepts 2–20 PDFs; Flow can process multiple document sources. Transcribe and Meeting accept one media source. Batch processing supports up to 100 items. API 1.5.0 bulk manifests support up to 100,000 items with incremental dispatch; see [bulk-guide.md](bulk-guide.md). General multi-operation chaining is not available.\n\n## Credits and price control\n\nAll web tools and API share a prepaid company wallet. No unlimited processing entitlement is advertised in this kit.\nThe server measures bytes, pages and media duration. It reserves the quoted amount on submission, charges that fixed quote on success, and releases the reservation on failure.\nUse `max_credits` to reject unexpectedly expensive submissions and `GET /v1/wallet` to track balance and reservations.\n\n| Operation | Pricing V2 |\n| --- | --- |\n| Raw OCR text (ocr-txt) | 3 credits/page |\n| OCR document output (ocr-docx / ocr-pdf) | 10 credits/page |\n| Flow extraction and export | 25 credits/page |\n| PDF/image/office/text file operation | 2 per started 25 MB or 10 pages, larger basis per source |\n| Transcribe | 8 per started minute |\n| Meeting | 15 per started minute |\n| File Toolbox audio/video | 4 per started minute |\n\nNominally 1 EUR = 1,000 credits. Top-ups: EUR10/10,000; EUR25/26,000; EUR50/53,000; EUR100/108,000; EUR250/280,000; EUR500/570,000. A retail EUR6.99/6,990 pack is also available.\n\nMonthly plans: Starter EUR29/30,000 credits; Business EUR79/85,000; Pro EUR199/225,000; Enterprise EUR499/600,000. Paid credits never expire; monthly credits roll over. Current plans share the deployment processing limits.\n\nWeb tools and API share one wallet. Submit reserves the measured quote; success charges it once; a permanent failure releases it. Temporary provider outages keep jobs queued with durable checkpoints and reserved credits. Existing quoted jobs and purchased checkout/subscription offers retain their original amounts.\n\nCanonical live catalog: GET /v1/pricing. Always estimate before submission and use max_credits rather than hard-coding a rate.\n\n## Reliability and integration rules\n\n- `queued → processing → completed | failed`; submission returns HTTP 202.\n- Replaying the identical job request with the same idempotency key returns the same job. Changing its payload returns 409.\n- Persist business request, exact payload, key and job ID in your system. If a response is lost, retry with the original key.\n- A failed job releases credits; an intentional new attempt uses a new key.\n- Provider outages and service restarts preserve durable jobs/checkpoints and reservations; permanent failures release them.\n- Webhooks deliver at least once; verify HMAC on the raw bytes and deduplicate event IDs. Polling remains supported.\n- Results/source retention defaults to 7 days. Storage Fabric policy, archive and hold control object retention; inspect retain_until and preserve results before expiry.\n- Extraction can be wrong. Check evidence and review flags; review line items against the source before downstream accounting.\n\n## Limits (current deployment)\n\n120 requests/minute per key/session; up to 100 files/500 MB per upload; 2 GB source storage/company; 100 pages/PDF; 100 queued jobs/company; 4 active platform jobs/company and 4 globally. Transcribe/Meeting input can be up to 10 hours; File Toolbox media up to 2 hours.\nThese are ceilings, not guaranteed throughput or an SLA. Back off on 429 and transient 5xx responses. Current plans share the processing limits.\n\n## Errors\n\nJSON errors retain `detail` and add `error_code`, `request_id` and `retryable`; X-Request-Id provides correlation. Use stable codes rather than matching error text. Validation does not echo secret inputs.\n\n| HTTP | Meaning |\n| --- | --- |\n| 401 / 403 | Missing/invalid authentication, scope or permission |\n| 402 | Insufficient credits |\n| 404 | Missing object or another company’s object |\n| 409 | State/idempotency conflict or max-credit rejection |\n| 410 | Expired result |\n| 413 / 422 | Upload/storage limit or invalid source/parameters |\n| 429 | Request/queue limit |\n| 502 / 503 / 504 | Processing/provider unavailable or timeout |\n\n## Security and support\n\nUse `Authorization: Bearer <your-key>` over HTTPS. Never expose keys in frontend apps, URLs, screenshots, commits or shared Postman environments. Company-scoped object IDs cannot access another company.\nCreate/revoke keys and register webhooks in the dashboard. PDF passwords are secrets; do not log payloads.\nReview [Privacy](https://www.skavio.eu/privacy/) and [Terms](https://www.skavio.eu/terms/) before regulated/sensitive inputs. Source processing can involve external providers; backup/provider retention is separate from platform deletion.\n\nReport integration issues through this repository with **synthetic reproductions only**, or contact **work@skavio.eu** privately. Never attach customer documents, keys or billing data to a public issue.\n\n## Versioning\n\nCompatible additions remain under /v1; clients should ignore unknown response fields. Breaking changes require a major API version or an announced migration. [Changelog](CHANGELOG.md).\n\n## License\n\nExample code and documentation in this developer kit are MIT licensed. This does not license the hosted backend or grant free use of the hosted service; service use follows its Terms and credit pricing.\n\n\n## Batch processing — API 1.5.0\n\nSubmit up to 100 items for any advertised operation through the same processing pipeline. A batch uses one operation/options set and creates ordinary child jobs. Processing remains 4 active jobs per company and 4 globally; a batch does not promise 100 parallel executions.\n\n1. Upload files with `POST /v1/uploads`; retain their upload IDs.\n2. Quote `POST /v1/batches/estimate` using `{\"operation\":\"jpg\",\"upload_ids\":[\"UPLOAD_A\",\"UPLOAD_B\"]}`.\n3. Submit the same payload to `POST /v1/batches` with `max_credits` set to the total quote and a persisted `Idempotency-Key` (8–128 allowed characters).\n4. Poll `GET /v1/batches/{batch_id}`. Each `items[].job` includes status, credits and result download paths. `progress` ranges from 0 to 1; terminal states are `completed`, `partial`, `failed`.\n5. Download `GET /v1/batches/{batch_id}/results.zip` after completion. The archive includes successful results and `manifest.json`. Missing/expired files return 410. ZIP output is limited to 1 GB; use individual authenticated downloads for larger results.\n6. Use `POST /v1/batches/{batch_id}/retry-failed` with a new persisted key to create a separately billed batch of failed items. Retry requests are bound to the original batch. Download successes before retention expires.\n\nCredits reserve atomically for the complete batch. Every child has separate accounting; successful items are charged and failed items release their reservation. Replaying the same submission does not reserve credits again. Changing a request while reusing its key returns 409. Batch items count toward the 100 queued jobs per company.\n\nUse either `upload_ids` (one child per source) or `items` (explicit groups), never both. PDF merge requires groups with 2–20 PDFs each:\n\n```json\n{\"operation\":\"pdf-merge\",\"items\":[{\"upload_ids\":[\"PDF_A\",\"PDF_B\"]},{\"upload_ids\":[\"PDF_C\",\"PDF_D\"]}],\"webhook_mode\":\"both\"}\n```\n\nGrouped Flow inputs use the same `items` shape. Optional `webhook_id` must belong to your company. `webhook_mode` accepts `batch` (parent events), `items` (child events), or `both`. Parent events are `batch.completed`, `batch.partial`, `batch.failed`; verify the same signature and deduplicate event IDs. Parent delivery IDs are distinct from child delivery IDs.\n\n`GET /v1/batches` lists recent company batches. `DELETE /v1/batches/{batch_id}` deletes terminal result/source specifications while preserving accounting. Source/result retention is seven days. A repeated-failure guard may return 429; contact support rather than continuously resubmitting failing sources. Save exact payloads, keys and returned IDs in durable storage.\n\n\nBatch starters: [Python](batch_quickstart.py) · [Node.js](batch_quickstart.mjs) · [Batch guide](batch-guide.md). Set `SKAVIO_API_KEY`, `SKAVIO_UPLOAD_IDS` (JSON array from upload), and a persisted `SKAVIO_BATCH_IDEMPOTENCY_KEY`. The examples poll and print authenticated result paths; archive results before retention expires.\n\n## Durable workflow pipelines\n\nLinear process/review/archive steps, checkpointed execution and per-step credit settlement. See [workflow guide](workflow-guide.md).\n\n## Official SDK clients\n\nPython and Node.js clients are in [sdk/README.md](sdk/README.md). Install from this repository with `python3 -m pip install ./sdk/python` or `npm install ./sdk/javascript`. They cover all 92 current OpenAPI operations, durable submission keys, resumable polling, signed webhook verification and authenticated result downloads. Packages are not yet published to PyPI/npm.\n\n\n## API 1.9.0 controls and notifications\n\nBulk pause and cancel require Idempotency-Key and enforce company isolation. Pause prevents new child admission; accepted jobs drain and settle once. Cancel is final and prevents further dispatch. GET /v1/notifications and GET /v1/notifications/settings are tenant-scoped. PUT /v1/notifications/settings requires a company administrator web session. Notifications are disabled by default; email requires configured transport. Frozen events are deduplicated and retried within a bounded delivery window. Email follows the current company billing address and supports cs/de/en.\n","schema":"https://schema.getpostman.com/json/collection/v2.0.0/collection.json","isPublicCollection":false,"owner":"58677341","team":44904798,"collectionId":"f6131e9e-a28d-4093-bc6e-71175c10ec49","publishedId":"2sBYHNWi4n","public":true,"publicUrl":"https://documenter-api.postman.tech/view/58677341/2sBYHNWi4n","privateUrl":"https://go.postman.co/documentation/58677341-f6131e9e-a28d-4093-bc6e-71175c10ec49","customColor":{"highlight":"4F46E5","right-sidebar":"111827","top-bar":"FFFFFF"},"documentationLayout":"classic-double-column","customisation":{"metaTags":[{"name":"title","value":"Skavio Processing API v1"},{"name":"description","value":"Process PDFs, OCR, documents, images, media and audio with one API. Credit estimates, idempotency, webhooks and company-scoped API keys."}],"appearance":{"themes":[{"name":"light","colors":{"highlight":"4F46E5","right-sidebar":"111827","top-bar":"FFFFFF"}},{"name":"dark","colors":{"highlight":"4F46E5","right-sidebar":"111827","top-bar":"FFFFFF"}}],"default":"light"}},"version":"8.12.8","publishDate":"2026-10-02T09:46:33.000Z","activeVersionTag":"latest","documentationTheme":"light","metaTags":{"title":"Skavio Processing API v1","description":"Process PDFs, OCR, documents, images, media and audio with one API. Credit estimates, idempotency, webhooks and company-scoped API keys."},"logos":{}},"statusCode":200},"environments":[],"user":{"authenticated":false,"permissions":{"publish":false}},"run":{"button":{"js":"https://run.pstmn.io/button.js","css":"https://run.pstmn.io/button.css"}},"web":"https://www.getpostman.com/","team":{"logo":"https://res.cloudinary.com/postman/image/upload/t_team_logo_pubdoc/v1/team/1194b7323df41e1d6a338575a85313dc11d56a6602929a34690a0e806e85ccda","favicon":""},"isEnvFetchError":false,"languages":"[{\"key\":\"csharp\",\"label\":\"C#\",\"variant\":\"HttpClient\"},{\"key\":\"csharp\",\"label\":\"C#\",\"variant\":\"RestSharp\"},{\"key\":\"curl\",\"label\":\"cURL\",\"variant\":\"cURL\"},{\"key\":\"dart\",\"label\":\"Dart\",\"variant\":\"http\"},{\"key\":\"go\",\"label\":\"Go\",\"variant\":\"Native\"},{\"key\":\"http\",\"label\":\"HTTP\",\"variant\":\"HTTP\"},{\"key\":\"java\",\"label\":\"Java\",\"variant\":\"OkHttp\"},{\"key\":\"java\",\"label\":\"Java\",\"variant\":\"Unirest\"},{\"key\":\"javascript\",\"label\":\"JavaScript\",\"variant\":\"Fetch\"},{\"key\":\"javascript\",\"label\":\"JavaScript\",\"variant\":\"jQuery\"},{\"key\":\"javascript\",\"label\":\"JavaScript\",\"variant\":\"XHR\"},{\"key\":\"c\",\"label\":\"C\",\"variant\":\"libcurl\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Axios\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Native\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Request\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Unirest\"},{\"key\":\"objective-c\",\"label\":\"Objective-C\",\"variant\":\"NSURLSession\"},{\"key\":\"ocaml\",\"label\":\"OCaml\",\"variant\":\"Cohttp\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"cURL\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"Guzzle\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"HTTP_Request2\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"pecl_http\"},{\"key\":\"powershell\",\"label\":\"PowerShell\",\"variant\":\"RestMethod\"},{\"key\":\"python\",\"label\":\"Python\",\"variant\":\"http.client\"},{\"key\":\"python\",\"label\":\"Python\",\"variant\":\"Requests\"},{\"key\":\"r\",\"label\":\"R\",\"variant\":\"httr\"},{\"key\":\"r\",\"label\":\"R\",\"variant\":\"RCurl\"},{\"key\":\"ruby\",\"label\":\"Ruby\",\"variant\":\"Net::HTTP\"},{\"key\":\"shell\",\"label\":\"Shell\",\"variant\":\"Httpie\"},{\"key\":\"shell\",\"label\":\"Shell\",\"variant\":\"wget\"},{\"key\":\"swift\",\"label\":\"Swift\",\"variant\":\"URLSession\"}]","languageSettings":[{"key":"csharp","label":"C#","variant":"HttpClient"},{"key":"csharp","label":"C#","variant":"RestSharp"},{"key":"curl","label":"cURL","variant":"cURL"},{"key":"dart","label":"Dart","variant":"http"},{"key":"go","label":"Go","variant":"Native"},{"key":"http","label":"HTTP","variant":"HTTP"},{"key":"java","label":"Java","variant":"OkHttp"},{"key":"java","label":"Java","variant":"Unirest"},{"key":"javascript","label":"JavaScript","variant":"Fetch"},{"key":"javascript","label":"JavaScript","variant":"jQuery"},{"key":"javascript","label":"JavaScript","variant":"XHR"},{"key":"c","label":"C","variant":"libcurl"},{"key":"nodejs","label":"NodeJs","variant":"Axios"},{"key":"nodejs","label":"NodeJs","variant":"Native"},{"key":"nodejs","label":"NodeJs","variant":"Request"},{"key":"nodejs","label":"NodeJs","variant":"Unirest"},{"key":"objective-c","label":"Objective-C","variant":"NSURLSession"},{"key":"ocaml","label":"OCaml","variant":"Cohttp"},{"key":"php","label":"PHP","variant":"cURL"},{"key":"php","label":"PHP","variant":"Guzzle"},{"key":"php","label":"PHP","variant":"HTTP_Request2"},{"key":"php","label":"PHP","variant":"pecl_http"},{"key":"powershell","label":"PowerShell","variant":"RestMethod"},{"key":"python","label":"Python","variant":"http.client"},{"key":"python","label":"Python","variant":"Requests"},{"key":"r","label":"R","variant":"httr"},{"key":"r","label":"R","variant":"RCurl"},{"key":"ruby","label":"Ruby","variant":"Net::HTTP"},{"key":"shell","label":"Shell","variant":"Httpie"},{"key":"shell","label":"Shell","variant":"wget"},{"key":"swift","label":"Swift","variant":"URLSession"}],"languageOptions":[{"label":"C# - HttpClient","value":"csharp - HttpClient - C#"},{"label":"C# - RestSharp","value":"csharp - RestSharp - C#"},{"label":"cURL - cURL","value":"curl - cURL - cURL"},{"label":"Dart - http","value":"dart - http - Dart"},{"label":"Go - Native","value":"go - Native - Go"},{"label":"HTTP - HTTP","value":"http - HTTP - HTTP"},{"label":"Java - OkHttp","value":"java - OkHttp - Java"},{"label":"Java - Unirest","value":"java - Unirest - Java"},{"label":"JavaScript - Fetch","value":"javascript - Fetch - JavaScript"},{"label":"JavaScript - jQuery","value":"javascript - jQuery - JavaScript"},{"label":"JavaScript - XHR","value":"javascript - XHR - JavaScript"},{"label":"C - libcurl","value":"c - libcurl - C"},{"label":"NodeJs - Axios","value":"nodejs - Axios - NodeJs"},{"label":"NodeJs - Native","value":"nodejs - Native - NodeJs"},{"label":"NodeJs - Request","value":"nodejs - Request - NodeJs"},{"label":"NodeJs - Unirest","value":"nodejs - Unirest - NodeJs"},{"label":"Objective-C - NSURLSession","value":"objective-c - NSURLSession - Objective-C"},{"label":"OCaml - Cohttp","value":"ocaml - Cohttp - OCaml"},{"label":"PHP - cURL","value":"php - cURL - PHP"},{"label":"PHP - Guzzle","value":"php - Guzzle - PHP"},{"label":"PHP - HTTP_Request2","value":"php - HTTP_Request2 - PHP"},{"label":"PHP - pecl_http","value":"php - pecl_http - PHP"},{"label":"PowerShell - RestMethod","value":"powershell - RestMethod - PowerShell"},{"label":"Python - http.client","value":"python - http.client - Python"},{"label":"Python - Requests","value":"python - Requests - Python"},{"label":"R - httr","value":"r - httr - R"},{"label":"R - RCurl","value":"r - RCurl - R"},{"label":"Ruby - Net::HTTP","value":"ruby - Net::HTTP - Ruby"},{"label":"Shell - Httpie","value":"shell - Httpie - Shell"},{"label":"Shell - wget","value":"shell - wget - Shell"},{"label":"Swift - URLSession","value":"swift - URLSession - Swift"}],"layoutOptions":[{"value":"classic-single-column","label":"Single Column"},{"value":"classic-double-column","label":"Double Column"}],"versionOptions":[],"environmentOptions":[{"value":"0","label":"No Environment"}],"canonicalUrl":"https://documenter.gw.postman.com/view/metadata/2sBYHNWi4n"}