Listar y obtener links
Consulta, actualiza y cancela los links de pago de tu cuenta con filtros y paginación.
Los links eliminados nunca aparecen en los listados.
Listar
GET /api/v1/payment-links
Parámetros
| Parámetro | Descripción |
|---|---|
status | Filtro: active, canceled o archived. Sin filtro se excluyen solo los eliminados. |
type | Filtro: one_time o recurring. Sin filtro devuelve ambos. |
limit | Cantidad por página (default 10, máximo 100). |
starting_after / ending_before | Cursor de paginación por id. |
Ejemplo
curl "https://api.vexorpay.com/api/v1/payment-links?status=active&type=one_time&limit=10" \
-H "Authorization: Bearer vxp_live_tu_clave"Respuesta exitosa
200 OK
{
"data": [
{
"id": "66f1c1e2c8a4b0d1f2e3a4b5",
"amountCents": 1900,
"currency": "usd",
"title": "Ebook UX en 30 días",
"description": null,
"image": "https://.../product/66f1c1e2c8a4b0d1f2e3a4c9",
"reference": "CATALOGO-UX",
"metadata": null,
"status": "active",
"slug": "a1b2c3d4e5",
"sandbox": false,
"type": "one_time",
"checkoutUrl": "https://buy.polar.sh/polar_cl_...",
"url": "https://www.vexorpay.com/p/a1b2c3d4e5",
"createdAt": "2026-01-02T12:00:00.000Z",
"updatedAt": "2026-01-02T12:00:00.000Z"
}
],
"has_more": false,
"total_count": 1,
"url": "/v1/payment-links?limit=10"
}En un link recurring el amountCents es 0 (el precio lo definen los planes) y se agrega el array prices:
{
"id": "66f1c1e2c8a4b0d1f2e3a4b7",
"amountCents": 0,
"currency": "usd",
"type": "recurring",
"prices": [
{
"id": "66f1c1e2c8a4b0d1f2e3a4c1",
"amountCents": 900,
"currency": "usd",
"interval": "month",
"intervalCount": 1,
"trialPeriodDays": 7,
"description": "Plan mensual",
"isDefault": true
}
]
}Obtener un link
GET /api/v1/payment-links/{id}
El entorno lo define la API key: una clave vxp_test_... opera sobre links de prueba.
curl https://api.vexorpay.com/api/v1/payment-links/66f1c1e2c8a4b0d1f2e3a4b5 \
-H "Authorization: Bearer vxp_live_tu_clave"Devuelve el link completo de tu cuenta o 404 link_not_found si no existe. checkoutUrl es el checkout de Polar del link.
url es el link público para compartir: cada visita genera una referencia única para esa compra, que vuelve en reference de la venta, sus webhooks y /events. Si quieres usar tu propio id, agrega ?reference=<tu-id> (por ejemplo https://www.vexorpay.com/p/a1b2c3d4e5?reference=ORD-1001). Ver Identificar cada venta. Comparte url, no checkoutUrl: la referencia se reenvía a Polar desde url.
Métricas de un link
GET /api/v1/payment-links/{id}/stats
Las mismas métricas que ves en el detalle del link en el dashboard. Los montos van en centavos de USD y son netos: sin impuestos, sin la comisión de Polar y sin lo reembolsado. Se calculan sobre las ventas de Polar del link.
Link de pago único (type: "one_time"):
{
"paymentLinkId": "68f1c1e2c8a4b0d1f2e3a4b5",
"type": "one_time",
"salesCount": 12,
"grossCents": 66000,
"netCents": 52740,
"avgTicketCents": 4395,
"customers": 11,
"repeatCustomers": 1,
"refunds": { "count": 1, "cents": 5000 },
"disputes": { "count": 0, "cents": 0 },
"topCountries": [{ "country": "US", "count": 5 }, { "country": "ES", "count": 3 }],
"weekly": [{ "weekStart": "2026-10-20T00:00:00.000Z", "netCents": 8790, "count": 2 }]
}Link recurrente (type: "recurring"):
{
"paymentLinkId": "68f1c1e2c8a4b0d1f2e3a4b6",
"type": "recurring",
"activeSubscribers": 18,
"mrrCents": 36000,
"trialing": { "count": 3, "mrrCents": 6000 },
"atRisk": { "count": 1, "mrrCents": 2000 },
"newThisMonth": 4,
"canceledThisMonth": 1,
"churnRate": 0.056,
"trialConversion": { "converted": 7, "total": 9 },
"netThisMonthCents": 31200,
"netTotalCents": 214500,
"ltvCents": 9750,
"monthly": [{ "month": "2026-10", "netCents": 31200 }]
}salesCount,customersyavgTicketCentscuentan las ventas vigentes: las reembolsadas por completo van enrefunds.netCentsya descuenta todos los reembolsos.mrrCentscuenta sólo las suscripcionesactive: las que están en prueba van entrialing(MRR potencial) y las de pago fallido enatRisk.churnRate= bajas del mes / suscriptores al empezar el mes (nullsin base).weeklytrae las últimas 12 semanas ymonthlylos últimos 12 meses.
Si el link no existe (o es de otro modo, prueba/producción) responde 404 link_not_found.
Actualizar un link
PATCH /api/v1/payment-links/{id}
El cambio se aplica en Polar. Puedes cambiar title, description, image_file_id (null quita la foto), reference, amount (en dólares, sólo pago único) y, en cada plan de un link recurring, amount (0 = plan gratis), description y trial_period_days. El precio nuevo aplica a las compras nuevas: las suscripciones vigentes siguen con su precio. El checkoutUrl no cambia.
- Cada plan va con su
idy la lista de planes tiene que ser la misma. Agregar o quitar planes, cambiarinterval/interval_counto editar un plan por asiento responde400 polar_link_structure_change: crea un link nuevo. Sinprices, los planes quedan como están. - El
typeno se puede editar (400 validation_error). Elamountno aplica a links recurrentes nipricesa links de pago único (validation_error). statusaceptaactiveoarchived:archivedlo saca de venta en Polar, igual queDELETE. Otro valor (p. ej.canceled) responde400 polar_field_not_supported. Un link archivado no se edita ni se reactiva (400 payment_link_inactive).
curl https://api.vexorpay.com/api/v1/payment-links/66f1c1e2c8a4b0d1f2e3a4b5 \
-X PATCH \
-H "Authorization: Bearer vxp_live_tu_clave" \
-H "Content-Type: application/json" \
-d '{
"prices": [
{ "id": "66f1c1e2c8a4b0d1f2e3a4c1", "amount": 12, "interval": "month", "description": "Mensual" },
{ "id": "66f1c1e2c8a4b0d1f2e3a4c2", "amount": 120, "interval": "year", "trial_period_days": 7, "description": "Anual" }
]
}'Items de prices:
| Campo | Tipo | Necesario | Notas |
|---|---|---|---|
id | string | sí | Id del plan existente (debe pertenecer a este link). |
amount | number | sí | Monto por ciclo en dólares (mínimo USD 4, o 0 para un plan gratis de precio fijo). amount * 100 es el amountCents. |
currency | string | no | Solo usd (default). |
interval | string | sí | day, week, month o year. |
interval_count | number | no | Cada N intervalos (default 1). |
trial_period_days | number | no | Trial gratuito en días (1 a 90); ausente o null = sin trial. |
description | string | no | Label del plan ("Mensual", "Anual"...). |
pricing_type | string | no | flat (default) o per_seat. |
per_seat_amount | number | no | Precio por asiento en dólares (variante "base + extra"). |
min_seats | integer | no | Asientos mínimos (default 1, máximo 1000). |
max_seats | integer | no | Asientos máximos. |
seat_tiers | array | null | no | Matriz de tramos (up_to, unit_amount o flat_amount, en centavos); null la borra. |
tiers_mode | string | no | graduated o volume. Requiere seat_tiers. |
Los campos de asientos son los mismos que en crear un link recurrente, pero en un link existente no se pueden cambiar (es un cambio de estructura).
Devuelve 200 con el link actualizado y sus prices.
El entorno de GET, PATCH y DELETE lo define la API key, no un parámetro del request.
Errores
| Error | HTTP | Causa |
|---|---|---|
validation_error | 400 | Intentaste cambiar type, mandaste prices en un link one_time, amount en un link recurring, id de un plan de otro link, un plan repetido, currency que no es USD o interval inválido. |
invalid_amount | 400 | El nuevo amount es menor a USD 4. |
minimum_amount | 400 | Algún plan con amount menor a USD 4. |
invalid_pricing_type | 400 | pricing_type no es flat ni per_seat. |
invalid_seat_tiers | 400 | Matriz seat_tiers inválida (no es lista, vacía, tramos pisos o de un plan de precio fijo). |
invalid_tiers_mode | 400 | tiers_mode no es graduated ni volume, o se indicó sin seat_tiers. |
invalid_interval_count | 400 | interval_count inválido (entero 1–12; year solo cada 1). |
invalid_trial | 400 | trial_period_days fuera de 1–90. |
prices_required | 400 | prices vacío o no es un array. |
too_many_prices | 400 | Más de 5 planes. |
payment_link_inactive | 400 | El link está archivado: no se edita ni se reactiva. |
invalid_request | 400 | El body del PATCH está vacío. |
polar_link_structure_change | 400 | Cambio de estructura (planes, intervalo o asientos). Crea un link nuevo. |
polar_field_not_supported | 400 | status distinto de active o archived (p. ej. canceled, que no existe en Polar). |
polar_link_not_editable_yet | 409 | El link de Polar todavía se está creando: reintenta en unos segundos. |
polar_update_failed | 502 | Polar rechazó o no respondió el cambio. No se cambió nada. |
link_not_found | 404 | No existe un link con ese id en tu cuenta (o es de otro modo). |
Eliminar un link
DELETE /api/v1/payment-links/{id}
Alineado con la acción Eliminar del dashboard: saca el link de venta y nunca borra ventas ya realizadas.
curl -X DELETE https://api.vexorpay.com/api/v1/payment-links/66f1c1e2c8a4b0d1f2e3a4b5 \
-H "Authorization: Bearer vxp_live_tu_clave"| Situación | HTTP | Respuesta |
|---|---|---|
| Link activo | 200 | El link con status: "archived": sus productos se archivan en Polar y su checkoutUrl deja de funcionar. Las suscripciones vigentes siguen cobrándose en Polar hasta que se cancelen. |
Link ya deleted o archived | 409 | already_processed. |
| No existe, es de otro usuario o de otro modo | 404 | link_not_found. |
Para que dejen de cobrarse las suscripciones de un link archivado, cancela cada una con
POST /api/v1/subscriptions/{id}/cancel.
{
"id": "66f1c1e2c8a4b0d1f2e3a4b5",
"amountCents": 499,
"currency": "usd",
"title": "Pack de presets",
"description": null,
"image": null,
"reference": null,
"metadata": {},
"status": "archived",
"slug": "a1b2c3d4e5",
"sandbox": false,
"url": "https://www.vexorpay.com/p/a1b2c3d4e5",
"type": "one_time",
"createdAt": "2026-01-02T12:00:00.000Z",
"updatedAt": "2026-02-02T09:30:00.000Z"
}Errores:
| Error | HTTP | Causa |
|---|---|---|
already_processed | 409 | El link ya fue eliminado o archivado. |
polar_archive_failed | 502 | Polar no archivó el link. Sigue activo en los dos lados: reintenta. |
link_not_found | 404 | No existe un link con ese id en tu cuenta (o es de otro modo). |