Skip to main content

TAP Topic Detection API

Overview

Topic detection classifies a completed or quiet TAP conversation into stable TAP topic taxonomy keys. It is intended for testing and integration validation through the on-demand endpoint first; scheduled polling is not enabled yet.

The TAP-facing endpoint is a tool route that runs the topic-detection workflow internally. It accepts caller-provided conversationId and threadId when TAP already has them. If they are omitted, the service creates an internal conversation identifier for the run.

note

All requests require the normal APIM authentication headers: Authorization: Bearer {access_token} and Ocp-Apim-Subscription-Key: {subscription_key}.

Endpoint

The path below includes the TAP APIM /ai prefix. Direct Mastra app smoke tests use the same path without /ai.

MethodEndpointDescription
POST/ai/tap/tools/topic-detectionClassify one completed or quiet conversation synchronously

Request

{
"conversationId": "chat-123",
"threadId": "thread-123",
"conversationStatus": "completed",
"analysisMode": "fast",
"providerLane": "standard",
"dryRun": true,
"messages": [
{
"messageId": "msg-1",
"role": "student",
"sequence": 1,
"text": "Are there scholarship deadlines for September?"
},
{
"messageId": "msg-2",
"role": "student",
"sequence": 2,
"text": "I also need advice about visa timing and accommodation."
}
]
}

Example request:

curl -X POST "{tap_apim_base_url}/ai/tap/tools/topic-detection" \
-H "Authorization: Bearer {access_token}" \
-H "Ocp-Apim-Subscription-Key: {subscription_key}" \
-H "Content-Type: application/json" \
-d '{
"conversationId": "chat-123",
"threadId": "thread-123",
"conversationStatus": "completed",
"analysisMode": "fast",
"providerLane": "standard",
"dryRun": true,
"messages": [
{
"messageId": "msg-1",
"role": "student",
"sequence": 1,
"text": "Are there scholarship deadlines for September?"
},
{
"messageId": "msg-2",
"role": "student",
"sequence": 2,
"text": "I also need advice about visa timing and accommodation."
}
]
}'

Transcript export request:

{
"chat_id": "520,241",
"chat_created_at": "June 15, 2026, 8:47 AM",
"analysisMode": "fast",
"providerLane": "standard",
"dryRun": true,
"messages": "2026-06-15 08:48 | Hi Dammy, Hope youre doing good\n\n2026-06-15 08:49 | Does your course include AI or fintech modules?"
}

When messages is a transcript string, each YYYY-MM-DD HH:mm | entry is converted into an ordered message with a deterministic evidence ID such as 520241-msg-0002. Formatted numeric chat_id values such as 520,241 are normalized to 520241.

Request Fields

FieldTypeRequiredDescription
conversationIdstringNoTAP conversation identifier. Generated internally when omitted.
chat_idstringNoBackward-compatible TAP export alias for conversationId. Thousands separators in numeric exports are removed.
threadIdstringNoTAP thread identifier or related chat grouping identifier.
conversationStatusstringNoUse completed when TAP knows the conversation is closed. Quiet-window detection is handled by the caller until scheduled polling is enabled.
analysisModestringNofast uses a compact candidate prompt. full sends the full taxonomy for QA or escalation. Defaults to fast.
providerLanestringNostandard is the low-latency lane. flex is intended for cheaper batch-style processing when available in the target environment.
dryRunbooleanNoUse true for smoke tests that should not apply current topic assignments.
messagesarray or stringYesPrefer ordered message objects with stable messageId, role, sequence, and text. Accepted roles are student, user, assistant, ambassador, staff, system, and unknown. A TAP transcript string is also accepted for query-export compatibility.

Validation errors return HTTP 422 with error.code: "validation_failed", a short error.summary, and the Zod error.issues array. The service records validation telemetry with route, tool, request/correlation IDs, and failing field paths; request bodies and message text are not logged by the route.

Response

The endpoint is synchronous. It waits for the topic-detection workflow to finish, then returns the standard tool-route envelope with the workflow result under data. It is not a create/start/poll tracked-run API.

{
"data": {
"workflowId": "tap.topic-detection.v1",
"runId": "11111111-1111-4111-8111-111111111111",
"conversationId": "chat-123",
"threadId": "thread-123",
"taxonomyVersion": "2026-06-15",
"status": "success",
"dryRun": true,
"analysisMode": "fast",
"providerLane": "standard",
"classifierStatus": "llm",
"detectedTopics": [
{
"sortOrder": 7,
"topicKey": "scholarships_and_funding",
"displayName": "Scholarships and funding",
"confidence": 0.92,
"evidenceMessageIds": ["msg-1"]
}
],
"candidateTopics": [],
"evidenceMessageIds": ["msg-1"],
"messageHashSet": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"localGate": "continue",
"applyStatus": "dry-run",
"validationErrors": [],
"providerLatencyMs": 27376,
"provider": {
"deployment": "dev-gpt-5_4-nano-eastus2",
"resultCode": "200",
"latencyMs": 27376
},
"inputSummary": {
"messageCount": 12,
"contextTruncated": false,
"includedMessageCount": 12,
"maxContextChars": 20000,
"contextChars": 4096
},
"promptTokens": 3052,
"completionTokens": 422,
"totalTokens": 3474,
"workflowDurationMs": 28604,
"stepTimingsMs": {
"normalize": 3,
"classify": 27376
},
"retryCount": 0
}
}

Valid requests continue to return HTTP 200 even when provider work is incomplete or retryable. In those cases the workflow result uses body-level status fields and optional diagnostics:

{
"data": {
"workflowId": "tap.topic-detection.v1",
"conversationId": "chat-123",
"status": "failed",
"classifierStatus": "failed",
"detectedTopics": [],
"retry": {
"retryable": true,
"reason": "rate_limited",
"retryAfterSeconds": 60
},
"provider": {
"deployment": "dev-gpt-5_4-nano-eastus2",
"resultCode": "429",
"latencyMs": 48720
},
"inputSummary": {
"messageCount": 286,
"contextTruncated": true,
"includedMessageCount": 74,
"maxContextChars": 20000,
"contextChars": 19972
}
}
}

Auditability

Topic detection preserves the same audit shape in fast and full modes:

  • detected topics
  • detected topic sortOrder and displayName from the active taxonomy
  • message-level findings
  • evidence message IDs
  • provider request ID when available
  • provider latency
  • provider result diagnostics when available
  • retryability and reason for provider rate limits or timeouts
  • input summary with message counts and context truncation status
  • token usage
  • workflow step timings
  • raw agent response
  • validation errors

Model Role

The workflow does not send every message directly to the LLM as the first step. The service first normalizes IDs, builds context, applies local gates, and ranks likely topic candidates from the taxonomy. The LLM then makes the semantic classification decision from the prepared context.

fast and full differ by prompt size only. Both modes return the same response and audit schema.