Skip to content

Create and deploy a workflow

This page walks through deploying a workflow so you can call the run API.

  1. Open your project.
  2. Open Create Workflow / Workflows (deployment UI).
  3. Choose the registered model(s) to bind.
  4. Upload or paste workflow.py.
  5. Configure optional routes (for example Dataset / Raw Data).
  6. Activate the workflow.
  7. Copy the generated cURL / Python snippet from the UI.

Screenshot: Create Workflow page — model binding and activate toggle

Set variables:

Terminal window
BASE="https://api.development.labelty.ai/api/v1"
KEY="lty_xxx"
H=(-H "Authorization: Bearer $KEY" -H "Content-Type: application/json")
PID=$(curl -s "$BASE/projects/current" "${H[@]}" \
| python -c "import sys,json; print(json.load(sys.stdin)['data']['id'])")

What the projects/current call does: resolves the project UUID bound to your API key. Deploy paths still include project_id; the run path for API keys does not.

from app.sdk.workflow import WorkflowBase, WorkflowInput, WorkflowOutput
# When packaged for users, the import may be published as labelty_sdk.workflow
class DefectChecker(WorkflowBase):
def load(self, models, config):
# Binding names must match deploy_config.models keys
self.detector = models["detector"]
self.threshold = config.get("threshold", 0.75)
def run(self, input: WorkflowInput) -> WorkflowOutput | None:
preds = self.detector.predict(input.asset)
kept = [
p for p in preds
if (p.metadata or {}).get("confidence", 1.0) >= self.threshold
]
out = WorkflowOutput(predictions=kept)
if any((p.metadata or {}).get("label") == "crack" for p in kept):
# Route name must match deploy_config.outputs
out.route("critical")
return out

Why two methods:

  • load runs when the runner starts the workflow (attach models + config once).
  • run executes per request on one image.

POST /projects/{project_id}/workflows

Terminal window
curl -sX POST "$BASE/projects/$PID/workflows" "${H[@]}" -d '{
"name": "defect-checker",
"version": "v1",
"description": "Filter low-confidence defects; route cracks to review",
"deploy_config": {
"models": { "detector": "<ml_model_uuid>" },
"outputs": { "critical": { "type": "dataset" } },
"config": { "threshold": 0.75 }
}
}'

Save data.id as WID.

outputs.*.type = "dataset" means out.route("critical") uploads the image + predictions into the project for human review (product copy: Raw Data, not the curated dataset until approved).

3. Upload workflow.py (presign → PUT → confirm)

Section titled “3. Upload workflow.py (presign → PUT → confirm)”
Terminal window
UP=$(curl -sX POST "$BASE/projects/$PID/workflows/$WID/presigned-upload" "${H[@]}" \
-d '{"filename": "workflow.py", "content_type": "text/x-python"}')
URL=$(echo "$UP" | jq -r '.data.upload_url')
curl -sX PUT "$URL" -H "Content-Type: text/x-python" --data-binary @workflow.py
curl -sX POST "$BASE/projects/$PID/workflows/$WID/confirm-upload" "${H[@]}" \
-d '{"filename": "workflow.py"}'

Why three steps: Labelty never streams the Python file through the API process; you upload directly to object storage with a short-lived URL, then confirm so the workflow row records the artifact.

Terminal window
curl -sX PATCH "$BASE/projects/$PID/workflows/$WID/active" "${H[@]}" \
-d '{"is_active": true}'

A workflow runs only when it is active and has a confirmed artifact, and deploy_config.models references valid models.

Terminal window
curl -s "$BASE/projects/$PID/workflows" "${H[@]}"
curl -s "$BASE/projects/$PID/workflows/$WID" "${H[@]}"
curl -sX PATCH "$BASE/projects/$PID/workflows/$WID/active" "${H[@]}" -d '{"is_active": false}'
curl -sX DELETE "$BASE/projects/$PID/workflows/$WID" "${H[@]}"

Call the synchronous run endpoint: Call the run API.