soft-shell crabvietnamese mud crabdifferent species of crab
17
17 Comments

How to automate refund reviews without giving AI the final say

A refund is about more than returning money. It’s about understanding what went wrong.

Here's a workflow to help you review each case, prepare a clear response, and identify patterns to improve your product.

Tools

  • Jotform: Customers submit refund requests here.
  • Zapier: Passes information between your apps and runs the workflow.
  • ChatGPT (OpenAI): Summarizes the request and writes a draft reply.
  • Google Sheets: Keeps all requests in one place.
  • Gmail: Creates reply drafts.
  • Stripe: Processes approved Stripe refunds.

Step 1 — Write the decision rules

Create a document titled "Refund Rules."

Add four possible decisions:

  • Approve: The order is valid, the customer qualifies for a refund, and there are no problems.
  • Support_first: The issue can probably be fixed without a refund.
  • Deny: The request clearly falls outside the published policy.
  • Escalate: You cannot verify the order, something looks unusual, the policy is not clear, or you need more info.

Before making a decision, compare the purchase date submitted through the refund request form with the transaction date in Stripe or your payment system. You should also check the customer’s product usage in your course platform, SaaS database, or store.

Every refund request should have one final decision.

Step 2 — Build the Jotform form

Open Jotform: Click Create Form → Start From Scratch → Classic Form.

Add these fields:

  1. Full Name
  2. Email
  3. Order or Invoice ID
  4. Product Purchased
  5. Purchase Date
  6. Refund Reason
  7. What Happened?
  8. What Have You Tried?
  9. Preferred Resolution
  10. Screenshot or File Upload

Add these options under Refund Reason:

  • Duplicate purchase
  • Cannot access product
  • Technical problem
  • Not as expected
  • Accidental purchase
  • Billing problem
  • Other

Add these options under Preferred Resolution:

  • Refund
  • Help fixing the issue
  • Replacement
  • Store credit
  • Not sure

Open Settings → Conditions → Show/Hide Field.

Create these rules:

  • Show What Have You Tried? if the Refund Reason is Cannot access product or Technical problem.
  • Show Describe the reason if the Refund Reason is Other.

Preview the form and test every option.

Add this notice (or something similar) above the Submit button: "Submitting this form does not guarantee a refund. We will review your order, product usage, and our refund policy: [POLICY LINK]."

Customers can now explain the problem and tell you what they want in one form.

Step 3 — Create the operations sheet

Create a Google Sheet called Refund Operations.

Add these column headers to row 1: Request ID, Submitted At, Month Key, Name, Email, Order ID, Product, Customer Reason, Customer Details, Preferred Resolution, AI Review, Issue Class, Order Verified, Policy Eligible, Usage Checked, Risk Flag, Decision, Workflow Status, Refund Amount, Refund Reference, Final Outcome, Product Fix.

Each row will represent one refund request.

Step 4 — Build the intake Zap

In Zapier, create a new Zap.

Set up the trigger:

  1. Click CreateZap.
  2. Choose Jotform → New Submission.
  3. Connect your Jotform account.
  4. Select the refund form.
  5. Click Test trigger.

Next, create the Month Key:

  1. Add Formatter by Zapier → Date/TimeFormat.
  2. Map the Jotform submission date into Input.
  3. Set the output format to YYYY-MM.
  4. Use the result as the Month Key.

Next, add ChatGPT (OpenAI) → Send Prompt.

Paste this prompt (or something similar):

"You assist a human refund reviewer. Use only the facts below.

Return exactly:
SUMMARY:
Likely_issue_class:
Missing_facts:
Risk_flags:
Support_option:

Allowed classes: ACCESS, TECHNICAL, EXPECTATION, BILLING, ACCIDENTAL, OTHER.

Do not approve or deny. Do not invent policy terms, usage, payment status, or customer history.

Order ID: [map Order ID]
Product: [map Product]
Purchase date claimed: [map Purchase Date]
Reason: [map Refund Reason]
Details: [map What Happened]
Tried: [map What Have You Tried]
Preferred resolution: [map Preferred Resolution]"

Finish the Zap with these steps:

  1. Open the advanced fields and set Temperature to 0.2. This makes the results more consistent.
  2. Add Google Sheets → Create Spreadsheet Row.
  3. Select the Refund Operations sheet.
  4. Map the form answers and Month Key to the correct columns.
  5. Map the full ChatGPT response to AI Review.
  6. Enter NEW in Workflow Status.

Each new form submission will now create a row in the sheet with the customer’s answers and an AI summary.

Step 5 — Review the facts

Open the matching order in your payment and product systems.

Set these values in the sheet:

  • Issue Class: Use a category that best fits the request. Here are some examples: Access, Technical, Expectation, Billing, Accidental, or Other.
  • Order Verified: Yes or No
  • Policy Eligible: Yes, No, or Exception
  • Usage Checked: Yes or No
  • Risk Flag: Choose the most appropriate flag. Here are some examples: None, Duplicate_refund, Chargeback, ID_mismatch, or Other
  • Decision: Approve, Support_first, Deny, or Escalate

Review the information before choosing a decision.

The AI summarizes the request. You make the final decision.

Step 6 — Build the decision Zap

Create a second Zap.

  • Choose Google Sheets → New or Updated Spreadsheet Row.
  • Connect your Refund Operations sheet and test the trigger.
  • Click the + button to add another step.
  • Search for Filter by Zapier and select it.

Set up the filter so the Zap only continues when both of these are true:

  • Decision exists.
  • Workflow Status exactly matches NEW.

Test the filter. If both conditions are true, the Zap should continue. Otherwise, it should stop.

  • Click the + button again.
  • Search for Paths by Zapier and select it. (Paths require a paid Zapier plan.)

Create four paths, one for each decision state listed in Step 1:

Path 1: Approve

  • Set the path rule: Decision exactly matches Approve
  • Add Gmail → Create Draft.

Address the draft to the customer. Leave placeholders for:

  • Refund amount
  • Processing date
  • Expected arrival time

Do not send the draft.

Path 2: Support_first

  • Set the path rule: Decision exactly matches _Support\first
  • Add ChatGPT (OpenAI) → Send Prompt.

The prompt should ask the AI to write a reply using only the verified information. Include the support action and explain what the customer should do if it doesn't solve the problem.

Add Gmail → Create Draft. Map the text from ChatGPT to the email body.

Path 3: Deny

  • Set the path rule: Decision exactly matches Deny.
  • Add Gmail → Create Draft.

State the policy rule that applies. Include a link to your refund policy.

Party 4: Escalate

  • Set the path rule: Decision exactly matches E_scalate_
  • Add Gmail → Send Email. Send the email to your review team.

Include the following in the email:

  • Order ID
  • Risk Flag
  • AI Review
  • Link to the Google Sheet

Add Google Sheets → Update Spreadsheet Row at the end of every path.

Use the trigger's Row ID. Set Workflow Status to _DRAFT\READY or ESCALATED. This updates the same row.

Step 7 — Process approved refunds

In Stripe:

  1. Open Payments.
  2. Find the payment.
  3. Click the overflow menu.
  4. Click Refund payment.
  5. Confirm the refund amount.
  6. Choose a reason.
  7. Click Refund.

If the refund is successful, add the refund amount and refund reference to your sheet, if available.

  • Set Final Outcome to Refunded.
  • Review and send the Gmail draft.

Do not tell the customer the refund has arrived. It may take several business days for the refund to appear.

Step 8 — Build the monthly learning loop

Create a third Zap.

  • Set: Schedule by Zapier → Every Month → day 1 → 9:00 AM.
  • Add Formatter → Date/Time → Add/Subtract Time.

Use the scheduled time as Input. Subtract one month.

  • Add another Formatter → Date/Time → Format.
  • Format the result as: YYYY-MM
  • Use Google Sheets → Lookup Spreadsheet Rows (Advanced) to find the matching rows.

Send the rows to ChatGPT with a prompt asking the AI to summarize the refund data and suggest improvements based solely on the information it receives.

Send the summary to yourself with Gmail → Send Email

What to avoid

  • Don't let AI approve refunds automatically.
  • Don't make customers who qualify for a refund complete extra support steps first.
  • Don't change your sheet headers after setting up the Zap.

Before you publish, test all four paths:

  • Approved
  • Support First
  • Denied
  • Escalated

Make sure that everything works as expected before using the workflow with real customers.

on July 30, 2026
  1. 1

    One gap I would add to step 7 that bites a lot of early teams. Refunding in Stripe does not revoke access in your app automatically. So you get refunded but still on paid plan as a silent leak. I have seen that same class of bug in billing audits where affiliate commission or rev share was paid on the original invoice and never reversed when the refund happened in the dashboard. Once a month pull all Stripe refunds and also charge dispute created events, since a dispute takes money faster and is more likely to still have access attached, and cross check against active entitlements. The rule should compare amount refunded to invoice total though, a partial refund should not revoke anything.

  2. 1

    This sounds like the right balance. AI can organize the evidence and flag suspicious patterns, but letting it make the final refund decision could create some painful customer experiences. How much review time did the system save your team?

  3. 1

    Great breakdown. The AI drafts, human decides split is exactly how we build automation for clients too. One thing that helps a lot: keep the actual refund rules in the sheet or rules layer, not just in the prompt. That way when policy changes, you always know what rule applied to an old request. Also agree with the point above about checking Stripe refunds against active access. We have seen that same leak on client accounts.

  4. 1

    The "AI drafts, human decides" split is the same principle EzWrite runs on for messages — it writes three versions, but nothing goes out until you pick and often tweak one. Feels like the right default for anything that touches an actual customer relationship, not just refunds.

  5. 1

    I like the “AI drafts, human decides” approach. The audit trail point is especially important because it makes mistakes easier to review and improve over time. For a small business, would you start with simple rule-based approvals first, then add AI only for summarizing and drafting replies?

  6. 1

    The core instinct is right - AI drafts, human decides - but I'd push the split even harder: the LLM should never be the thing that decides the money, and it also shouldn't be the thing that knows the policy.

    What tends to work:

    • Deterministic rules make the easy calls, not the AI. "Within 14 days AND under $Y AND first request -> auto-approve" is a rules-engine decision, not an LLM judgment - and it safely clears the boring 70% without anyone reviewing them.
    • The LLM handles the fuzzy, no-risk parts: summarize the request, categorize the reason, draft the reply, flag repeat-requester/fraud signals. It surfaces information; it doesn't pull the trigger.
    • Everything ambiguous (high value, repeat requester, weird reason) routes to a person with the AI summary attached.

    Two failure modes to design against: (1) a confidently-worded draft that quotes your refund policy wrong - keep the actual policy in the rules layer, not in the prompt, or it'll hallucinate terms; (2) no audit trail - log the AI summary and the human decision side by side so you can catch drift when the model or your product changes.

    And the pattern-detection piece you mention is honestly the best ROI in the whole flow - clustering refund reasons turns support cost into product feedback, with zero decision risk.

  7. 1

    Automate refund reviews by letting AI analyze requests, detect risks, and recommend actions, while human reviewers make the final decision. This improves speed, accuracy, consistency, and customer trust without sacrificing oversight.

  8. 1

    Add two columns to that sheet: days since purchase, and number of sessions before the request. Most of our refunds clustered inside the first 72 hours with almost no usage, which meant the problem was activation, not the product, and no amount of reply drafting would have fixed it. The Zaps handle the response, those two fields tell you whether to change onboarding or change the product.

  9. 1

    The refund analytics layer is underrated — most founders treat refunds as losses to minimize, not as product feedback. We just finished migrating all our payment infrastructure from PayPal to Creem today, and one thing Creem does better: refund data is centralized and queryable. Before that, PayPal gave us zero insight into why people refunded — just a transaction ID.

    One pattern we caught after centralizing: a spike in refunds right after checkout completion. Turned out our "thank you" page didn't display the download link clearly enough for mobile users. That's a $0 fix that recovered revenue. Would love to see your Jotform→Zapier→ChatGPT workflow include a "refund reason categorization" step — cluster them weekly and flag product issues vs. payment issues.

  10. 1

    One operational detail I’d add is making the refund step idempotent and event-driven.

    A Zap retry, a duplicate form submission, or a reviewer clicking twice could otherwise create a duplicate refund. Before calling Stripe, the workflow should persist the internal request ID, check whether a refund already exists, and store the Stripe refund ID after the action.

    I’d also separate states such as REFUND_PENDING and REFUNDED. The workflow can mark a request as pending after the API call, then use the Stripe event or a reconciliation job to confirm the final state. That seems safer than treating a successful button action as proof that the refund has completed.

    How are you planning to handle retries, duplicate submissions, and partial refunds?

  11. 1

    Great workflow, but it stops one step early. Step 7 refunds the money in Stripe, and nothing here pulls the access. Refunds live in Stripe, entitlements live in your app, and no tool connects the two automatically — so "refunded but still on the paid plan" is one of the most common silent leaks I see doing billing reconciliation. Same class of problem as the affiliate commissions point mihir raised.
    Cheap fix that fits your monthly loop: pull the full refunds from Stripe and cross-check them against accounts that still have active access. Takes minutes, and every hit is money quietly walking out the door.

    1. 1

      the cross-check catches less than you'd want if it only queries refunds. charge.dispute.created is a separate event from charge.refunded, and a disputed charge takes the money faster and is the one more likely to still have access sitting on it. worth pulling both in the same monthly sweep.

      one caveat on the revoke side: a partial refund shouldn't revoke anything, so the rule has to compare amount_refunded against the invoice total rather than just checking that a refund exists.

  12. 1

    I’m not very technical, but keeping the final decision with a person makes sense to me. A customer might explain the problem badly or have an unusual situation, and an AI summary could miss something important.

    I’d also want the reviewer to see the customer’s original message, not only the AI version. In this workflow, does the human always read the original request before making the decision?

  13. 1

    the four outcomes are a good taxonomy. the one I would put a clock on is deny.

    a denied refund does not end the transaction, it moves it. the customer still has a chargeback window open, and a chargeback costs you the disputed amount plus a fee, gets decided by the issuer rather than by your policy, and leaves a dispute rate behind that follows you. so on a borderline case the real comparison is not refund versus keep the money, it is refund now versus possibly lose the same money later with a fee attached.

    separate thing worth wiring in at the approve step: a refund usually has downstream money attached to it. affiliate commission, rev share, sales commission. if any of those were paid out on the original invoice, approving the refund in a form does not reverse them, and a refund issued by hand in the stripe dashboard will not either unless something on your side is listening for the event.

  14. 1

    What stood out to me is that the workflow treats AI as a way to reduce interpretation effort rather than decision responsibility.

    That boundary feels important. The more consequential the outcome, the more valuable it becomes to separate preparing a decision from actually making one.

  15. 1

    Keeping AI out of the final refund decision is the right boundary. Summarization, classification, and drafting are relatively easy to review, but an approval decision depends on policy, customer history, usage data, and exceptions that may not fit clean rules.

    One thing I’d add is an audit trail showing exactly which verified facts were available when the human made the decision. That would make it easier to review inconsistent decisions later and improve the rules without relying only on the AI summary.

    Have you found a good way to handle policy exceptions without gradually turning the spreadsheet into a second, undocumented refund policy?

  16. 1

    totally agree, a refund always holds a teachable moment.

Trending on Indie Hackers
How to rank #1 on ChatGPT? User Avatar 87 comments We scanned 50,000 domains. Your cold email list is really four systems. User Avatar 71 comments I Tested Agenmatic for Finding Customers in Communities — Here’s What I Learned User Avatar 62 comments A chat assistant that runs your server so you don't have to live in the terminal User Avatar 32 comments Just got invited to Web Summit Lisbon. Now I need 5 more clients in 13 days. User Avatar 29 comments Day 8 of building StartupHQ: How would you visualize AI agents working in the background? User Avatar 25 comments