📘 Public beta · Endpoints are stable; OpenAPI specs and SDKs ship monthly. See changelog →
Products
Orchestration
Quickstart

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:

POST/api/service-connections
Auth · API keyScope · service-connections:write
curl -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

POST/api/workflows
Auth · API keyScope · workflows:write
{
  "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

POST/api/workflows/{id}/publish
Auth · API keyScope · workflows:write
curl -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

POST/api/workflows/{slug}/execute
Auth · API keyScope · executions:write

Pass 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

GET/api/executions/{id}
Auth · API keyScope · executions:read
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