How an evidence packet gets built
Gathering evidence is mechanical; turning it into a rebuttal a bank will actually read is the part where the system earns its keep. This post is about how one Bedrock call assembles the packet — mapping each piece of real evidence to the precise thing the dispute’s reason code is contesting, writing a tight cover narrative, and rendering it to a PDF — and, crucially, how it stays grounded: it never fabricates a tracking number or a message, and when the evidence simply isn’t there it says the case is unwinnable and recommends taking the loss.
Key takeaways
- One Bedrock Haiku 4.5 call turns the gathered evidence and the reason code into a structured rebuttal.
- A reason-code playbook tells the model which claim each piece of evidence is meant to answer.
- The packet is rendered to a PDF in S3, with every claim citing the exact evidence it rests on.
- The model gives an honest winnability read — and recommends accepting the loss when the proof isn’t there.
- It is grounded strictly in the gathered evidence: it never writes a tracking number or a message that doesn’t exist.
The reason-code playbook does the thinking up front
A good rebuttal answers the specific thing the bank is testing, and that thing is set by the reason code. So the assemble Lambda starts from a small playbook — a plain text file in S3, editable without a deploy — that maps each common reason code to the claim it makes and the evidence that rebuts it. Product not received → rebut with proof of delivery to the customer’s address, plus any message acknowledging receipt. Unauthorised / fraudulent → rebut with the AVS and CVC match, the shipping-to-billing address, the customer’s device or order history. Subscription cancelled → rebut with the agreed terms and the cancellation timestamp showing the charge predated it. The playbook means the model isn’t guessing what a strong response looks like for this dispute — it’s following the structure card networks actually score against.
One grounded Bedrock call
The Lambda reads the evidence index from the previous step, pulls the relevant pieces out of S3, and makes a single Bedrock Haiku 4.5 call. The prompt hands the model three things: the dispute (amount, reason code, what the customer claims), the playbook entry for that reason code, and the gathered evidence — the order fields, the tracking events, the message excerpts, the policy text — each tagged with its evidence-index id. The model’s job is narrow and mechanical: write a tight cover narrative and a point-by-point rebuttal in which every claim cites the evidence id it rests on. It is told, firmly, to use only the evidence provided and to flag anything the playbook expects but the manifest marks missing. It returns structured JSON — the narrative, the cited points, the list of attached exhibits, and a winnability verdict — not free-form prose. Structured output is what lets the next step render a clean packet and lets the filer attach the right exhibits in the right fields.
Render the packet to a PDF
The structured rebuttal is turned into a single PDF in the S3 evidence bucket: a cover page with the dispute reference and the cover narrative, the point-by-point rebuttal, and then the exhibits — the order confirmation, the carrier’s proof of delivery, the relevant message excerpts, the accepted policy — each labelled and cross-referenced from the narrative. Rendering happens in the Lambda from a fixed template, so the layout is consistent and nothing about it depends on the model. Many processors also take evidence as discrete fields and file attachments through their API rather than one PDF; the assemble step produces both shapes from the same structured output, so the filer can submit whatever this processor expects.
An honest winnability read
The same call returns a winnability verdict — strong, mixed, or weak — with a one-line reason grounded in the manifest. Strong: the decisive evidence for this reason code is present and consistent. Mixed: the evidence is there but has a soft spot (delivered, but to a slightly different address; acknowledged, but with a complaint). Weak: the decisive piece is missing or actively unhelpful — no proof of delivery on a not-received claim, or a support thread where the customer was promised a refund that never came. On a weak case the system doesn’t quietly file a hopeless packet; it writes a recommend accept-loss outcome and tells the owner why. Fighting a chargeback you’ll lose still costs the dispute fee and your attention, so knowing when to stand down is part of doing this well.
Why it can’t fabricate
The guardrail against invented evidence isn’t a polite instruction — it’s the structure. The model is given the evidence manifest and told to cite an evidence id on every claim; the renderer then checks each cited id actually exists in the index before it puts the claim in the packet. A claim that cites nothing, or cites a piece marked missing, is dropped, not printed. So the worst a model drift can do is produce a weaker packet — never a fictional one. The customer’s tracking number is the carrier’s tracking number or it isn’t in the packet at all; the “customer acknowledged receipt” line exists only if a real message is attached behind it. This is what makes the system safe to point at a bank: it can only ever say true things about what you have.
Design rules for building the packet
- Start from the reason-code playbook, so the rebuttal answers the exact claim the bank is testing.
- One grounded Bedrock call, structured output, every point citing a real evidence id.
- Render from a fixed template, in both PDF and field-and-attachment form, so any processor’s API can take it.
- Give an honest winnability read, and recommend accepting the loss when the decisive evidence is missing.
- Cited-evidence checks at render time mean a model can only weaken a packet, never invent one.