Skip to content
Get started

Published apps

#What a published app is

A published app is a single agent exposed as a small, branded web app (its own template, logo, and colours) that an end user fills in directly — no API key, no console access. Under the hood it still runs an agent in your workspace and charges your workspace's credits; the app just adds its own access gate and per-day usage caps on top.

The Drop & extract template is the first one: a branded header, a drop zone (accepted file types come from the underlying agent's inputSpec), any extra input fields the preset defines, and a result page showing the structured output as a table plus a JSON/CSV download.

#Creating and managing an app

Management routes require scope apps:manage:

bash
curl -X POST "https://api.agentflowbind.com/v1/apps" \
  -H "Authorization: Bearer afb_live_xxxxxxxxxxxx" -H "Content-Type: application/json" \
  -d '{
    "agentId": "3c1a9e40-...-77bd",
    "template": "drop-and-extract",
    "access": { "mode": "password", "password": "correct-horse-battery-staple" }
  }'
json
{
  "id": "d4a1e9c0-...",
  "workspaceId": "1a2b3c4d-...",
  "profileId": "3c1a9e40-...-77bd",
  "template": "drop-and-extract",
  "slug": "drop-and-extract-9f2c1e4b",
  "theme": {},
  "access": { "mode": "password", "requireBotCheck": false },
  "domain": null,
  "customDomain": null,
  "domainVerification": null,
  "caps": {},
  "endUserAuth": null,
  "status": "draft",
  "createdAt": "2026-03-14T09:00:00.000Z",
  "updatedAt": "2026-03-14T09:00:00.000Z"
}

Omit slug to get one generated from template; supply your own (3–63 lowercase letters/digits/hyphens) and a taken one returns 409 slug_taken. A password's hash is never returned (nor is a link mode's linkToken regenerated unless you pass access.regenerateLinkToken: true on a PATCH).

bash
curl "https://api.agentflowbind.com/v1/apps" -H "Authorization: Bearer afb_live_xxxxxxxxxxxx"
curl -X PATCH "https://api.agentflowbind.com/v1/apps/d4a1e9c0-..." -H "Authorization: Bearer afb_live_xxxxxxxxxxxx" -H "Content-Type: application/json" -d '{"caps": {"dailyRuns": 50, "dailyCredits": 2000, "maxFileMb": 10}}'
curl -X DELETE "https://api.agentflowbind.com/v1/apps/d4a1e9c0-..." -H "Authorization: Bearer afb_live_xxxxxxxxxxxx"

# Go live / take offline — a draft app's public routes 404 until published
curl -X POST "https://api.agentflowbind.com/v1/apps/d4a1e9c0-.../publish" -H "Authorization: Bearer afb_live_xxxxxxxxxxxx"
curl -X POST "https://api.agentflowbind.com/v1/apps/d4a1e9c0-.../pause" -H "Authorization: Bearer afb_live_xxxxxxxxxxxx"

#Access modes

Mode POST .../session body Notes
public {} No gate at all
link { "linkToken": "..." } Must match the app's own access.linkToken
password { "password": "..." } Checked against the sealed hash set at creation/update
magic_link { "email": "..." } Sends a sign-in email; redeem with .../session/verify

#Custom domains

bash
curl -X POST "https://api.agentflowbind.com/v1/apps/d4a1e9c0-.../domain" \
  -H "Authorization: Bearer afb_live_xxxxxxxxxxxx" -H "Content-Type: application/json" \
  -d '{ "domain": "invoices.acme.example" }'
json
{ "domain": "invoices.acme.example", "txtRecordName": "_afb-verify.invoices.acme.example", "txtRecordValue": "9f2c1e4b7a...", "app": { "...": "..." } }

Create the TXT record at txtRecordName with txtRecordValue, then:

bash
curl -X POST "https://api.agentflowbind.com/v1/apps/d4a1e9c0-.../domain/verify" -H "Authorization: Bearer afb_live_xxxxxxxxxxxx"

which does a live DNS lookup and returns 422 domain_verification_failed if the record isn't found yet — retry once DNS has propagated.

#Usage

bash
curl "https://api.agentflowbind.com/v1/apps/d4a1e9c0-.../usage" -H "Authorization: Bearer afb_live_xxxxxxxxxxxx"
json
{
  "day": "2026-03-14",
  "today": { "runs": 12, "credits": 340 },
  "caps": { "dailyRuns": 50, "dailyCredits": 2000, "maxFileMb": 10 },
  "retryAfterSeconds": 51840,
  "days": [ { "day": "2026-03-13", "runs": 41, "credits": 1180 } ]
}

retryAfterSeconds is how long until the daily cap resets (UTC midnight) — the same value a capped end user's 429 would carry.

#The public runtime (what an end user's browser calls)

No API key, no session cookie — identified entirely by :slug (or a verified custom domain), then an afb_app_session cookie for everything past the access gate. Every route is rate-limited per client IP (60 req/min), separately from the key-based limits in Authentication & scopes.

bash
# 1. Read the app's public config
curl "https://api.agentflowbind.com/v1/public/apps/drop-and-extract-9f2c1e4b"
# -> { "slug", "template", "theme", "access": { "mode", "requireBotCheck" }, "locale", "inputSpec" }

# 2. Open a session (here: password mode) — sets afb_app_session
curl -c cookies.txt -X POST "https://api.agentflowbind.com/v1/public/apps/drop-and-extract-9f2c1e4b/session" \
  -H "Content-Type: application/json" -d '{ "password": "correct-horse-battery-staple" }'

# 3. Upload, then create a run — same shapes as the authenticated /v1/files and /v1/runs
curl -b cookies.txt -X POST "https://api.agentflowbind.com/v1/public/apps/drop-and-extract-9f2c1e4b/files" \
  -H "Content-Type: application/pdf" -H "X-Filename: invoice.pdf" --data-binary @invoice.pdf
curl -b cookies.txt -X POST "https://api.agentflowbind.com/v1/public/apps/drop-and-extract-9f2c1e4b/runs" \
  -H "Content-Type: application/json" -d '{ "input": { "fileIds": ["..."] } }'
# -> 202 { "id", "status": "queued" }

# 4. Read it back — only through the same session that created it
curl -b cookies.txt "https://api.agentflowbind.com/v1/public/apps/drop-and-extract-9f2c1e4b/runs/{id}"
curl -b cookies.txt "https://api.agentflowbind.com/v1/public/apps/drop-and-extract-9f2c1e4b/runs/{id}/output"

GET .../runs/:id/events streams the same SSE shape as the authenticated run events endpoint, capped at 20 concurrent streams per app. A run created this way lands in the owner workspace with input.fields.sourceApp set to the app's slug — it shows up in the owner's own GET /v1/runs list like any other run, and (per row) is only ever readable back through the exact app session that created it, never through a different session or slug.

#Errors specific to published apps

HTTP code When
401 app_session_required Called files/runs/run-read without a valid afb_app_session cookie
401 invalid_app_credentials Wrong password, link token, or magic-link token
400 bot_check_failed access.requireBotCheck is set and the bot-check token was missing/invalid
429 app_cap_reached The app's caps.dailyRuns/dailyCredits for today is exhausted — Retry-After set to seconds until UTC midnight
503 app_unavailable The underlying agent has no published version, or the owner workspace's credit balance can't cover the run (never disclosed to the end user as a billing error)
422 upload_rejected The upload scanner flagged the file
404 not_found Unknown slug, or an app that's draft/paused (indistinguishable from "never existed")

#Data handling

A published app processes the end user's upload the same way any other run does: through your workspace's chosen model providers, billed to your workspace. The app's own /about-data page explains this to the end user in plain terms — see Data & providers for the underlying provider-transparency model that page draws from.