Part 2 of 7 · Ticket router series ~4 min read

How a ticket gets read

The router can only sort what reaches it. So the first job is turning every way a customer can ask for help into one clean ticket. There are three ways a ticket gets in: a customer emails your support address, fills in the form on your site, or sends a chat message. The first is the most common. The other two exist because customers reach for whatever’s in front of them — and the same person will often try all three about the same problem. Once a ticket is in, one small model call reads it: what’s it about, how urgent is it, and how does the customer sound?

Key takeaways

  • Three intake lanes feed one queue: an email inbox, a web form, and a chat lane.
  • Each new ticket is written once, given an id, and checked against recent tickets so duplicates merge.
  • One Bedrock Haiku 4.5 call reads each ticket and returns topic, urgency, and tone.
  • The model only reads. It never writes a reply — that stays with a person.
  • A burst of tickets queues up in SQS so nothing is dropped when a busy hour hits.

Three lanes into one queue

Three intake lanes funnel into one ticket queue A diagram with three vertical lane columns at the top and a single unified row at the bottom. Lane one, Email inbox: a customer emails your support address; SES inbound writes the raw email to S3 and triggers an intake Lambda that pulls out the sender, subject, and body and creates a ticket. Lane two, Web form: the contact-support form on your site posts straight to a Lambda Function URL; the same intake creates a ticket from the form fields. Lane three, Chat: your chat tool posts a finished conversation to another Function URL; the intake folds the conversation into one ticket. All three lanes write the new ticket once, give it an id, and drop it on an SQS queue so a busy hour never drops anything. A read Lambda takes each ticket off the queue and makes one Bedrock Haiku 4.5 call to read topic, urgency, and tone, then writes those tags back to the ticket. A note at the bottom: every lane becomes one ticket on one queue — and the model only reads it, it never writes the reply. Lane 1 · SES inbound Email inbox • Customer emails support address • SES writes email to S3 • Intake pulls out sender, subject, body • One ticket created, given an id Lane 2 · Function URL Web form • Support form posts to a Function URL • Intake reads the form fields • Same ticket shape as the email lane • One ticket created, given an id Lane 3 · chat tool Chat lane • Chat tool posts a finished chat • Intake folds it into one ticket • Same shape as the other two lanes • One ticket created, given an id One ticket on one SQS queue id · customer · subject · body · source · received-at · de-duped read Lambda: one Haiku 4.5 call → topic, urgency, tone to router, next post Every lane becomes one ticket on one queue — and the model only reads it, never the reply.
Fig 2. Three lanes converge on one queue. Email, web form, and chat all become the same shape of ticket. A read Lambda takes each one off the queue and makes a single model call to tag the topic, urgency, and tone.

Lane 1: the email inbox (the lane most teams live in)

Set up a dedicated inbound address — something like support@your-company.com — via Amazon SES. SES is Amazon’s email service; here it’s receiving mail, not sending it. When a customer emails that address, SES writes the raw email to s3://tr-raw-mail/. The S3 PUT triggers an intake Lambda. The Lambda reads the email, pulls out the sender, the subject, and the body (and strips the long quoted history off replies so the router reads the new message, not the whole thread), and creates one ticket with an id.

This lane covers the bulk of real support. People email. They reply to their own ticket. They forward something a colleague sent. The intake treats a reply to an existing ticket as part of that ticket, not a brand-new one, by matching the email’s thread headers against tickets already in flight.

Lane 2: the web form

The “Contact support” form on your site posts straight to a Lambda Function URL — a plain web address that runs a Lambda, with no API Gateway in front of it. The form sends the customer’s name, email, a subject, and a message. The intake reads those fields and creates a ticket in exactly the same shape as the email lane, so everything downstream treats the two identically. A simple shared secret on the form post keeps random internet traffic from creating junk tickets.

The form lane is worth having because a customer on your pricing page who hits a problem will use the form in front of them rather than hunt for an email address. Catching them there means the ticket arrives with the page they were on already attached.

Lane 3: the chat lane

If you run a chat widget, finished conversations can become tickets too. When a chat ends without resolution — the visitor left, or the chat tool couldn’t answer — the tool posts the conversation to another Function URL. The intake folds the back-and-forth into one ticket body so the router reads it like any other message. This is the most optional of the three lanes; a team without chat loses nothing.

Reading the ticket: one small model call

However a ticket arrived, it lands on an SQS queue. SQS is a simple waiting line for work; if a hundred tickets arrive in a minute, they line up instead of overwhelming anything. A read Lambda takes tickets off the queue one at a time and makes a single Bedrock Haiku 4.5 call. The prompt is short: “Read this support ticket. Return JSON only. Give the topic from this list, an urgency of low, medium, or high, and a tone of calm, confused, or angry. If you are unsure of the topic, say unsure.” The list of topics comes from the rules sheet, and a handful of labelled examples are included so the model has a few real tickets to pattern-match against.

That one call is the only place a model touches a ticket. It reads — topic, urgency, tone — and writes those three tags back onto the ticket. It does not draft a reply, suggest an answer, or message the customer. Everything after this point is plain Python working from those three tags, which is what the next post covers.

Why everything funnels to one queue

Three lanes in, but only one queue the router reads from. That’s deliberate. If each lane routed on its own, “why did this ticket go there?” would mean checking three code paths. Funneling everything to one queue means there is exactly one ticket per request, in one shape, read by one model call, sorted by one set of rules. The lanes are first-class for getting tickets in, but they all become the same ticket on the way.

Next post: how the router takes the topic, urgency, and tone and turns them into one of four moves.

All posts