{"openapi":"3.0.0","info":{"title":"144 Pay API","version":"1.2.0","description":"REST API for the 144 Pay gateway. Supports USDT on EVM networks (Polygon `chain=polygon`, BSC `chain=bsc`) and USDC and USDT (SPL) on Solana (`chain=solana`, `token=USDC` or `token=USDT`):\n\n- **Payments**: the merchant creates a payment and receives a disposable deposit address. Once the payment confirms, funds are swept to the hot wallet and distributed per the split configuration.\n- **Dispersions**: the merchant defines a list of recipients and amounts. The gateway generates a disposable wallet, waits for the deposit and runs the dispersion: on EVM via the `Disperse` contract (1 tx), on Solana via batched SPL transfers.\n\n## Fee model\nThe **payer absorbs the full cost**: they pay `base + fee + gas`. A payment's `expectedAmount` already includes the merchant fee and a gas surcharge; the merchant receives its `baseAmount` in full.\n\n## Addresses per network\nEVM uses `0x…` addresses (40 hex); Solana uses base58 (case-sensitive).\n\n## Webhooks\nThe gateway notifies state changes via a POST to the merchant `webhook_url`, signed with HMAC-SHA256 in `X-Signature`. They are at-least-once: your endpoint must tolerate duplicate deliveries."},"tags":[{"name":"Cobros","description":"Create and query payments"},{"name":"Dispersiones","description":"Batch payouts: send to N recipients in a single on-chain tx (on Solana via batched SPL)"},{"name":"Sistema","description":"Service status"}],"components":{"securitySchemes":{"apiKey":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key del comercio, con prefijo pk_live_"}},"schemas":{"Payment":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"pi_3f9a..."},"reference":{"type":"string"},"status":{"type":"string","enum":["PENDING","AWAITING_PAYMENT","DETECTED","CONFIRMING","CONFIRMED","OVERPAID","UNDERPAID","SWEEPING","SWEPT","DISTRIBUTING","COMPLETED","EXPIRED","FAILED"],"description":"Estado del cobro"},"chain":{"type":"string","enum":["polygon","bsc","solana"],"description":"Red de la blockchain","example":"polygon"},"token":{"type":"string","enum":["USDT","USDC"],"description":"Símbolo del token","example":"USDT"},"depositAddress":{"type":"string","description":"Dirección desechable donde enviar el pago"},"expectedAmount":{"type":"string","description":"Monto total a pagar (base + fee + gas), unidades humanas"},"expectedAmountRaw":{"type":"string","description":"Monto total a pagar, unidad mínima del token"},"baseAmount":{"type":"string","nullable":true,"description":"Monto base que recibe el merchant íntegro, unidades humanas"},"feeQuoted":{"type":"string","nullable":true,"description":"Fee del merchant cobrado al usuario, unidades humanas"},"gasSurchargeQuoted":{"type":"string","nullable":true,"description":"Recargo de gas cobrado al usuario, unidades humanas"},"receivedAmount":{"type":"string","description":"Monto recibido hasta ahora, unidades humanas"},"expiresAt":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"transactions":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["DEPOSIT","GAS_FUNDING","SWEEP","PAYOUT","FEE_PAYOUT","DISPERSE_APPROVE","DISPERSE_BATCH","DISPERSE_FEE_SWEEP"]},"txHash":{"type":"string","nullable":true},"amount":{"type":"string","description":"Monto en unidad mínima del token"},"status":{"type":"string","enum":["PENDING","MINED","CONFIRMED","FAILED","DROPPED"]},"confirmations":{"type":"integer"}},"required":["type","txHash","amount","status","confirmations"]}}},"required":["id","reference","status","chain","token","depositAddress","expectedAmount","expectedAmountRaw","baseAmount","feeQuoted","gasSurchargeQuoted","receivedAmount","expiresAt","createdAt"]},"ErrorResponse":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"data":{"nullable":true},"error":{"type":"object","properties":{"code":{"type":"string","example":"VALIDATION_ERROR"},"message":{"type":"string","example":"el monto debe ser mayor que cero"}},"required":["code","message"]}},"required":["success","data","error"]},"CreatePaymentRequest":{"type":"object","properties":{"reference":{"type":"string","minLength":1,"maxLength":128,"description":"Identificador de la orden en el sistema del comercio","example":"order-8821"},"chain":{"type":"string","enum":["polygon","bsc","solana"],"description":"Red de la blockchain","example":"polygon"},"token":{"type":"string","enum":["USDT","USDC"],"description":"Símbolo del token","example":"USDT"},"amount":{"type":"string","pattern":"^\\d+(\\.\\d+)?$","description":"Monto a cobrar, en unidades humanas del token","example":"150.00"}},"required":["reference","chain","token","amount"]},"PaymentList":{"type":"array","items":{"$ref":"#/components/schemas/Payment"}},"Accepted":{"type":"object","properties":{"accepted":{"type":"boolean"}},"required":["accepted"]},"DispersionStatusView":{"type":"object","properties":{"phase":{"type":"string","enum":["awaiting_deposit","processing","done","failed","needs_attention"],"description":"Fase global de alto nivel"},"isComplete":{"type":"boolean","description":"true solo cuando los destinatarios ya recibieron los fondos"},"needsAttention":{"type":"boolean","description":"true cuando el merchant debe actuar o contactar soporte"},"attentionReason":{"type":"string","nullable":true,"enum":["underpaid","execution_failed","expired"],"description":"Motivo de la atención requerida, si aplica"},"statusMessage":{"type":"object","properties":{"es":{"type":"string"},"ptBR":{"type":"string"}},"required":["es","ptBR"],"description":"Mensaje legible para mostrar al usuario final"},"deposit":{"type":"object","properties":{"status":{"type":"string","enum":["pending","partial","complete"]},"missingAmountRaw":{"type":"string","description":"Monto faltante para completar el depósito (unidad mínima)"}},"required":["status","missingAmountRaw"],"description":"Estado del depósito del usuario"},"payout":{"type":"object","properties":{"status":{"type":"string","enum":["not_started","in_progress","sent","failed"]},"txHash":{"type":"string","nullable":true},"explorerUrl":{"type":"string","nullable":true,"description":"URL del explorer para el txHash del payout, si existe"}},"required":["status","txHash","explorerUrl"],"description":"Estado del envío a los destinatarios"}},"required":["phase","isComplete","needsAttention","attentionReason","statusMessage","deposit","payout"]},"Dispersion":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"reference":{"type":"string","nullable":true},"status":{"type":"string","enum":["PENDING","AWAITING_DEPOSIT","DETECTED","CONFIRMING","UNDERPAID","OVERPAID","CONFIRMED","FUNDING_GAS","APPROVING","DISPERSING","COMPLETED","FAILED","EXPIRED"],"description":"Estado de la dispersión"},"chain":{"type":"string","enum":["polygon","bsc","solana"],"description":"Red de la blockchain","example":"polygon"},"token":{"type":"string","enum":["USDT","USDC"],"description":"Símbolo del token","example":"USDT"},"depositAddress":{"type":"string","description":"Dirección desechable donde el usuario debe enviar totalToDeposit"},"totalAmount":{"type":"string","description":"Suma de los montos a dispersar a los destinatarios (humano)"},"totalAmountRaw":{"type":"string"},"feeAmount":{"type":"string","description":"Comisión de la pasarela (humano)"},"feeAmountRaw":{"type":"string"},"gasSurchargeQuoted":{"type":"string","nullable":true,"description":"Recargo de gas incluido en totalToDeposit (humano)"},"totalToDeposit":{"type":"string","description":"Monto que el user debe depositar = totalAmount + feeAmount + gasSurcharge (humano)"},"totalToDepositRaw":{"type":"string"},"receivedAmount":{"type":"string","description":"Monto recibido hasta ahora (humano)"},"summary":{"$ref":"#/components/schemas/DispersionStatusView"},"destinations":{"type":"array","items":{"type":"object","properties":{"address":{"type":"string"},"amount":{"type":"string","description":"Monto en unidades humanas"},"amountRaw":{"type":"string","description":"Monto en unidad mínima del token"},"position":{"type":"integer"}},"required":["address","amount","amountRaw","position"]}},"txHashApprove":{"type":"string","nullable":true},"txHashDisperse":{"type":"string","nullable":true},"txHashFeeSweep":{"type":"string","nullable":true},"expiresAt":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","reference","status","chain","token","depositAddress","totalAmount","totalAmountRaw","feeAmount","feeAmountRaw","gasSurchargeQuoted","totalToDeposit","totalToDepositRaw","receivedAmount","summary","destinations","txHashApprove","txHashDisperse","txHashFeeSweep","expiresAt","createdAt"]},"DispersionDestinationInput":{"type":"object","properties":{"address":{"type":"string","minLength":1,"description":"Dirección destinataria (EVM 0x… o Solana base58)","example":"0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb1"},"amount":{"type":"string","pattern":"^\\d+(\\.\\d+)?$","description":"Monto a enviar a esta dirección, en unidades humanas del token","example":"50.00"}},"required":["address","amount"]},"CreateDispersionRequest":{"type":"object","properties":{"reference":{"type":"string","minLength":1,"maxLength":128,"description":"Identificador opcional del merchant para esta dispersión","example":"payout_2026_05_23_001"},"chain":{"type":"string","enum":["polygon","bsc","solana"],"description":"Red de la blockchain","example":"polygon"},"token":{"type":"string","enum":["USDT","USDC"],"description":"Símbolo del token","example":"USDT"},"destinations":{"type":"array","items":{"$ref":"#/components/schemas/DispersionDestinationInput"},"minItems":1,"maxItems":200,"description":"Lista de destinatarios (min 1, max 200 por batch)"}},"required":["chain","token","destinations"]},"DispersionList":{"type":"array","items":{"$ref":"#/components/schemas/Dispersion"}},"AccessRequestResponse":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"},"createdAt":{"anyOf":[{"type":"string"},{"type":"string"}]}},"required":["id","status","createdAt"]},"AccessRequestInput":{"type":"object","properties":{"email":{"type":"string","format":"email","example":"alice@example.com"},"company":{"type":"string","maxLength":200},"useCase":{"type":"string","minLength":5,"maxLength":2000,"description":"Para qué quiere usar la pasarela (cobros, dispersiones, ambos)","example":"Quiero pagar a 200 afiliados cada mes en USDT vía Polygon"},"estimatedMonthlyVolume":{"type":"string","maxLength":100,"example":"$50k USDT/mes"},"source":{"type":"string","maxLength":100,"description":"De dónde nos conociste (ej: twitter, google, referido por X)"},"website":{"type":"string"}},"required":["email","useCase"]}},"parameters":{}},"paths":{"/api/v1/health":{"get":{"tags":["Sistema"],"summary":"Liveness check","responses":{"200":{"description":"El servicio está vivo","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ok"]},"timestamp":{"type":"string","format":"date-time"}},"required":["status","timestamp"]}}}}}}},"/api/v1/payments":{"post":{"tags":["Cobros"],"summary":"Crear un cobro","description":"Genera un cobro y devuelve una dirección de depósito desechable. Enviar el header `Idempotency-Key` evita duplicar el cobro si se reintenta.","security":[{"apiKey":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePaymentRequest"}}}},"responses":{"201":{"description":"Cobro creado","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/Payment"},"error":{"nullable":true}},"required":["success","data","error"]}}}},"400":{"description":"Solicitud inválida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"get":{"tags":["Cobros"],"summary":"Listar cobros","security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","format":"uuid"},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"string","enum":["PENDING","AWAITING_PAYMENT","DETECTED","CONFIRMING","CONFIRMED","OVERPAID","UNDERPAID","SWEEPING","SWEPT","DISTRIBUTING","COMPLETED","EXPIRED","FAILED"],"description":"Estado del cobro"},"required":false,"name":"status","in":"query"}],"responses":{"200":{"description":"Lista de cobros","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/PaymentList"},"error":{"nullable":true}},"required":["success","data","error"]}}}}}}},"/api/v1/payments/{id}":{"get":{"tags":["Cobros"],"summary":"Consultar un cobro","security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Detalle del cobro","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/Payment"},"error":{"nullable":true}},"required":["success","data","error"]}}}},"404":{"description":"Cobro no encontrado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/payments/{id}/webhook-retry":{"post":{"tags":["Cobros"],"summary":"Reintentar la entrega del webhook","security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"202":{"description":"Reintento encolado","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/Accepted"},"error":{"nullable":true}},"required":["success","data","error"]}}}},"404":{"description":"Cobro no encontrado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/dispersions":{"post":{"tags":["Dispersiones"],"summary":"Crear una dispersión (payout batch)","description":"Crea una dispersión: el merchant envía N destinatarios+montos y el gateway devuelve una wallet desechable. Cuando el user deposita totalToDeposit en esa wallet, el gateway ejecuta el contrato Disperse y envía a todos los destinatarios en una sola transacción (~50% menos gas vs N transferencias). El header `Idempotency-Key` evita duplicar la dispersión si se reintenta.","security":[{"apiKey":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDispersionRequest"}}}},"responses":{"201":{"description":"Dispersión creada","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/Dispersion"},"error":{"nullable":true}},"required":["success","data","error"]}}}},"400":{"description":"Solicitud inválida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Disperse no desplegado en la chain solicitada","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"get":{"tags":["Dispersiones"],"summary":"Listar dispersiones","security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","format":"uuid"},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"string","enum":["PENDING","AWAITING_DEPOSIT","DETECTED","CONFIRMING","UNDERPAID","OVERPAID","CONFIRMED","FUNDING_GAS","APPROVING","DISPERSING","COMPLETED","FAILED","EXPIRED"],"description":"Estado de la dispersión"},"required":false,"name":"status","in":"query"}],"responses":{"200":{"description":"Lista de dispersiones","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/DispersionList"},"error":{"nullable":true}},"required":["success","data","error"]}}}}}}},"/api/v1/dispersions/{id}":{"get":{"tags":["Dispersiones"],"summary":"Consultar una dispersión","security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Detalle de la dispersión","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/Dispersion"},"error":{"nullable":true}},"required":["success","data","error"]}}}},"404":{"description":"Dispersión no encontrada","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/access-requests":{"post":{"tags":["Acceso"],"summary":"Solicitar acceso al producto","description":"Endpoint público (sin API key) para que cualquier persona registre interés en usar la pasarela. Un admin contacta al solicitante para completar el onboarding manualmente. No garantiza acceso automático.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccessRequestInput"}}}},"responses":{"201":{"description":"Solicitud registrada","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/AccessRequestResponse"},"error":{"nullable":true}},"required":["success","data","error"]}}}},"400":{"description":"Solicitud inválida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}}}