SEATFROG · CUSTOMER SUPPORT CLAUDE CODE PLUGIN --:--
Departures · Refund pipeline

Bulk RefundChecks

One pipeline runs a batch of refund tickets end to end — ingest, triage, verify, check, collate — into a single Approve / Flag result set, while holding the line against fraudulent refunds.

Refund typeClassHydrated fromStatus
Upgrade First / Std Premium RDS · sale On pipeline
Superfare Standard · Avanti flex_state_prod On pipeline
SecretFare Standard · various TOCs flex_state_prod On pipeline
First-time setup

Setup

Four one-time steps. Do these the first time you use it — the checks and fallbacks rely on each one — and every run after just works. The full companion guide lives on Notion.

1 Install the plugin

Add it to your Claude

  1. Type /plugin
  2. Scroll to Marketplaces
  3. Select Seatfrog Internal
  4. Then Update Marketplace
  5. Then Browse Plugins
  6. Select bulk-refund-checks
  7. Type /reload-plugins
2 Claude in Chrome

Enable browser access

Powers the service-status check: when our own records can't answer, it falls back to the live train-times sites (Recent Train Times / On Time Trains).

  1. Set up the Claude in Chrome extension and keep Chrome open while the pipeline runs.
  2. The first time it opens a train-times site, allow the extension when it asks.

Without it, any row that needs the fallback comes back as service status unknown and is flagged — never wrongly approved.

3 Zendesk

Sign in, then enable replies

Signing in is required — it's how the pipeline reads the tickets in the first place. Enabling writes is what additionally lets it send the automatic replies for the few matched flag reasons.

  1. Required — log in with /zendesk-mcp:login (opens your browser, no tokens to manage).
  2. Then turn public replies on with /zendesk-mcp:zendesk-writes on (off by default).

Signed in but writes left off → matched replies are recorded as (writes disabled) to send by hand. Notes, status and tags are always on.

4 Ticket Keeper

Sign in to TTK

On the first run, when the process reaches the UTN (barcode) check, it prompts you to log in with the TTK device username and password.

  1. Enter them when prompted — or create ~/.config/seatfrog-mcp/ttk.env yourself beforehand to skip the prompt.
  2. It saves them there (readable only by you) and reuses them on every run after.

The password is never shown on screen, written into the output files, or stored in the tool itself.

Handled for you — installing the plugin auto-installs and enables its Zendesk and Seatfrog MCP dependencies (Seatfrog MCP from either marketplace), so there's no separate install. AWS sign-in is refreshed by the tool itself — you just need an AWS SSO profile. And every run opens with a readiness check that confirms all of the above and walks you through anything still missing.

That's it — then just tell Claude ‘Do the bulk refunds’ and it takes it from there.

In plain terms

You point it at the tickets. It does the checks.

One tool works through a whole batch of refund tickets in one go — it gathers them, decides which it can handle automatically, confirms who the customer is and whether they're owed a refund, runs its safety checks, and hands back two lists.

It clears the easy, obvious refunds on its own, stops the dodgy or ineligible ones getting through, and passes anything unclear to a person instead of guessing. You don't run the checks yourself — you tell it the refund type and point it at the tickets.

The rules · NRCoT 2026
Condition 30 · Cancellation

Didn't travel

Refund due if the train was cancelled, delayed or rescheduled and the customer chose not to travel. Claim within 28 days.

Conditions 15.6 / 31 · Declassification

First Class not provided

Refund due if First Class was declassified or not provided. Claim within 28 days.

Condition 32 · Delay Repay

Travelled but delayed

That's a Train Operating Company matter — Delay Repay, not a Seatfrog refund.

The short version: used the ticket → no refund from us. Didn't travel because the service was cancelled or disrupted → refund. Travelled but delayed → send them to the train company.

The line · Calling points

This ticket calls at

Every refund follows the same route. The orchestrator, BulkRefundChecks, owns the ingest and the final collation and hands each stage to a specialist skill. It calls at five stops before the verdict.

  1. Stop 01 · Ingest

    Gather & dedupe

    Pull the New refund tickets from a Zendesk view, search, or id-list — read across every page and reconciled against the view count, so nothing is missed. Deduplicate by booking key: duplicates are worked first (an internal note links each to the primary ticket) and the checks continue on the surviving ticket. Anything raised today is left out (travel must have passed).

    Reads every pageToday excludedDuplicates worked first
  2. Stop 02 · Triage

    Sort — does it belong here?

    Read the customer's own words (and any clear screenshot) to decide what the request really is: a genuine cancellation or declassification, or something for a person — a mistake, a change of mind, or a travelled-but-delayed claim. For upgrades it also splits cancellation from declassification.

    Scope checkScreenshots read defensivelyCancellation vs declassification
  3. Stop 03 · Verify

    Who they are & are they eligible

    One lookup confirms identity and the 28-day window, then returns the booking — route, travel date, ticket numbers, receipt total. Upgrades match on User ID + Sale ID; Flex matches on the booking reference, and the ticket email must match the booking's owner email. A ticket whose travel date hasn't passed is deferred — an internal note on the ticket says so, and a later run picks it up.

    Identity gate28-day windowDeferred → internal note
  4. Stop 04 · Checks

    Split by case & check

    The fraud guard: a UTN check asks whether the barcode was clipped on board the train, or a Delay Repay claim started — scans at the station barriers don't count as travel. Cancellations then get a service check — did the booked train actually run — from our own records first, live sources only as fallback. A clipped ticket still gets a records-only check, so the decline can carry Delay Repay advice.

    UTN scan · did they travelService · did the train runDeclassification → UTN only
  5. Terminus · Collate

    Pull it together

    Each ticket lands on one outcome — Approve, Flag, or Deny — written to two lists in your Downloads folder. Flagged cases are then worked one-by-one in the terminal; the Approve list gets its replies only after a human confirms the refunds are processed. Customer-facing writes stay tightly gated throughout.

    Approve listFlag listFlags worked 1-by-1
Under the hood · Five skills

One orchestrator,
four specialists

The pipeline is five composable skills. The sub-skills run in pipeline mode inside the orchestrator; the two check skills also run standalone for a one-off scan or service lookup.

Orchestrator Start here

BulkRefundChecks

bulk-refund-checks:BulkRefundChecks

Confirms the refund type, ingests and deduplicates tickets, sequences the four specialists, then collates the single Approve / Flag result set. Owns everything the sub-skills don't.

Stage · Triage

RefundTriageAndClassify

…:RefundTriageAndClassify

Reads ticket text and any clean screenshot to set the disposition, and for upgrades the cancellation / declassification category. Makes no admin lookups.

Stage · Verify

RefundVerifyAndHydrate

…:RefundVerifyAndHydrate

The single query. Enforces the identity and 28-day gates and returns the hydrated sale record the rest of the pipeline reuses.

Check · Fraud guard

BulkUTNChecks

…:BulkUTNChecks

Reports each sale's UTN scan status as APPROVE / FLAG / DENY. Standalone or in pipeline. Scan status only — no service lookup.

Check · Did it run

BulkServiceStatusChecks

…:BulkServiceStatusChecks

Looks up whether the booked train was cancelled, delayed, or disrupted — refunds-hsp archive first, RTT / OTT via Chrome as fallback. Makes no refund decision.

At the gate · Three outcomes

Approve · Flag · Deny

The scan gates, the actuals confirm. A delay never auto-approves — that is a Train Operating Company matter, not a Seatfrog refund.

Approve

Didn't travel/the upgrade was unused, and the service status checks confirm the booked segment was cancelled or disrupted. Safe to refund.

NRCoT 30 · 15.6 / 31
Flag

Needs a human: ambiguous scan, unknown service, identity mismatch, out-of-scope request, or a duplicate ticket.

Human review
Deny

Proof of use — a clip scan on board the train — or a Delay Repay claim, or a claim raised more than 28 days after travel. Barrier entry/exit scans alone never deny: a customer can pass through the gates, meet a cancelled train, and leave without travelling.

Surfaced, not silently actioned

How each case is decided

Declassification — Upgrade only · the UTN check decides
UTN check saysResultWhy
Clipped on board the train or Delay Repay claim already madeDenyThe upgrade is marked as used, claiming with the TOC is the correct course of action. Barrier entry/exit scans don't count as use.
UnclearFlagA person should look.
Not usedApproveWe can't disprove it and the upgrade wasn't used → refund stands.
Cancellation — all Flex + Upgrade cancellations · the scan gates, the service confirms
UTN checkDid the train run?ResultWhy
Clipped on boardChecked from our records, for the reply onlyDenyThey travelled — and if the records show their train was disrupted, Claude sends Delay Repay advice with the decline.
Not usedCancelled / disrupted on their legApproveDidn't travel → refund.
Not usedRan (on time / only delayed)FlagClaim not supported — a delay is for the train company; a person decides.
Not usedCan't tellFlagNeeds a person to review.
UnclearFlagThe scan isn't clear enough; needs a person.
One useful exception. If a cancellation ticket is about to be flagged for "the train ran" but the customer's own words clearly describe a declassification ("no first class carriage", "first class wasn't available"), they've probably picked the wrong reason on the form. The tool sends it down the declassification path instead — because "did the train run?" is the wrong question for a First-Class-not-provided complaint. This is the only time the written note overrides the reason selected.

What you get back — two lists

Both land in your Downloads folder, named by product and date. The first column of each is refund_type — each product is refunded through a different system, so it must show on every row.

{Product}Approve{date}.csv
The clean auto-approve worklist — safe to refund. refund_type,zendesk_ticket_id,sale_id, user_id,category,utn,service_status
{Product}Flag{date}.csv
Every row a person must action — one combined list, always with a reason. flag_type,refund_type,zendesk_ticket_id,…, reason,service_status,related_ticket_id,macro_reply
flag_typeWhat it meansWhat you do
DENYThe checks would refuse it — proof they travelled, Delay Repay, or a claim over 28 days after travel.Work the refusal — reply / decline.
FLAGNeeds judgement — unclear scan, unknown train status, email didn't match.Review and decide.
SURFACETriage caught it as not belonging here — a mistake, a delay claim, an early termination.Handle by hand.
DUPLICATEA second ticket for a booking already in the batch.Close it against the primary (in related_ticket_id).
The refund amount is the receipt TOTAL. For upgrades that includes the £3.00 Platform Fee per passenger — never quote the bare upgrade price or the purchase price; both miss the fee and short-change the customer by £3 each.

Both written to ~/Downloads · the customer's email is never written to any file — you find them by zendesk_ticket_id

Beyond the two lists

Replies & housekeeping

The tool writes back to a customer in exactly two situations — a matched flag reason, and the post-refund confirmation you sign off — and it keeps the two lists honest so no ticket is worked twice.

The automatic replies — only a few cases

A small set of flag reasons have an approved canned reply. Claude reads the macro first, sends it whole so the right tags and fields go on too, and then sets the ticket to Pending (unless the macro sets its own status). Everything else is left for the one-by-one session below. If Zendesk replies are switched off, the tool skips the send and marks the row (writes disabled).

TypeReasonCanned reply
UpgradeWrong / unconfirmable referenceIncorrect reference
UpgradeDelay Repay already claimedUpgrades: Delay Repay Already Started
FlexDelay Repay already claimedClaimed for Delay Replay

More on the way: a set of new Claude: macros (Delay Repay advice after travel, refund window passed, service ran, duplicate request) is drafted and awaiting creation in Zendesk — each joins this table once it exists and a human has approved it.

Working the Flag list — one by one, in the terminal

1

One ticket at a time, never a batch. When you're ready, Claude walks the Flag list with you in the terminal: for each row it re-checks the ticket is still New, finishes any outstanding investigation by default, and briefs you on the reason and evidence.

2

You decide, it does. It proposes an action with a recommendation — a permitted macro, an internal note, or leave-for-human — and only acts once you confirm. The worked ticket goes to Pending and comes straight off the list.

Replying to the Approve list — after the refunds are processed

1

Approvals are refunded by a person, in each product's refund system — the tool never replies to an Approve row on its own.

2

Once you confirm the refunds are done, Claude bulk-replies to those tickets with the Refund processed reply — checking each is still New first, and removing each row from the list as its reply lands.

Keeping the lists tidy

1

Before starting on a list, the tool re-checks every ticket's status. Any ticket that's no longer New has already been picked up — by a colleague, an earlier step, a merge, or an automatic rule — so it comes off the list rather than being worked.

2

And again immediately before every single action. A check done at the start goes stale on a shared queue — so no reply, note, or status change happens without confirming the ticket is still New right then.

3

As each ticket is worked, it comes off the list — instantly. The file on disk is rewritten there and then, never saved up for the end. The lists shrink to empty as the batch clears — which is what stops two people refunding or replying to the same ticket.

4

Only a pipeline run adds rows. Nothing is hand-added or re-added — if a ticket needs rework, it comes back through a fresh run.

Running it

Point it at a view

you ▸ Run the bulk upgrade refunds in Zendesk view 11663011261455

Confirmed refund type … Upgrade
Ingested 102 of 102 tickets · 3 same-day excluded · 2 duplicates
Triage → verify → UTN → service status …
✓ UpgradesApprove16July2026.csv written to ~/Downloads (74 rows)
✓ UpgradesFlag16July2026.csv written to ~/Downloads (23 rows)

zendesk-mcp

Ticket ingestion, the gated public-reply macros and post-refund confirmations, plus always-on internal notes and status changes.

seatfrog-mcp

RDS sale & flex_state_prod queries, the refunds-hsp archive, and TTK scan lookups.

claude-in-chrome

Only for the Recent Train Times / On Time Trains service-status fallback.

Identity gate requester = booking owner 28-day window NRCoT Same-day exclusion Dedup by booking key TTK cap 250 / device / day Screenshots read defensively
Spot-checking its work

Worth remembering

Check the refund type first. It decides the list, the details, and how tickets are sorted.

Today's tickets are always skipped — travel has to have happened first.

We never refund a used ticket, and a delay is always a train-company matter, never an automatic yes.

A mismatched email means flag, not decline — it's almost always a typo, not fraud.

Refund figures always include the Platform Fee — the receipt TOTAL, £3 per passenger on upgrades.

Never work a ticket that isn't New — someone's already on it. When in doubt, it gets flagged, not guessed.

On the horizon

Upcoming changes

Live

Flagged cases are surfaced in the terminal to review and action one-by-one — removed from the Flag list as they're worked, with any outstanding investigation completed by default.

Live

Duplicates are worked first — a linking internal note goes on the extra ticket, and the checks continue on the surviving one. Deferred tickets (travel date not yet passed) get an internal note too.

Live

Approve-list tickets get a bulk Refund processed reply — once a human confirms the refunds have been paid. And every worked ticket is set to Pending by default.

Soon

New Claude: macros — drafted and awaiting creation in Zendesk. Once live, even less manual handling of flagged cases.

Live

When a ticket was clipped on board, Claude still checks (from our own records) whether the train was disrupted — and the decline goes out with Delay Repay advice, once its Claude: macro is created. Barrier entry/exit scans no longer count as proof of travel.

Soon

Fully automatic merging of duplicate requests (the merge itself is still done by a person today).