All jobs

Review Every Refund Request With Stripe From Your Backend

Yes. Your backend starts one run per refund request; the agent reads the customer's Stripe history and returns a JSON recommendation without refunding.

What the agent does

Your backend sends one request per refund ticket. The customer's email, the order id and their message go into input after the instructions, so every run sees exactly one case and nothing else. There is no repository in the request: the run starts in a sandbox of its own, and the only outside system it can reach on your behalf is the Stripe connection the request names in connections. A key run gets only the connections it names, so a run started for refunds cannot read your Slack or your mail.

The agent works through the Stripe MCP server (https://mcp.stripe.com/, the Stripe row in Rush's connector catalog), whose tools look up customers, charges, subscriptions and balances. It finds the customer by email, then the charge that matches the order id, by its metadata or its description and amount. It reads that charge's amount, currency, creation date and how much of it is already refunded, lists the customer's earlier charges and refunds to see whether this is a pattern, and checks for disputes on the charge. If the customer has a subscription, it notes the plan, the current period and whether the request falls inside the period just billed.

Then it weighs the evidence against the policy text you put in the prompt, such as a 30-day window or prorated refunds for annual plans. A charge that is already fully refunded or under dispute leads to decline with that reason, because refunding a disputed charge can mean paying twice. A request inside the window on a first purchase leads to approve for the charge amount; a partial use of a period leads to partial with the amount it computed and the arithmetic in reason.

The answer is one JSON object: recommendation (approve, partial or decline), amount in the charge's currency, reason in a sentence or two a support agent can paste, and evidence, the Stripe ids it relied on (cus_…, ch_…, re_…, dp_…). The prompt forbids any write, and the agent creates no refund, changes no customer and cancels no subscription. A person, or your own code after reading the recommendation, issues the refund.

Start it with one API call

POST https://api.prix.dev/o/{org}/sessions with this body:

{
  "agent": "claude",
  "input": "A customer asked for a refund; their email, order id and message follow this prompt. Our policy: full refund within 30 days of the charge on a first purchase, prorated refund for the unused months of an annual plan, no refund on a charge already refunded or under dispute. Use the Stripe connection to find the customer, the charge for this order, earlier refunds and any disputes. Return only a JSON object with `recommendation` (approve, partial or decline), `amount` (in the charge's currency, minor units), `reason` (two sentences at most) and `evidence` (the Stripe ids you relied on). Do not refund, update or cancel anything in Stripe.\n\nEmail: {email}\nOrder: {order_id}\nMessage: {message}",
  "connections": [
    "stripe"
  ]
}
  1. Get an API key. In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
  2. Send the request. POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
  3. Read what came back. Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

What comes back

What comes back is the turn's final answer: the JSON object, in result on the session read and in data.result on the run.completed webhook (it is included there up to 16 KiB, far more than one recommendation needs). Your webhook handler verifies the Standard Webhooks signature, parses result, and attaches the recommendation to the ticket in your support tool or refund queue.

Each id in evidence is something a support agent can open in the Stripe Dashboard to check the agent's reading: the charge, the earlier refund, the dispute. The transcript at GET /o/{org}/sessions/{id}/messages keeps every Stripe tool call the agent made with its arguments and output, so when a recommendation looks wrong you can see which charge it matched and why.

Before acting on it, a reviewer checks that the charge in evidence belongs to the order the customer named, that amount does not exceed the charge's unrefunded balance, and that the reason quotes the policy you gave rather than one the agent assumed. Treat a reply that is not valid JSON, or a run that ends failed, as a case for a person, not as a decline.

Run it on every refund request

Wherever refund requests arrive today (a form in your app, a support inbox, a help desk ticket with a refund tag), have that handler call POST /o/{org}/sessions with the request above, filling the email, order id and message into input. Send the ticket id as the Idempotency-Key header, so a retried call returns the run the first call started instead of reviewing the same refund twice.

Give the API key a webhook URL and Rush POSTs run.completed or run.failed within about 30 seconds of each run's turn ending. A key runs at most max_concurrent sessions at once (4 by default) and answers 429 past that, so a backlog after a holiday weekend should go through a queue that retries on 429.

The use case this job follows, with its automation and trigger: Run an agent per customer task.

Why run it on Rush?

A refund review reads a customer's payment history, which is exactly the data you do not want an assistant reading from a support agent's laptop with a broad Stripe key pasted into a config file. On Rush each review is its own sandbox that can reach Stripe through one connection, named in the request, and nothing else you have connected. The Stripe token is used outside the sandbox and never comes back in any answer.

Every recommendation comes with its record. The transcript keeps each Stripe lookup and its output, so a disputed decision can be traced to the charge the agent read instead of argued from memory, and the evidence ids are checkable in the Dashboard. Because each request is an independent run, a hundred tickets are a hundred runs your queue feeds at the key's concurrency limit, with no shared state between customers.

Frequently asked questions

Can the agent issue the refund itself?

Not with this request: the prompt forbids any write, and the result is a recommendation. If you want refunds issued automatically, let your own code call Stripe's refund API after it reads recommendation and amount, so the money moves through code you control and test. Keeping the agent read-only also keeps a misread charge from becoming a real refund.

Should it use test mode first?

Yes. Connect a Stripe account in test mode, seed customers, charges, refunds and a dispute that match your real cases, and send the same request for each. When the recommendations match what your team would decide, connect the live account and name that connection instead.

How does my backend get a typed answer instead of prose?

The prompt asks for a JSON object with four named fields and nothing else, and the final answer arrives in result. Validate it with your own schema (zod, pydantic) before using it, and route anything that fails validation to a person. The API has no separate output-schema field; the shape lives in the prompt.

Which connection does `stripe` refer to?

A name in connections matches what GET /me/connections lists for the key's creator, so connect the Stripe MCP server once and use the name it shows. If two of your connections share that name the run is refused with 422 CONNECTION_AMBIGUOUS, which lists their ids; send the id instead. A run cannot reach a connection that belongs to someone else.