Orchestration · Quickstart
Define a small workflow, trigger it, watch it run.
Time: ~15 minutes.
Prerequisites
Sandbox API key with scopes: workflows:write, executions:write, service-connections:write.
1. Register sibling services
Before you can call AML or Identity from a workflow, register them in your service connections:
/api/service-connectionscurl -X POST https://sandbox.quantumelixir.tech/orchestration/api/service-connections \
-H "Authorization: Bearer $QE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"serviceSlug": "identity",
"baseUrl": "https://sandbox.quantumelixir.tech/identity",
"apiKey": "qe_sk_sandbox_..."
}'Repeat for aml, anti-fraud, bank-statement, document-intelligence, ai-automation as needed. The API key here is the sibling product's key, not your Orchestration key. Stored encrypted at rest (AES-256-GCM).
2. Create a workflow
/api/workflows{
"slug": "indonesian-kyc-v1",
"name": "Indonesian KYC onboarding",
"description": "KTP capture → AML screening → notify on success",
"spec": {
"startNodeId": "kyc",
"nodes": [
{
"id": "kyc",
"type": "service_call",
"service": "identity",
"method": "POST",
"path": "/api/identity/document/ktp/capture",
"body": {
"customerId": "${trigger.customerId}",
"bundle": "${trigger.bundle}",
"hmacKey": "${trigger.hmacKey}"
}
},
{
"id": "branch_on_ktp",
"type": "decision",
"expression": "step.kyc.output.ktpBundle.verdict",
"cases": {
"passed": { "next": "screening" },
"requires_review": { "next": "wait_for_analyst" },
"failed": { "next": "notify_failure" }
}
},
{
"id": "screening",
"type": "service_call",
"service": "aml",
"method": "POST",
"path": "/api/screenings",
"body": {
"lane": "onboarding",
"customerId": "${trigger.customerId}",
"subjectType": "individual",
"fullName": "${step.kyc.output.ktpBundle.ocr.fullName}",
"nik": "${step.kyc.output.ktpBundle.ocr.nik}"
}
},
{
"id": "wait_for_analyst",
"type": "human_approval",
"actionLabel": "KTP requires manual review",
"assignTo": "role:kyc_analyst"
},
{
"id": "notify_failure",
"type": "webhook_emit",
"event": "kyc.failed",
"payload": { "customerId": "${trigger.customerId}" }
}
],
"edges": [
{ "from": "kyc", "to": "branch_on_ktp" },
{ "from": "branch_on_ktp", "to": "screening", "case": "passed" },
{ "from": "branch_on_ktp", "to": "wait_for_analyst", "case": "requires_review" },
{ "from": "branch_on_ktp", "to": "notify_failure", "case": "failed" },
{ "from": "wait_for_analyst", "to": "screening" }
]
},
"triggerType": "api"
}Response (201):
{
"data": {
"workflow": {
"id": "wkf_01HXY...",
"slug": "indonesian-kyc-v1",
"name": "Indonesian KYC onboarding",
"spec": { ... },
"status": "draft",
"version": 1,
"createdAt": "..."
}
}
}New workflows start in draft. Publish to activate:
3. Publish
/api/workflows/{id}/publishcurl -X POST .../api/workflows/indonesian-kyc-v1/publish \
-H "Authorization: Bearer $QE_API_KEY"status flips to active. Now it accepts execution triggers.
4. Trigger an execution
/api/workflows/{slug}/executePass whatever input your workflow's spec references via ${trigger.*}:
curl -X POST .../api/workflows/indonesian-kyc-v1/execute \
-H "Authorization: Bearer $QE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"customerId": "cus_01HXY...",
"bundle": { /* the KTP capture bundle */ },
"hmacKey": "qe_whsec_..."
}'Response (202 — async):
{
"data": {
"execution": {
"id": "exe_01HXY...",
"workflowId": "wkf_01HXY...",
"status": "pending",
"inputs": { ... },
"createdAt": "..."
}
}
}5. Watch it run
/api/executions/{id}curl .../api/executions/exe_01HXY... \
-H "Authorization: Bearer $QE_API_KEY"{
"data": {
"execution": {
"id": "exe_01HXY...",
"workflowId": "wkf_01HXY...",
"status": "succeeded",
"inputs": { ... },
"steps": [
{ "id": "stp_01...", "nodeId": "kyc", "status": "succeeded", "output": { "ktpBundle": { "verdict": "passed", ... } }, "startedAt": "...", "completedAt": "..." },
{ "id": "stp_02...", "nodeId": "branch_on_ktp", "status": "succeeded", "output": { "selected": "passed" } },
{ "id": "stp_03...", "nodeId": "screening", "status": "succeeded", "output": { "screeningId": "scr_...", "status": "no_match" } }
],
"createdAt": "...",
"completedAt": "..."
}
}
}In production you'd subscribe to workflow.execution.completed webhook instead of polling.
6. Handle approval (for the requires_review branch)
If branch went to wait_for_analyst, the execution sits in status awaiting_approval. Your analyst dashboard surfaces it; or programmatically:
curl -X POST .../api/executions/exe_01HXY.../approve \
-H "Authorization: Bearer $QE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"decision": "approve",
"notes": "Glare on hologram but OCR fields self-consistent. Approved per supervisor."
}'Execution resumes from the next node.
Always pass externalId
Real workflows should pass externalId in the execute body (your loan application ID, your customer ID, your case ID). It makes retries idempotent and gives your audit log a stable cross-reference.
Next steps
- Workflows → — full node spec reference
- Executions →
- Human approvals →
- Service connections →