AI Automation · Quickstart
Define a flow with an LLM classification step, trigger it, watch it run.
Time: ~15 minutes.
Prerequisites
Sandbox API key with scopes: flows:write, runs:write.
1. Create a flow
/api/flows{
"slug": "ticket-triage-v1",
"name": "Ticket triage",
"description": "Classify and route a customer-support ticket using Quantum AI",
"spec": {
"startNodeId": "classify",
"nodes": [
{
"id": "classify",
"type": "llm_step",
"model": "quantum-ai:default",
"systemPrompt": "You are a triage assistant for an Indonesian bank's customer-support inbox. Classify the user's message into one of: account_access, transaction_dispute, card_request, general_inquiry, complaint. Also rate urgency 1-5 and write a one-sentence summary.",
"userPrompt": "Ticket subject: ${trigger.subject}\\nTicket body: ${trigger.body}",
"outputSchema": {
"type": "object",
"properties": {
"category": { "type": "string", "enum": ["account_access", "transaction_dispute", "card_request", "general_inquiry", "complaint"] },
"urgency": { "type": "integer", "minimum": 1, "maximum": 5 },
"summary": { "type": "string" }
},
"required": ["category", "urgency", "summary"]
}
},
{
"id": "route",
"type": "decision",
"expression": "step.classify.output.urgency",
"cases": {
"5": { "next": "page_oncall" },
"4": { "next": "page_oncall" }
},
"defaultCase": "tag_ticket"
},
{
"id": "page_oncall",
"type": "http_sink",
"url": "https://your-pager.example.com/page",
"body": {
"title": "Urgent ticket: ${step.classify.output.summary}",
"category": "${step.classify.output.category}"
}
},
{
"id": "tag_ticket",
"type": "service_call",
"service": "custom:crm",
"method": "PATCH",
"path": "/api/tickets/${trigger.ticketId}",
"body": {
"category": "${step.classify.output.category}",
"urgency": "${step.classify.output.urgency}",
"ai_summary": "${step.classify.output.summary}"
}
}
],
"edges": [
{ "from": "classify", "to": "route" },
{ "from": "route", "to": "page_oncall", "case": "5" },
{ "from": "route", "to": "page_oncall", "case": "4" },
{ "from": "route", "to": "tag_ticket" }
]
},
"triggerType": "api",
"status": "active"
}Response (201):
{
"data": {
"flow": {
"id": "flw_01HXY...",
"slug": "ticket-triage-v1",
"name": "Ticket triage",
"status": "active",
"triggerType": "api",
"version": 1,
"webhookToken": "wht_...",
"webhookSecret": "qe_whsec_...",
"createdAt": "..."
}
}
}webhookToken and webhookSecret are populated even for triggerType: api — you can switch trigger modes later without re-creating.
2. Trigger a run
/api/flows/{id}/executecurl -X POST .../api/flows/ticket-triage-v1/execute \
-H "Authorization: Bearer $QE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"ticketId": "tkt_01HXY...",
"subject": "Saldo saya berkurang tanpa transaksi",
"body": "Halo, pagi ini saya cek aplikasi dan saldo saya berkurang Rp 5 juta. Tapi saya tidak ingat melakukan transfer apapun. Tolong cek!"
}'Response (202):
{
"data": {
"run": {
"id": "run_01HXY...",
"flowId": "flw_01HXY...",
"status": "pending",
"trigger": "manual",
"createdAt": "..."
}
}
}3. Watch it run
/api/runs/{runId}curl .../api/runs/run_01HXY... \
-H "Authorization: Bearer $QE_API_KEY"{
"data": {
"flow": { "id": "flw_01HXY...", "name": "Ticket triage", "slug": "ticket-triage-v1" },
"run": {
"id": "run_01HXY...",
"flowId": "flw_01HXY...",
"status": "succeeded",
"trigger": "manual",
"input": { ... },
"steps": [
{
"id": "stp_01HXY...",
"nodeId": "classify",
"type": "llm_step",
"status": "succeeded",
"output": {
"text": "{\"category\":\"transaction_dispute\",\"urgency\":5,\"summary\":\"Customer reports unauthorized IDR 5M debit; needs immediate fraud investigation.\"}",
"parsed": {
"category": "transaction_dispute",
"urgency": 5,
"summary": "Customer reports unauthorized IDR 5M debit; needs immediate fraud investigation."
},
"model": "quantum-ai:default",
"inferenceMs": 1250
}
},
{ "id": "stp_02HXZ...", "nodeId": "route", "type": "decision", "status": "succeeded", "output": { "selected": "5" } },
{ "id": "stp_03HXA...", "nodeId": "page_oncall", "type": "http_sink", "status": "succeeded", "output": { "statusCode": 200 } }
],
"finishedAt": "..."
}
}
}Notice step.classify.output.parsed — the LLM's response was schema-validated and parsed for you. Downstream nodes use the parsed fields.
4. Convert to a webhook-triggered flow
If you want your CRM to fire a flow when a new ticket arrives, switch triggerType:
curl -X PATCH .../api/flows/ticket-triage-v1 \
-H "Authorization: Bearer $QE_API_KEY" \
-d '{ "triggerType": "webhook" }'Now your CRM can POST to:
POST https://sandbox.quantumelixir.tech/ai-automation/api/triggers/wh/wht_...
X-Quantum-Signature: sha256=<hmac of body with webhookSecret>
Content-Type: application/json
{ "ticketId": "...", "subject": "...", "body": "..." }See Triggers → for the signature scheme.
5. Convert to a scheduled flow
For "every 15 minutes, fetch new tickets and triage":
curl -X PATCH .../api/flows/ticket-triage-v1 \
-d '{
"triggerType": "cron",
"cronExpression": "*/15 * * * *",
"cronEnabled": true
}'GET /api/cron-preview?expression=*/15+*+*+*+* previews the next N fire times.
LLM cost is logged per step
Every llm_step records token usage on the step's output.tokens field. Aggregate these across runs for a per-flow cost dashboard. The dashboard's Billing → Usage page does this automatically.
Next steps
- Flows → — full node spec
- Triggers → — webhook HMAC + cron details
- Agents & conversations →
- Webhooks →