Template - Revenue
ChoiceCustomer message routing
Which team should own a customer message: sales, customer success, support, or billing.
A choice decision for shared inboxes where one address receives purchase questions, onboarding requests, technical problems, and invoices.
The decision
Which team should own this customer message?
Choices
- •
sales: New purchases, pricing, demos, and quotes. - •
customer_success: Onboarding, adoption, renewals, and expansion. - •
support: Troubleshooting and technical problems with the product. - •
billing: Invoices, payments, and refunds.
Evidence checks (asked in the same request)
- Technical failure: The message reports an error, crash, or failed request.
- Cancellation intent: The customer says they intend to cancel or not renew.
Expected output: One of the four values, a confidence value, and a probability for every option, plus one probability per evidence check.
Input
The Decisions API reads one input. Combine these fields into it as labeled text so the model sees the facts the decision depends on.
| Field | Type | Required | Description |
|---|---|---|---|
message | string | Yes | The customer's message, verbatim. Include the subject line if there is one. |
is_customer | boolean | No | Whether the sender is an existing customer. Helps separate sales from customer success. |
Worked examples
A new customer planning their rollout
We signed last month and need help planning the rollout. Can someone run onboarding training for our 30 agents and share best practices?
A careful reviewer would say: customer_success. An existing customer asking for onboarding and adoption help.
Run this exampleA seat count dispute on an invoice
Our invoice for October shows a charge for 50 seats but we only have 35. Please correct it.
A careful reviewer would say: billing. An invoice correction.
Run this example
Expected answers are human judgments written for this template, not model output.
How it works
This is often the first decision teams automate, and the one where generic routing products already do a reasonable job. What makes it yours is the definitions: where an expansion request goes, who owns a renewal with a technical blocker.
The cancellation check exists because a cancellation request sent to billing as a plan change is a lost save opportunity. Gate on it separately.
Human review and thresholds
Recommended review policy
Automate confident routes. Send low-confidence messages to a triage queue rather than guessing, since a wrong route costs a hand-off and a slower first response.
Choosing thresholds
Misroutes are cheap individually and expensive in aggregate. Measure the re-route rate per team after launch and adjust the bar for the teams where misroutes hurt most.
- Starting automation threshold
- 75%
- Starting don’t-decide threshold
- 50%
Starting points for the playground, not recommendations. The right values depend on your costs and on how the model’s confidence tracks accuracy on your data. Calibrate them.
Limitations
- Messages that span teams get one owner. Decide in advance which team leads when a billing problem is caused by a product bug.
- Four options is a deliberately small set. Each added team makes every boundary harder to hold.
- The model doesn't know your team's capacity or working hours.
Use it in your code
The template definition is plain JSON: the question, the evidence checks, the input fields, and the starting policy. The SDK call below sends the same question and checks to the Decisions API from your server.
{
"$format": "thethinkloop.template/v1",
"slug": "account-team-routing",
"title": "Customer message routing",
"decides": "Which team should own a customer message: sales, customer success, support, or billing.",
"question": {
"name": "decision",
"type": "choice",
"instructions": "Which team should own this customer message?",
"choices": [
{
"value": "sales",
"description": "New purchases, pricing, demos, and quotes."
},
{
"value": "customer_success",
"description": "Onboarding, adoption, renewals, and expansion."
},
{
"value": "support",
"description": "Troubleshooting and technical problems with the product."
},
{
"value": "billing",
"description": "Invoices, payments, and refunds."
}
]
},
"evidenceChecks": [
{
"name": "factor_1",
"label": "Technical failure",
"type": "predicate",
"instructions": "The message reports an error, crash, or failed request."
},
{
"name": "factor_2",
"label": "Cancellation intent",
"type": "predicate",
"instructions": "The customer says they intend to cancel or not renew."
}
],
"inputs": [
{
"name": "message",
"type": "string",
"required": true,
"description": "The customer's message, verbatim. Include the subject line if there is one."
},
{
"name": "is_customer",
"type": "boolean",
"required": false,
"description": "Whether the sender is an existing customer. Helps separate sales from customer success."
}
],
"expectedOutput": "One of the four values, a confidence value, and a probability for every option, plus one probability per evidence check.",
"examples": [
{
"title": "A new customer planning their rollout",
"input": "We signed last month and need help planning the rollout. Can someone run onboarding training for our 30 agents and share best practices?",
"expected": "customer_success"
},
{
"title": "A seat count dispute on an invoice",
"input": "Our invoice for October shows a charge for 50 seats but we only have 35. Please correct it.",
"expected": "billing"
}
],
"startingPolicy": {
"automateAt": 0.75,
"abstainBelow": 0.5,
"note": "A starting point for exploration, not a recommendation. Calibrate against your own labeled examples."
}
}import OpenAI from "openai"; // openai >= 7.30.0
const client = new OpenAI(); // reads OPENAI_API_KEY on the server
const decision = await client.decisions.create({
model: "gpt-6-luna",
input: "We signed last month and need help planning the rollout. Can someone run onboarding training for our 30 agents and share best practices?",
questions: [
{
type: "choice",
name: "decision",
instructions: "Which team should own this customer message?",
choices: [
{
value: "sales",
description: "New purchases, pricing, demos, and quotes."
},
{
value: "customer_success",
description: "Onboarding, adoption, renewals, and expansion."
},
{
value: "support",
description: "Troubleshooting and technical problems with the product."
},
{
value: "billing",
description: "Invoices, payments, and refunds."
}
]
},
{
type: "predicate",
name: "factor_1",
instructions: "The message reports an error, crash, or failed request."
},
{
type: "predicate",
name: "factor_2",
instructions: "The customer says they intend to cancel or not renew."
}
],
});
// Answers come back in question order. A question can also be refused.
const [main, ...checks] = decision.answers;
if (main.type === "refusal") throw new Error("The model declined to answer.");
if (main.type === "choice") {
console.log(main.choice, main.confidence, main.probabilities);
}
Download it as account-team-routing.json, or browse all developer resources.
Need this decision tuned to your policy?
Related
- Choice template
Sales routing
Which sales motion an inbound inquiry belongs to.
- Predicate template
Billing escalation
Whether a billing issue should leave the support queue and go to finance.
- Choice template
Feature request classification
Whether a message is a bug report, a feature request, a how-to question, or something else.
- Guide
AI decision routing
A routing decision is several smaller decisions plus a rule that combines them. How to design that rule, where people fit, and what to log.
- Guide
Decisions API tutorial
Start here. The request and response shapes, how to read each answer type safely, asking several questions about one input, and what the API does not do.
- Guide
Predicate vs choice vs score
Most decisions can be phrased three ways, and each returns different information. Which type fits which decision, with worked examples and common mistakes.