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.

CallSendsReads
GET /api/disputeslimit, 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}/evidencemultipart/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 optionalstate becomes INQUIRY at once; a second submit is refused with already_submitted and the time of the first
POST /api/disputes/{id}/acceptno bodystate 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.