API Reference
Base URL: http://localhost:5143 Β· All endpoints return JSON Β· No auth in dev
Per-tenant orchestrator settings: HITL mode, rate limits, contact window, frequency caps.
| Name | Type | Description |
|---|---|---|
| parentId | long | Tenant ID required |
{ "id": 1, "parentId": 1001, "hitlMode": "always_ask",
"autoSendAfterMinutes": 15, "suggestionExpiryMinutes": 60,
"listenerWaitHours": 24, "maxFollowupsBeforeRecycle": 3,
"maxAiCallsPerEntityDay": 10, "maxAiCallsPerTenantDay": 500,
"isOrchestratorEnabled": true,
"contactWindowJson": null, "frequencyCapsJson": null }
| Field | Type | Default | Notes |
|---|---|---|---|
| hitlMode | string | always_ask | always_ask | auto_if_no_response | fully_autonomous |
| autoSendAfterMinutes | int | 15 | Auto-execute delay for auto_if_no_response mode |
| suggestionExpiryMinutes | int | 60 | HITL card TTL |
| listenerWaitHours | int | 24 | How long to wait for a reply before timeout |
| maxFollowupsBeforeRecycle | int | 3 | Recycle entity after N unanswered follow-ups |
| maxAiCallsPerEntityDay | int | 10 | Per-entity daily AI call cap |
| maxAiCallsPerTenantDay | int | 500 | Tenant-wide daily AI call cap |
| isOrchestratorEnabled | bool | true | Kill switch β disable all AI for this tenant |
| contactWindowJson | string | null | JSON: {"start":"09:00","end":"20:00","timezone":"Asia/Kolkata","dnd_days":["sunday"]} |
| frequencyCapsJson | string | null | JSON: {"max_messages_per_day":5,"max_calls_per_day":2} |
| Name | Values | Description |
|---|---|---|
| preset | conservative | balanced | aggressive | Preset name required |
Development endpoints for smoke-testing and debugging. Remove or gate behind auth before production.
{ "timestamp": "2026-06-03T...", "services": { "api":"ok",
"hangfire":"configured", "masstransit":"configured", "semanticKernel":"configured" } }
{ "status": "published", "message": { "parentId":1001, "entityType":"lead",
"entityId":42, "signalType":"email_received", ... } }
{ "pipeline": { "wasProcessed":true, "planId":5,
"decision": { "reasoning":"...", "aiModel":"gpt-4o-mini", "tokensUsed":620,
"steps":[{"stepOrder":1,"toolKey":"draft_email","delayMinutes":0}] } },
"hitlCards": [{ "cardRef":"abc123...", "title":"AI suggests: draft_email", "status":"pending" }] }
| Field | Type | Description | |
|---|---|---|---|
| parentId | long | required | Tenant ID |
| entityType | string | required | lead or enquiry |
| entityId | long | required | Entity ID from the monolith |
| signalType | string | required | e.g. email_received, call_completed |
| signalPayload | string | optional | JSON payload for the signal |
| mockSnapshot | object | optional | EntitySnapshot β use instead of calling the monolith API |
// Example with mock data (no monolith required)
{ "parentId":1001, "entityType":"lead", "entityId":9999, "signalType":"email_received",
"mockSnapshot":{ "name":"Priya Sharma", "status":"hot", "stage":"evaluation",
"engagementScore":82, "minutesSinceLastInteraction":45,
"slotsCollected":["name","phone"], "slotsMissing":["budget"],
"pendingTasks":1, "pendingTickets":0 } }
| Outcome | Meaning |
|---|---|
| SUCCESS | AI decided steps β shown in decision.steps[] |
| NOT_ROUTED | No agent configured for this signal type |
| BLOCKED_RECYCLED | Entity is recycled β orchestrator would skip |
| AI_ERROR | OpenAI API call failed |
{ "response": "[DRAFT] To: john@example.com\nSubject: Follow-up on Enterprise Plan..." }
| Name | Type | Default |
|---|---|---|
| parentId | long | 1001 |
Human-in-the-Loop approval cards. The AI creates cards for actions it wants to take; users approve, dismiss, or redirect them.
| Name | Type | |
|---|---|---|
| parentId | long | required |
| Name | Description |
|---|---|
| cardRef | 12-char hex card reference (e.g. e40b48589d2e) |
| Field | Type | Description | |
|---|---|---|---|
| action | string | required | send | dismiss | rephrase | tell_ai |
| responseText | string | optional | Required for rephrase and tell_ai |
| actorUserId | long | optional | User ID for audit trail |
| Action | What happens |
|---|---|
| send | Approve β executes the plan step immediately via Hangfire |
| dismiss | Skip β marks step as skipped, no action taken |
| rephrase | Re-send with new text β replaces message body, executes step |
| tell_ai | Give feedback β re-runs pipeline with user's text as new signal payload |
// Request
{ "action": "rephrase", "responseText": "Hi, wanted to follow up about your interest in our Pro plan.", "actorUserId": 7 }
// Response
{ "result": "ok", "action": "rephrase", "planStepId": 3 }
| Name | Type | Default |
|---|---|---|
| parentId | long | required |
| entityType | string | optional |
| entityId | long | optional |
| take | int | 20 |
Configure how AI agents behave per tenant: system prompt, tools, model, temperature, knowledge base.
[{ "id":1, "agentKey":"lead_nurture", "displayName":"Lead Nurture Agent",
"entityTypes":"lead,enquiry", "signalTypes":"email_received,whatsapp_received,...",
"model":"gpt-4o-mini", "temperature":0.3, "maxTokens":2000, "isEnabled":true,
"useKnowledgeBase":false, "priority":1 }]
| Field | Type | Description |
|---|---|---|
| displayName | string | Human-readable agent name |
| entityTypes | string | Comma-separated: lead,enquiry |
| signalTypes | string | Comma-separated: email_received,whatsapp_received |
| systemPrompt | string | Full system prompt text |
| availableTools | string | Comma-separated tool keys |
| modelOverride | string | e.g. gpt-4o β overrides default model |
| temperature | double | 0.0β1.0, default 0.3 |
| maxTokens | int | Max LLM output tokens |
| maxSteps | int | Max plan steps agent can return |
| useKnowledgeBase | bool | Inject relevant KB chunks into prompt |
| knowledgeScope | string | Scope filter for KB retrieval: products, pricing, all |
| specialInstructions | string | Appended to system prompt at runtime |
| priority | int | Lower = checked first when routing (default 1) |
| isEnabled | bool | Disable agent without deleting |
Control which tools (actions) an agent can use, whether they require HITL, and per-tool constraints.
[{ "key":"draft_email", "label":"Draft Email", "category":"communication", "requiresHitl":true },
{ "key":"send_whatsapp", "label":"Send WhatsApp", "category":"communication", "requiresHitl":false },
{ "key":"create_task", "label":"Create Task", "category":"crm", "requiresHitl":false }, ...]
{ "tools": [
{ "toolKey": "draft_email", "isEnabled": true, "requiresHitl": true },
{ "toolKey": "create_task", "isEnabled": true, "requiresHitl": false },
{ "toolKey": "send_whatsapp", "isEnabled": false }
] }
{ "isEnabled": true, "requiresHitl": false,
"paramConstraintsJson": "{\"max_message_length\": 500}" }
Multi-channel, multi-modal chat. One session per channel per entity. Supports text, image, audio, and video.
| Name | Type | Notes | |
|---|---|---|---|
| parentId | long | required | |
| entityType | string | required | lead | enquiry |
| entityId | long | required | |
| channel | string | optional | webchat (default) | whatsapp | email | sms | in_app |
{ "id": 1, "channel": "whatsapp", "status": "active",
"createdAt": "2026-06-03T...", "lastMessageAt": "2026-06-03T..." }
| Name | Type | Default |
|---|---|---|
| page | int | 1 |
| pageSize | int | 50 |
[{ "id":2, "direction":"inbound", "senderType":"lead", "messageType":"audio",
"textContent":null, "aiTranscript":"Can you send me the pricing details?",
"aiDescription":null, "status":"delivered", "mediaUrl":"/tmp/orc-media/...",
"createdAt":"2026-06-03T..." }]
// Request
{ "parentId":1001, "entityType":"lead", "entityId":42,
"channel":"whatsapp", "textContent":"Hi! Thanks for reaching out. Here's the pricing..." }
// Response
{ "status": "sent" }
| Field | Type | |
|---|---|---|
| file | file | required |
| parentId | long | required |
| entityType | string | required |
| entityId | long | required |
| channel | string | optional |
| caption | string | optional |
| Type | Processing |
|---|---|
| image | GPT-4o Vision β aiDescription |
| audio | OpenAI Whisper β aiTranscript |
| video | FFmpeg audio strip β Whisper + keyframe β GPT-4o Vision |
// Response (202 Accepted)
{ "chatMessageId": 5, "status": "processing", "storageUrl": "/tmp/orc-media/..." }
ChatMessageProcessed for the transcript/description when ready.{ "parentId":1001, "entityType":"lead", "entityId":42,
"channel":"whatsapp", "messageType":"text",
"textContent":"I'm interested, please send the brochure",
"externalMessageId":"wamid.abc123" }
// Response
{ "chatMessageId": 6 }
pgvector-backed knowledge store. Ingest product docs, pricing, FAQs β agents with useKnowledgeBase=true automatically retrieve relevant chunks.
[{ "documentId":"pricing_2026.pdf", "scope":"pricing", "chunkCount":12,
"createdAt":"2026-06-03T..." }]
// Request
{ "documentId": "pricing_2026.pdf",
"scope": "pricing",
"chunks": [
{ "text": "Pro plan: βΉ12,000/month for up to 10 users.", "metadata": {"page": "2"} },
{ "text": "Enterprise plan: custom pricing, unlimited users.", "metadata": {"page": "3"} }
] }
// Response
{ "success":true, "documentId":"pricing_2026.pdf", "scope":"pricing", "chunksIngested":2 }
| Scope | Use for |
|---|---|
| products | Product descriptions, features |
| pricing | Pricing, plans, discounts |
| policies | Refund, SLA, terms policies |
| faq | Frequently asked questions |
| all | General β retrieved for any query |
| Field | Type | Description |
|---|---|---|
| file | File | required β PDF, DOCX, TXT, CSV, MD |
| scope | string | all β products | pricing | policies | faq | all |
| documentId | string | optional β defaults to filename |
1. PDF β word grouping per page via PdfPig | DOCX β paragraph elements | TXT/CSV/MD β line split
2. Chunks into ~400-word windows with 60-word overlap
3. Batch embeds all chunks via text-embedding-ada-002
4. Stores in orc_knowledge_chunks (pgvector) β re-upload replaces old chunks
// Response
{ "success": true, "documentId": "pricing.pdf", "scope": "pricing",
"filename": "pricing.pdf", "sizeBytes": 204800, "chunksIngested": 14 }
| Name | Type | Default |
|---|---|---|
| q | string | required |
| scope | string | optional |
| topK | int | 5 |
{ "query":"what does the pro plan include", "scope":null, "topK":5, "resultCount":2,
"results":[
{ "text":"Pro plan: βΉ12,000/month for up to 10 users...", "documentId":"pricing_2026.pdf",
"scope":"pricing", "relevance":0.9231 },
{ "text":"Free trial: 14 days, all Pro features included.", "documentId":"pricing_2026.pdf",
"scope":"pricing", "relevance":0.7841 }
] }
Immutable structured log of every AI decision, plan step, HITL action, and error.
| Name | Type | Default | Description |
|---|---|---|---|
| parentId | long | required | |
| entityType | string | optional | Filter by entity type |
| entityId | long | optional | Filter by entity ID |
| planId | long | optional | Filter by plan |
| eventType | string | optional | e.g. decision_made, step_executed |
| errorsOnly | bool | optional | Only return error events |
| from | DateTimeOffset | optional | Start of range |
| to | DateTimeOffset | optional | End of range |
| page | int | 1 | |
| pageSize | int | 50 | Max 200 |
| Event | When |
|---|---|
| decision_requested | Signal passed cost guard, agent called |
| decision_made | Agent returned a plan |
| cost_guard_blocked | Signal rejected by cost guard |
| step_scheduled | Plan step queued in Hangfire |
| step_executed | Plan step ran successfully |
| step_failed | Plan step execution failed |
| hitl_card_created | HITL card pushed to user |
| hitl_response | User responded to a HITL card |
| listener_created | Wait-for-reply listener registered |
| listener_matched | Inbound reply matched listener |
| listener_timed_out | Listener expired without reply |
| Name | Type | Default |
|---|---|---|
| parentId | long | required |
| days | int | 7 |
{ "decisionsMade":42, "totalTokens":28400, "hitlCreated":38,
"stepsExecuted":31, "totalErrors":2, "entitiesRecycled":0 }
| Name | Type | Default |
|---|---|---|
| parentId | long | required |
| entityType | string | required |
| entityId | long | required |
| take | int | 50 |
Hub path: /hubs/orchestrator Β· Use @microsoft/signalr
await connection.invoke("JoinTenantGroup", 1001);
await connection.invoke("JoinChatSession", 1);
await connection.invoke("SendTypingIndicator", 1, true);
{ cardRef, cardType, title, body, agentKey, entityType, entityId,
availableActions, autoAction, autoExecuteAt, createdAt }
{ cardRef, action, respondedAt }
{ sessionId, messageId, direction, senderType, messageType,
textContent, mediaUrl, status, createdAt }
{ messageId, sessionId, transcript, description, textForLlm, status }
{ sessionId, connectionId, isTyping }