API Reference

Base URL: http://localhost:5143  Β·  All endpoints return JSON  Β·  No auth in dev

βš™οΈ Tenant Configuration

Per-tenant orchestrator settings: HITL mode, rate limits, contact window, frequency caps.

GET /api/tenant-config/{parentId} Get orchestrator config for a tenant β–Ό
Path Parameters
NameTypeDescription
parentIdlongTenant ID required
Example Response
{ "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 }
PUT /api/tenant-config/{parentId} Create or update tenant config (upsert) β–Ό
Request Body β€” all fields optional
FieldTypeDefaultNotes
hitlModestringalways_askalways_ask | auto_if_no_response | fully_autonomous
autoSendAfterMinutesint15Auto-execute delay for auto_if_no_response mode
suggestionExpiryMinutesint60HITL card TTL
listenerWaitHoursint24How long to wait for a reply before timeout
maxFollowupsBeforeRecycleint3Recycle entity after N unanswered follow-ups
maxAiCallsPerEntityDayint10Per-entity daily AI call cap
maxAiCallsPerTenantDayint500Tenant-wide daily AI call cap
isOrchestratorEnabledbooltrueKill switch β€” disable all AI for this tenant
contactWindowJsonstringnullJSON: {"start":"09:00","end":"20:00","timezone":"Asia/Kolkata","dnd_days":["sunday"]}
frequencyCapsJsonstringnullJSON: {"max_messages_per_day":5,"max_calls_per_day":2}
POST /api/tenant-config/{parentId}/apply-preset Apply a named preset β–Ό
Query Parameters
NameValuesDescription
presetconservative | balanced | aggressivePreset name required
conservative = always_ask, 30 min cooldown Β· balanced = auto_if_no_response, 15 min Β· aggressive = fully_autonomous
πŸ”§ Dev & Testing DEV ONLY

Development endpoints for smoke-testing and debugging. Remove or gate behind auth before production.

GET /api/dev/health Infrastructure health check β–Ό
{ "timestamp": "2026-06-03T...", "services": { "api":"ok",
  "hangfire":"configured", "masstransit":"configured", "semanticKernel":"configured" } }
POST /api/dev/publish-signal Publish a hardcoded test signal to RabbitMQ (lead#42, tenant 1001) β–Ό
No request body needed. Always publishes: email_received for lead#42, tenant 1001.
{ "status": "published", "message": { "parentId":1001, "entityType":"lead",
  "entityId":42, "signalType":"email_received", ... } }
POST /api/dev/integration-test Full pipeline test β€” bypasses RabbitMQ, runs DecisionPipeline directly β–Ό
Creates tenant config for 1001 if missing. Returns full pipeline result including plan and HITL cards created.
{ "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" }] }
POST /api/dev/dry-run Simulate the full pipeline with NO database writes β€” shows what would happen β–Ό
Request Body
FieldTypeDescription
parentIdlongrequiredTenant ID
entityTypestringrequiredlead or enquiry
entityIdlongrequiredEntity ID from the monolith
signalTypestringrequirede.g. email_received, call_completed
signalPayloadstringoptionalJSON payload for the signal
mockSnapshotobjectoptionalEntitySnapshot β€” 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 } }
Response β€” outcome values
OutcomeMeaning
SUCCESSAI decided steps β€” shown in decision.steps[]
NOT_ROUTEDNo agent configured for this signal type
BLOCKED_RECYCLEDEntity is recycled β€” orchestrator would skip
AI_ERROROpenAI API call failed
POST /api/dev/test-sk Test Semantic Kernel β€” calls LLM with EmailPlugin registered β–Ό
{ "response": "[DRAFT] To: john@example.com\nSubject: Follow-up on Enterprise Plan..." }
GET /api/dev/state-summary Live state snapshot β€” entity states, plans, HITL cards, listeners, audit β–Ό
Query Parameters
NameTypeDefault
parentIdlong1001
πŸƒ HITL Cards

Human-in-the-Loop approval cards. The AI creates cards for actions it wants to take; users approve, dismiss, or redirect them.

GET /api/hitl/cards All pending HITL cards for a tenant β–Ό
Query Parameters
NameType
parentIdlongrequired
GET /api/hitl/cards/{cardRef} Get a specific card by reference β–Ό
Path Parameters
NameDescription
cardRef12-char hex card reference (e.g. e40b48589d2e)
POST /api/hitl/cards/{cardRef}/respond Respond to a HITL card β–Ό
Request Body
FieldTypeDescription
actionstringrequiredsend | dismiss | rephrase | tell_ai
responseTextstringoptionalRequired for rephrase and tell_ai
actorUserIdlongoptionalUser ID for audit trail
Action behaviours
ActionWhat happens
sendApprove β€” executes the plan step immediately via Hangfire
dismissSkip β€” marks step as skipped, no action taken
rephraseRe-send with new text β€” replaces message body, executes step
tell_aiGive 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 }
GET /api/hitl/history HITL card history for an entity β–Ό
Query Parameters
NameTypeDefault
parentIdlongrequired
entityTypestringoptional
entityIdlongoptional
takeint20
πŸ€– Agent Configs

Configure how AI agents behave per tenant: system prompt, tools, model, temperature, knowledge base.

GET /api/agent-configs/{parentId} List all agent configs for a tenant β–Ό
[{ "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 }]
GET /api/agent-configs/{parentId}/{agentKey} Get a single agent config with full system prompt β–Ό
PUT /api/agent-configs/{parentId}/{agentKey} Create or update an agent config (upsert) β–Ό
Request Body
FieldTypeDescription
displayNamestringHuman-readable agent name
entityTypesstringComma-separated: lead,enquiry
signalTypesstringComma-separated: email_received,whatsapp_received
systemPromptstringFull system prompt text
availableToolsstringComma-separated tool keys
modelOverridestringe.g. gpt-4o β€” overrides default model
temperaturedouble0.0–1.0, default 0.3
maxTokensintMax LLM output tokens
maxStepsintMax plan steps agent can return
useKnowledgeBaseboolInject relevant KB chunks into prompt
knowledgeScopestringScope filter for KB retrieval: products, pricing, all
specialInstructionsstringAppended to system prompt at runtime
priorityintLower = checked first when routing (default 1)
isEnabledboolDisable agent without deleting
POST /api/agent-configs/{parentId}/seed-defaults Seed default agents: lead_nurture + voice_followup β–Ό
Idempotent β€” skips agents that already exist. Good starting point before customising prompts.
DELETE /api/agent-configs/{parentId}/{agentKey} Delete an agent config β–Ό
πŸ”§ Agent Tools

Control which tools (actions) an agent can use, whether they require HITL, and per-tool constraints.

GET /api/agent-tools/registry All available tools from the monolith tool registry β–Ό
[{ "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 }, ...]
GET /api/agent-tools/{parentId}/{agentKey} Get tools assigned to a specific agent with their permissions β–Ό
PUT /api/agent-tools/{parentId}/{agentKey} Set all tools for an agent (replaces existing mappings) β–Ό
{ "tools": [
    { "toolKey": "draft_email",     "isEnabled": true, "requiresHitl": true },
    { "toolKey": "create_task",     "isEnabled": true, "requiresHitl": false },
    { "toolKey": "send_whatsapp",   "isEnabled": false }
  ] }
PATCH /api/agent-tools/{parentId}/{agentKey}/{toolKey} Toggle or configure a single tool β–Ό
{ "isEnabled": true, "requiresHitl": false,
  "paramConstraintsJson": "{\"max_message_length\": 500}" }
POST /api/agent-tools/{parentId}/{agentKey}/seed-from-registry Auto-assign all registry tools to this agent (all enabled by default) β–Ό
πŸ’¬ Chat

Multi-channel, multi-modal chat. One session per channel per entity. Supports text, image, audio, and video.

GET /api/v1/chat/sessions Get or create the active chat session for an entity on a channel β–Ό
Query Parameters
NameTypeNotes
parentIdlongrequired
entityTypestringrequiredlead | enquiry
entityIdlongrequired
channelstringoptionalwebchat (default) | whatsapp | email | sms | in_app
{ "id": 1, "channel": "whatsapp", "status": "active",
  "createdAt": "2026-06-03T...", "lastMessageAt": "2026-06-03T..." }
GET /api/v1/chat/sessions/{sessionId}/messages Paginated message history for a session (newest first) β–Ό
Query Parameters
NameTypeDefault
pageint1
pageSizeint50
[{ "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..." }]
POST /api/v1/chat/messages/text Send an outbound text message (agent/sales rep reply) β–Ό
// Request
{ "parentId":1001, "entityType":"lead", "entityId":42,
  "channel":"whatsapp", "textContent":"Hi! Thanks for reaching out. Here's the pricing..." }
// Response
{ "status": "sent" }
POST /api/v1/chat/messages/media Upload and send a media message β€” image, audio, or video (max 100 MB) β–Ό
Request β€” multipart/form-data
FieldType
filefilerequired
parentIdlongrequired
entityTypestringrequired
entityIdlongrequired
channelstringoptional
captionstringoptional
Processing (async via Hangfire)
TypeProcessing
imageGPT-4o Vision β†’ aiDescription
audioOpenAI Whisper β†’ aiTranscript
videoFFmpeg audio strip β†’ Whisper + keyframe β†’ GPT-4o Vision
// Response (202 Accepted)
{ "chatMessageId": 5, "status": "processing", "storageUrl": "/tmp/orc-media/..." }
Subscribe to SignalR ChatMessageProcessed for the transcript/description when ready.
POST /api/v1/chat/inbound Webhook β€” inbound message from external channel provider (WhatsApp gateway, SMS, etc.) β–Ό
{ "parentId":1001, "entityType":"lead", "entityId":42,
  "channel":"whatsapp", "messageType":"text",
  "textContent":"I'm interested, please send the brochure",
  "externalMessageId":"wamid.abc123" }
// Response
{ "chatMessageId": 6 }
Also triggers the full DecisionPipeline β€” the AI sees the inbound message in its context and may create a plan + HITL cards.
🧠 Knowledge Base (RAG)

pgvector-backed knowledge store. Ingest product docs, pricing, FAQs β€” agents with useKnowledgeBase=true automatically retrieve relevant chunks.

GET /api/knowledge/{parentId}/documents List all ingested documents for a tenant β–Ό
[{ "documentId":"pricing_2026.pdf", "scope":"pricing", "chunkCount":12,
   "createdAt":"2026-06-03T..." }]
POST /api/knowledge/{parentId}/ingest Embed and store document chunks (max 500 chunks per call, idempotent) β–Ό
// 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 values
ScopeUse for
productsProduct descriptions, features
pricingPricing, plans, discounts
policiesRefund, SLA, terms policies
faqFrequently asked questions
allGeneral β€” retrieved for any query
POST /api/knowledge/{parentId}/upload Upload a raw file β€” server extracts text, chunks, embeds, and stores automatically β–Ό
Request β€” multipart/form-data Β· max 20 MB
FieldTypeDescription
fileFilerequired β€” PDF, DOCX, TXT, CSV, MD
scopestringall β€” products | pricing | policies | faq | all
documentIdstringoptional β€” defaults to filename
How it works

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 }
GET /api/knowledge/{parentId}/search Test RAG retrieval β€” returns top-K relevant chunks for a query β–Ό
Query Parameters
NameTypeDefault
qstringrequired
scopestringoptional
topKint5
{ "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 }
  ] }
DELETE /api/knowledge/{parentId}/documents/{documentId} Delete all chunks for a specific document β–Ό
DELETE /api/knowledge/{parentId} Delete the entire knowledge base for a tenant β–Ό
Irreversible. Deletes all embedded chunks for the tenant.
πŸ“œ Audit Logs

Immutable structured log of every AI decision, plan step, HITL action, and error.

GET /api/audit Filtered, paginated audit log β–Ό
Query Parameters
NameTypeDefaultDescription
parentIdlongrequired
entityTypestringoptionalFilter by entity type
entityIdlongoptionalFilter by entity ID
planIdlongoptionalFilter by plan
eventTypestringoptionale.g. decision_made, step_executed
errorsOnlybooloptionalOnly return error events
fromDateTimeOffsetoptionalStart of range
toDateTimeOffsetoptionalEnd of range
pageint1
pageSizeint50Max 200
Event types
EventWhen
decision_requestedSignal passed cost guard, agent called
decision_madeAgent returned a plan
cost_guard_blockedSignal rejected by cost guard
step_scheduledPlan step queued in Hangfire
step_executedPlan step ran successfully
step_failedPlan step execution failed
hitl_card_createdHITL card pushed to user
hitl_responseUser responded to a HITL card
listener_createdWait-for-reply listener registered
listener_matchedInbound reply matched listener
listener_timed_outListener expired without reply
GET /api/audit/stats Aggregated tenant statistics over N days β–Ό
Query Parameters
NameTypeDefault
parentIdlongrequired
daysint7
{ "decisionsMade":42, "totalTokens":28400, "hitlCreated":38,
  "stepsExecuted":31, "totalErrors":2, "entitiesRecycled":0 }
GET /api/audit/entity-timeline All audit events for a specific entity, chronologically β–Ό
Query Parameters
NameTypeDefault
parentIdlongrequired
entityTypestringrequired
entityIdlongrequired
takeint50
⚑ SignalR Hub WS

Hub path: /hubs/orchestrator  Β·  Use @microsoft/signalr

CLIENT→SERVER JoinTenantGroup(parentId) Subscribe to all events for a tenant ▼
await connection.invoke("JoinTenantGroup", 1001);
CLIENT→SERVER JoinChatSession(sessionId) Subscribe to events for a specific chat session ▼
await connection.invoke("JoinChatSession", 1);
CLIENT→SERVER SendTypingIndicator(sessionId, isTyping) Broadcast typing state to other session subscribers ▼
await connection.invoke("SendTypingIndicator", 1, true);
Server β†’ Client events
SERVER→CLIENT HitlCardCreated New HITL card pushed to tenant group ▼
{ cardRef, cardType, title, body, agentKey, entityType, entityId,
  availableActions, autoAction, autoExecuteAt, createdAt }
SERVER→CLIENT HitlCardResponded User responded to a card ▼
{ cardRef, action, respondedAt }
SERVER→CLIENT ChatMessageReceived New chat message (inbound or outbound) — sent to tenant group ▼
{ sessionId, messageId, direction, senderType, messageType,
  textContent, mediaUrl, status, createdAt }
SERVER→CLIENT ChatMessageProcessed Media processing complete — contains Whisper transcript or GPT-4V description ▼
{ messageId, sessionId, transcript, description, textForLlm, status }
SERVER→CLIENT TypingIndicator Real-time typing state for a chat session ▼
{ sessionId, connectionId, isTyping }
API Reference Β· AI Orchestrator v3.0 Β· .NET 8 / Semantic Kernel / pgvector