VipterCentro de Ayuda

Referencia de la API (v1)

Cada endpoint de la API de Vipter con sus parámetros, un ejemplo de llamada y de respuesta, y la tabla de campos de cada objeto: cuenta, cliente, suscripción y sus acciones, cobro en la suscripción, uso medido (medidores, eventos de uso, ítems y períodos), pedido, oferta, producto, sesión de checkout, sesión del portal del cliente, eventos y endpoints de webhook.

Admin o PropietarioTodos los planes

Todos los endpoints están en https://api.vipter.com/v1, piden una clave de API y siguen las convenciones: dinero en enteros en la unidad mínima, fechas en segundos Unix, moneda en minúsculas, listas paginadas por cursor. Los endpoints GET piden el alcance read; los POST y DELETE piden el alcance write y aceptan una Idempotency-Key, que el cobro en la suscripción exige.

La descripción legible por máquina, en OpenAPI 3.1, está en GET https://api.vipter.com/v1/openapi.json. Sirve para generar clientes. Una colección Postman generada a partir de ella, con una carpeta por recurso y cuerpos de ejemplo, está en GET https://api.vipter.com/v1/postman.json; se importa en Postman, Bruno e Insomnia. Para agentes de IA, consulta Integrar con agentes de IA.

Los ejemplos usan IDs ficticios y montos en reales brasileños. El Authorization está acortado a vk_live_….

Cuenta

Consultar la cuenta

GET /v1/account

Devuelve la tienda y la clave que hicieron la llamada. Es la primera llamada de una integración nueva: si responde 200, la clave es correcta y la API está habilitada.

curl https://api.vipter.com/v1/account \
  -H "Authorization: Bearer vk_live_…"
{
  "id": "a3f1c2d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
  "object": "account",
  "name": "Loja Demo",
  "slug": "loja-demo",
  "country": "BR",
  "currency": "brl",
  "timezone": "America/Sao_Paulo",
  "api_key": {
    "id": "ak_4f8e2c1a9b7d6e5f3a2b1c0d",
    "name": "ERP",
    "scopes": ["read"],
    "default_version": "2026-11-01"
  },
  "api_version": "2026-11-01",
  "livemode": true
}
CampoTipoContenido
idtextoID de la tienda en Vipter.
name, slugtextoNombre y slug de la tienda. slug es null si la tienda no tiene uno.
countrytextoPaís de la tienda, ISO 3166-1 alfa-2.
currencytextoMoneda predeterminada de la tienda.
timezonetextoZona horaria de la tienda, en formato IANA.
api_keyobjetoLa clave usada: id, name, scopes (read, write) y default_version, la versión de la API que la clave usa cuando la llamada no envía Vipter-Version.
api_versiontextoLa versión usada en esta llamada.
livemodebooleanotrue en producción.

Clientes

Un cliente es quien compró o se suscribió en la tienda. Lo crea el checkout, el equipo en el panel o POST /v1/customers.

Listar clientes

GET /v1/customers
ParámetroTipoContenido
emailtextoSolo clientes con ese correo, sin distinguir mayúsculas.
limit, starting_after, ending_beforePaginación.
curl "https://api.vipter.com/v1/customers?email=ana@example.com" \
  -H "Authorization: Bearer vk_live_…"
{
  "object": "list",
  "url": "https://api.vipter.com/v1/customers",
  "has_more": false,
  "data": [
    {
      "id": "cust_9d2e4f6a8b1c3d5e",
      "object": "customer",
      "email": "ana@example.com",
      "name": "Ana Souza",
      "phone": "+5511999990000",
      "document": { "type": "cpf", "number_masked": "*******1234" },
      "address": {
        "line1": "Rua das Flores, 100",
        "line2": "Apto 42",
        "city": "São Paulo",
        "state": "SP",
        "postal_code": "01310-100",
        "country": "BR"
      },
      "country": "BR",
      "locale": "pt-BR",
      "metadata": {},
      "livemode": true,
      "created": 1790186400
    }
  ]
}

Buscar un cliente

GET /v1/customers/{id}
curl https://api.vipter.com/v1/customers/cust_9d2e4f6a8b1c3d5e \
  -H "Authorization: Bearer vk_live_…"

Devuelve el objeto customer. Un ID que no existe en la tienda recibe 404 resource_missing.

Crear un cliente

POST /v1/customers

Crea el cliente en la tienda antes de la primera compra, para abrir una sesión de checkout con customer o para guardar metadata. Pide el alcance write. El proveedor de pagos exige nombre completo, teléfono y documento, por eso son obligatorios aquí.

ParámetroTipoContenido
emailtextoObligatorio. Se guarda en minúsculas.
nametextoObligatorio. Nombre completo, de 3 a 120 caracteres.
phonetextoObligatorio. En formato internacional (+5511999990000), o un número nacional del país de la tienda. Un número que no es válido para el país recibe 400 parameter_invalid con param phone.
document[type], document[number]textoObligatorio. type es cpf, cnpj, passport o tax_id; number puede venir con puntos y guiones, que se eliminan.
addressobjetoDirección de facturación: line1, city, state, postal_code y country (ISO 3166-1 alfa-2) obligatorios dentro del objeto; line2, number y district opcionales.
metadataobjetoDatos libres, con los mismos límites del metadata de las sesiones de checkout.
curl -X POST https://api.vipter.com/v1/customers \
  -H "Authorization: Bearer vk_live_…" \
  -H "Idempotency-Key: 2a7c0e4b-9d1f-4b3a-8e6c-5f2d7a1b0c9e" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "ana@example.com",
    "name": "Ana Souza",
    "phone": "+5511999990000",
    "document": { "type": "cpf", "number": "123.456.789-09" },
    "metadata": { "user_id": "user_8213" }
  }'

La respuesta es el objeto customer, con 201 cuando el cliente se creó. Si ya existe un cliente con ese correo en la tienda, la respuesta es 200 con el cliente existente, sin modificar ningún campo: para cambiar los datos, usa Modificar un cliente. Un cliente nuevo genera el evento customer.created.

codeHTTPSignificado
parameter_missing, parameter_invalid400Falta un campo obligatorio o tiene el formato incorrecto. param dice cuál.
customer_rejected400El proveedor de pagos rechazó el registro. El message trae el motivo que informó, como un documento inválido.

Modificar un cliente

POST /v1/customers/{id}

Acepta los mismos campos de Crear un cliente, todos opcionales. Solo se modifica lo que se envía, con una excepción: metadata reemplaza el mapa entero, como en Stripe. Para borrar una clave, envía el mapa sin ella. Un ID que no existe recibe 404 resource_missing antes de cualquier cambio.

curl -X POST https://api.vipter.com/v1/customers/cust_9d2e4f6a8b1c3d5e \
  -H "Authorization: Bearer vk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "metadata": { "user_id": "user_8213", "plan": "pro" } }'

Devuelve el objeto customer actualizado y genera el evento customer.updated. Los errores son los mismos de la creación.

El objeto customer

CampoTipoContenido
idtextocust_…
objecttexto"customer"
emailtextoCorreo del cliente. Es lo que identifica a la persona en el checkout y en el portal del cliente.
nametexto o nullNombre indicado en el checkout.
phonetexto o nullTeléfono en formato internacional, con + y el código del país.
documentobjeto o nulltype (como cpf o cnpj) y number_masked, solo con los últimos cuatro dígitos. La API nunca devuelve el documento completo.
addressobjeto o nullDirección de facturación: line1, line2, city, state, postal_code, country. Cada campo puede ser null.
countrytexto o nullPaís del cliente, ISO 3166-1 alfa-2.
localetexto o nullIdioma del cliente, como pt-BR, en o es.
metadataobjetoLos datos guardados por POST /v1/customers o POST /v1/customers/{id}. {} en los clientes creados por el checkout o por el panel. El metadata de una sesión de checkout va al pedido y a la suscripción, no al cliente.
livemodebooleanotrue en producción.
createdenteroCuándo se creó el cliente.

Suscripciones

Listar suscripciones

GET /v1/subscriptions
ParámetroTipoContenido
customertextoSolo suscripciones de ese cliente (cust_…).
statustextoUno de trialing, active, past_due, paused, canceled, expired. Otro valor recibe 400 parameter_invalid.
limit, starting_after, ending_beforePaginación.
curl "https://api.vipter.com/v1/subscriptions?status=active&limit=1" \
  -H "Authorization: Bearer vk_live_…"
{
  "object": "list",
  "url": "https://api.vipter.com/v1/subscriptions",
  "has_more": true,
  "data": [
    {
      "id": "sub_3c7a9e1f5b2d8c4e",
      "object": "subscription",
      "status": "active",
      "customer": "cust_9d2e4f6a8b1c3d5e",
      "customer_email": "ana@example.com",
      "offer": { "id": "ofr_6e2b8d4f1a9c3e7b", "name": "Plano Pro mensal" },
      "product": { "id": "prd_1a5c9e3b7d2f6a8c", "name": "Plano Pro" },
      "billing_cycle": "monthly",
      "custom_billing_days": null,
      "currency": "brl",
      "amount": 9900,
      "current_period_start": 1790186400,
      "current_period_end": 1792778400,
      "next_billing_at": 1792778400,
      "trial_start": null,
      "trial_end": null,
      "cycles_completed": 1,
      "cycle_limit": null,
      "cancel_at_period_end": false,
      "canceled_at": null,
      "ended_at": null,
      "cancellation_details": null,
      "default_payment_method": { "id": "pm_8f3d1c7e2a5b9d4f", "type": "card" },
      "installments": null,
      "past_due_details": null,
      "checkout_session": "cs_4b7e2d9a1c3f5e8b0d2a6c4e1f3b",
      "client_reference_id": "user_8213",
      "metadata": { "plan": "pro" },
      "livemode": true,
      "created": 1790186400
    }
  ]
}

Buscar una suscripción

GET /v1/subscriptions/{id}
curl https://api.vipter.com/v1/subscriptions/sub_3c7a9e1f5b2d8c4e \
  -H "Authorization: Bearer vk_live_…"

Devuelve el objeto subscription.

El objeto subscription

CampoTipoContenido
idtextosub_…
objecttexto"subscription"
statustextotrialing (en prueba gratis), active, past_due (el último cobro falló y Vipter está reintentando), paused, canceled, expired (llegó al límite de ciclos o al final sin renovar).
customertexto o nullcust_… del suscriptor.
customer_emailtexto o nullCorreo del suscriptor, para no necesitar otra llamada.
offerobjeto o nullLa oferta actual: id (ofr_…) y name.
productobjeto o nullEl producto: id (prd_…) y name.
billing_cycletexto o nullEl intervalo de cobro: daily, biweekly, monthly, quarterly, half_yearly, yearly o custom.
custom_billing_daysentero o nullCon billing_cycle custom, el intervalo en días.
currencytexto o nullMoneda de la suscripción.
amountentero o nullMonto de cada cobro, en la unidad mínima.
current_period_start, current_period_endentero o nullEl período ya pagado.
next_billing_atentero o nullCuándo está previsto el próximo cobro. null cuando no hay próximo.
trial_start, trial_endentero o nullEl período de prueba gratis, si lo hubo.
cycles_completedenteroCuántos cobros ya se hicieron.
cycle_limitentero o nullCuántos cobros hace la suscripción en total, en las ofertas con número fijo de ciclos. null es sin límite.
cancel_at_period_endbooleanotrue cuando la cancelación se programó para el fin del período pagado. El status sigue active hasta entonces.
canceled_atentero o nullCuándo se pidió la cancelación.
ended_atentero o nullCuándo la suscripción dejó de valer.
cancellation_detailsobjeto o nullreason (código del motivo), comment (texto libre) y source: quién canceló, como el panel, el portal del cliente o el proveedor.
default_payment_methodobjeto o nullLa tarjeta guardada que paga las renovaciones: id y type (card). null cuando la suscripción paga por PIX u otro medio sin tarjeta guardada.
installmentsentero o nullEn cuántas cuotas se hace cada cobro, cuando la oferta lo permite.
past_due_detailsobjeto o nullSolo con status past_due: attempts (cuántos intentos ya fallaron), next_retry_at y since (cuándo falló el primero).
checkout_sessiontexto o nullcs_… de la sesión de checkout que creó la suscripción. null en las suscripciones que vinieron de un enlace común o del panel.
client_reference_idtexto o nullTu identificador, copiado del client_reference_id de la sesión de checkout o guardado por POST /v1/subscriptions/{id}.
metadataobjetoEl subscription_data[metadata] de la sesión de checkout o, cuando no se envió, el metadata de la sesión; o lo que guardaste por POST /v1/subscriptions/{id}. {} en las demás suscripciones.
livemodebooleanotrue en producción.
createdenteroCuándo empezó la suscripción.

Acciones en la suscripción

Las mismas acciones que el equipo hace en la página de la suscripción en el panel, y que el suscriptor hace en el portal del cliente. Todas piden el alcance write, aceptan una Idempotency-Key y devuelven el objeto subscription ya actualizado: lee status, cancel_at_period_end, offer y next_billing_at en la respuesta en vez de esperar el webhook. El evento correspondiente sale igual que en una acción desde el panel; consulta el catálogo de eventos.

Un ID que no existe en la tienda recibe 404 resource_missing. Una acción que no encaja en el estado actual de la suscripción, como pausar una suscripción ya pausada o reanudar una que no está pausada, recibe 400 provider_error, con el motivo en el message.

Modificar una suscripción

POST /v1/subscriptions/{id}

Guarda tu referencia y el metadata en la suscripción, o programa la cancelación para el fin del período pagado, como el cancel_at_period_end de Stripe.

ParámetroTipoContenido
client_reference_idtexto o nullTu identificador, hasta 200 caracteres. null lo borra.
metadataobjetoReemplaza el mapa entero, como en Stripe. Para borrar una clave, envía el mapa sin ella. Mismos límites que el metadata de las sesiones de checkout.
cancel_at_period_endbooleanotrue programa la cancelación: el status sigue active hasta current_period_end, y la suscripción no se renueva. false se rechaza con 400 parameter_invalid: para deshacer una cancelación programada, llama a POST …/reactivate.
cancellation_details[reason]textoCon cancel_at_period_end: true: el motivo, uno de too_expensive, not_using, missing_features, switching, temporary, other. Otro valor se ignora.
cancellation_details[comment]textoCon cancel_at_period_end: true: un comentario libre, hasta 500 caracteres.
curl -X POST https://api.vipter.com/v1/subscriptions/sub_3c7a9e1f5b2d8c4e \
  -H "Authorization: Bearer vk_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "cancel_at_period_end": true,
    "cancellation_details": { "reason": "switching", "comment": "Pasará al plan anual en enero." }
  }'

Devuelve el objeto subscription con cancel_at_period_end: true y cancellation_details con el motivo y el comentario. Cambiar client_reference_id o metadata no genera evento, y programar la cancelación tampoco: el objeto aparece con cancel_at_period_end: true en el próximo evento de la suscripción y, cuando termina el período, sale customer.subscription.deleted. Cuando la llamada trae la referencia y la programación juntas, la referencia se guarda primero; si la programación se rechaza, ya quedó guardada.

Cancelar ahora

DELETE /v1/subscriptions/{id}

Cancela en el momento, sin esperar el fin del período pagado, como el DELETE de Stripe. El motivo va en la query string, con los mismos valores de Modificar una suscripción.

curl -X DELETE "https://api.vipter.com/v1/subscriptions/sub_3c7a9e1f5b2d8c4e?cancellation_details[reason]=not_using" \
  -H "Authorization: Bearer vk_live_…"

Devuelve el objeto subscription con status: "canceled" y genera customer.subscription.deleted (subscription.cancelled en el catálogo original). En cancellation_details, el source de una cancelación por la API viene como dashboard, el mismo valor que una cancelación por el equipo. El suscriptor recibe los mismos avisos que en una cancelación desde el panel.

Pausar y reanudar

POST /v1/subscriptions/{id}/pause
POST /v1/subscriptions/{id}/resume

Pausar interrumpe las renovaciones sin cancelar; reanudar vuelve a cobrar. El cuerpo de pause acepta reason, un texto libre de hasta 200 caracteres que queda en el historial de la suscripción; resume no tiene cuerpo.

curl -X POST https://api.vipter.com/v1/subscriptions/sub_3c7a9e1f5b2d8c4e/pause \
  -H "Authorization: Bearer vk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "El cliente pidió una pausa de dos meses." }'

Devuelven el objeto subscription con status: "paused" y, después, "active", y generan customer.subscription.paused y customer.subscription.resumed. Una suscripción pausada no acepta cobros sueltos.

Reactivar

POST /v1/subscriptions/{id}/reactivate

Deshace una cancelación programada (cancel_at_period_end vuelve a false y la suscripción se renueva con normalidad) o reactiva una suscripción cancelada o vencida, cuando el proveedor de suscripciones lo permite; una reactivación que él rechaza recibe 400 provider_error. Sin cuerpo.

curl -X POST https://api.vipter.com/v1/subscriptions/sub_3c7a9e1f5b2d8c4e/reactivate \
  -H "Authorization: Bearer vk_live_…"

Devuelve el objeto subscription y genera customer.subscription.updated (subscription.reactivated en el catálogo original).

Cambiar de oferta

POST /v1/subscriptions/{id}/change_offer

Mueve la suscripción a otra oferta, como un upgrade del plan mensual al anual.

ParámetroTipoContenido
offertextoObligatorio. El ofr_… de la oferta nueva. Solo el ID; el slug no se acepta. Una oferta que no existe en la tienda recibe 404 resource_missing con param offer.

La oferta nueva tiene que pertenecer al mismo producto, o a la misma familia de productos, que la actual; de lo contrario, el cambio se rechaza con 400 provider_error.

curl -X POST https://api.vipter.com/v1/subscriptions/sub_3c7a9e1f5b2d8c4e/change_offer \
  -H "Authorization: Bearer vk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "offer": "ofr_9a4c2e8b6d1f3a7c" }'

Devuelve el objeto subscription con offer, amount, billing_cycle y next_billing_at ya de la oferta nueva, y genera customer.subscription.updated (subscription.upgraded o subscription.downgraded en el catálogo original, según el valor nuevo sea mayor o igual, o menor, que el anterior).

Cobros en la suscripción

Un cobro en la suscripción es un monto suelto cobrado ahora en la tarjeta guardada de una suscripción, fuera del ciclo: un excedente de uso, un adicional, un servicio extra. La fecha de la próxima renovación y el monto recurrente no cambian. Es el mismo cobro que el equipo hace en el panel con el botón Cobrar importe extra… de la suscripción (Cobro suelto); los dos aparecen en la misma lista, y el campo source dice de dónde vino cada uno (API o Panel en la página de la suscripción).

El cobro no le pide nada al suscriptor: Vipter cobra la tarjeta guardada sin el cliente presente, por el mismo proveedor que hizo el primer cobro de la suscripción. Cada cobro aprobado se convierte en un pedido pagado y cuenta como un pedido en la cuota del plan Vipter de la tienda. La guía para cobrar uso en un SaaS está en Cobro adicional por uso.

Cobrar la tarjeta guardada

POST /v1/subscriptions/{id}/charges

Pide el alcance write y exige una Idempotency-Key: sin ella, la llamada recibe 400 idempotency_key_required antes que nada. Usa un valor que identifique el cobro que pretendes hacer, como el ID del período de uso en tu sistema. La misma clave nunca cobra dos veces: devuelve el mismo cobro, con 200 y el encabezado Idempotent-Replayed: true. A diferencia de las otras llamadas, la clave de un cobro no vence a las 24 horas: queda ligada al cobro para siempre, y repetirla meses después sigue devolviendo el mismo cobro.

ParámetroTipoContenido
amountenteroObligatorio. El total a cobrar, en la unidad mínima de la moneda (4990 son R$ 49,90). El mínimo es una unidad de la moneda (100 en brl o usd); si no, 400 amount_too_small.
currencytextoOpcional. Tiene que ser la moneda de la suscripción; si no, 400 currency_mismatch. Sin ella, se usa la moneda de la suscripción.
descriptiontextoHasta 140 caracteres. Es la descripción del cobro enviada al proveedor y, sin lines, el nombre del único ítem del pedido. Sin description, vale la descripción de la primera línea.
lines[]listaDe 1 a 50 ítems que explican el monto; se convierten en los ítems del pedido. Cada ítem: description (obligatorio, hasta 140 caracteres), quantity (entero, por defecto 1) y unit_amount (entero, en la unidad mínima). La suma de quantity × unit_amount tiene que ser igual a amount; si no, 400 lines_total_mismatch. Sin lines, el pedido tiene un solo ítem, con description y amount.
metadataobjetoDatos libres, con los mismos límites del metadata de las sesiones de checkout. Queda en el cobro y vuelve en los eventos subscription_charge.*.
curl -X POST https://api.vipter.com/v1/subscriptions/sub_3c7a9e1f5b2d8c4e/charges \
  -H "Authorization: Bearer vk_live_…" \
  -H "Idempotency-Key: usage:user_8213:2026-09" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 5750,
    "currency": "brl",
    "description": "Uso adicional de septiembre",
    "lines": [
      { "description": "Llamadas más allá de la franquicia", "quantity": 2300, "unit_amount": 2 },
      { "description": "Almacenamiento adicional (GB)", "quantity": 23, "unit_amount": 50 }
    ],
    "metadata": { "user_id": "user_8213", "period": "2026-09" }
  }'

La respuesta es 201 con el objeto subscription_charge:

{
  "id": "sch_7e2a9c4b1d3f5a6e8b0c2d4f",
  "object": "subscription_charge",
  "status": "succeeded",
  "subscription": "sub_3c7a9e1f5b2d8c4e",
  "customer": "cust_9d2e4f6a8b1c3d5e",
  "amount": 5750,
  "currency": "brl",
  "description": "Uso adicional de septiembre",
  "lines": [
    { "description": "Llamadas más allá de la franquicia", "quantity": 2300, "unit_amount": 2, "amount": 4600 },
    { "description": "Almacenamiento adicional (GB)", "quantity": 23, "unit_amount": 50, "amount": 1150 }
  ],
  "order": "ord_2f8c4e6a1b3d5f7e",
  "transaction": "tx_9b1d3f5a7c2e4a6c",
  "failure_code": null,
  "failure_message": null,
  "source": "api",
  "metadata": { "user_id": "user_8213", "period": "2026-09" },
  "livemode": true,
  "created": 1790790400,
  "settled_at": 1790790403
}

Lo que pasa con un cobro aprobado:

  • Se convierte en un pedido con billing_reason: "manual", order_type: "api", recurrence: "unscheduled", subscription completado y las lines como ítems (kind: "charge"). El sch_… del cobro queda en external_order_id del pedido.
  • Salen los eventos invoice.paid (con el pedido) y subscription_charge.succeeded (con el cobro) en el catálogo 2026-11-01, y order.paid en el original.
  • El suscriptor recibe el correo de confirmación de compra de la tienda, cuando está activo, y ve el pedido en el portal del cliente.

Un cobro que el proveedor deja en análisis vuelve con 201 y status: "pending", sin settled_at. Se concluye después, cuando el proveedor avisa, y entonces pasa a succeeded o failed y genera los eventos. Consúltalo por GET /v1/subscription_charges/{id} o espera subscription_charge.succeeded / subscription_charge.failed.

Tarjeta rechazada. La respuesta es 402 con type card_error, como en Stripe. El code es el código de rechazo informado por el proveedor, o declined cuando no informa ninguno; los códigos varían por proveedor, así que no dependas de una lista fija. El cobro rechazado viene entero en error.subscription_charge, con status: "failed", failure_code y failure_message:

{
  "error": {
    "type": "card_error",
    "code": "insufficient_funds",
    "message": "Saldo insuficiente.",
    "doc_url": "https://docs.vipter.com/pt-br/developers/api-conventions#erros",
    "subscription_charge": {
      "id": "sch_7e2a9c4b1d3f5a6e8b0c2d4f",
      "object": "subscription_charge",
      "status": "failed",
      "subscription": "sub_3c7a9e1f5b2d8c4e",
      "customer": "cust_9d2e4f6a8b1c3d5e",
      "amount": 5750,
      "currency": "brl",
      "description": "Uso adicional de septiembre",
      "lines": [
        { "description": "Llamadas más allá de la franquicia", "quantity": 2300, "unit_amount": 2, "amount": 4600 },
        { "description": "Almacenamiento adicional (GB)", "quantity": 23, "unit_amount": 50, "amount": 1150 }
      ],
      "order": "ord_2f8c4e6a1b3d5f7e",
      "transaction": "tx_9b1d3f5a7c2e4a6c",
      "failure_code": "insufficient_funds",
      "failure_message": "Saldo insuficiente.",
      "source": "api",
      "metadata": { "user_id": "user_8213", "period": "2026-09" },
      "livemode": true,
      "created": 1790790400,
      "settled_at": 1790790402
    }
  }
}

Un rechazo genera subscription_charge.failed y, cuando el proveedor registró un pedido para el intento, invoice.payment_failed con ese pedido (status: "failed"). Vipter no reintenta por su cuenta: el cobro queda failed, y repetir la misma Idempotency-Key devuelve el mismo 402. Para cobrar otra vez, después de que el suscriptor cambie la tarjeta en el portal del cliente, por ejemplo, haz una llamada nueva con otra clave.

Errores de esta llamada, además de los errores generales:

codeHTTPSignificado
idempotency_key_required400La llamada vino sin Idempotency-Key. El type es idempotency_error.
resource_missing404La suscripción no existe en la tienda.
subscription_not_chargeable400La suscripción está paused, canceled o expired. Solo active, trialing y past_due aceptan cobro.
no_payment_method400La suscripción no tiene tarjeta guardada: paga por PIX u otro medio sin tarjeta.
payment_method_not_chargeable400La tarjeta está guardada en un proveedor que no acepta cobros sin el cliente presente. Hoy, Mercado Pago.
currency_mismatch400currency es distinta de la moneda de la suscripción. El message dice cuál es.
amount_too_small400amount es menor que una unidad de la moneda.
lines_total_mismatch400La suma de las líneas no coincide con amount. El message trae los dos valores.
provider_error400El proveedor rechazó la solicitud de cobro antes de llegar a la tarjeta. El message trae el motivo. El cobro queda registrado como failed con esa clave; usa otra para intentar de nuevo.
project_inactive403La tienda no puede cobrar porque la mensualidad de Vipter está atrasada. El type es permission_error.

Listar los cobros de una suscripción

GET /v1/subscriptions/{id}/charges
ParámetroTipoContenido
statustextopending, succeeded o failed.
limit, starting_after, ending_beforePaginación.
curl "https://api.vipter.com/v1/subscriptions/sub_3c7a9e1f5b2d8c4e/charges?status=succeeded" \
  -H "Authorization: Bearer vk_live_…"

Devuelve un objeto list de subscription_charge, del más nuevo al más antiguo, con los cobros hechos por la API y los hechos en el panel.

Buscar un cobro

GET /v1/subscription_charges/{id}
curl https://api.vipter.com/v1/subscription_charges/sch_7e2a9c4b1d3f5a6e8b0c2d4f \
  -H "Authorization: Bearer vk_live_…"

Devuelve el objeto subscription_charge. Es la llamada para seguir un cobro que quedó pending.

El objeto del cobro

CampoTipoContenido
idtextosch_…
objecttexto"subscription_charge"
statustextopending (en análisis en el proveedor), succeeded (pagado) o failed (rechazado, o la solicitud de cobro fue rechazada).
subscriptiontextosub_… de la suscripción cobrada.
customertexto o nullcust_… del suscriptor.
amountenteroEl total cobrado, en la unidad mínima.
currencytextoLa moneda, en minúsculas: siempre la de la suscripción.
descriptiontexto o nullLa descripción enviada, o la de la primera línea.
lineslistaLas líneas enviadas: description, quantity, unit_amount y amount (unit_amount × quantity). [] cuando el cobro vino sin lines.
ordertexto o nullord_… del pedido que el cobro generó. null cuando el proveedor no registró un pedido.
transactiontexto o nullEl ID de la transacción en el proveedor de pagos.
failure_codetexto o nullCon status failed: el código de rechazo del proveedor, declined cuando no informó ninguno, o el código del error cuando la solicitud de cobro fue rechazada.
failure_messagetexto o nullCon status failed: el motivo, en el texto del proveedor.
sourcetextoDe dónde vino el cobro: api (esta API), dashboard (el botón del panel) o usage (cobro por uso medido, hecho por Vipter al cierre de un período).
metadataobjetoLo que enviaste. {} en los cobros hechos en el panel.
livemodebooleanotrue en producción.
createdenteroCuándo se pidió el cobro.
settled_atentero o nullCuándo pasó a succeeded o failed. null mientras está pending.

Uso medido

Cobro por consumo con la cuenta del lado de Vipter, en el formato de los Billing Meters de Stripe: un medidor por tipo de uso, eventos de uso por cliente, un ítem de uso que le pone precio al medidor en la suscripción, y un período por ciclo de la suscripción, que cierra y se convierte en un cobro en la suscripción con source: "usage". La guía, con ejemplos de precio y qué pasa al final del ciclo, está en Uso medido (Meters). Los GET piden el alcance read; los POST y DELETE, el alcance write.

Crear un medidor

POST /v1/billing/meters
ParámetroTipoContenido
display_nametextoObligatorio. Nombre del medidor, de 1 a 250 caracteres. Aparece en la línea del pedido del comprador.
event_nametextoObligatorio. El nombre que usan los eventos: solo a-z, 0-9, _, . y -, hasta 100 caracteres, único en la tienda. No cambia después.
default_aggregation[formula]textosum (suma los valores, predeterminado), count (cuenta los eventos, ignora el valor) o last (se queda con el último valor del período).
customer_mapping[event_payload_key]textoLa clave del payload que trae el cust_… del cliente. Predeterminado customer_id. customer_mapping[type] solo acepta by_id.
value_settings[event_payload_key]textoLa clave del payload que trae el valor. Predeterminado value.
curl -X POST https://api.vipter.com/v1/billing/meters \
  -H "Authorization: Bearer vk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "display_name": "Llamadas de API", "event_name": "api_calls", "default_aggregation": { "formula": "sum" } }'

La respuesta es 201 con el objeto billing.meter. Un event_name que ya existe en la tienda recibe 400 event_name_taken.

Listar medidores

GET /v1/billing/meters
ParámetroTipoContenido
statustextoactive o inactive.
limitenteroTamaño de la página.

Devuelve un objeto list de billing.meter, del más nuevo al más antiguo.

Buscar y modificar un medidor

GET /v1/billing/meters/{id}
POST /v1/billing/meters/{id}

El POST acepta solo display_name; los demás campos del medidor no cambian. Un ID que no existe en la tienda recibe 404 resource_missing.

Desactivar un medidor

POST /v1/billing/meters/{id}/deactivate

Sin cuerpo. El medidor pasa a inactive con status_transitions.deactivated_at completado, y los eventos nuevos con su event_name reciben 400 meter_inactive. Los períodos abiertos siguen mostrando la cantidad del medidor, con precio cero. No hay reactivación por la API.

El objeto billing.meter

{
  "id": "mtr_4f8e2c1a9b7d6e5f3a2b1c0d",
  "object": "billing.meter",
  "display_name": "Llamadas de API",
  "event_name": "api_calls",
  "default_aggregation": { "formula": "sum" },
  "customer_mapping": { "type": "by_id", "event_payload_key": "customer_id" },
  "value_settings": { "event_payload_key": "value" },
  "status": "active",
  "status_transitions": { "deactivated_at": null },
  "livemode": true,
  "created": 1791100800,
  "updated": 1791100800
}
CampoTipoContenido
idtextomtr_…
objecttexto"billing.meter"
display_nametextoEl nombre del medidor.
event_nametextoEl nombre que usan los eventos.
default_aggregationobjetoformula: sum, count o last.
customer_mappingobjetotype (by_id) y event_payload_key, la clave del payload con el cliente.
value_settingsobjetoevent_payload_key, la clave del payload con el valor.
statustextoactive o inactive.
status_transitionsobjetodeactivated_at: cuándo se desactivó el medidor, o null.
livemodebooleanotrue en producción.
created, updatedenteroCreación y última modificación.

Registrar un evento de uso

POST /v1/billing/meter_events
ParámetroTipoContenido
event_nametextoObligatorio. El event_name del medidor.
payloadobjetoObligatorio. Hasta 20 claves con valores texto, número o booleano. Tiene que traer el cliente en la clave del medidor (customer_id por defecto; stripe_customer_id se acepta como alias) y, fuera de los medidores count, el valor numérico en la clave del valor (value por defecto). subscription_id elige la suscripción cuando el cliente tiene más de una con precio para el medidor.
identifiertextoHasta 100 caracteres. Idempotencia por medidor: el mismo identifier devuelve el evento ya guardado, con 200. Sin él, Vipter genera uno.
timestampenteroCuándo ocurrió el uso, en segundos Unix: hasta 35 días atrás y hasta 5 minutos adelante. Predeterminado: ahora.
curl -X POST https://api.vipter.com/v1/billing/meter_events \
  -H "Authorization: Bearer vk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "event_name": "api_calls", "identifier": "req_01J9X3K7M2", "payload": { "customer_id": "cust_9d2e4f6a8b1c3d5e", "value": 1 } }'

La respuesta es 201 con el objeto billing.meter_event, o 200 con el evento ya guardado cuando el identifier se repite. Al llegar, el evento se vincula a la suscripción active, trialing o past_due del cliente que tiene un ítem de uso para el medidor (la más reciente, si hay más de una). Un evento sin suscripción se guarda con subscription: null, no se cobra, y dispara billing.meter.error_report_triggered, como máximo una vez por medidor y por hora. Consulta Eventos sin suscripción.

codeHTTPSignificado
no_meter_found400Ningún medidor tiene ese event_name.
meter_inactive400El medidor fue desactivado.
invalid_payload400Falta el cliente, o el valor no es un número. param dice la clave (payload.customer_id, payload.value).
timestamp_out_of_range400timestamp fuera de la ventana de 35 días atrás a 5 minutos adelante.
resource_missing404El cliente del payload no existe en la tienda. param es la clave del cliente.

Registrar eventos en lote

POST /v1/billing/meter_events/batch
ParámetroTipoContenido
events[]listaObligatorio. De 1 a 100 eventos, cada uno con los campos de Registrar un evento de uso.
curl -X POST https://api.vipter.com/v1/billing/meter_events/batch \
  -H "Authorization: Bearer vk_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "events": [
      { "event_name": "api_calls", "identifier": "req_01J9X3K7M2", "payload": { "customer_id": "cust_9d2e4f6a8b1c3d5e", "value": 1 } },
      { "event_name": "api_calls", "identifier": "req_01J9X3K7M3", "payload": { "customer_id": "cust_0000000000000000", "value": 1 } }
    ]
  }'
{
  "object": "billing.meter_event_batch",
  "accepted": 1,
  "duplicates": 0,
  "errors": 1,
  "results": [
    { "status": "accepted", "event": { "id": "mev_1a5c9e3b7d2f6a8c0e4b2d6f", "object": "billing.meter_event", "event_name": "api_calls", "identifier": "req_01J9X3K7M2", "payload": { "customer_id": "cust_9d2e4f6a8b1c3d5e", "value": 1 }, "customer": "cust_9d2e4f6a8b1c3d5e", "subscription": "sub_3c7a9e1f5b2d8c4e", "value": 1, "timestamp": 1791101000, "livemode": true, "created": 1791101000 } },
    { "status": "error", "error": { "code": "customer_not_found", "message": "No such customer: 'cust_0000000000000000'", "param": "payload.customer_id" } }
  ]
}

Cada evento se acepta o se rechaza por su cuenta, en el orden enviado: results[i] responde a events[i] con status accepted (guardado ahora), duplicate (el identifier ya existía; event es el guardado) o error (error con code, message y param, los mismos códigos de la llamada unitaria, con customer_not_found en lugar de resource_missing). La respuesta es 200 siempre que al menos un evento fue aceptado o repetido, y 400 solo cuando ninguno entró. Un lote con un campo inválido en el cuerpo (como events vacío) recibe 400 parameter_invalid sin guardar nada.

El objeto billing.meter_event

CampoTipoContenido
idtextomev_…
objecttexto"billing.meter_event"
event_nametextoEl medidor.
identifiertextoTu identifier, o el generado por Vipter.
payloadobjetoEl payload enviado.
customertextocust_… del cliente.
subscriptiontexto o nullLa suscripción en la que se cobrará el evento. null cuando ninguna suscripción activa del cliente tiene precio para el medidor.
valuenúmeroEl valor del evento. 1 en los medidores count.
timestampenteroCuándo ocurrió el uso. Decide en qué período cae el evento.
livemodebooleanotrue en producción.
createdenteroCuándo llegó el evento.

Consultar el uso agregado de un cliente

GET /v1/billing/meters/{id}/event_summaries
ParámetroTipoContenido
customertextoObligatorio. El cust_….
start_time, end_timeenteroObligatorios. El intervalo, en segundos Unix: start_time inclusive, end_time exclusive. end_time tiene que ser mayor, y el intervalo puede abarcar hasta un año, si no 400 parameter_invalid.
value_grouping_windowtextohour o day: un resumen por hora o por día (en UTC), solo de los intervalos que tienen eventos. Sin él, un solo resumen.
curl "https://api.vipter.com/v1/billing/meters/mtr_4f8e2c1a9b7d6e5f3a2b1c0d/event_summaries?customer=cust_9d2e4f6a8b1c3d5e&start_time=1790186400&end_time=1792778400&value_grouping_window=day" \
  -H "Authorization: Bearer vk_live_…"
{
  "object": "list",
  "url": "https://api.vipter.com/v1/billing/meters/mtr_4f8e2c1a9b7d6e5f3a2b1c0d/event_summaries",
  "has_more": false,
  "data": [
    { "object": "billing.meter_event_summary", "meter": "mtr_4f8e2c1a9b7d6e5f3a2b1c0d", "customer": "cust_9d2e4f6a8b1c3d5e", "start_time": 1790186400, "end_time": 1790208000, "aggregated_value": 412, "livemode": true },
    { "object": "billing.meter_event_summary", "meter": "mtr_4f8e2c1a9b7d6e5f3a2b1c0d", "customer": "cust_9d2e4f6a8b1c3d5e", "start_time": 1790208000, "end_time": 1790294400, "aggregated_value": 1180, "livemode": true }
  ]
}

aggregated_value aplica la fórmula del medidor (sum, count o last) a los eventos del cliente en el intervalo, vinculados o no a una suscripción. La lista no está paginada.

Ítems de uso de una suscripción

GET /v1/subscriptions/{id}/usage_items
POST /v1/subscriptions/{id}/usage_items
DELETE /v1/subscriptions/{id}/usage_items/{itemId}

Un ítem de uso le pone precio a un medidor en una suscripción: un ítem por medidor. Nace de la oferta (configurado en el panel, en la página de la oferta) en la primera corrida del uso medido después de crearse la suscripción, en hasta 10 minutos, con source: "offer", o se define aquí, con source: "api". Un ítem definido por la API nunca es sobrescrito por la herencia de la oferta. El POST del mismo meter reemplaza el ítem.

ParámetroTipoContenido
metertextoObligatorio. El mtr_…. Un medidor que no existe recibe 404 resource_missing.
currencytextoOpcional. Tiene que ser la moneda de la suscripción, si no 400 currency_mismatch.
unit_amountnúmeroObligatorio. Precio de una unidad, en la menor unidad de la moneda, con fracciones: 0.4 es R$ 0,004. 0 o más. Con tiers, no entra en el cálculo.
included_unitsnúmeroFranquicia: unidades del período que no se cobran. Predeterminado 0.
tiers[]listaDe 1 a 20 tramos graduados sobre las unidades más allá de la franquicia, cada uno con up_to (límite superior, inclusive; null en el último), unit_amount (por unidad en el tramo) y flat_amount (opcional, cobrado una vez cuando se usa el tramo). up_to tiene que ser creciente y el último null, si no 400 parameter_invalid con param tiers.
roundingtextoup (hacia arriba, predeterminado) o nearest (más cercano), aplicado al total de la línea, en centavos enteros.
billing_thresholdenteroEn centavos. El período cierra y cobra antes del fin del ciclo cuando el total acumulado lo alcanza.
labeltextoHasta 120 caracteres, para tu control.
curl -X POST https://api.vipter.com/v1/subscriptions/sub_3c7a9e1f5b2d8c4e/usage_items \
  -H "Authorization: Bearer vk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "meter": "mtr_4f8e2c1a9b7d6e5f3a2b1c0d", "unit_amount": 0.4, "included_units": 10000, "billing_threshold": 20000 }'

El POST responde 201 con el objeto usage_item; el GET devuelve un objeto list de usage_item, sin paginación; el DELETE devuelve { "id": "usi_…", "object": "usage_item", "deleted": true }, o 404 resource_missing si el ítem no es de esa suscripción.

El objeto usage_item

{
  "id": "usi_9d2e4f6a8b1c3d5e7f0a2b4c",
  "object": "usage_item",
  "meter": "mtr_4f8e2c1a9b7d6e5f3a2b1c0d",
  "subscription": "sub_3c7a9e1f5b2d8c4e",
  "offer": null,
  "currency": "brl",
  "unit_amount": 0.4,
  "included_units": 10000,
  "tiers": null,
  "rounding": "up",
  "billing_threshold": 20000,
  "label": null,
  "source": "api",
  "livemode": true,
  "created": 1791100900
}
CampoTipoContenido
idtextousi_…
objecttexto"usage_item"
metertextomtr_… del medidor.
subscriptiontextosub_… de la suscripción.
offernullReservado. En los ítems de una suscripción viene siempre null.
currencytextoLa moneda, en minúsculas: la de la suscripción.
unit_amount, included_units, tiers, rounding, billing_threshold, labelComo se enviaron. tiers y billing_threshold vienen null cuando no hay.
sourcetextooffer (heredado de la oferta), api o dashboard.
livemodebooleanotrue en producción.
createdenteroCuándo se creó el ítem.

Consultar los períodos de uso

GET /v1/subscriptions/{id}/usage
curl https://api.vipter.com/v1/subscriptions/sub_3c7a9e1f5b2d8c4e/usage \
  -H "Authorization: Bearer vk_live_…"

Devuelve un objeto list con los 12 períodos más recientes de la suscripción, del más nuevo al más antiguo, sin paginación. El período open se calcula en el momento, a partir de los eventos; los closed vienen como quedaron al cierre. Una suscripción sin ítems de uso devuelve una lista vacía.

El objeto usage_period

{
  "id": "usp_7b3e9f1c2a8d4e6f0b2d4f6a",
  "object": "usage_period",
  "subscription": "sub_3c7a9e1f5b2d8c4e",
  "status": "closed",
  "close_reason": "period_end",
  "period_start": 1790186400,
  "period_end": 1792778400,
  "currency": "brl",
  "lines": [
    { "meter": "mtr_4f8e2c1a9b7d6e5f3a2b1c0d", "event_name": "api_calls", "quantity": 12300, "included": 10000, "billable": 2300, "unit_amount": 0.4, "amount": 920 }
  ],
  "amount_total": 920,
  "charge": "sch_7e2a9c4b1d3f5a6e8b0c2d4f",
  "computed_at": 1792778700,
  "closed_at": 1792778700,
  "livemode": true,
  "created": 1790187000
}
CampoTipoContenido
idtextousp_…
objecttexto"usage_period"
subscriptiontextosub_… de la suscripción.
statustextoopen (acumulando), closing (en cobro) o closed.
close_reasontexto o nullperiod_end (terminó el ciclo), threshold (alcanzó el billing_threshold) o subscription_ended (la suscripción dejó de ser cobrable). null mientras está abierto.
period_start, period_endenteroEl intervalo del período: el ciclo de la suscripción, o el tramo que queda después de un cierre por umbral.
currencytextoLa moneda, en minúsculas.
lineslistaUna por medidor con ítem: meter, event_name, quantity (el agregado), included (la franquicia), billable (quantity menos included), unit_amount y amount (en centavos).
amount_totalenteroLa suma de las líneas, en centavos.
chargetexto o nullsch_… del cobro del período, aprobado o rechazado. null mientras está abierto, cuando el total fue cero, o cuando el cobro fue rechazado antes de llegar a la tarjeta.
computed_atentero o nullCuándo se calcularon las líneas. En el período abierto, la hora de la llamada.
closed_atentero o nullCuándo cerró el período.
livemodebooleanotrue en producción.
createdenteroCuándo abrió el período.

Errores del uso medido

Además de los errores generales:

codeHTTPDóndeSignificado
event_name_taken400Crear un medidorYa existe un medidor con ese event_name.
no_meter_found400EventosNingún medidor tiene ese event_name.
meter_inactive400EventosEl medidor fue desactivado.
invalid_payload400EventosFalta el cliente en el payload, o el valor no es un número.
timestamp_out_of_range400Eventostimestamp fuera de la ventana de 35 días atrás a 5 minutos adelante.
customer_not_foundLoteSolo dentro de results[]: el cliente no existe. En la llamada unitaria es 404 resource_missing.
currency_mismatch400Ítems de usocurrency es distinta de la moneda de la suscripción.
parameter_invalid400Ítems de uso, resúmenestiers fuera de orden o sin el último tramo null; end_time antes de start_time o intervalo mayor a un año.
resource_missing404TodosMedidor, suscripción, ítem o cliente que no existe en la tienda.

Pedidos

Un pedido es un cobro: una compra suelta, el primer cobro de una suscripción, una renovación, un cobro suelto en la suscripción hecho en el panel o por la API. Es lo que Stripe llama factura (invoice); el nombre sigue lo que el dueño de la tienda ve en el panel.

Listar pedidos

GET /v1/orders
ParámetroTipoContenido
customertextoSolo pedidos de ese cliente (cust_…).
subscriptiontextoSolo pedidos de esa suscripción (sub_…).
statustextoUno de pending, pre_authorized, authorized, failed, canceled, refund_pending, partially_refunded, refunded, charged_back.
limit, starting_after, ending_beforePaginación.
curl "https://api.vipter.com/v1/orders?subscription=sub_3c7a9e1f5b2d8c4e&limit=1" \
  -H "Authorization: Bearer vk_live_…"
{
  "object": "list",
  "url": "https://api.vipter.com/v1/orders",
  "has_more": true,
  "data": [
    {
      "id": "ord_7b3e9f1c2a8d4e6f",
      "object": "order",
      "status": "authorized",
      "paid": true,
      "status_source": "provider",
      "billing_reason": "subscription_cycle",
      "customer": "cust_9d2e4f6a8b1c3d5e",
      "customer_email": "ana@example.com",
      "subscription": "sub_3c7a9e1f5b2d8c4e",
      "offer": "ofr_6e2b8d4f1a9c3e7b",
      "order_type": "renewal",
      "recurrence": "subsequent",
      "currency": "brl",
      "amount_total": 9900,
      "amount_refunded": 0,
      "amount_discount": 0,
      "amount_shipping": 0,
      "amount_interest": 0,
      "installments": 1,
      "payment_method": "credit_card",
      "provider": "pagarme",
      "coupon_codes": [],
      "lines": [
        {
          "description": "Plano Pro mensal",
          "quantity": 1,
          "unit_amount": 9900,
          "amount": 9900,
          "offer": "ofr_6e2b8d4f1a9c3e7b",
          "product": "prd_1a5c9e3b7d2f6a8c",
          "kind": "main"
        }
      ],
      "shipping": null,
      "external_order_id": null,
      "checkout_session": null,
      "client_reference_id": null,
      "metadata": {},
      "paid_at": 1790790412,
      "livemode": true,
      "created": 1790790400
    }
  ]
}

Buscar un pedido

GET /v1/orders/{id}
curl https://api.vipter.com/v1/orders/ord_7b3e9f1c2a8d4e6f \
  -H "Authorization: Bearer vk_live_…"

Devuelve el objeto order.

El objeto order

CampoTipoContenido
idtextoord_…
objecttexto"order"
statustextoConsulta Estado del pedido.
paidbooleanotrue cuando el dinero entró, aunque después se haya reembolsado total o parcialmente. Usa status para el detalle.
status_sourcetextoprovider cuando el estado vino del proveedor de pagos; manual cuando alguien definió el estado en el panel.
billing_reasontextoPor qué existe el pedido: purchase (compra suelta), subscription_create (primer cobro de una suscripción), subscription_cycle (renovación), manual (cobro suelto en una suscripción, por el panel o por la API), usage (cobro por uso medido, al cierre de un período).
customer, customer_emailtexto o nullEl comprador.
subscriptiontexto o nullsub_… cuando el pedido pertenece a una suscripción.
offertexto o nullofr_… de la oferta principal del pedido.
order_typetexto o nullCómo nació el pedido: checkout, renewal (renovación), api (cobro iniciado por el dueño de la tienda), trial_setup o card_setup (registro de tarjeta sin cobro). Puede ganar valores nuevos.
recurrencetexto o nullinitial, subsequent o unscheduled en los pedidos de suscripción; null en las compras sueltas.
currencytextoMoneda del pedido.
amount_totalenteroEl total cobrado, en la unidad mínima, con descuento, envío e intereses ya aplicados.
amount_refundedenteroCuánto ya se reembolsó.
amount_discountenteroEl descuento de cupones.
amount_shippingenteroEl envío, en productos físicos.
amount_interestenteroLos intereses de las cuotas trasladados al comprador.
installmentsentero o nullEn cuántas cuotas se pagó.
payment_methodtexto o nullcredit_card, debit_card, pix, boleto o wallet.
providertexto o nullEl proveedor que procesó, como pagarme, stripe, mercadopago o asaas.
coupon_codeslista de textoLos cupones aplicados.
lineslistaLos ítems del pedido. Consulta Líneas del pedido.
shippingobjeto o nullSolo en pedidos con entrega: status de la entrega, carrier, tracking_code, tracking_url y address, en el mismo formato que la dirección del cliente.
external_order_idtexto o nullUn identificador tuyo, cuando el pedido vino con uno. En los pedidos de un cobro en la suscripción, el sch_… del cobro.
checkout_sessiontexto o nullcs_… de la sesión de checkout que generó el pedido. Solo en el pedido pagado por la sesión: las renovaciones de la suscripción vienen con null, y las conectas con tu sistema por subscription.
client_reference_idtexto o nullTu identificador, copiado del client_reference_id de la sesión de checkout.
metadataobjetoEl metadata de la sesión de checkout. {} en los demás pedidos.
paid_atentero o nullCuándo se confirmó el pago.
livemodebooleanofalse cuando el pago pasó por una conexión de prueba del proveedor.
createdenteroCuándo se creó el pedido.

Estado del pedido

statuspaidSignificado
pendingfalseEsperando el pago: PIX generado y no pagado, boleto emitido, tarjeta en análisis.
pre_authorizedfalseMonto reservado en la tarjeta, todavía no capturado.
authorizedtruePagado.
failedfalseEl pago fue rechazado o venció.
canceledfalseCancelado antes de ser pagado.
refund_pendingtrueReembolso pedido y todavía no confirmado por el proveedor.
partially_refundedtrueParte del monto fue devuelta. amount_refunded dice cuánto.
refundedtrueTodo el monto fue devuelto.
charged_backtrueEl comprador disputó el cobro en el banco.

Líneas del pedido

Cada ítem de lines:

CampoTipoContenido
descriptiontexto o nullEl nombre del ítem como apareció en el checkout.
quantityenteroCantidad.
unit_amountentero o nullPrecio unitario, en la unidad mínima.
amountentero o nullunit_amount por quantity.
offer, producttexto o nullofr_… y prd_… del ítem.
kindtextoEl papel del ítem en el pedido: main (el ítem principal), bump (oferta adicional marcada en el checkout), composition (línea compuesta por el equipo en un enlace rápido) o charge (línea de un cobro en la suscripción).

Ofertas

Una oferta es lo que el comprador puede pagar: un producto con precio, ciclo de cobro y condiciones. Es el equivalente al price de Stripe. Cada oferta tiene un enlace de checkout listo.

Listar ofertas

GET /v1/offers
ParámetroTipoContenido
producttextoSolo ofertas de ese producto (prd_…).
activetrue o falseSolo ofertas activas, o solo las que no están activas.
typetextoone_time o recurring.
limit, starting_after, ending_beforePaginación. Las ofertas vienen en orden alfabético por nombre.
curl "https://api.vipter.com/v1/offers?active=true&type=recurring" \
  -H "Authorization: Bearer vk_live_…"
{
  "object": "list",
  "url": "https://api.vipter.com/v1/offers",
  "has_more": false,
  "data": [
    {
      "id": "ofr_6e2b8d4f1a9c3e7b",
      "object": "offer",
      "name": "Plano Pro mensal",
      "slug": "pro-mensal",
      "product": { "id": "prd_1a5c9e3b7d2f6a8c", "name": "Plano Pro" },
      "type": "recurring",
      "billing_cycle": "monthly",
      "custom_billing_days": null,
      "cycle_limit": null,
      "trial_days": 7,
      "setup_charge": false,
      "status": "active",
      "active": true,
      "prices": [
        { "id": "prc_2d8f4a6c1e9b3d7f", "currency": "brl", "unit_amount": 9900, "first_charge_amount": null, "default": true },
        { "id": "prc_7a1c3e5b9d2f4a6c", "currency": "usd", "unit_amount": 1900, "first_charge_amount": null, "default": false }
      ],
      "checkout_url": "https://pay.vipter.com/pro-mensal",
      "livemode": true,
      "created": 1788300000
    }
  ]
}

Buscar una oferta

GET /v1/offers/{id}
curl https://api.vipter.com/v1/offers/ofr_6e2b8d4f1a9c3e7b \
  -H "Authorization: Bearer vk_live_…"

Devuelve el objeto offer.

El objeto offer

CampoTipoContenido
idtextoofr_…
objecttexto"offer"
nametextoNombre de la oferta.
slugtexto o nullEl slug del enlace de checkout, cuando la oferta tiene uno.
productobjetoid (prd_…) y name del producto.
typetextoone_time (compra suelta) o recurring (suscripción).
billing_cycletexto o nullEl intervalo de cobro en las ofertas recurrentes: daily, biweekly, monthly, quarterly, half_yearly, yearly o custom. null en las sueltas.
custom_billing_daysentero o nullCon billing_cycle custom, el intervalo en días.
cycle_limitentero o nullCuántos cobros hace la suscripción en total. null es sin límite.
trial_daysentero o nullDías de prueba gratis. null cuando la oferta no tiene prueba.
setup_chargebooleanotrue cuando el primer cobro tiene un monto distinto a los demás (first_charge_amount en prices).
statustextoEl estado en el catálogo, como active o inactive.
activebooleanotrue cuando status es active. Solo las ofertas activas aceptan compras.
priceslistaUn precio por moneda: id, currency, unit_amount, first_charge_amount (el monto del primer cobro cuando es distinto, si no null) y default (la moneda que el checkout usa cuando el comprador no elige).
checkout_urltexto o nullEl enlace de checkout de la oferta, en el dominio propio de la tienda cuando hay uno. null cuando el enlace está desactivado en la configuración de la oferta. Acepta los parámetros de URL.
livemodebooleanotrue en producción.
createdentero o nullCuándo se creó la oferta.

Productos

Un producto agrupa ofertas: "Plan Pro" es el producto, "Plan Pro mensual" y "Plan Pro anual" son ofertas suyas.

Listar productos

GET /v1/products
ParámetroTipoContenido
activetrue o falseSolo productos activos, o solo los que no están activos.
limit, starting_after, ending_beforePaginación.
curl "https://api.vipter.com/v1/products?active=true" \
  -H "Authorization: Bearer vk_live_…"
{
  "object": "list",
  "url": "https://api.vipter.com/v1/products",
  "has_more": false,
  "data": [
    {
      "id": "prd_1a5c9e3b7d2f6a8c",
      "object": "product",
      "name": "Plano Pro",
      "description": "Acesso completo à plataforma.",
      "type": "digital",
      "status": "active",
      "active": true,
      "product_family": null,
      "metadata": {},
      "livemode": true,
      "created": 1788200000
    }
  ]
}

Buscar un producto

GET /v1/products/{id}
curl https://api.vipter.com/v1/products/prd_1a5c9e3b7d2f6a8c \
  -H "Authorization: Bearer vk_live_…"

Devuelve el objeto product.

El objeto product

CampoTipoContenido
idtextoprd_…
objecttexto"product"
nametextoNombre del producto.
descriptiontexto o nullDescripción.
typetexto o nullEl tipo de producto, como digital o physical. Puede ganar valores nuevos.
statustextoEl estado en el catálogo, como active o inactive.
activebooleanotrue cuando el producto está activo y no fue eliminado.
product_familytexto o nullEl ID de la familia de productos, cuando el producto pertenece a una.
metadataobjetoDatos libres guardados en el producto.
livemodebooleanotrue en producción.
createdentero o nullCuándo se creó el producto.

Sesiones de checkout

Una sesión de checkout es un checkout abierto por tu sistema para una oferta, con el comprador, tu referencia y el destino después del pago ya definidos. La respuesta trae la url a la que mandas al comprador. Cuando paga, la sesión pasa a apuntar a customer, order y subscription, y el evento checkout.session.completed sale en el catálogo 2026-11-01. Es el equivalente de la Checkout Session de Stripe. El paso a paso con código está en SaaS: del registro al dashboard.

La sesión fija la oferta, el paquete, la moneda y el cupón: el comprador no cambia ninguno de ellos en la página. Con customer o customer_email, el campo de correo viene completado y bloqueado. Crear la sesión no crea nada en el proveedor de pagos; eso pasa cuando el comprador completa el formulario, como en un enlace de checkout común.

Crear una sesión

POST /v1/checkout/sessions

Pide el alcance write. Envía una Idempotency-Key para repetir la llamada después de un error de red sin crear dos sesiones.

ParámetroTipoContenido
offertextoObligatorio, salvo que venga line_items. La oferta: ofr_… o el slug del enlace de checkout. Una oferta que no existe o es de otra tienda recibe 404 resource_missing con param offer.
line_items[0][price], line_items[0][quantity]texto, enteroAlias en el formato de Stripe: price es la oferta y quantity es el paquete. Solo un ítem. Cuando offer o pack también vienen, ellos valen.
packenteroEl paquete, en unidades (1 a 999). La oferta tiene que tener un paquete con esa cantidad, si no 400 pack_unavailable.
currencytextoMoneda de la sesión, ISO 4217 (brl, usd). Tiene que existir en prices de la oferta, si no 400 currency_unsupported, con las monedas disponibles en el message. Sin ella, el checkout abre en la moneda predeterminada de la oferta y el comprador puede cambiarla, como en un enlace común.
customertextocust_… de un cliente de la tienda. Su correo viene completado y bloqueado; nombre, teléfono y documento vienen completados. Un ID que no existe recibe 404 resource_missing con param customer.
customer_emailtextoCorreo del comprador, cuando todavía no es cliente. El campo viene completado y bloqueado: el checkout solo acepta pagar con ese correo. Se guarda en minúsculas.
customer_nametextoNombre para completar el formulario, de 2 a 120 caracteres. El comprador puede cambiarlo.
client_reference_idtextoTu identificador, hasta 200 caracteres: el ID del usuario en tu sistema. Se copia al pedido y a la suscripción, y filtra la lista de sesiones.
metadataobjetoHasta 50 claves; clave de hasta 40 caracteres, valor de hasta 500. Los números y booleanos se guardan como texto; null elimina la clave. Queda en la sesión y se copia al pedido.
subscription_data[metadata]objetoEl metadata de la suscripción que la sesión cree, en las ofertas recurrentes. Sin él, la suscripción recibe el metadata de la sesión. Mismos límites.
discounts[0][coupon]textoEl código de un cupón activo de la tienda, sin distinguir mayúsculas. Viene aplicado en el checkout. Un cupón inexistente o inactivo recibe 400 coupon_invalid. Solo un cupón.
success_urltextoAdónde va el comprador después de pagar. https://, hasta 2000 caracteres (http://localhost se acepta en desarrollo). El texto {CHECKOUT_SESSION_ID} se reemplaza por el id de la sesión. Consulta Después del pago.
cancel_urltextoSe convierte en el enlace de volver arriba del checkout. También es adónde va el comprador si abre la sesión después de que venció. Mismas reglas de formato.
redirect_delayenteroSegundos que la página de gracias de Vipter queda en pantalla antes de ir a success_url: de 0 a 30, predeterminado 5.
expires_atenteroCuándo vence la sesión, en segundos Unix: entre 30 minutos y 24 horas a partir de ahora, si no 400 parameter_invalid. Predeterminado: 24 horas.
localetextoIdioma del comprador, guardado en la sesión y devuelto en el objeto: en, pt, es, fr, de, it, ja, ko, ru o zh.
modetextopayment o subscription. Opcional: Vipter lo deduce por el tipo de la oferta y lo devuelve en el objeto. Un valor distinto del tipo de la oferta recibe 400 mode_mismatch.
allow_promotion_codesbooleanoAceptado por compatibilidad con Stripe e ignorado por ahora.
curl -X POST https://api.vipter.com/v1/checkout/sessions \
  -H "Authorization: Bearer vk_live_…" \
  -H "Idempotency-Key: 9c1f0a52-7e4b-4d3a-9b8e-2f6c1d0a7e45" \
  -H "Content-Type: application/json" \
  -d '{
    "offer": "ofr_6e2b8d4f1a9c3e7b",
    "customer_email": "ana@example.com",
    "client_reference_id": "user_8213",
    "metadata": { "plan": "pro" },
    "success_url": "https://app.example.com/billing/success?session_id={CHECKOUT_SESSION_ID}",
    "cancel_url": "https://app.example.com/billing"
  }'

La respuesta es 201 con el objeto checkout.session:

{
  "id": "cs_4b7e2d9a1c3f5e8b0d2a6c4e1f3b",
  "object": "checkout.session",
  "status": "open",
  "payment_status": "unpaid",
  "url": "https://pay.vipter.com/c/cs_4b7e2d9a1c3f5e8b0d2a6c4e1f3b",
  "mode": "subscription",
  "offer": { "id": "ofr_6e2b8d4f1a9c3e7b", "name": "Plano Pro mensal" },
  "pack": null,
  "currency": null,
  "amount_total": 9900,
  "customer": null,
  "customer_email": "ana@example.com",
  "customer_name": null,
  "client_reference_id": "user_8213",
  "metadata": { "plan": "pro" },
  "subscription_data": { "metadata": {} },
  "discounts": [],
  "success_url": "https://app.example.com/billing/success?session_id={CHECKOUT_SESSION_ID}",
  "cancel_url": "https://app.example.com/billing",
  "redirect_delay": 5,
  "locale": null,
  "order": null,
  "subscription": null,
  "expires_at": 1790272800,
  "completed_at": null,
  "livemode": true,
  "created": 1790186400
}

Manda al comprador a url. La dirección está en https://pay.vipter.com/c/{id} o, cuando la tienda tiene un dominio propio activo, en ese dominio.

Errores de esta llamada, además de los errores generales:

codeHTTPSignificado
resource_missing404La oferta (param offer) o el cliente (param customer) no existe en la tienda.
offer_unavailable400La oferta existe pero no se puede vender ahora: enlace de checkout desactivado, oferta archivada, sin precio, o la tienda no está en condiciones de vender. El message dice el motivo.
pack_unavailable400La oferta no tiene un paquete con la cantidad pedida en pack.
currency_unsupported400La oferta no tiene precio en la moneda pedida.
mode_mismatch400mode no coincide con el tipo de la oferta.
coupon_invalid400El cupón no existe o está inactivo. param es discounts[0].coupon.
selling_blocked403La tienda no puede vender porque la mensualidad de Vipter está atrasada. El type es permission_error.

Buscar una sesión

GET /v1/checkout/sessions/{id}
curl https://api.vipter.com/v1/checkout/sessions/cs_4b7e2d9a1c3f5e8b0d2a6c4e1f3b \
  -H "Authorization: Bearer vk_live_…"

Devuelve el objeto checkout.session. Es la llamada que tu página de éxito hace con el id que llegó en la success_url: revisa status y payment_status en lugar de confiar solo en la redirección. Después del pago, customer, order y, en las ofertas recurrentes, subscription vienen completados. subscription puede llegar unos segundos después de order, cuando se procesa el aviso del proveedor; si todavía viene null, consulta de nuevo o espera el webhook.

Listar sesiones

GET /v1/checkout/sessions
ParámetroTipoContenido
customertextoSolo sesiones de ese cliente (cust_…), incluidas las que ganaron el cliente al pagarse.
client_reference_idtextoSolo sesiones creadas con ese client_reference_id, coincidencia exacta.
statustextoopen, complete o expired.
payment_statustextounpaid, paid o pending.
limit, starting_after, ending_beforePaginación.
curl "https://api.vipter.com/v1/checkout/sessions?client_reference_id=user_8213&status=complete" \
  -H "Authorization: Bearer vk_live_…"

Devuelve un objeto list de checkout.session, de la más nueva a la más antigua.

Expirar una sesión

POST /v1/checkout/sessions/{id}/expire

Cierra una sesión abierta antes del plazo: el comprador que abra la url después de eso va a cancel_url, o ve una página de enlace no disponible. Útil cuando el usuario desistió en tu sistema o eligió otro plan. Pide el alcance write.

curl -X POST https://api.vipter.com/v1/checkout/sessions/cs_4b7e2d9a1c3f5e8b0d2a6c4e1f3b/expire \
  -H "Authorization: Bearer vk_live_…"

Devuelve el objeto con status: "expired" y genera el evento checkout.session.expired. Una sesión que ya está complete o expired recibe 400 checkout_session_not_open, con el estado actual en el message.

El objeto checkout.session

CampoTipoContenido
idtextocs_…
objecttexto"checkout.session"
statustextoopen, complete o expired. Consulta Estado de la sesión.
payment_statustextounpaid, paid o pending.
urltexto o nullLa dirección del checkout. Solo mientras status es open; después viene null.
modetexto o nullpayment en una oferta única, subscription en una recurrente.
offerobjetoid (ofr_…) y name de la oferta.
packentero o nullEl paquete fijado, en unidades.
currencytexto o nullLa moneda fijada al crear. null cuando la sesión dejó elegir al comprador.
amount_totalentero o nullEl precio de la oferta en la moneda de la sesión (o en la predeterminada), en la unidad mínima: el monto del primer cobro, cuando es distinto. Viene antes de cupón, envío y adicionales, y null en las sesiones con paquete. El monto cobrado de hecho está en amount_total del pedido.
customertexto o nullcust_…: el que pasaste, o el cliente creado cuando el comprador pagó.
customer_email, customer_nametexto o nullLo que pasaste; con customer, el correo y el nombre del cliente.
client_reference_idtexto o nullTu identificador.
metadataobjetoLo que enviaste.
subscription_dataobjetometadata: lo que enviaste en subscription_data[metadata], o {}.
discountslista[{ "coupon": "CODIGO" }] cuando la sesión tiene cupón; si no [].
success_url, cancel_urltexto o nullComo los enviaste, con el {CHECKOUT_SESSION_ID} todavía sin reemplazar.
redirect_delayenteroSegundos antes de la redirección, de 0 a 30.
localetexto o nullEl idioma enviado.
ordertexto o nullord_… del pedido que generó la sesión. Se completa cuando el comprador paga o genera un PIX.
subscriptiontexto o nullsub_… de la suscripción creada, en las ofertas recurrentes.
expires_atenteroCuándo vence la sesión.
completed_atentero o nullCuándo la sesión pasó a complete.
livemodebooleanotrue en producción.
createdenteroCuándo se creó la sesión.

Estado de la sesión

statuspayment_statusSignificado
openunpaidEl comprador todavía no pagó. Una tarjeta rechazada deja la sesión abierta: puede intentar de nuevo en la misma página.
completepaidPagado: tarjeta aprobada, o PIX pagado. order está completado.
completependingEl comprador generó un PIX, o la tarjeta quedó en análisis, y el dinero todavía no entró. order está completado con status: "pending". La sesión queda así hasta que el pago se confirme (paid) o falle (unpaid).
completeunpaidEl pago pendiente no ocurrió: el PIX venció o el cobro fue rechazado después del análisis. La sesión no se reabre; crea otra.
expiredunpaidEl plazo pasó sin pago, o alguien llamó a POST …/expire.

Cada cambio genera un evento del catálogo 2026-11-01: checkout.session.completed cuando la sesión pasa a complete (con paid o pending), checkout.session.async_payment_succeeded cuando un pago pendiente se confirma, checkout.session.async_payment_failed cuando falla, y checkout.session.expired. Una sesión solo se completa una vez.

Después del pago

  1. El comprador paga en la página de Vipter y ve la página de gracias de la tienda.
  2. Con el pago confirmado, la página cuenta redirect_delay segundos y va a success_url, con {CHECKOUT_SESSION_ID} reemplazado por el id de la sesión. Mientras un PIX no se paga, la página queda esperando y no redirige.
  3. La success_url de la sesión tiene prioridad sobre la URL de éxito del enlace rápido, la de la oferta y la predeterminada de la tienda. Sin success_url, vale la siguiente de la lista.

Cuando el producto tiene archivos para descargar, la página no redirige sola: muestra los archivos y un botón para seguir a la success_url.

Un comprador que abre la url de una sesión ya pagada es llevado a la página de gracias del pedido. Una sesión vencida lleva a cancel_url o, sin ella, a una página de enlace no disponible. Las sesiones vencidas se marcan como expired por una rutina periódica, y también en el momento, si alguien abre el enlace.

Portal del cliente

Crear una sesión del portal

POST /v1/billing_portal/sessions

Genera un enlace de entrada directa al portal del cliente de la tienda para un cliente, sin el paso del código por correo. Sirve para el botón "administrar suscripción" de tu sistema: el cliente cambia la tarjeta, cancela o ve los cobros en el portal de Vipter. Pide el alcance write.

ParámetroTipoContenido
customertextoObligatorio. cust_… del cliente.
return_urltextoAceptado por compatibilidad con Stripe y devuelto en el objeto. El portal todavía no tiene un botón para volver a él.
curl -X POST https://api.vipter.com/v1/billing_portal/sessions \
  -H "Authorization: Bearer vk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "customer": "cust_9d2e4f6a8b1c3d5e" }'
{
  "id": "bps_3e9a1c7f5b2d4e6a8c0b",
  "object": "billing_portal.session",
  "url": "https://portal.vipter.com/loja-demo/verify?token=EJEMPLO-TOKEN",
  "customer": "cust_9d2e4f6a8b1c3d5e",
  "return_url": null,
  "expires_at": 1790187300,
  "livemode": true,
  "created": 1790186400
}
CampoTipoContenido
idtextobps_…. La sesión no se puede consultar después.
objecttexto"billing_portal.session"
urltextoEl enlace de entrada. Vale por 15 minutos y sirve para una entrada: al abrirse, crea la sesión del portal en el navegador del cliente y deja de funcionar. Genera un enlace nuevo en cada clic, en el momento del clic, y redirige al cliente a él; no lo guardes ni lo envíes por correo.
customertextoEl cliente.
return_urltexto o nullLo que enviaste.
expires_atenteroCuándo deja de valer el enlace.
livemodebooleanotrue en producción.
createdenteroCuándo se creó la sesión.

El enlace abre en la dirección del portal de la tienda: el dominio propio, cuando hay uno activo, o portal.vipter.com/{slug}.

codeHTTPSignificado
resource_missing404El cliente no existe en la tienda.
portal_disabled400El portal del cliente está desactivado en la configuración de la tienda. Consulta Portal del cliente.
portal_unavailable400La tienda todavía no tiene una dirección de portal: falta un slug o un dominio propio activo.

Eventos

Todo evento que Vipter genera queda guardado y se puede consultar por la API, en los dos catálogos: sirve para revisar qué recibió tu endpoint, recuperar lo que no recibió mientras estuvo desactivado y pedir un reenvío por código. El cuerpo de cada evento es el mismo sobre que llega al endpoint, y api_version dice de qué catálogo es. Los eventos de prueba no aparecen en la lista.

Listar eventos

GET /v1/events
ParámetroTipoContenido
typetextoUn tipo exacto, como invoice.paid, o un patrón con *, como invoice.* o customer.subscription.*.
created[gte], created[gt], created[lte], created[lt]enteroSolo eventos creados a partir de, después de, hasta o antes de ese momento, en segundos Unix.
limit, starting_after, ending_beforePaginación. El cursor es el id de un evento.
curl "https://api.vipter.com/v1/events?type=invoice.*&created[gte]=1790726400" \
  -H "Authorization: Bearer vk_live_…"
{
  "object": "list",
  "url": "https://api.vipter.com/v1/events",
  "has_more": false,
  "data": [
    {
      "id": "evt_5a7c9e1b3d5f7a9c1e3b5d7f",
      "object": "event",
      "type": "invoice.paid",
      "api_version": "2026-11-01",
      "created": 1790790413,
      "livemode": true,
      "pending_webhooks": 0,
      "request": { "id": null, "idempotency_key": null },
      "data": {
        "object": { "id": "ord_7b3e9f1c2a8d4e6f", "object": "order", "status": "authorized", "paid": true, "…": "…" }
      }
    }
  ]
}

La lista viene del más nuevo al más antiguo y junta los dos catálogos: el mismo hecho aparece dos veces cuando la tienda tiene endpoints en las dos versiones, una con cada nombre. Un evento de la versión 2026-09-01 viene sin pending_webhooks ni request, como en el sobre de esa versión.

Buscar un evento

GET /v1/events/{id}
curl https://api.vipter.com/v1/events/evt_5a7c9e1b3d5f7a9c1e3b5d7f \
  -H "Authorization: Bearer vk_live_…"

Devuelve el objeto event, el mismo JSON que el endpoint recibió en el POST. En la versión 2026-11-01, pending_webhooks se calcula en el momento de la consulta: cuántas entregas del evento todavía no funcionaron. Un evento de prueba se puede buscar por el id que devuelve la llamada de prueba. Un id que no existe en la tienda recibe 404 resource_missing.

Reenviar un evento

POST /v1/events/{id}/resend

Vuelve a poner el evento en la cola de entrega, con el mismo id y el mismo created, y lo envía en el momento. Pide el alcance write.

ParámetroTipoContenido
webhook_endpointtextoEl id de un endpoint. Con él, solo ese endpoint recibe el evento: su entrega vuelve a pending con los intentos en cero, o se crea si el endpoint nunca tuvo una, como un endpoint creado después del evento o que estaba desactivado cuando salió. Sin él, se reenvía toda entrega que el evento ya tiene, incluso las que ya habían funcionado.
curl -X POST https://api.vipter.com/v1/events/evt_5a7c9e1b3d5f7a9c1e3b5d7f/resend \
  -H "Authorization: Bearer vk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "webhook_endpoint": "0b7e2c4a-9d1f-4e3b-8a6c-5f2d1e0c9b8a" }'
{
  "object": "event_resend",
  "event": "evt_5a7c9e1b3d5f7a9c1e3b5d7f",
  "webhook_endpoint": "0b7e2c4a-9d1f-4e3b-8a6c-5f2d1e0c9b8a",
  "deliveries": 1
}

deliveries es cuántas entregas se pusieron en la cola. Sin webhook_endpoint, es el número de endpoints que ya tenían una entrega del evento; 0 cuando ninguno la tenía. El endpoint tiene que recibir el catálogo del evento: un evento invoice.paid (2026-11-01) no se puede enviar a un endpoint 2026-09-01. Reenviar a un endpoint desactivado cierra la entrega con endpoint disabled; actívalo antes. Si la entrega falla, sigue el calendario de reintentos desde el principio. Tu control de idempotencia trata el reenvío como repetición.

codeHTTPSignificado
resource_missing404El evento, o el endpoint en webhook_endpoint, no existe en la tienda.
invalid_event_type400El endpoint recibe otro catálogo. El message dice cuál es el del evento y cuál es el del endpoint.

Endpoints de webhook

Los mismos endpoints de la página GeneralIntegracionesAutomatizacionesWebhooks del panel, creados y administrados por código. Un endpoint creado por la API aparece en el panel como cualquier otro, y las reglas son las de Recibir eventos en tu sistema: URL https://, un catálogo por endpoint, reintentos y desactivación automática. El id de un endpoint es un UUID; trátalo como texto opaco.

Listar endpoints

GET /v1/webhook_endpoints
curl https://api.vipter.com/v1/webhook_endpoints \
  -H "Authorization: Bearer vk_live_…"

Devuelve un objeto list con todos los endpoints de la tienda, del más antiguo al más nuevo, sin filtros. El secret no viene en la lista.

Crear un endpoint

POST /v1/webhook_endpoints

Pide el alcance write. La respuesta es 201 y trae el secret (whsec_…) una sola vez: guárdalo en tu servidor para verificar la firma. Después, solo rotar el secreto genera otro.

ParámetroTipoContenido
urltextoObligatorio. Dirección https:// de tu servidor, hasta 2000 caracteres, en un host público. Un host de la red interna (localhost, 10.x, 192.168.x, nombres .internal o .local) recibe 400 private_host; una dirección sin https:// recibe 400 invalid_url.
descriptiontextoUn recordatorio para el equipo, hasta 200 caracteres.
enabled_eventslistaLos tipos que recibe el endpoint, hasta 100: nombres del catálogo de la versión, como invoice.paid, o patrones con *, como customer.subscription.*. Omitida, vacía o ["*"], el endpoint recibe todos los eventos del catálogo, incluso los que se creen en el futuro. Un nombre que no existe en la versión recibe 400 invalid_event_type.
api_versiontextoEl catálogo que recibe el endpoint: 2026-11-01 (predeterminado) o 2026-09-01. No cambia después de creado.
curl -X POST https://api.vipter.com/v1/webhook_endpoints \
  -H "Authorization: Bearer vk_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://app.example.com/webhooks/vipter",
    "description": "Facturación del SaaS",
    "enabled_events": ["checkout.session.*", "customer.subscription.*", "invoice.paid"]
  }'
{
  "id": "0b7e2c4a-9d1f-4e3b-8a6c-5f2d1e0c9b8a",
  "object": "webhook_endpoint",
  "url": "https://app.example.com/webhooks/vipter",
  "description": "Facturación del SaaS",
  "enabled_events": ["checkout.session.*", "customer.subscription.*", "invoice.paid"],
  "api_version": "2026-11-01",
  "status": "enabled",
  "disabled_reason": null,
  "consecutive_failures": 0,
  "created_via": "api",
  "secret": "whsec_EJEMPLO0000000000000000000000000000",
  "livemode": true,
  "created": 1790186400
}

El endpoint nace activo y empieza a recibir los eventos creados a partir de ahí; los eventos anteriores no le llegan, salvo por un reenvío con webhook_endpoint.

Buscar un endpoint

GET /v1/webhook_endpoints/{id}
curl https://api.vipter.com/v1/webhook_endpoints/0b7e2c4a-9d1f-4e3b-8a6c-5f2d1e0c9b8a \
  -H "Authorization: Bearer vk_live_…"

Devuelve el objeto webhook_endpoint, sin secret. Un id que no existe en la tienda recibe 404 resource_missing.

Modificar un endpoint

POST /v1/webhook_endpoints/{id}

Pide el alcance write. Solo cambian los campos enviados.

ParámetroTipoContenido
urltextoLa dirección nueva, con las mismas reglas de la creación. Las entregas siguientes ya van a ella.
descriptiontexto o nullnull borra la descripción.
enabled_eventslistaReemplaza la lista entera. ["*"] vuelve a recibir todos los eventos del catálogo. Desde el panel esta lista no se puede cambiar; por la API, sí.
statustextoenabled o disabled. Es el interruptor Activo del panel: enabled pone consecutive_failures en cero y borra disabled_reason, lo que vuelve a encender un endpoint desactivado automáticamente. Mientras está disabled, los eventos nuevos no se guardan para él.

Un api_version en el cuerpo se ignora: la versión no cambia. Para pasar al otro catálogo, crea un endpoint nuevo y quita el anterior.

curl -X POST https://api.vipter.com/v1/webhook_endpoints/0b7e2c4a-9d1f-4e3b-8a6c-5f2d1e0c9b8a \
  -H "Authorization: Bearer vk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "enabled_events": ["*"], "status": "enabled" }'

Devuelve el objeto webhook_endpoint actualizado.

Quitar un endpoint

DELETE /v1/webhook_endpoints/{id}

Borra el endpoint, el secreto y su historial de entregas; las entregas pendientes se detienen. Pide el alcance write.

{ "id": "0b7e2c4a-9d1f-4e3b-8a6c-5f2d1e0c9b8a", "object": "webhook_endpoint", "deleted": true }

Rotar el secreto

POST /v1/webhook_endpoints/{id}/rotate_secret

Genera un secreto nuevo y devuelve el objeto webhook_endpoint con él en secret, una sola vez. El secreto anterior sigue firmando por 24 horas: en ese período, cada entrega trae dos v1= en el encabezado Vipter-Signature, y tu servidor puede cambiar la variable de entorno sin perder eventos. Consulta Rotar el secreto. Pide el alcance write.

curl -X POST https://api.vipter.com/v1/webhook_endpoints/0b7e2c4a-9d1f-4e3b-8a6c-5f2d1e0c9b8a/rotate_secret \
  -H "Authorization: Bearer vk_live_…"

Enviar un evento de prueba

POST /v1/webhook_endpoints/{id}/test

Crea un evento customer.created de prueba y lo entrega solo a este endpoint, en su catálogo: un objeto customer en el formato de la versión del endpoint, con "test": true de más y livemode: false. Es lo mismo que el botón Enviar evento de prueba de la tarjeta del endpoint en el panel. El evento no pasa por automatizaciones, correos, facturas, áreas de miembros ni píxeles, no llega a los otros endpoints y no aparece en GET /v1/events. El formato está en el catálogo. Pide el alcance write.

curl -X POST https://api.vipter.com/v1/webhook_endpoints/0b7e2c4a-9d1f-4e3b-8a6c-5f2d1e0c9b8a/test \
  -H "Authorization: Bearer vk_live_…"
{
  "object": "webhook_test",
  "webhook_endpoint": "0b7e2c4a-9d1f-4e3b-8a6c-5f2d1e0c9b8a",
  "event": "evt_1c3e5a7b9d0f2e4c6a8b0d2f"
}

La entrega sale en el momento. Síguela en GET /v1/webhook_endpoints/{id}/deliveries o en Entregas recientes en el panel. Si tu servidor falla, sigue el calendario de reintentos de un evento real y cuenta para la desactivación automática. Con el endpoint desactivado, la entrega termina en el momento con endpoint disabled.

Listar las entregas de un endpoint

GET /v1/webhook_endpoints/{id}/deliveries
ParámetroTipoContenido
statustextopending, delivering, succeeded, failed o exhausted.
limit, starting_after, ending_beforePaginación. El cursor es el id de una entrega.
curl "https://api.vipter.com/v1/webhook_endpoints/0b7e2c4a-9d1f-4e3b-8a6c-5f2d1e0c9b8a/deliveries?status=failed" \
  -H "Authorization: Bearer vk_live_…"
{
  "object": "list",
  "url": "https://api.vipter.com/v1/webhook_endpoints/0b7e2c4a-9d1f-4e3b-8a6c-5f2d1e0c9b8a/deliveries",
  "has_more": false,
  "data": [
    {
      "id": "6f1d2c3b-4a5e-4f6a-9b8c-7d6e5f4a3b2c",
      "object": "webhook_delivery",
      "event": "evt_5a7c9e1b3d5f7a9c1e3b5d7f",
      "webhook_endpoint": "0b7e2c4a-9d1f-4e3b-8a6c-5f2d1e0c9b8a",
      "status": "failed",
      "attempts": 2,
      "next_attempt_at": 1790790780,
      "last_attempt_at": 1790790480,
      "delivered_at": null,
      "last_response_status": 500,
      "last_error": "HTTP 500",
      "last_duration_ms": 184,
      "created": 1790790413
    }
  ]
}

Devuelve un objeto list de webhook_delivery, de la más nueva a la más antigua: una entrega por evento que el endpoint recibió. Lo que significa cada estado está en Reintentos, desactivación y reenvío.

El objeto del endpoint

CampoTipoContenido
idtextoUUID del endpoint.
objecttexto"webhook_endpoint"
urltextoLa dirección que recibe los eventos.
descriptiontexto o nullLa descripción.
enabled_eventslistaLos tipos y patrones que recibe el endpoint. ["*"] cuando recibe todos los eventos del catálogo.
api_versiontexto2026-11-01 o 2026-09-01: el catálogo que recibe el endpoint.
statustextoenabled o disabled.
disabled_reasontexto o nullEl motivo, cuando Vipter desactivó el endpoint por su cuenta: auto-disabled after 20 exhausted deliveries. null en los demás casos, incluso cuando el equipo apagó el interruptor.
consecutive_failuresenteroEntregas seguidas que agotaron los intentos. En 20, el endpoint se desactiva; una entrega succeeded lo pone en cero.
created_viatextodashboard o api: dónde se creó el endpoint.
secrettextoEl secreto de firma, whsec_…. Solo en la respuesta de crear y de rotar el secreto.
livemodebooleanotrue en producción.
createdenteroCuándo se creó el endpoint.

El objeto de la entrega

CampoTipoContenido
idtextoUUID de la entrega.
objecttexto"webhook_delivery"
eventtextoevt_… del evento entregado. Búscalo en GET /v1/events/{id}.
webhook_endpointtextoEl endpoint.
statustextopending (en cola), delivering (enviándose), succeeded (tu servidor respondió 2xx), failed (el último intento falló y hay otro programado) o exhausted (se acabaron los intentos, o el endpoint estaba desactivado).
attemptsenteroCuántos intentos ya se hicieron, de hasta 8. Vuelve a 0 en un reenvío.
next_attempt_atentero o nullCuándo está programado el próximo intento. Solo con status pending o failed.
last_attempt_atentero o nullCuándo fue el último intento.
delivered_atentero o nullCuándo tu servidor respondió 2xx.
last_response_statusentero o nullEl código HTTP de la última respuesta. null cuando no hubo respuesta.
last_errortexto o nullEl error del último intento: HTTP 500, timeout, endpoint disabled o el mensaje de red. null cuando funcionó.
last_duration_msentero o nullCuánto tardó el último intento, en milisegundos.
createdenteroCuándo se creó la entrega.

Próximamente

Lo que todavía no existe en la API: un filtro por client_reference_id en GET /v1/subscriptions y en GET /v1/customers. Para encontrar la suscripción de un usuario tuyo, usa el campo subscription de la sesión de checkout que la creó, o guarda el sub_… que llega en el webhook. El uso medido, antes listado aquí, ya está disponible: consulta Uso medido.

Qué hacer después

¿Te ayudó esta página?

En esta página

CuentaConsultar la cuentaClientesListar clientesBuscar un clienteCrear un clienteModificar un clienteEl objeto customerSuscripcionesListar suscripcionesBuscar una suscripciónEl objeto subscriptionAcciones en la suscripciónModificar una suscripciónCancelar ahoraPausar y reanudarReactivarCambiar de ofertaCobros en la suscripciónCobrar la tarjeta guardadaListar los cobros de una suscripciónBuscar un cobroEl objeto del cobroUso medidoCrear un medidorListar medidoresBuscar y modificar un medidorDesactivar un medidorEl objeto billing.meterRegistrar un evento de usoRegistrar eventos en loteEl objeto billing.meter_eventConsultar el uso agregado de un clienteÍtems de uso de una suscripciónEl objeto usage_itemConsultar los períodos de usoEl objeto usage_periodErrores del uso medidoPedidosListar pedidosBuscar un pedidoEl objeto orderEstado del pedidoLíneas del pedidoOfertasListar ofertasBuscar una ofertaEl objeto offerProductosListar productosBuscar un productoEl objeto productSesiones de checkoutCrear una sesiónBuscar una sesiónListar sesionesExpirar una sesiónEl objeto checkout.sessionEstado de la sesiónDespués del pagoPortal del clienteCrear una sesión del portalEventosListar eventosBuscar un eventoReenviar un eventoEndpoints de webhookListar endpointsCrear un endpointBuscar un endpointModificar un endpointQuitar un endpointRotar el secretoEnviar un evento de pruebaListar las entregas de un endpointEl objeto del endpointEl objeto de la entregaPróximamenteQué hacer después
Idioma