Public API

Convert files programmatically using an API key.

Getting started

  1. Go to Organization → Settings → API Keys and create a key. The full value is shown once — copy it now.
  2. Submit a job:
curl -X POST https://convertere.io/v1/jobs \
  -H "Authorization: Bearer cvio_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "tasks": {
      "import-1": { "operation": "import/upload" },
      "convert-1": {
        "operation": "convert",
        "input": "import-1",
        "input_format": "png",
        "output_format": "webp"
      },
      "export-1": { "operation": "export/url", "input": "convert-1" }
    }
  }'
  1. Upload your file to the uploadUrl returned on the import-1 task, using a PUT request with the file as the body.
  2. Poll GET /v1/jobs/{id} until status is finished, or register a webhook and wait for the notification.
  3. Download the result from the url on the export-1 task.

Authentication

Every request (except GET /v1/openapi.json) needs a header:

Authorization: Bearer cvio_live_...

or equivalently X-API-Key: cvio_live_.... Keys come in two modes:

  • live — real conversions, debits your organization's credit balance.
  • test — the full flow runs (status transitions, webhooks, a downloadable result), but no worker is ever contacted and nothing is charged. Use this to build and rehearse your integration before switching to a live key.

A missing, revoked, or expired key returns 401 with no distinguishing signal between those cases.

The task DAG

Requests use a minimal task graph — one import/upload, one convert, one export/url, chained by name:

{
	"tasks": {
		"import-1": { "operation": "import/upload" },
		"convert-1": {
			"operation": "convert",
			"input": "import-1",
			"input_format": "png",
			"output_format": "webp",
			"options": { "quality": 80 }
		},
		"export-1": { "operation": "export/url", "input": "convert-1" }
	},
	"tag": "optional-client-reference"
}

tag is an optional string echoed back on every status response and webhook payload, for correlating a job with your own records. options passes through unchanged to the target format's conversion options.

This is deliberately not a byte-identical clone of any other provider's API — response fields, status values, and errors follow this platform's own conventions.

Job status

GET /v1/jobs/{id} returns:

{
	"id": "...",
	"status": "processing",
	"mode": "single",
	"testMode": false,
	"credits_estimated": 3,
	"tasks": [
		{ "name": "import-1", "operation": "import/upload", "status": "finished" },
		{ "name": "convert-1", "operation": "convert", "status": "processing" },
		{ "name": "export-1", "operation": "export/url", "status": "waiting" }
	],
	"createdAt": "2026-07-31T12:00:00Z"
}

status is one of waiting, processing, finished, error. Once finished, credits_used and the export-1 task's result.url are present. Polling this endpoint also opportunistically advances the job the moment your upload is detected — you don't need to call anything else after uploading.

Capability catalog

GET /v1/operations lists every currently supported conversion with an estimated cost:

{
	"operations": [
		{
			"input_format": "png",
			"output_format": "webp",
			"group": "image",
			"credits_estimated": 1
		}
	]
}

Webhooks

Register an endpoint from the dashboard to receive job.finished / job.failed notifications instead of polling:

{
	"event": "job.finished",
	"job": { "...": "same shape as GET /v1/jobs/{id}" },
	"sentAt": "2026-07-31T12:05:00Z"
}

Each delivery is signed:

X-Webhook-Signature: sha256=<hex HMAC-SHA256 of the raw body, keyed by your endpoint's secret>

Verify it (Node example):

import { createHmac, timingSafeEqual } from "node:crypto";

function isValid(rawBody, header, secret) {
	const expected = `sha256=${createHmac("sha256", secret).update(rawBody).digest("hex")}`;
	return timingSafeEqual(Buffer.from(header), Buffer.from(expected));
}

Failed deliveries retry up to 5 times over roughly 80 minutes. Your job's true status via GET /v1/jobs/{id} is always authoritative, independent of whether a webhook ever successfully delivered.

Errors

{ "error": { "code": "insufficient_credits", "message": "..." } }
StatusMeaning
400Malformed request, or the source/target format pair isn't supported
401Missing, invalid, revoked, or expired API key
402Organization balance can't cover this job's maximum cost
404Job not found, or not owned by this specific key
409Idempotency-Key reused with a different request body
429Rate limit or per-key concurrency cap exceeded — see Retry-After

Rate limits

60 requests/minute per API key, and at most 3 jobs in progress per key at once. Both scope to the individual key, not the organization — an org with multiple keys effectively gets a multiple of each limit.

Idempotency

Pass an Idempotency-Key header on POST /v1/jobs to safely retry after a network failure. The same key with the same request body returns the original job instead of creating a new one; the same key with a different body returns 409.

On this page