Ofertas del Merchant
Crea, lista, actualiza, archiva, restaura y elimina (soft delete) ofertas — los planes de precios asociados a un producto — desde la superficie de Merchant del Tokeflow Dashboard.
Una oferta es un plan comprable asociado a un producto: define el ciclo de cobro, el comportamiento de trial y de setup, los límites de ciclo y uno o más precios específicos por moneda. Un producto puede tener varias ofertas — un plan mensual, un plan anual, una variante promocional — y cada oferta lleva sus propios precios en unidades menores por moneda ISO 4217. Las ofertas son lo que los clientes realmente suscriben o compran en el checkout.
Estos endpoints impulsan las pantallas de gestión de ofertas del Tokeflow Dashboard para un único Merchant. Las ofertas hacen referencia a un producto y, a su vez, son referenciadas por suscripciones, por lo que la eliminación está protegida: una oferta con cualquier historial de suscripción se conserva para los registros de cobro.
Estos endpoints impulsan la interfaz del Tokeflow Dashboard. Están autenticados con una sesión del dashboard (JWT bearer) obtenida al iniciar sesión — no con claves de API sk_/pk_. Cada solicitud también debe enviar el header x-merchant-id que identifica el contexto del Merchant (mrc_…). Consulta Autenticación del Dashboard.
Ciclo de vida de la oferta
Archivar oculta una oferta de los nuevos checkouts manteniéndola totalmente legible; es reversible con unarchive. La eliminación por soft delete quita la oferta (y se propaga en cascada a sus precios), pero se rechaza con 409 mientras cualquier suscripción — en cualquier estado — aún la referencie, y puede revertirse con restore.
El objeto oferta
Derivado de OfferResponseDto, devuelto por cada endpoint de esta página que devuelve un cuerpo.
| Campo | Tipo | Descripción |
|---|---|---|
id | string | ID de la oferta, con el prefijo ofr_. |
product_id | string | Producto padre, con el prefijo prd_. |
name | string | Nombre visible de la oferta. |
slug | string | Identificador en minúsculas, único por producto. |
description | string | null | Descripción de texto libre. |
billing_cycle | string | Uno de daily, biweekly, monthly, quarterly, half_yearly, yearly, custom, none. |
custom_billing_days | number | null | Período en días. Obligatorio cuando billing_cycle = custom. |
cycle_limit | number | null | Número fijo de cobros. null = cobrar hasta la cancelación. |
free_trial | boolean | Si la oferta comienza con un trial gratuito. |
trial_days | number | null | Duración del trial en días. Obligatorio cuando free_trial = true. |
setup_charge | boolean | Si el primer ciclo se cobra con un importe de primer cobro distinto. |
renew_after_cycle_limit | boolean | Si la suscripción se renueva después de alcanzar cycle_limit. |
renewal_offer_id | string | null | Oferta usada para la renovación posterior a cycle_limit. null = renovar con la misma oferta. |
is_default | boolean | Si esta es la oferta predeterminada de su producto. |
status | string | active o archived. |
created_at | string | Marca de tiempo de creación (ISO 8601 UTC). |
updated_at | string | Marca de tiempo de la última actualización (ISO 8601 UTC). |
prices | array | Precios de la oferta. Se rellena cuando los precios se cargan de forma eager (create, get por ID). |
billing_cycle: "none" describe una oferta única, sin recurrencia. Para ciclos recurrentes que no encajan en los presets, usa custom junto con custom_billing_days.
Endpoints
GET/api/v1/merchant/offers
Auth: Dashboard session (JWT bearer). Se requiere membresía del Merchant.
Devuelve una lista paginada de las ofertas del Merchant, filtrable por producto, estado, ciclo de cobro y nombre.
Parámetros de consulta
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
page | number | No | Número de página, indexado desde 1 (predeterminado 1, mín. 1). |
limit | number | No | Elementos por página (predeterminado 20, mín. 1, máx. 100). |
product_id | string | No | Filtra las ofertas a un único producto. |
status | string | No | Filtra por estado: active o archived. |
billing_cycle | string | No | Filtra por ciclo de cobro: daily, biweekly, monthly, quarterly, half_yearly, yearly, custom o none. |
is_default | boolean | No | Filtra solo las ofertas predeterminadas. |
name | string | No | Filtro de coincidencia parcial por el nombre de la oferta. |
Ejemplo de solicitud
curl -G https://api.tokeflow.com/api/v1/merchant/offers \
-H "Authorization: Bearer <session-jwt>" \
-H "x-merchant-id: mrc_9f8e7d6c5b" \
--data-urlencode "product_id=prd_a1b2c3d4e5" \
--data-urlencode "status=active" \
--data-urlencode "page=1" \
--data-urlencode "limit=20"Ejemplo de respuesta — 200 OK
{
"data": [
{
"id": "ofr_a1b2c3d4e5",
"product_id": "prd_a1b2c3d4e5",
"name": "Mensal",
"slug": "mensal",
"description": "Plano mensal padrão",
"billing_cycle": "monthly",
"custom_billing_days": null,
"cycle_limit": 12,
"free_trial": false,
"trial_days": null,
"setup_charge": false,
"renew_after_cycle_limit": false,
"renewal_offer_id": null,
"is_default": true,
"status": "active",
"created_at": "2026-05-19T12:00:00Z",
"updated_at": "2026-05-19T12:00:00Z"
}
],
"meta": {
"page": 1,
"limit": 20,
"total": 42,
"total_pages": 3,
"has_next": true,
"has_prev": false
}
}POST/api/v1/merchant/offers
Auth: Dashboard session (JWT bearer). Se requiere membresía del Merchant.
Crea una oferta con sus precios iniciales. Se requiere al menos un precio; cada precio es un importe en unidades menores para una moneda ISO 4217.
Campos de la solicitud
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
product_id | string | Sí | Producto padre, con el prefijo prd_. |
name | string | Sí | Nombre visible (máx. 255 caracteres). |
slug | string | Sí | Identificador en minúsculas, único por producto (máx. 100 caracteres). |
description | string | No | Descripción de texto libre (máx. 2000 caracteres). |
billing_cycle | string | Sí | Uno de daily, biweekly, monthly, quarterly, half_yearly, yearly, custom, none. |
custom_billing_days | number | Condicional | Período en días (mín. 1). Obligatorio cuando billing_cycle = custom. |
cycle_limit | number | No | Número fijo de cobros (mín. 1). Omite o null = cobrar hasta la cancelación. |
free_trial | boolean | No | Si la oferta comienza con un trial gratuito. Predeterminado false. |
trial_days | number | Condicional | Duración del trial en días (mín. 1). Obligatorio cuando free_trial = true. |
setup_charge | boolean | No | Cobra el primer ciclo por el first_charge_amount de cada precio. Predeterminado false. |
renew_after_cycle_limit | boolean | No | Renueva la suscripción después de cycle_limit cobros. Predeterminado false. |
renewal_offer_id | string | No | Oferta para la renovación posterior a cycle_limit. Omite = renovar con la misma oferta. |
is_default | boolean | No | Convierte esta en la oferta predeterminada de su producto. Predeterminado false. |
status | string | Sí | active o archived. |
prices | array | Sí | Al menos un precio (ver abajo). |
Campos de precio (prices[])
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
currency | string | Sí | Código de moneda ISO 4217. |
amount | number | Sí | Importe en unidades menores (mín. 0). Ej.: R$99,00 = 9900. |
first_charge_amount | number | No | Importe del primer ciclo en unidades menores. Usado cuando setup_charge = true. 0 = solo validación de tarjeta. |
is_default | boolean | No | Marca esta moneda como la predeterminada de la oferta. |
Ejemplo de solicitud
curl -X POST https://api.tokeflow.com/api/v1/merchant/offers \
-H "Authorization: Bearer <session-jwt>" \
-H "x-merchant-id: mrc_9f8e7d6c5b" \
-H "Content-Type: application/json" \
-d '{
"product_id": "prd_a1b2c3d4e5",
"name": "Mensal",
"slug": "mensal",
"description": "Plano mensal padrão",
"billing_cycle": "monthly",
"cycle_limit": 12,
"free_trial": false,
"setup_charge": false,
"is_default": true,
"status": "active",
"prices": [
{ "currency": "BRL", "amount": 9900, "is_default": true },
{ "currency": "USD", "amount": 1900 }
]
}'Ejemplo de respuesta — 201 Created
{
"id": "ofr_a1b2c3d4e5",
"product_id": "prd_a1b2c3d4e5",
"name": "Mensal",
"slug": "mensal",
"description": "Plano mensal padrão",
"billing_cycle": "monthly",
"custom_billing_days": null,
"cycle_limit": 12,
"free_trial": false,
"trial_days": null,
"setup_charge": false,
"renew_after_cycle_limit": false,
"renewal_offer_id": null,
"is_default": true,
"status": "active",
"created_at": "2026-05-19T12:00:00Z",
"updated_at": "2026-05-19T12:00:00Z",
"prices": [
{
"currency": "BRL",
"amount": 9900,
"is_default": true
},
{
"currency": "USD",
"amount": 1900,
"is_default": false
}
]
}Un trial gratuito no necesita un cobro de setup: free_trial: true con trial_days ya cobra 0 al inicio, y la tarjeta se sigue registrando mediante una validación de tarjeta. Para cobrar una tarifa de alta junto con el trial, añade setup_charge: true con un first_charge_amount mayor que 0 en el precio; first_charge_amount: 0 es la forma explícita de decir "validar la tarjeta, no cobrar nada".
GET/api/v1/merchant/offers/:id
Auth: Dashboard session (JWT bearer). Se requiere membresía del Merchant.
Recupera una única oferta por ID, incluidos sus precios. Devuelve el objeto oferta.
Ejemplo de solicitud
curl https://api.tokeflow.com/api/v1/merchant/offers/ofr_a1b2c3d4e5 \
-H "Authorization: Bearer <session-jwt>" \
-H "x-merchant-id: mrc_9f8e7d6c5b"Ejemplo de respuesta — 200 OK
{
"id": "ofr_a1b2c3d4e5",
"product_id": "prd_a1b2c3d4e5",
"name": "Mensal",
"slug": "mensal",
"description": "Plano mensal padrão",
"billing_cycle": "monthly",
"custom_billing_days": null,
"cycle_limit": 12,
"free_trial": false,
"trial_days": null,
"setup_charge": false,
"renew_after_cycle_limit": false,
"renewal_offer_id": null,
"is_default": true,
"status": "active",
"created_at": "2026-05-19T12:00:00Z",
"updated_at": "2026-05-19T12:00:00Z",
"prices": [
{
"currency": "BRL",
"amount": 9900,
"is_default": true
}
]
}PATCH/api/v1/merchant/offers/:id
Auth: Dashboard session (JWT bearer). Se requiere membresía del Merchant.
Actualiza una oferta. Envía solo los campos que quieras cambiar; se devuelve el objeto oferta actualizado.
Ejemplo de solicitud
curl -X PATCH https://api.tokeflow.com/api/v1/merchant/offers/ofr_a1b2c3d4e5 \
-H "Authorization: Bearer <session-jwt>" \
-H "x-merchant-id: mrc_9f8e7d6c5b" \
-H "Content-Type: application/json" \
-d '{ "name": "Mensal Plus", "description": "Plano mensal com upgrade" }'Ejemplo de respuesta — 200 OK
{
"id": "ofr_a1b2c3d4e5",
"product_id": "prd_a1b2c3d4e5",
"name": "Mensal Plus",
"slug": "mensal",
"description": "Plano mensal com upgrade",
"billing_cycle": "monthly",
"custom_billing_days": null,
"cycle_limit": 12,
"free_trial": false,
"trial_days": null,
"setup_charge": false,
"renew_after_cycle_limit": false,
"renewal_offer_id": null,
"is_default": true,
"status": "active",
"created_at": "2026-05-19T12:00:00Z",
"updated_at": "2026-05-19T14:30:00Z"
}DELETE/api/v1/merchant/offers/:id
Auth: Dashboard session (JWT bearer). Se requiere membresía del Merchant.
Elimina una oferta por soft delete, propagándose en cascada a sus precios. La solicitud se rechaza con 409 mientras cualquier suscripción — en cualquier estado — referencie la oferta; las ofertas referenciadas por suscripciones se conservan para el historial de cobro. Una oferta eliminada por soft delete puede recuperarse con restore.
Códigos de respuesta
| Código | Cuándo |
|---|---|
204 No Content | Oferta eliminada por soft delete. |
404 Not Found | Oferta no encontrada. |
409 Conflict | La oferta tiene suscripciones y no puede eliminarse. |
Ejemplo de solicitud
curl -X DELETE https://api.tokeflow.com/api/v1/merchant/offers/ofr_a1b2c3d4e5 \
-H "Authorization: Bearer <session-jwt>" \
-H "x-merchant-id: mrc_9f8e7d6c5b"Ejemplo de respuesta — 204 No Content (cuerpo vacío)
Cuando una oferta no puede eliminarse porque hay suscripciones que la referencian, archívala en su lugar — las ofertas archivadas quedan fuera de los nuevos checkouts mientras su historial de cobro permanece intacto.
POST/api/v1/merchant/offers/:id/archive
Auth: Dashboard session (JWT bearer). Se requiere membresía del Merchant.
Archiva una oferta, estableciendo status en archived. Devuelve el objeto oferta actualizado.
Ejemplo de solicitud
curl -X POST https://api.tokeflow.com/api/v1/merchant/offers/ofr_a1b2c3d4e5/archive \
-H "Authorization: Bearer <session-jwt>" \
-H "x-merchant-id: mrc_9f8e7d6c5b"Ejemplo de respuesta — 201 Created
{
"id": "ofr_a1b2c3d4e5",
"product_id": "prd_a1b2c3d4e5",
"name": "Mensal",
"slug": "mensal",
"description": "Plano mensal padrão",
"billing_cycle": "monthly",
"custom_billing_days": null,
"cycle_limit": 12,
"free_trial": false,
"trial_days": null,
"setup_charge": false,
"renew_after_cycle_limit": false,
"renewal_offer_id": null,
"is_default": false,
"status": "archived",
"created_at": "2026-05-19T12:00:00Z",
"updated_at": "2026-05-19T15:00:00Z"
}POST/api/v1/merchant/offers/:id/unarchive
Auth: Dashboard session (JWT bearer). Se requiere membresía del Merchant.
Desarchiva una oferta, estableciendo status de nuevo en active. Devuelve el objeto oferta actualizado.
Ejemplo de solicitud
curl -X POST https://api.tokeflow.com/api/v1/merchant/offers/ofr_a1b2c3d4e5/unarchive \
-H "Authorization: Bearer <session-jwt>" \
-H "x-merchant-id: mrc_9f8e7d6c5b"Ejemplo de respuesta — 201 Created
{
"id": "ofr_a1b2c3d4e5",
"product_id": "prd_a1b2c3d4e5",
"name": "Mensal",
"slug": "mensal",
"description": "Plano mensal padrão",
"billing_cycle": "monthly",
"custom_billing_days": null,
"cycle_limit": 12,
"free_trial": false,
"trial_days": null,
"setup_charge": false,
"renew_after_cycle_limit": false,
"renewal_offer_id": null,
"is_default": false,
"status": "active",
"created_at": "2026-05-19T12:00:00Z",
"updated_at": "2026-05-19T15:10:00Z"
}POST/api/v1/merchant/offers/:id/restore
Auth: Dashboard session (JWT bearer). Se requiere membresía del Merchant.
Restaura una oferta eliminada por soft delete y sus precios. Devuelve el objeto oferta restaurado.
Ejemplo de solicitud
curl -X POST https://api.tokeflow.com/api/v1/merchant/offers/ofr_a1b2c3d4e5/restore \
-H "Authorization: Bearer <session-jwt>" \
-H "x-merchant-id: mrc_9f8e7d6c5b"Ejemplo de respuesta — 201 Created
{
"id": "ofr_a1b2c3d4e5",
"product_id": "prd_a1b2c3d4e5",
"name": "Mensal",
"slug": "mensal",
"description": "Plano mensal padrão",
"billing_cycle": "monthly",
"custom_billing_days": null,
"cycle_limit": 12,
"free_trial": false,
"trial_days": null,
"setup_charge": false,
"renew_after_cycle_limit": false,
"renewal_offer_id": null,
"is_default": false,
"status": "active",
"created_at": "2026-05-19T12:00:00Z",
"updated_at": "2026-05-19T16:00:00Z"
}GET/api/v1/merchant/offers/:id/default-price
Auth: Dashboard session (JWT bearer). Se requiere membresía del Merchant.
Devuelve el precio predeterminado de una oferta para una moneda dada, o null si la oferta no tiene precio predeterminado en esa moneda.
Parámetros de consulta
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
currency | string | Sí | Código de moneda ISO 4217 para resolver el precio predeterminado. |
Ejemplo de solicitud
curl -G https://api.tokeflow.com/api/v1/merchant/offers/ofr_a1b2c3d4e5/default-price \
-H "Authorization: Bearer <session-jwt>" \
-H "x-merchant-id: mrc_9f8e7d6c5b" \
--data-urlencode "currency=BRL"Ejemplo de respuesta — 200 OK
{
"currency": "BRL",
"amount": 9900,
"is_default": true
}GET/api/v1/merchant/offers/:fromId/transitions/:toId/effective-behavior
Auth: Dashboard session (JWT bearer). Se requiere membresía del Merchant.
Resuelve el change_charge_behavior efectivo para un par de transición de oferta — cómo se cobrará una suscripción que pasa de la oferta :fromId a la oferta :toId. Úsalo para previsualizar el comportamiento de cobro que aplicará el Dashboard antes de confirmar un cambio de plan.
Ejemplo de solicitud
curl https://api.tokeflow.com/api/v1/merchant/offers/ofr_a1b2c3d4e5/transitions/ofr_f6g7h8i9j0/effective-behavior \
-H "Authorization: Bearer <session-jwt>" \
-H "x-merchant-id: mrc_9f8e7d6c5b"Ejemplo de respuesta — 200 OK
Errores
Respuestas de error comunes para este recurso:
| HTTP | Causa típica |
|---|---|
400 | Cuerpo inválido — ej.: prices ausente, billing_cycle: "custom" sin custom_billing_days, o free_trial: true sin trial_days. |
401 | Sesión del dashboard ausente o expirada. |
403 | El usuario de la sesión no es miembro del Merchant en x-merchant-id. |
404 | Oferta no encontrada o no perteneciente al Merchant. |
409 | Eliminación rechazada — la oferta es referenciada por al menos una suscripción. |
Relacionados
- Productos — las entradas de catálogo a las que pertenecen las ofertas.
- Suscripciones — cobro recurrente creado a partir de ofertas.
- Sesiones de checkout — donde los clientes eligen una oferta y pagan.
- Autenticación del Dashboard — cómo se emiten las sesiones del dashboard.