ChatCompletion icon
Script icon
Process icon
If icon
DiscordIncomingWebhook icon
Log icon

Schema-Enforced Ticket Triage with Mistral Structured Output

Triage support tickets with Mistral structured output in Kestra. A JSON schema enforces category and severity, and high severity tickets page Discord.

Categories
AI

LLM triage is only automatable if the output shape is guaranteed. This blueprint passes a JSON schema to io.kestra.plugin.mistral.ChatCompletion through jsonResponseSchema, so the Mistral API itself constrains the response to an object with category, severity, and summary, with enums on the first two and no additional properties. Whatever the ticket text says, it cannot change the output structure, which makes the downstream If branch a safe, deterministic router.

How it works

  1. triage (io.kestra.plugin.mistral.ChatCompletion) sends the ticket as a USER message with a SYSTEM message that frames the task and tells the model to treat ticket text as data. The jsonResponseSchema property wraps the schema as response_format.type=json_schema, so the API returns a schema valid JSON string in {{ outputs.triage.response }}.
  2. parse_triage (io.kestra.plugin.scripts.python.Script on the Process runner) receives the JSON through an environment variable, parses it, and emits category, severity, and summary as scalar outputs via Kestra's outputs protocol.
  3. route (io.kestra.plugin.core.flow.If) checks {{ outputs.parse_triage.vars.severity == 'high' }}. High severity tickets page Discord with the parsed scalars; everything else lands in the execution log.
  4. The errors block posts a distinct Discord alert when classification or parsing fails.

What you get

  • Structured output enforced by the API, not by prompt hoping: the schema rejects free-form text, extra fields, and injected instructions.
  • Category and severity as scalar flow outputs, safe to interpolate into JSON payloads and reusable by downstream tasks.
  • A paging policy in code: only high severity interrupts a human, the rest is logged and auditable.
  • A failure alert so an API error never leaves a ticket silently unclassified.

Who it's for

  • Support engineering teams automating first-pass ticket routing.
  • Platform teams who tried prompt-only JSON extraction and got burned by malformed output.
  • Anyone building LLM decision steps that feed conditional logic.

Why orchestrate this with Kestra

The hard part of LLM triage is not the API call, it is everything around it: keeping the key in a secret store, retrying transient failures, branching on the result, and proving after the fact why a ticket was or was not escalated. Kestra declares all of that in one YAML flow, and every execution records the exact model response next to the routing decision it produced.

Prerequisites

  • A Mistral API key with access to the chosen model.
  • A Discord incoming webhook for pages and failure alerts.
  • Python available on the Kestra host for the Process runner, or swap in a Docker task runner.

Secrets

  • MISTRAL_API_KEY: Mistral API bearer token.
  • DISCORD_WEBHOOK_URL: Discord incoming webhook URL.

Quick start

  1. Add the two secrets to your Kestra namespace.
  2. Execute the flow with the default ticket text; it classifies as high severity and pages Discord.
  3. Rerun with a routine request in ticket_text and confirm it only logs.

How to extend

  • Add fields such as product_area or language to the schema and the parser.
  • Replace the Discord page with a ticketing task that sets priority from {{ outputs.parse_triage.vars.severity }}.
  • Trigger the flow from a webhook so tickets are triaged the moment they arrive.
  • Fan out with a Subflow per category once the categories map to different teams.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.