Dyver × Runflow

AI Workflow Showcase

Production results and integration reference for AI-powered image processing workflows.

Get started

Three steps to your first batch

You'll need a Runflow account, an API key, and enough credit to run a batch. About two minutes end to end.

1
Create an account

Sign up at app.runflow.io and confirm your email.

2
Create an API key

Go to Settings → API Keys and create one. Copy it immediately — the full secret is only shown at creation. Keys start with rf_live_.

3
Top up your balance

Add credits in your account billing settings. Batches are admission-gated — if the projected cost exceeds your balance, the request is rejected with 402 INSUFFICIENT_CREDITS and nothing is created or charged.

First request

Run a single image through the full pipeline

curl
curl -X POST https://api.runflow.io/v1/models/runflow/dyver-removal/batches \
  -H "Authorization: Bearer $RUNFLOW_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "input": [
      {
        "image_url": "https://cdn.example.com/shirt.jpg",
        "config": {
          "elements": ["model_removal", "tag_removal", "product_isolation", "background_removal"]
        }
      }
    ]
  }'

You'll get back a batch ID. Poll GET /v1/batches/{id}, or set a callback_url to receive results via webhook. The API tab has the full contract.

Overview

Batch Image Processing

Process images one at a time, or batch up to 100 per request. Each item runs independently — partial success is first-class, and you only pay for items that succeed. Results come back via webhook or polling.

The model

runflow/dyver-removal

A 4-in-1 pipeline. Pick any combination of operations per item — they always run in the fixed order below, skipping any you didn't ask for. The response returns every stage as a separate image (the intermediates) plus a final composite. Match result_* in the URL to pick out the final; intermediates are named after their stage (e.g. bg_remover_00001_.webp).

1. Model removal

model_removal

2. Tag removal

tag_removal

3. Product isolation

product_isolation

4. Background removal

background_removal
Per-item input

What you send for each image

Item
{
  "image_url": "https://cdn.example.com/shirt.jpg",
  "config": {
    "elements": ["tag_removal", "background_removal"],
    "aspect_ratio": "1:1",
    "product_isolation_prompt": "the white sneaker"
  },
  "client_ref": "sku-123"
}

elements is required — one or more of the operations above. aspect_ratio and product_isolation_prompt are optional. client_ref is your own label; we echo it back verbatim so you can match items to your records.

Single image

Run one image

For one-off runs, skip the batch wrapper and hit the singleton endpoint. Same per-item body, wrapped in input.

Request
POST https://api.runflow.io/v1/models/runflow/dyver-removal/runs
Authorization: Bearer {api_key}
Content-Type: application/json

{
  "input": {
    "image_url": "https://cdn.example.com/shirt.jpg",
    "config": { "elements": ["tag_removal", "background_removal"] }
  },
  "callback_url": "https://you.example/webhooks/runflow",
  "metadata": { "sku": "123" }
}

Poll GET /v1/runs/{run_id} or wait for the callback. Per-run callbacks fire once at terminal state with event run.completed, run.failed, or run.cancelled:

Callback payload
{
  "event": "run.completed",
  "run_id": "01984c44-...",
  "status": "succeeded",
  "output": {
    "image_urls": [
      "https://.../tag_remover_00001_.webp",
      "https://.../bg_remover_00001_.webp",
      "https://.../result_00001_.webp"
    ]
  },
  "duration_ms": 18420,
  "created_at": "2026-04-16T10:00:00Z",
  "completed_at": "2026-04-16T10:00:18Z",
  "metadata": { "sku": "123" }
}

If the run fails or is cancelled, output is null and status is failed or cancelled. Webhook signing and retry behavior are the same as batches (see below).

Batches

Run up to 100 images in one request

Request
POST https://api.runflow.io/v1/models/runflow/dyver-removal/batches
Authorization: Bearer {api_key}
Content-Type: application/json

{
  "input": [
    {
      "image_url": "https://cdn.example.com/a.jpg",
      "config": { "elements": ["tag_removal", "background_removal"] },
      "client_ref": "sku-123"
    },
    {
      "image_url": "https://cdn.example.com/b.jpg",
      "config": { "elements": ["model_removal"] }
    }
  ],
  "callback_url": "https://you.example/webhooks/runflow",
  "metadata": { "campaign": "spring-drop" }
}
Response — 201 Created
{
  "id": "01984c44-...",
  "status_code": "queued",
  "items_total": 2,
  "items_succeeded": 0,
  "items_failed": 0,
  "items": [
    { "id": "01984c45-...", "sequence_index": 0, "client_ref": "sku-123", "run_id": "01984c46-...", "status_code": "queued" },
    { "id": "01984c47-...", "sequence_index": 1, "client_ref": null,      "run_id": "01984c48-...", "status_code": "queued" }
  ],
  "output": null,
  "created_at": "2026-04-16T10:00:00Z"
}

Every item comes back with four correlation handles — id, sequence_index, client_ref, and run_id. Use whichever fits your system.

Get results

Poll or webhook

1
Polling

GET /v1/batches/{batch_id} — done when items_succeeded + items_failed + items_cancelled == items_total.

2
Webhook

Set callback_url on create. We POST one signed webhook at terminal state — batch.succeeded, batch.partial_succeeded, batch.failed, or batch.cancelled.

3
Read the envelope

At terminal, batch.output lists every item in order, each with its original input plus either an output or an error. No cross-referencing needed.

Terminal envelope (partial success)
{
  "status_code": "partial_succeeded",
  "items_succeeded": 1,
  "items_failed": 1,
  "cost": "0.410000",
  "output": {
    "items": [
      {
        "sequence_index": 0,
        "status_code": "succeeded",
        "client_ref": "sku-123",
        "input":  { "image_url": "...", "config": { "elements": ["tag_removal", "background_removal"] } },
        "output": { "image_urls": [
          "https://.../tag_remover_00001_.webp",
          "https://.../bg_remover_00001_.webp",
          "https://.../result_00001_.webp"
        ] }
      },
      {
        "sequence_index": 1,
        "status_code": "failed",
        "input": { "image_url": "...", "config": { "elements": ["model_removal"] } },
        "error": { "kind": "upstream_failed", "message": "..." }
      }
    ],
    "succeeded": 1,
    "failed": 1,
    "total": 2
  }
}
Callbacks

Webhook signing

Create an org-scoped secret once. The raw secret is returned at creation time only — store it safely.

One-time setup
POST /v1/callback-secrets
{ "label": "webhook-prod" }

→ { "id": "...", "secret": "rf_whsec_...", "label": "webhook-prod" }

Every delivery then carries three headers:

Callback headers
Runflow-Signature: v1=9f3a1e...
Runflow-Timestamp: 1745234442
Runflow-Request-Id: 01984d10-7c2b-7f3e-8b4d-1a2b3c4d5e6f-1

Verify by computing HMAC-SHA256(secret, timestamp + "." + raw_body) and comparing against the v1= value. Reject timestamps more than five minutes off. Retries reuse the delivery ID — dedupe by stripping the trailing -{attempt_number} from Runflow-Request-Id.

Errors & limits

Common responses

Error envelope
{
  "code": "VALIDATION_ERROR",
  "message": "Request validation failed",
  "errors": [
    { "loc": ["body", "input", 0, "image_url"], "msg": "field required", "type": "missing" }
  ]
}

Watch for 401 UNAUTHORIZED (bad key), 402 INSUFFICIENT_CREDITS (admission-gated, nothing is created or charged), 404 NOT_FOUND, 422 VALIDATION_ERROR (per-field detail in errors[]), and 429 RATE_LIMITED (respect Retry-After).

Limits: up to 100 items per batch · client_ref ≤ 255 chars · metadata ≤ 16 KB · callback_url must be HTTPS.

Need higher batch sizes? The 100-item cap is the current default — reach out to your Runflow contact and we'll raise it for your org.

1 / 100
Navigate  ·  Esc Close  ·  Drag to compare