Skip to content

AI

Verified against source (23)

  • Extensions/AttachmentExtensions.cs
  • Extensions/AbstractExtensions.cs
  • Extensions/LabelExtensions.cs
  • Extensions/MessageExtensions.cs
  • Extensions/ImportFormExtensions.cs
  • Extensions/ReportExtensions.cs
  • Extensions/ImportFileExtensions.cs
  • Extensions/QueueObjectBatchExtensions.cs
  • qbo4.AI.Bridge/AiBatchEngine.cs
  • qbo4.AI.Bridge/AiDocumentBatchEngine.cs
  • qbo4.AI.Bridge/ReadOnlySqlGuard.cs
  • qbo4.AI.Bridge/SqlAgentOrchestrator.cs
  • qbo4.AI.Bridge/AiBridgeOptions.cs
  • Usage/LedgerUsageHandler.cs
  • qbo4.AI/CompleteManager.cs
  • qbo4.AI/GenerativeAI.cs
  • Extensions/ServiceCollectionExtensions.cs
  • Extensions/ServiceCollectionExtensions.cs
  • Extensions/ServiceCollectionExtensions.cs
  • Extensions/ServiceCollectionExtensions.cs
  • Extensions/ServiceCollectionExtensions.cs
  • Config/CustomRoutes.config
  • Attachment/Attachment.Search.xslt

AI methods are ordinary QBO operations, added to modules as extension methods. They follow the standard route pattern:

  • /api/{module}/{operation} runs synchronously on the web server.
  • /api/{module}/queue/{operation} queues the operation for an app server. The document menu’s Classify and Transcribe options use the queued form; use it for anything that touches many records.

Every call runs as the authenticated user, against records that user can reach.

Most methods take an agent name in AgentName (AiName is accepted as a synonym). There are two registries behind that name:

Registry Interface Used by Configured in
Agents IAgentManager → IChatClient Review, update, document import, form completion, the AI modal and import engines, batch engines, aitoquery GenerativeAI:Agents:{Claude|Gemini|OpenAI|Bedrock}, plus AiAgent rows in the database
Completion services ICompleteManager → IComplete Classify, label, reply drafting, the AI score engine, web-search aiimport Provider sections such as OpenAI and Google:Gemini

Completion services fall back to the configured default when no name is passed. Agent-based methods require a name, except aitoquery, which defaults to the SQL agent.

On agent-based methods, any parameter prefixed Chat_ binds to the request’s chat options, for example Chat_Temperature=0 or Chat_MaxOutputTokens=4000. Chat_ parameters that aren’t chat options travel with the request as tags; Chat_LedgerID is how you post token cost to a ledger (see Token cost tracking).

Operation Module What it does Key parameters
aiclassify attachment Sets AttachmentTemplateID by classifying the description against template descriptions. OCRs first when the description is empty and OcrCommand is configured. ID (list allowed), AgentName, DefaultTemplateID, Template_* filters, Template_nocache
aitranscribe attachment Transcribes an mp3 (audio/mpeg) with the configured transcription service (OpenAI) and appends the text to Description. Optionally calculates a score afterward. ID, ScoreTemplateID
ailabel any Adds a CollectionMember for each label (collection) that applies, from labels whose SourceObject is this table. ID, AgentName, Prompt, LabelName, Label_* filters
aireview any Reviews the record’s summary, plus its file if it is a document and any AttachmentIDs, and saves a Message bound to the record. ID, AgentName, Template or MessageTemplateID, MessageType, AttachmentIDs
aiupdate any Sends the record’s summary (and its file, for documents) and sets fields from the JSON response. ID, AgentName, Template (import template) or MessageTemplate, IncludeXml
aiupdatexml any Like aiupdate, but writes response keys that aren’t table columns into XmlData. Same as aiupdate
aiimport any Imports records the model returns. With a document (AttachmentIDs, or called on an attachment) it uses an agent; otherwise it asks a completion service to research the prompt. Prompt, AgentName, AttachmentIDs, SqlColumns, Preview, AddMessage, SubscriberURN, any column value
aicomplete importform Fills a form’s standard fields and question answers from its parent record. ID, AgentName, MessageTemplateID
airesponsedraft message Drafts an HTML reply to an inbound message. Returns it unsaved. ID, AgentName, IncludeRecipients
airesponsesave message Drafts and saves the reply without sending it. Same as airesponsedraft
airesponsesend message Drafts, saves, and sends the reply. Same as airesponsedraft
aitoquery report Turns a prompt into one validated read-only SELECT. Prompt, AgentName, ConnectionStringName
GET /api/attachment/aiclassify?ID=127&AgentName=Gemini&DefaultTemplateID=4
GET /api/attachment/queue/aiclassify?ID=127,128,129&AgentName=Gemini

Candidate templates default to every attachment template with a description. Narrow them with Template_ parameters, which are passed to the template search, and add Template_nocache=true after editing descriptions; the candidate list is cached per filter.

GET /api/attachment/aireview?ID=1042&AgentName=DocumentReview&Template=Intake Review
GET /api/contact/aireview?ID=88&AgentName=DocumentReview&MessageTemplateID=105&AttachmentIDs=1060,1061

The message template’s body becomes the instructions. The model returns Message, BodyText (Markdown, also rendered to BodyHTML), Priority, Status, and MessageType, and QBO saves it as a message on the record.

Import records from a document or a prompt

Section titled “Import records from a document or a prompt”
GET /api/contact/aiimport?AgentName=DocumentImport&AttachmentIDs=2207&Object=Organization&ObjectID=123&Preview=true
GET /api/contact/aiimport?Prompt=Who handles employment verification at this organization?&Object=Organization&ObjectID=123&ContactType=Employment Verification

The prompt is extended with the target table’s columns (or SqlColumns) and asks for {Table}Collection / {Table}Item XML, with the source in SourceUrlFromAi. Every other parameter is added to each imported item, with substitution from the item, so VerificationSource={SourceUrlFromAi} works. SubscriberID defaults to the table name and the item’s own name field, for example quandis.ai-Contact-{Contact} for contacts. Unless AddMessage=false, the raw response is also saved as an AI Response message. Preview=true returns the XML without importing.

GET /api/message/airesponsedraft?ID=5512&IncludeRecipients=true

The prompt carries the parent record’s summary and the inbound body. The reply’s subject is prefixed RE:, it inherits type, priority, status, and secure-delivery settings, and ParentMessageID threads it. Without IncludeRecipients, it is addressed to the sender only.

GET /api/report/aitoquery?Prompt=Open claims over 10000 by adjuster
<AiToQuery>
<Query>SELECT …</Query>
<Message>Assumed "open" means no closed date.</Message>
</AiToQuery>

The agent (default qbo-sql, from AiBridgeOptions.SqlAgent) answers with a fenced SQL block or with prose. Prose is returned in Message with no retry. SQL goes through ReadOnlySqlGuard, which parses T-SQL and rejects anything but a single SELECT (including SELECT … INTO and multi-statement batches), and then through sys.sp_describe_first_result_set on ConnectionStringName (default qbo.AdHoc), which binds the query without executing it. Failures trigger up to two corrective retries; after that the last SQL is returned with a warning in Message.

Engine Plugs into Notes
AIScoreEngine Score templates Text-based extraction from Attachment.Description (converted to text if empty) or Message/BodyText. Uses a completion service.
AiModalEngine Score templates Sends the original file as document content; also builds content from data reader rows. Uses an agent.
AiImportEngine Import templates Instructions in ImportFileTemplate.Description; target in AppliesTo; optional ContextClassName / ContextObjectID. Saves through ImportXmlAsync, so nested collections import with their parent.
AiDocumentBatchEngine, ClassificationBatchEngine Import templates Submit an Attachment/Search result as a provider batch and apply results per document.

Engine parameters for score templates go in ScoreTemplate.EngineParameters (AgentName, UseConfidence, FileExpirationMinutes, ContentBuilders). When AgentName is omitted, the engine’s configured name minus the Engine suffix is used.

GET /api/score/calculate?ScoreTemplateID=12&Object=Attachment&ObjectID=2207

Claude, Gemini, and OpenAI agents, and Bedrock agents with batch enabled, support provider batch jobs (IBatchChatClient), typically at lower cost for work that isn’t urgent. QBO drives batches through its own queue and scheduler:

  1. Document batches. An import file using a batch engine searches attachments, submits one job (splitting oversized documents into chunks), and stores the provider’s batch ID on the import file. ImportFile/AiBatchPoll re-schedules itself every PollIntervalMinutes (default 60) until the job completes, then invokes the template’s ResultMethod once per document.
  2. Queue batches. The queue object’s AiBatchDrain operation reads up to MaxItems (default 1,000) messages from an input queue, each carrying a Prompt and optional Instructions and CustomId, submits them as a job, and schedules AiBatchPoll. On completion, the CallbackMethodSignature runs once per result with the response attached.

aiupdate and aisave accept a completed batch response (ChatResponse) in place of a live call, so a batch ResultMethod can point straight at them.

Agent calls report token usage (input, cached input, output, reasoning) to registered IUsageHandlers. Configuring GenerativeAI:LedgerUsage registers LedgerUsageHandler, which writes LedgerItems when a call carries a ledger:

GET /api/attachment/aireview?ID=1042&AgentName=DocumentReview&Template=Intake Review&Chat_LedgerID=310

For each non-zero token type it saves a debit with Units set to the token type, UnitCount the token count, LedgerItemType the model ID, AmountMoney from the configured per-million rate, and a shared TransactionNumber (the conversation ID when present). Chat_LedgerTemplateItemID overrides the default line item per call; LedgerTemplateItemIDs maps each token type to its own line. Usage is posted only when the acting user was captured on the request thread; batch attributes are never trusted for identity.

Completion services can redact before sending: list SSN under GenerativeAI:Redactors and Social Security numbers are replaced with placeholders in the prompt and restored in the answer. Redaction applies to the completion-service path (classify, label, reply drafting, the AI score engine, web-search import). It does not apply to agent-based methods, which send record summaries and original files; control what those see through the agent’s instructions, the record’s security, and your choice of provider.