DEVELOPERS / API V1

Image API

Remove backgrounds and generate catalog images through one production API.

Quick start

Background removal is async by default. Catalog generation returns a persisted PNG synchronously. Production base URL:

https://api.blankground.ru

Hosted workspaces receive an HTTPS URL. Bodies are JSON unless an endpoint uses multipart or binary data.

curl -X POST https://api.blankground.ru/v1/removals \
  -H "Authorization: Bearer $BLANKGROUND_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Prefer: wait=8" \
  -F "image=@product.jpg" \
  -F 'options={"quality":"balanced","alpha":{"mode":"raw","feather":0},"output":"cutout"}'

A fast job returns 200 OK. After eight seconds, you get 202 Accepted and a job URL in Location.

Service documents and health

Method and pathPurpose
GET /openapi.jsonOpenAPI 3.1 contract used to generate SDKs
GET /health/liveConfirms that the API process is alive
GET /health/readyReports whether required processing dependencies are ready

Authentication

Protected requests use a bearer API key:

Authorization: Bearer bg_live_••••••••••••••••••••

Keys appear once, are stored as hashes, and support scopes and expiry. Keep them server-side and out of URLs, logs, browsers, and support requests. Anonymous removal uses a signed device cookie and excludes full resolution, batches, keys, webhooks, and projects.

Common scopes:

ScopeAllows
images:writeUploads, removal jobs, batches, refinements, exports
jobs:readJob, event, result, history, and credit reads
webhooks:writeWebhook endpoint management
keys:writeAPI-key management

Request conventions

Idempotency

Creation endpoints require Idempotency-Key. Retry the same operation with the same key and body. Different input returns 409 idempotency_conflict.

Bounded waiting

Use Prefer: wait=8 to wait up to eight seconds. Success returns 200; otherwise you get 202.

States and stages

States: queued, processing, succeeded, failed, canceled. Stages: preparing, segmenting, refining, rendering, storing, complete.

Limits

LimitValue
Input formatsJPEG, PNG, WebP, AVIF, HEIC, HEIF
Minimum dimensions32×32
Maximum pixels25 megapixels
Maximum bytes50 MB
Batch size100 images
Remote redirects5
AI refinements20 per project
Result URL10 minutes
Binary retention24 hours after terminal completion

Source URLs must be public HTTP or HTTPS. Private networks, metadata services, credentials, unsafe DNS, and rebinding are blocked.

Uploads

Use direct uploads for large files and batches. Local mode returns an API path; persistent environments may return a presigned storage URL.

POST/v1/uploads

Creates a short-lived upload reservation. No body is required.

{
  "id": "019c…",
  "uploadUrl": "/v1/uploads/019c…/content",
  "expiresAt": "2026-08-05T12:15:00.000Z"
}
PUT/v1/uploads/{id}/content

Upload raw bytes with Content-Type. The server still validates the decoded image.

curl -X PUT "$BASE_URL/v1/uploads/$UPLOAD_ID/content" \
  -H "Authorization: Bearer $BLANKGROUND_API_KEY" \
  -H "Content-Type: image/jpeg" \
  --data-binary @product.jpg
POST/v1/uploads/{id}/complete

Inspect and confirm the upload. Returns verified dimensions, media type, and ready status.

Background removals

POST/v1/removals

Accepts multipart data with image, or JSON with exactly one of uploadId and sourceUrl.

Multipart parameters:

NameTypeRequiredDescription
imagefileyesJPEG, PNG, WebP, AVIF, HEIC, or HEIF
optionsJSON stringyesStrict removal options object

Removal options:

FieldValuesMeaning
qualityfast, balanced, highControls inference resolution
alpha.moderaw, binaryPreserves model alpha or creates a hard mask
alpha.thresholdnumber from 0 to 1Required for binary; rejected for raw
alpha.feathernumber from 0 to 4Edge softening in pixels; use 0 for an untouched edge
outputmask, cutoutReturns a grayscale PNG mask or transparent PNG cutout

The object is strict and mandatory. Legacy format and recipe fields are rejected.

JSON request:

{
  "uploadId": "019c…",
  "options": {
    "quality": "high",
    "alpha": { "mode": "binary", "threshold": 0.55, "feather": 1 },
    "output": "mask"
  }
}

Response:

{
  "id": "019c…",
  "projectId": "019c…",
  "status": "queued",
  "stage": "queued",
  "format": "png",
  "createdAt": "2026-08-05T12:00:00.000Z",
  "result": null,
  "error": null,
  "warnings": [],
  "links": {
    "self": "/v1/jobs/019c…",
    "events": "/v1/jobs/019c…/events",
    "result": "/v1/jobs/019c…/result",
    "cancel": "/v1/jobs/019c…"
  }
}

Catalog image generation

POST/v1/catalog-images

Generates a product-only PNG from one to four ordered references and persists it in Blankground's private MinIO storage. This endpoint requires an account or API key with images:write.

curl --fail-with-body https://api.blankground.ru/v1/catalog-images \
  -H "Authorization: Bearer $BLANKGROUND_API_KEY" \
  -F "references=@front.webp" \
  -F "references=@back.webp" \
  -F "references=@detail.webp" \
  -F "prompt=Create an exact front-facing catalog image of this garment" \
  -F "width=768" \
  -F "height=768" \
  -F "steps=6" \
  --output catalog.png

Put the clean front view first, followed by back, side, and detail views. Prompts accept Unicode; English is usually the most precise for materials, construction, lighting, and composition. The response is 201 image/png. Location points to the saved result, X-Catalog-Image-Id contains its ID, and X-Generation-Seed makes the run reproducible.

GET/v1/catalog-images/{id}

Downloads the saved PNG for the owning workspace. Catalog images follow the same 24-hour binary retention policy as removal results.

Jobs and results

GET/v1/jobs/{id}

Returns status, stage, timestamps, model version, result metadata, or a stable error.

GET/v1/jobs

Returns workspace history. Anonymous history requires a known job ID in the same device session.

GET/v1/jobs/{id}/events

Returns a text/event-stream. Event fields include id, type, occurredAt, workspaceId, aggregateId, correlationId, and data.

id: 019c…
event: image.job.completed.v1
data: {"id":"019c…","type":"image.job.completed.v1","data":{"jobId":"019c…"}}

Reconnect with the last event ID. After a gap, reconcile with GET /v1/jobs/{id}.

DELETE/v1/jobs/{id}

Cancel queued jobs immediately. Active jobs may briefly remain processing. Cancellation releases the credit hold.

GET/v1/jobs/{id}/result

Returns the result or a signed ten-minute URL. Expired files return 410 expired_asset; job metadata remains.

Batches

POST/v1/batches

Creates up to 100 removal jobs from completed upload IDs. Requires authentication and Idempotency-Key.

{
  "uploadIds": ["019c…", "019d…"],
  "options": {
    "quality": "balanced",
    "alpha": { "mode": "raw", "feather": 0 },
    "output": "cutout"
  }
}

The response is 202 Accepted with a batch ID, total, status, and child jobs.

GET/v1/batches/{id}

Returns batch progress and child jobs. Partial failure keeps successful results.

Projects and editing

POST/v1/projects/{id}/revisions

Creates an immutable revision. A stale baseRevisionId returns 409 revision_conflict.

{
  "baseRevisionId": "019c…",
  "strokes": [
    {
      "tool": "restore",
      "points": [
        [0.42, 0.17],
        [0.43, 0.18]
      ],
      "size": 0.02,
      "hardness": 0.7,
      "opacity": 1
    }
  ],
  "recipe": { "version": 1, "background": { "type": "transparent" } }
}

Stroke coordinates and sizes are normalized to the source image so server-side rasterization is deterministic at original resolution.

POST/v1/projects/{id}/refinements

Queues point or box refinement. It shares the workspace slot, costs no extra credit, and is capped at 20 per project.

{
  "points": [
    { "x": 0.42, "y": 0.33, "label": "foreground" },
    { "x": 0.61, "y": 0.45, "label": "background" }
  ],
  "baseRevisionId": "019c…"
}
POST/v1/projects/{id}/exports

Renders a project revision with an optional recipe and png, webp, jpeg, zip, or psd format. Renders do not consume another removal credit.

Credits

GET/v1/usage

Workspace response:

{
  "kind": "workspace",
  "limit": 100,
  "available": 87,
  "held": 2,
  "used": 11,
  "periodEndsAt": "2026-09-01T00:00:00.000Z"
}

Anonymous responses show the 7-per-day balance and UTC reset. Credits are held on start, charged on success, and released on failure or cancellation.

DELETE/v1/device

Deletes this browser’s anonymous cookie. It does not delete an account or reset the allowance.

Email authentication

POST/v1/auth/email/start

Body: { "email": "person@example.com" }. Sends a six-digit, ten-minute, single-use code. Responses do not reveal whether an email already exists.

POST/v1/auth/email/verify

Body: { "email": "person@example.com", "code": "123456" }. Creates the default workspace when needed and returns the authenticated session representation. Five failed attempts invalidate the challenge.

API keys

GET/v1/api-keys

Lists key metadata—ID, name, prefix, scopes, creation, expiry, and last use—never the secret.

POST/v1/api-keys
{
  "name": "Catalog production",
  "scopes": ["images:write", "jobs:read"],
  "expiresAt": "2027-01-01T00:00:00.000Z"
}

Returns 201 Created and the secret once. Store it immediately.

DELETE/v1/api-keys/{id}

Revokes the key now. For rotation, create the new key before deleting the old one.

Webhooks

GET/v1/webhooks

Lists workspace webhook endpoints and delivery configuration without returning signing secrets.

POST/v1/webhooks
{
  "url": "https://example.com/hooks/blankground",
  "events": ["image.job.completed.v1", "image.job.failed.v1"]
}

The signing secret is displayed once. Public endpoints must use HTTPS.

DELETE/v1/webhooks/{id}

Disables delivery without destroying its audit history.

GET/v1/webhooks/{id}/deliveries

Returns the latest 100 attempts with status, attempt count, endpoint HTTP status, and failure detail.

POST/v1/webhooks/{endpointId}/deliveries/{deliveryId}/replay

Queues a failed or completed delivery for replay. The delivery ID remains stable for deduplication.

Verifying a delivery

webhook-timestamp, webhook-id, and webhook-signature identify the delivery. Compute HMAC-SHA256 over:

{timestamp}.{deliveryId}.{rawRequestBody}

Compare in constant time, reject stale timestamps, and store delivery IDs against replay. Return 2xx after durable acceptance. Retries continue for 24 hours.

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

function verify(
  secret: string,
  timestamp: string,
  deliveryId: string,
  rawBody: string,
  supplied: string,
) {
  const expected = createHmac("sha256", secret)
    .update(`${timestamp}.${deliveryId}.${rawBody}`)
    .digest("hex");
  return timingSafeEqual(Buffer.from(expected), Buffer.from(supplied));
}

Device authorization

Photoshop and other limited-input clients use a device-code flow.

POST/v1/device-authorizations

Body: { "client": "photoshop" }. Returns deviceCode, human-readable userCode, verification URI, expiry, and polling interval.

POST/v1/device-authorizations/{code}/approve

Called by an authenticated browser after the user confirms the code and workspace. The code is short lived and single use.

POST/v1/device-authorizations/token

Body: { "deviceCode": "…" }. Returns 428 authorization_pending until approval, then returns access and refresh credentials. Clients must respect the polling interval.

Errors

Errors use application/problem+json and always say whether retrying is safe:

{
  "type": "https://docs.blankground.ru/errors/quota-exhausted",
  "status": 402,
  "code": "quota_exhausted",
  "title": "Quota Exhausted",
  "detail": "No full-resolution credits remain for this period.",
  "retryable": false,
  "requestId": "req_019c…"
}
HTTPCodeMeaning
400invalid_inputBody, parameters, image, or dimensions are invalid
401invalid_api_keyCredential is absent, invalid, expired, or revoked
403insufficient_scopeKey lacks the required scope
404job_not_foundResource does not exist in the authenticated workspace
409idempotency_conflictKey was reused with different input
409revision_conflictEdit base is stale
402quota_exhaustedNo workspace processing capacity remains
410asset_expiredMetadata exists but result bytes expired
413image_too_largeByte or pixel limit exceeded
415unsupported_mediaFormat or signature is unsupported
422unsafe_urlRemote URL resolves to a prohibited target
428authorization_pendingDevice flow awaits approval
429rate_limit_exceededRequest rate, anonymous allowance, or safety cap hit
500processing_failedTerminal processor failure
503processor_unavailableNo healthy processing capacity

Retry 429 after reset. Retry idempotent 5xx requests with backoff and jitter. Fix invalid input before retrying. Every response includes X-Request-Id, RateLimit, and RateLimit-Policy; retryable throttling and availability errors also include Retry-After.

SDK examples

The TypeScript and Python SDKs are generated from the same OpenAPI 3.1 document served at /openapi.json.

import { Blankground } from "@bg/sdk";

const client = new Blankground(process.env.BLANKGROUND_API_KEY!);
const result = await client.remove({ image: file, waitSeconds: 8 });
import os
from blankground_api_client import AuthenticatedClient
from blankground_api_client.api.processing import create_removal
from blankground_api_client.models.create_removal_files_body import CreateRemovalFilesBody
from blankground_api_client.types import File

client = AuthenticatedClient(
    base_url="https://api.blankground.ru",
    token=os.environ["BLANKGROUND_API_KEY"],
)

with open("product.jpg", "rb") as image:
    response = create_removal.sync_detailed(
        client=client,
        body=CreateRemovalFilesBody(image=File(payload=image, file_name="product.jpg")),
        idempotency_key="catalog-item-184-v1",
        prefer="wait=8",
    )

Generated method signatures track the committed OpenAPI document. Check the SDK README for the exact version’s multipart wrapper types.