BlitFlow
HTTP API

Artifacts

Turn a remote URL or local bytes into an organization-owned Artifact — the only way binary media enters a run.

Run inputs are Artifact-only: binary media crosses the run boundary as a { kind, ref } Artifact, never as inline bytes, base64, or a data: URI. These endpoints turn media you have — a fetchable URL, or bytes on disk — into an organization-owned, first-party Artifact you pass unchanged to POST /v1/runs or POST /v1/runs/node.

Two paths:

  • Remote import — you have a public URL; blitflow fetches it server-side.
  • Signed upload — you have local bytes; you upload them directly to storage with a short-lived, single-pathname authorization. The bytes never transit blitflow's API servers.

Import a remote URL

POST/v1/artifacts/import

Fetch a public http(s) URL server-side and copy it into first-party storage as an owned Artifact.

Request body

FieldTypeDescription
urlstringThe http(s) URL to import. Must be publicly reachable
orgstringOrganization slug; omit for your personal organization
kindstringOverride the kind derived from the content type: image, audio, video, or file
retentionstringtemporary (default) or durable — see retention

Private and internal destinations (localhost, private IP ranges, cloud metadata endpoints) are rejected, including on redirects. The same size cap and content-type allowlist as direct uploads apply.

curl -X POST "https://studio.blitflow.com/api/v1/artifacts/import" \
  -H "Authorization: Bearer $BLITFLOW_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/tower.jpg"}'

Response

{
  "id": "9c2b1e1a-…",
  "artifact": {
    "kind": "image",
    "ref": "https://…/blitflow/uploads/2026-08/1f9d….jpg",
    "mimeType": "image/jpeg"
  },
  "contentType": "image/jpeg",
  "sizeBytes": 482133,
  "expiresAt": "2026-09-17T12:00:00.000Z"
}

Pass artifact unchanged as a run input:

{ "inputs": { "photo": { "kind": "image", "ref": "https://…/1f9d….jpg" } } }

Errors

StatusCause
400Unreachable URL, blocked (private/internal) destination, or fetch failure
413Remote file over the 50 MB cap
415Content type outside the allowlist

Upload local bytes

Local bytes use a two-step signed upload: prepare an authorization, PUT the bytes directly to storage, then complete to record the Artifact.

Prepare

POST/v1/artifacts/uploads

Create an upload intent and mint a short-lived token constrained to one randomized pathname, the declared content type, and the declared size.

FieldTypeDescription
contentTypestringMIME type of the bytes (see allowlist)
sizeBytesintegerSize of the bytes; the upload is rejected beyond it. Max 52428800 (50 MB)
orgstringOrganization slug; omit for your personal organization
kindstringOverride the kind derived from the content type: image, audio, video, or file
retentionstringtemporary (default) or durable
{
  "id": "5d0a7c22-…",
  "pathname": "blitflow/uploads/2026-08/8ba3….png",
  "uploadUrl": "https://vercel.com/api/blob/blitflow/uploads/2026-08/8ba3….png",
  "token": "vercel_blob_client_…",
  "contentType": "image/png",
  "maxSizeBytes": 482133,
  "tokenExpiresAt": "2026-08-18T13:00:00.000Z",
  "expiresAt": "2026-08-18T14:00:00.000Z"
}

The token authorizes exactly one pathname with the declared content type and size cap, and expires at tokenExpiresAt (1 hour). The intent itself expires at expiresAt (2 hours): complete before then or the intent — and any uploaded bytes — is discarded.

Upload the bytes

Directly to storage — not to blitflow's API. With @vercel/blob:

import { put } from "@vercel/blob/client";

await put(prepared.pathname, bytes, {
  access: "public",
  token: prepared.token,
});

Or raw HTTP:

curl -X PUT "$UPLOAD_URL" \
  -H "Authorization: Bearer $UPLOAD_TOKEN" \
  -H "x-content-type: image/png" \
  -H "x-vercel-blob-access: public" \
  --data-binary @./tower.png

Complete

POST/v1/artifacts/uploads/:id/complete

Verify the uploaded blob against the intent and record the owned Artifact.

Verifies the blob at the prepared pathname — existence, size within the declared cap, matching content type, your organization — then records exactly one artifact ledger row and returns the same shape as /v1/artifacts/import.

Idempotent: completing the same intent again (a retry, a race) returns the same Artifact.

StatusCause
400Nothing uploaded at the prepared pathname yet
404Unknown intent, or an intent belonging to another organization
409Uploaded blob doesn't match the declared size cap or content type
410Intent expired before completion

Limits and retention

LimitValue
Max size50 MB per artifact (uploads and imports)
Content typesimage/*, audio/*, video/*, application/pdf, application/octet-stream

Retention follows the retention field:

  • temporary (default) — the artifact expires 30 days after creation, the same window as run outputs. Right for one-off run inputs.
  • durable — never expires; for workflow Library and recipe assets.

A temporary artifact referenced by a workflow graph is promoted to durable instead of expiring — the same part-of-the-recipe rule that protects run outputs.

The hosted MCP server exposes these operations as artifacts_import, artifacts_uploads_prepare, and artifacts_uploads_complete. No tool accepts base64, byte arrays, data: URIs, or local file paths — models must never serialize file bytes into tool arguments.

On this page