How do I let others run my workflow via API with their own credits?
Publish your workflow as an App: others run it via API with POST /api/v1/apps/{slug}/execute and their own key, and their own credits pay for each run.
To let other people run your workflow through the API, publish it as an App. Anyone with a Wireflow API key that has the apps:execute scope can then start it with POST /api/v1/apps/{slug}/execute, and the key owner's credits pay for every node in the run, not yours.
Your own workflows are already an API for you: POST /api/v1/workflows/{id}/run runs any workflow you own with your key, as shown in How do I run an AI workflow from my app. That route is owner-only. Publishing adds a second route, by slug, that other accounts can call. This page covers the API side. For the app's web page, sign-in and subscriptions, see Publishing Apps.
How do I deploy a workflow as a production API?
Publish it, either with the Publish button in the editor or with POST /api/v1/workflows/{id}/publish and a key that has the workflows:write scope. The reply gives you the app's slug, which is the name callers use.
curl -X POST https://www.wireflow.ai/api/v1/workflows/YOUR_WORKFLOW_ID/publish \
-H "Authorization: Bearer $WIREFLOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Kawarimi",
"description": "Prompt in, image out.",
"customSlug": "kawarimi"
}'
const res = await fetch('https://www.wireflow.ai/api/v1/workflows/YOUR_WORKFLOW_ID/publish', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.WIREFLOW_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
title: 'Kawarimi',
description: 'Prompt in, image out.',
customSlug: 'kawarimi',
}),
});
if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
const app = await res.json();
console.log(app.slug, app.url);
import os
import requests
res = requests.post(
"https://www.wireflow.ai/api/v1/workflows/YOUR_WORKFLOW_ID/publish",
headers={"Authorization": f"Bearer {os.environ['WIREFLOW_API_KEY']}"},
json={"title": "Kawarimi", "description": "Prompt in, image out.", "customSlug": "kawarimi"},
)
res.raise_for_status()
print(res.json()["slug"])
Response:
{
"slug": "kawarimi",
"url": "https://kawarimi.wireflow.ai",
"publishedAt": "2026-10-02T12:00:00.000Z",
"publishSettings": {
"title": "Kawarimi",
"exposedInputs": ["node-prompt"],
"dailyCreditBudget": 100,
"creditsPerRun": 0,
"visibility": "public"
}
}
Before you rely on it:
customSlugtakes 3 to 60 lowercase letters, numbers and hyphens, starting and ending with a letter or number. A slug that another app already uses returns409.exposedInputslists the input node ids that callers may set. Leave it out and every Input node is exposed.- Every publish call rebuilds the settings from its body. Send
title,description,exposedInputs,exampleOutputsanddailyCreditBudgetagain on each update, or they reset.visibilityis the exception: leave it out and it stays as it was. - The app runs the saved workflow at call time, so your edits apply from the next run.
- Callers get the outputs of the nodes you picked as example outputs, or, if you picked none, of the workflow's last nodes.
DELETE /api/v1/workflows/{id}/publishunpublishes the app. The slug stays reserved for you.
How do I find the input keys callers send?
GET /api/v1/apps/{slug} returns the app's public config, and each entry in inputs has the key to send. For a public app, this call needs no API key.
curl https://www.wireflow.ai/api/v1/apps/kawarimi
{
"title": "Kawarimi",
"description": "Prompt in, image out.",
"inputs": [
{
"nodeId": "node-prompt",
"key": "prompt",
"label": "Prompt",
"type": "text",
"placeholder": ""
}
],
"ownerName": "Your Name",
"publishedAt": "2026-10-02T12:00:00.000Z"
}
- A key is the key set on the Input node, or else its label or name: lowercased, with any run of characters other than A to Z and 0 to 9 turned into one underscore, and cut to 64 characters.
Reference Imagesbecomesreference_images. - Renaming the label changes the key, so tell your callers when you rename one.
- The config never includes an input's saved value, only its id, key, label, type and placeholder.
- After you publish, the Publish dropdown in the editor shows a ready-made curl call with your slug filled in.
How do I call a published workflow with curl?
Send the input values to POST /api/v1/apps/{slug}/execute with your API key, then GET the pollUrl it returns until the run finishes.
curl -X POST https://www.wireflow.ai/api/v1/apps/kawarimi/execute \
-H "Authorization: Bearer $WIREFLOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{"inputs": {"prompt": "a fox in a paper boat"}}'
Response 200 OK, which means the run has started, not finished:
{
"executionId": "cm1exec123",
"status": "RUNNING",
"pollUrl": "/api/v1/apps/kawarimi/executions/cm1exec123/poll",
"pollToken": "..."
}
Put https://www.wireflow.ai in front of pollUrl and poll it every few seconds:
curl https://www.wireflow.ai/api/v1/apps/kawarimi/executions/cm1exec123/poll \
-H "Authorization: Bearer $WIREFLOW_API_KEY"
{
"status": "COMPLETED",
"outputs": [
{
"nodeId": "node-image",
"type": "image:generate",
"urls": ["https://cdn.wireflow.ai/outputs/abc.png"]
}
],
"outputsWithheld": 0
}
The same flow in JavaScript and Python:
const HOST = 'https://www.wireflow.ai';
const headers = {
Authorization: `Bearer ${process.env.WIREFLOW_API_KEY}`,
'Content-Type': 'application/json',
};
const FINAL = ['COMPLETED', 'FAILED', 'CANCELLED', 'TIMEOUT'];
const start = await fetch(`${HOST}/api/v1/apps/kawarimi/execute`, {
method: 'POST',
headers,
body: JSON.stringify({ inputs: { prompt: 'a fox in a paper boat' } }),
});
if (!start.ok) throw new Error(`${start.status}: ${await start.text()}`);
const { pollUrl } = await start.json();
let result;
while (true) {
const res = await fetch(`${HOST}${pollUrl}`, { headers });
if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
result = await res.json();
if (FINAL.includes(result.status)) break;
await new Promise((r) => setTimeout(r, 3000));
}
if (result.status !== 'COMPLETED') throw new Error(result.error || result.status);
for (const item of result.outputs) console.log(item.nodeId, item.urls ?? item.text);
import os
import time
import requests
HOST = "https://www.wireflow.ai"
HEADERS = {"Authorization": f"Bearer {os.environ['WIREFLOW_API_KEY']}"}
FINAL = {"COMPLETED", "FAILED", "CANCELLED", "TIMEOUT"}
start = requests.post(
f"{HOST}/api/v1/apps/kawarimi/execute",
headers=HEADERS,
json={"inputs": {"prompt": "a fox in a paper boat"}},
)
start.raise_for_status()
poll_url = start.json()["pollUrl"]
while True:
res = requests.get(HOST + poll_url, headers=HEADERS)
res.raise_for_status()
result = res.json()
if result["status"] in FINAL:
break
time.sleep(3)
if result["status"] != "COMPLETED":
raise RuntimeError(result.get("error") or result["status"])
for item in result["outputs"]:
print(item["nodeId"], item.get("urls") or item.get("text"))
Keep polling while status is RUNNING or PENDING. COMPLETED and FAILED are the normal endings, CANCELLED and TIMEOUT can also appear, and a failed run adds an error string. Each item in outputs has nodeId and type, plus urls for media, text, or both. outputsWithheld counts outputs that were held back because they matched a value the app's author saved, and 0 means nothing was held back.
Rules every call follows:
- A value is a string of at most 2000 characters, and an image input must be an
https://URL. - Video and audio inputs cannot be set through the Apps API yet. A value sent under such a key is refused with
400(unsupported_media_input), and nothing is charged. - A key that matches no exposed input returns
400withcode: "unknown_input"and the list ofvalidKeys. Nothing runs and nothing is charged. - Execute returns once the run has started. There is no option to wait for the result in one call.
- Agents can run the same app over MCP with
run_app, using the same input keys.
Who pays when someone else runs my workflow?
The owner of the API key that starts the run pays the credits for every node in it. You, the app's creator, are not charged.
- The key owner needs more than 0 credits to start a run, or the call returns
402. - A call with no
Authorizationheader counts as a visitor run on the public page. You pay for it, a limit of 3 requests per minute per IP applies, and the app'sdailyCreditBudget, 100 credits a day by default, caps public use. A header that does not start withBearer sk-is treated the same way, so use a key for anything programmatic. - Calling your own app with your own key costs your credits, like any other key call.
What is the difference between my API key and an app's key?
An app has no key of its own. Every caller uses their own sk- API key with the apps:execute scope, and that key decides who pays and who can read the result.
- A Full access key has
apps:execute. A new key starts read-only, so tick the scope or choose Full access when you create the key in Settings > API Keys. - The poll accepts any
apps:executekey from the same account that started the run, or that run'spollToken. A key from another account gets404. - Results are readable by the key or token that started the run, not by you as the app's creator through this route.
- To check which account a key acts as, call
GET /api/v1/me.
How do I charge or limit usage per user?
Over the API you don't set a price: each caller pays the model credits for their own run, and each API key can start 20 app runs a minute. A per-run price (creditsPerRun) set on the app is charged to signed-in visitors on the app's web page, not on API key calls, and subscriptions are sold on that page too. Set those up in Publishing Apps.
- Over 20 execute calls a minute, a key gets
429. Wait the number of seconds in theRetry-Afterheader. - Keyless visitor runs stop for the day with
429once the app'sdailyCreditBudgetis spent.
Can I make the API private to my own account?
Yes. Publish with "visibility": "private", or flip the Private switch in the Publish dropdown, and only your account can open or run the app. Everyone else gets 404 with {"error": "App not found"}, the same answer as a slug that does not exist.
curl -X POST https://www.wireflow.ai/api/v1/workflows/YOUR_WORKFLOW_ID/publish \
-H "Authorization: Bearer $WIREFLOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Kawarimi", "customSlug": "kawarimi", "visibility": "private"}'
- Call it with your own key that has
apps:execute, or from your own MCP client withrun_app. - Private is about the app, not the board. Link sharing and the
templatetag are separate settings. - To make it public again, publish with
"visibility": "public".
Next steps
- Publish an app
Turn your workflows into standalone apps with subdomains, user auth, billing, and analytics.
- Credits and billing
How Wireflow credits work: what each plan includes and how many runs it gives a month, what models cost per run, and how to buy credits or change your plan.
Related
- Apps API
Publish a workflow as an App, then call it over HTTP with an API key. The caller pays for the run. Make it private to keep it yours.
- Authentication
Create API keys, configure scopes, and authenticate requests to the Wireflow API.