For AI agents: this page is also available as Markdown at https://docs.refabric.com/task-apis/recipes.md, and the index of every page is https://docs.refabric.com/llms.txt.
Task APIs
Recipes
Chains of tasks that turn one result into the input of the next, run one step at a time.
A recipe is not one job; you run each step. Every step is an ordinary POST /v1/tasks/{name} with
its own job, its own estimate and its own charge. A recipe tells you which tasks to call in which
order, and which output of one step goes into which input of the next.
The recipes are listed live by GET /v1/recipes: each entry has a name, a title, a summary
and its steps — the task names, in order (List recipes).
{ "items": [ { "name": "<recipe name>", "title": "<what it makes>",
"summary": "<one paragraph>", "steps": ["<first task>", "<next task>", "…"] } ] }How a step feeds the next
A step's output is an address the next step takes as it is — no download, no re-upload:
| The step delivers | You pass it as | Into, for example |
|---|---|---|
a file (art:…) | that string | any media field: image, references[].image |
a record (moodboard:…, brand_kit:…, …) | that handle | the field that takes the record: moodboards, brand_kit |
a part of a record (data.items[].url) | that url | any media field |
Read each step's handle from its result's files[].file (Asynchronous jobs).
Example: from a moodboard to a refined design
Each step waits for the previous job to end. run submits a task and returns its result.
import os, time, uuid, requests
API = "https://api.refabric.com/v1"
s = requests.Session()
s.headers["Authorization"] = f"Key {os.environ['REFABRIC_API_KEY']}"
def run(task, body):
job = s.post(f"{API}/tasks/{task}", json=body, headers={"Idempotency-Key": str(uuid.uuid4())})
job.raise_for_status()
handle = job.json()
while (status := s.get(handle["status_url"]).json())["lifecycle"] != "terminal":
time.sleep(5)
if status["outcome"] != "succeeded":
raise RuntimeError(status["error"])
return s.get(handle["result_url"]).json()
board = run("moodboard.create", {"kind": "brand_dna", "name": "Resort", "images": LOOK_URLS})
board_id = board["files"][0]["file"] # moodboard:…
designs = run("image.generate", {"prompt": "a relaxed linen shirt dress", "moodboards": [board_id]})
first = designs["files"][0]["file"] # art:…
refined = run("image.generate", {"prompt": "the same dress with short sleeves",
"references": [{"image": first}]})LOOK_URLS is your list of look images; moodboard.create states how many it takes. A moodboard
must have finished its analysis before image.generate can use it: a board that is not ready is
refused with moodboard_not_ready, so read it (GET /v1/files/moodboard:<id>) until its
data.status is ready.
If a step fails
- Each step is charged on its own. The steps that succeeded are charged for what they delivered; the step that failed is charged only for what it delivered before failing (Pricing).
- What the earlier steps made stays yours. Continue from the failed step with the same inputs — you do not run the earlier steps again.
- Before you retry a step, read its error's
retryable(Task errors). Retry with the sameIdempotency-Keyonly to repeat a submit whose answer you did not receive.