Disputes in the app
Disputes in the app
A merchant reads every dispute and answers it from the phone on the same routes the web dashboard uses. A team member reads with view_transactions and answers (evidence, submit, accept) with refunds; every call below accepts x-team-account.
| Call | Sends | Reads |
|---|---|---|
GET /api/disputes | limit, offset, state (PENDING, INQUIRY, WON, LOST, ACCEPTED) | disputes[], total. Every row carries transaction (the disputed charge: amount, currency, customerName, customerEmail, statementDescriptor, createdAt, instrument = brand, lastFour, instrumentType; null when we never recorded the charge), fees.chargebackFeeCents (our chargeback fee, 0 when none or waived) with fees.chargebackFeeStatus, responseState and impact (disputedCents, chargebackFeeCents, totalIfLostCents) |
GET /api/disputes/summary | — | needsResponse, underReview, won, lost, atStakeCents, dueSoon, dueSoonDays |
GET /api/disputes/{id} | — | the record in data with evidence[] (each item has id, fileName, fileType, fileSize, state, downloadUrl), submittedAt and submissionNote once answered, plus the same transaction, fees, responseState and impact as the list |
POST /api/disputes/{id}/evidence | multipart/form-data, one field file (≤ 10 MB) | data = the evidence item; data.state is pending_processor_upload when the processor refused the file |
DELETE /api/disputes/{id}/evidence/{evidenceId} | — | removes a file while the dispute is PENDING; 409 already_submitted after |
GET /api/disputes/{id}/evidence/{evidenceId}/download | — | the file itself (same auth as every call) |
POST /api/disputes/{id}/submit | { "note": "…" }, note optional | state becomes INQUIRY at once; a second submit is refused with already_submitted and the time of the first |
POST /api/disputes/{id}/accept | no body | state becomes LOST |
States. state is the processor's: PENDING, INQUIRY, WON, LOST, ACCEPTED. The processor keeps a dispute PENDING after the merchant answers, so switch on responseState instead: needs_response (PENDING, nothing sent), responded (PENDING, the bank has the evidence — submittedAt is set, whether it was sent through the app or the processor's dashboard; show "Evidence submitted", no Submit button), under_review (INQUIRY), won, lost, accepted. respondBy is a UTC instant (ISO 8601). The summary counts follow the same rule: needsResponse excludes responded disputes and underReview includes them.
Pushes. data.type is dispute and data.kind is dispute_opened (when the processor reports it) or dispute_due_soon (daily, once the deadline is within three days), with data.disputeId, data.merchantId and data.respondBy. The account owner gets both the in-app notification and the push; team members who hold refunds for that business get the push.