Disputas
Consulta las disputas (chargebacks) de tu cuenta, el detalle con la evidencia cargada y responde una disputa con evidencia.
Las disputas (dispute, o chargeback) son reclamos que abre el titular de una tarjeta. Podés listarlas, ver el detalle con el estado en vivo de Stripe y responder con evidencia antes del plazo.
La aceptación de disputas no está disponible por API. Aceptar (perder) una disputa es irreversible y sólo puede hacerse desde el dashboard. Esta API sólo permite listar, ver y responder.
Listar disputas
GET /api/v1/disputes
Parámetros
| Parámetro | Descripción |
|---|---|
status | warning_needs_response, warning_under_review, warning_closed, needs_response, under_review, won, lost, expired o closed. |
charge_id | Filtrar por el cobro original asociado a la disputa. |
sandbox | true (solo disputas de prueba) o false (solo producción). Sin el parámetro devuelve ambos entornos. |
limit / starting_after / ending_before | Paginación por cursor (default 10, máximo 100). |
Ejemplo
curl "https://api.vexorpay.com/api/v1/disputes?status=needs_response" \
-H "Authorization: Bearer vxp_u_tu_clave"Respuesta exitosa
200 OK
{
"data": [
{
"id": "68f1c1e2c8a4b0d1f2e3a4b5",
"chargeId": "67f1c1e2c8a4b0d1f2e3a4b5",
"paymentLinkId": "66f1c1e2c8a4b0d1f2e3a4b5",
"reference": "ORD-1001",
"customerEmail": "cliente@mail.com",
"stripeDisputeId": "dp_1UC0lPPRu4jOz4R",
"status": "needs_response",
"statusLabel": "Requiere respuesta",
"reason": "fraudulent",
"reasonLabel": "Pago fraudulento",
"amountCents": 1900,
"currency": "usd",
"feeCents": 1500,
"evidenceDeadline": "2026-10-15T12:00:00.000Z",
"daysLeft": 7,
"isOpen": true,
"canRespond": true,
"canAccept": true,
"outcome": null,
"recoveredCents": null,
"compensatedCents": null,
"sandbox": false,
"paymentLinkUrl": "https://www.vexorpay.com/p/a1b2c3d4e5",
"createdAt": "2026-10-08T12:03:00.000Z",
"resolvedAt": null
}
],
"has_more": false,
"total_count": 1,
"url": "/v1/disputes?limit=10"
}Campos clave:
status: estado de la disputa. Abiertas:needs_response,under_review(y las consultas tempranaswarning_needs_response,warning_under_review). Cerradas:won,lost,expired,warning_closed.isOpen/canRespond:canRespondesfalsecuando ya venció el plazo o la disputa está cerrada.canAccept: si se puede aceptar (perder) la disputa. Esfalsepara las consultas tempranas (warning_*), que se cierran solas.feeCents: la fee de Stripe por la disputa (USD 15), que se cobra al creador en todos los desenlaces.evidenceDeadline/daysLeft: plazo para enviar evidencia (nullcuando no aplica o ya venció).
Obtener una disputa
GET /api/v1/disputes/{id}
El id es el id local de la disputa (el campo id de la lista, no el stripeDisputeId). En sandbox, mandá ?sandbox=true.
Además del resumen, la respuesta incluye el estado en vivo de Stripe (por si el webhook todavía no llegó), la evidencia ya cargada, el costo estimado y el enlace al panel de Stripe.
{
"id": "68f1c1e2c8a4b0d1f2e3a4b5",
"stripeDisputeId": "dp_1UC0lPPRu4jOz4R",
"status": "needs_response",
"stripe": {
"status": "needs_response",
"evidenceDueBy": "2026-10-15T12:00:00.000Z",
"hasEvidence": false,
"submissionCount": 0,
"pastDue": false,
"cardBrand": "visa",
"cardCaseType": "fraudulent",
"networkReasonCode": "10.4",
"reason": "fraudulent"
},
"evidence": {
"productDescription": null,
"customerCommunication": null,
"receipt": null,
"uncategorizedText": null
},
"cost": {
"amountInDisputeCents": 1900,
"feeCents": 1500,
"feeChargedToCreator": true,
"recoveredCents": null,
"compensatedCents": null
},
"paymentLinkUrl": "https://www.vexorpay.com/p/a1b2c3d4e5",
"stripeDashboardUrl": "https://dashboard.stripe.com/disputes/dp_1UC0lPPRu4jOz4R"
}Devuelve la disputa o 404 dispute_not_found.
Responder una disputa
POST /api/v1/disputes/{id}/response
Carga evidencia en una disputa abierta y la envía a Stripe (submit: true). La evidencia va en snake_case; al menos un campo debe tener contenido. Una vez enviada, la disputa pasa a under_review y el plazo deja de aplicar.
Soporta Idempotency-Key: un replay con la misma clave y el mismo body devuelve el detalle actual de la disputa sin re-enviar evidencia (200); con la misma clave y un body distinto responde 409 idempotency_conflict.
Body
| Campo | Descripción |
|---|---|
product_description | Qué se entregó/servició y por qué el cobro es legítimo (campo principal). |
customer_communication | Conversación con el cliente (se concatena con notes en uncategorized_text). |
notes | Notas libres. |
service_date | Fecha de entrega/servicio (YYYY-MM-DD). |
shipping_carrier / shipping_tracking_number | Envío (para product_not_received). |
refund_policy_url | URL pública de la política de reembolso. |
cancellation_policy_url | URL pública de la política de cancelación. |
cancellation_rebuttal | Refutación de la cancelación (suscripciones). |
duplicate_charge_id / duplicate_charge_explanation | Cobro duplicado (reason duplicate). |
attach_receipt | Adjunta el comprobante PDF del pago como evidencia (default false). |
sandbox | true para operar sobre disputas de prueba. |
Ejemplo
curl https://api.vexorpay.com/api/v1/disputes/68f1c1e2c8a4b0d1f2e3a4b5/response \
-X POST \
-H "Authorization: Bearer vxp_u_tu_clave" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: dispute-68f1c1e2-response-1" \
-d '{
"product_description": "Ebook de UX en PDF enviado por email el 2026-01-02.",
"customer_communication": "El cliente confirmó la recepción por email.",
"notes": "Entrega verificada en el log de envíos.",
"attach_receipt": true
}'Respuesta exitosa
200 OK con el detalle de la disputa actualizado (mismo shape que GET /api/v1/disputes/{id}). El campo stripe.status pasa a under_review.
Sobre los textos de evidencia: la conversación con el cliente (customer_communication) y tus notes se concatenan en uncategorized_text de Stripe. Cada campo de texto admite hasta 20.000 caracteres: los valores más largos se recortan para que la evidencia nunca falle por largo.
Errores
| Error | HTTP | Causa |
|---|---|---|
validation_error | 400 | status no es un estado válido en el listado. |
dispute_not_found | 404 | No existe esa disputa en tu cuenta (o es de otro modo). |
dispute_not_respondable | 400 | La disputa ya está cerrada: no admite respuesta. |
dispute_deadline_passed | 400 | Venció el plazo para enviar evidencia. |
evidence_required | 400 | Ningún campo de evidencia tiene contenido. |
dispute_evidence_rejected | 400 | Stripe rechazó la evidencia (el mensaje trae el detalle). |
idempotency_conflict | 409 | La Idempotency-Key ya se usó con un body distinto. |