Referencia de la API
La API de Polya conecta tu sistema con tu cuenta: consultá tu saldo, enviá transferencias a cualquier CBU, CVU o alias, seguí las que recibís y cobrá con pagos por transferencia, cada uno con su propio CVU y alias.
Es una API REST: cada recurso tiene su URL, las requests y respuestas van en JSON, usamos códigos HTTP estándar y te autenticás con tu clave secreta. No hay pagos con tarjeta.
URL base: https://api.polya.cash
Autenticación
Mandá tu clave secreta en el header Authorization con el esquema Bearer. Creala en Negocio › Developers › API Keys: la ves completa una sola vez. Las claves sk_test_ operan en sandbox; las sk_live_ en producción.
curl https://api.polya.cash/v1/balance \ -H "Authorization: Bearer $POLYA_SECRET_KEY"
Errores de autenticación: API_KEY_MISSING, API_KEY_INVALID, API_KEY_EXPIRED (401) y API_KEY_ACCOUNT_LOCKED (403, data.reason es user_blocked o kyc_required).
Errores
Los 2xx indican éxito; los 4xx, algo a corregir o que no se pudo hacer; los 5xx, un problema nuestro. Cada respuesta trae el header Polya-Request-Id. El body de un error:
{
"error": {
"code": "PARAMETER_INVALID",
"message": "amount tiene que ser un número con hasta 2 decimales (por ejemplo, 1500 o 1500.5).",
"data": { "field": "amount" }
}
}Códigos frecuentes: INVALID_JSON, PARAMETER_MISSING, PARAMETER_INVALID, PARAMETER_UNKNOWN (400) · MERCHANT_NOT_CONFIGURED, TRANSFERS_BLOCKED, KYC_REQUIRED (403) · TRANSFER_NOT_FOUND, PAYMENT_NOT_FOUND (404) · IDEMPOTENCY_KEY_REUSED, IDEMPOTENCY_KEY_IN_USE, PAYMENT_NOT_REFUNDABLE (409) · INSUFFICIENT_BALANCE, LIMIT_EXCEEDED, DESTINATION_NOT_FOUND, DESTINATION_NOT_SUPPORTED, REFUND_FAILED (422) · RATE_LIMITED (429) · BANK_UNAVAILABLE, CVU_UNAVAILABLE (503).
Idempotencia
Con el header Idempotency-Key podés reintentar un POST sin miedo a duplicarlo. Es obligatorio en Crear una transferencia y recomendado en el resto. Usá un UUID v4 nuevo por operación; la guardamos 24 horas. Misma clave con otros datos: 409 IDEMPOTENCY_KEY_REUSED.
Paginación
Los listados devuelven un objeto list del más nuevo al más viejo, con limit (1–100, por defecto 20) y cursor opaco.
{ "object": "list", "data": [ … ], "has_more": true, "next_cursor": "MjAyNi0x…" }Balance
{ "object": "balance", "available": 152340.5, "pending_release": 12000, "currency": "ARS" }Transferencias
POST /v1/transfers
{ "amount": 25000, "destination": "0000003100010000000001" }
201 Created
{
"id": "8f4d2c1e-…", "object": "transfer", "direction": "outbound", "status": "pending",
"amount": 25000, "fee": 0, "net_amount": 25000, "currency": "ARS",
"counterparty": { "name": "Proveedora Ejemplo S. A.", "tax_id": "30123456782", "account": "0000003100010000000001", "alias": null },
"description": "Transferencia", "reference": null, "created_at": "2026-11-10T13:58:12.000Z"
}Validamos la cuenta destino y te devolvemos el titular. A cuentas de Polya se completa al instante; al resto queda pending y te avisamos con transfer.sent o transfer.failed.
Pagos
Un pago es un cobro único por transferencia. Al crearlo le asignamos un CVU y un alias exclusivos; quien paga transfiere ahí y el pago pasa a succeeded solo. Estados: pending → succeeded (o failed si transfieren de menos: lo devolvemos y queda refunded; expired si nadie paga a tiempo).
{
"id": "c2a7e5d1-…", "object": "payment", "status": "pending",
"amount": 15000, "amount_received": null, "currency": "ARS",
"description": "Pedido #1042", "external_reference": "pedido-1042",
"bank_transfer": { "cvu": "0000001700000010000010", "alias": "pago.k7x2mq" },
"checkout_url": "https://polya.ar/pay/c2a7e5d1-…", "return_url": null,
"payer": null, "failure_code": null, "metadata": {},
"expires_at": "2026-11-11T14:00:00.000Z", "paid_at": null, "refunded_at": null, "created_at": "2026-11-10T14:00:00.000Z"
}Links de pago
Cada persona que abre el link genera un pago propio con su CVU exclusivo; los ves en Pagos con payment_link_id.
Webhooks
Configurá destinos en Developers › Webhooks. Cada evento llega como POST JSON con los headers Polya-Event-Id, Polya-Event-Type, Polya-Delivery-Id y Polya-Signature. Respondé 2xx en menos de 15 segundos; si falla reintentamos a 1 min, 5 min, 30 min, 2 h, 5 h, 10 h y 24 h.
Eventos: transfer.received, transfer.sent, transfer.failed, payment.created, payment.succeeded, payment.failed, payment.refunded.
Verificar la firma
El header Polya-Signature trae t (timestamp) y v1. Calculá HMAC-SHA256 con el secreto del destino (whsec_…) sobre `${t}.${body}` y comparalo en tiempo constante. Rechazá si t difiere más de 5 minutos.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verify(rawBody: string, header: string, secret: string) {
const parts = Object.fromEntries(header.split(",").map((kv) => kv.split("=")));
const expected = createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
return fresh && timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}Sandbox
Con claves sk_test_ todo opera con dinero simulado. Para simular que alguien transfirió a un CVU o alias (incluido el CVU de un pago):
curl https://api.polya.cash/sandbox/bank/incoming \
-H "Content-Type: application/json" \
-d '{ "to": "pago.k7x2mq", "amount": 15000, "from_name": "MARIA EJEMPLO" }'