Back to Projects

6 challenges · about 10 minutes

Which endpoints do you need?

Six real product requests and a page of payments API docs. For each one, tick the endpoints you'd need to get the result, then check your answer and see the calls, in order, with what comes back.

How it works

  1. 1Read the goal and the result it needs. That's the product request, in the words you'd hear it.
  2. 2Tick the endpoints you'd need. They're all real, from Stripe's API reference, and some are there to tempt you.
  3. 3Check your answer to see the calls in order, what each one returns and the outcome.

New to reading docs? How to read API documentation covers everything you need, in about fifteen minutes.

Challenge 1 of 6

Refund one item

I want to…
A customer emails support asking for £12.50 back for one item from their last order. The agent has their email address, and nothing else.
The result I need
A refund of £12.50 against the right payment, and a refund ID to put in the ticket.
Which endpoints do you need? Tick every one.

Show the answer

The calls, in order

  1. 1

    GET /v1/customers?email=(their email)

    Turn the email into a customer ID.

    Returns "id": "cus_DEMO42"

  2. 2

    GET /v1/payment_intents?customer=cus_DEMO42&limit=1

    Find their latest payment. Lists come newest first, so one is enough.

    Returns "id": "pi_DEMO123", "amount": 4997

  3. 3

    POST /v1/refunds payment_intent=pi_DEMO123 amount=1250

    Refund just the item. Leave out amount and the whole £49.97 goes back.

    Returns "id": "re_DEMO123", "status": "pending"

The outcome: Three calls, and the order matters: each one needs an ID from the one before. If your own order system already stored the payment intent ID, the first two calls disappear, which is a good argument for saving it.

Next challenge: Refund history on an order

Challenge 2 of 6

Refund history on an order

I want to…
On the order page in your admin tool, show every refund made against the order. Your order system already saves each order's payment intent ID.
The result I need
A list of refunds for one payment: amount, reason, status and date for each.
Which endpoints do you need? Tick every one.

Show the answer

The calls, in order

  1. 1

    GET /v1/refunds?payment_intent=pi_DEMO123

    One call, filtered to this payment.

    Returns "data": [ { "id": "re_DEMO123", "amount": 1250, "status": "succeeded" } ], "has_more": false

The outcome: One call. The filter does the work, and the payment intent ID you already store is the key. has_more tells you if there's another page (unlikely for one order).

Next challenge: Alert on failed refunds

Challenge 3 of 6

Alert on failed refunds

I want to…
Sometimes a refund fails days later, for example because the card was cancelled. Support should find out straight away so they can refund the customer another way.
The result I need
A message to the support team the moment any refund fails, with the amount and customer.
Which endpoints do you need? Tick every one.

Show the answer

The calls, in order

  1. 1

    Listen for refund.failed at your webhook address

    Stripe tells you, so nobody has to keep asking.

    Returns "type": "refund.failed", "data": { "object": { "id": "re_DEMO123", "failure_reason": "expired_or_canceled_card" } }

The outcome: No calls at all: an event. You could check every refund every few minutes with the list endpoint, but that's slow, wasteful and still late. When something happens later, look for an event first.

Next challenge: Yesterday's refunds for finance

Challenge 4 of 6

Yesterday's refunds for finance

I want to…
Finance wants a spreadsheet every morning of every refund made the day before. On a busy day there are hundreds.
The result I need
Every refund created between midnight and midnight yesterday, not just the first page.
Which endpoints do you need? Tick every one.

Show the answer

The calls, in order

  1. 1

    GET /v1/refunds?created[gte]=1793836800&created[lt]=1793923200&limit=100

    Filter by date (in seconds since 1970) and ask for the biggest page.

    Returns "data": [ …100 refunds… ], "has_more": true

  2. 2

    GET /v1/refunds?created[gte]=…&starting_after=re_LAST&limit=100

    Keep asking for the next page, starting after the last one, until has_more is false.

    Returns "data": [ …the rest… ], "has_more": false

The outcome: One endpoint, called several times. The trap is pagination: a report that only reads the first page looks right on a quiet day and silently misses refunds on a busy one. Write "every page" into the acceptance criteria.

Next challenge: Undo a stuck refund

Challenge 5 of 6

Undo a stuck refund

I want to…
An agent started a refund to a payment method that needs the customer to act, then the customer paid by another route. The agent wants to undo the refund before it goes through.
The result I need
The refund cancelled, or a clear message if it's too late.
Which endpoints do you need? Tick every one.

Show the answer

The calls, in order

  1. 1

    GET /v1/refunds/re_DEMO123

    Check its status first. Cancelling only works while it's requires_action.

    Returns "status": "requires_action"

  2. 2

    POST /v1/refunds/re_DEMO123/cancel

    Cancel it.

    Returns "status": "canceled"

The outcome: Two calls. Checking the status first lets the tool hide the Cancel button when it's too late, instead of letting the agent press it and see an error.

Next challenge: Spot chargebacks early

Challenge 6 of 6

Spot chargebacks early

I want to…
When a customer disputes a payment with their bank (a chargeback), there's a deadline to send evidence. The team keeps missing it.
The result I need
The team told the moment a dispute is opened, and a weekly count of disputes for the dashboard.
Which endpoints do you need? Tick every one.

Show the answer

The calls, in order

  1. 1

    Listen for charge.dispute.created

    Tells you the moment a dispute is opened, with the dispute inside.

    Returns "type": "charge.dispute.created", "data": { "object": { "id": "du_DEMO7", "reason": "fraudulent" } }

  2. 2

    GET /v1/disputes?created[gte]=…

    For the weekly count, list the disputes created in the week, every page.

    Returns "data": [ … ], "has_more": false

The outcome: An event for "now" and a list for "how many". Two needs, two tools: that split comes up in almost every integration.

What the challenges have in common

  • Follow the IDs. Most goals are a chain: each call needs an ID from the one before. Ask which IDs your product already stores.
  • Lists have filters. "Every refund for this order" is one list call with a filter, not a call per refund.
  • "Later" means an event. When something happens without anyone asking, look for a webhook before you plan to keep checking.
  • Every page, not just the first. Anything that counts or reports has to follow has_more to the end.
  • Check before you change. Reading a status first means the product can hide a button rather than show an error.