API Reference — Subscriptions & Licenses
Esta sección documenta los endpoints disponibles para la gestión de providers hijos, credenciales, y consulta de licencias y suscripciones.
🔒 Todos los endpoints requieren autenticación. Consulta la sección de Autenticación para obtener tu token y usarlo en la cabecera
Authorization: Bearer <token>.
Crear provider hijo
Permite al provider padre registrar un nuevo provider hijo en la plataforma.
👨🏻💻 QA:
POST https://api-qa.speaknosis.com/iam/v1/register/childProvider
🏥 Producción:
POST https://api-prod.speaknosis.com/iam/v1/register/childProvider
Body (application/json):
{
"name": "Clínica Hija",
"country": "Chile",
"city": "Santiago"
}
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
name | string | Sí | Nombre del establecimiento. |
country | string | Sí | País de ubicación. |
city | string | Sí | Ciudad de ubicación. |
Respuesta exitosa (200 OK):
{
"success": true,
"code": 200,
"response": {
"providerId": 1042,
"clientId": "speaknosis-client-xxxxx",
"secret": "xxxxxxxxxxxxxxxxxxxxxxxx"
}
}
⚠️ Guarda el
providerId,clientIdysecret. Los necesitarás para todos los pasos siguientes.
Obtener credenciales del provider hijo
Recupera las credenciales de autenticación de un provider hijo existente.
👨🏻💻 QA:
GET https://api-qa.speaknosis.com/iam/v1/provider/{childProviderId}/credentials
🏥 Producción:
GET https://api-prod.speaknosis.com/iam/v1/provider/{childProviderId}/credentials
| Parámetro | Ubicación | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
childProviderId | path | string | Sí | ID del provider hijo. |
Respuesta exitosa (200 OK):
{
"success": true,
"code": 200,
"response": {
"clientId": "speaknosis-client-xxxxx",
"secret": "xxxxxxxxxxxxxxxxxxxxxxxx"
}
}
Listar providers hijos
Retorna todos los providers hijos vinculados al provider padre autenticado.
👨🏻💻 QA:
GET https://api-qa.speaknosis.com/core/v1/provider/{providerId}/child
🏥 Producción:
GET https://api-prod.speaknosis.com/core/v1/provider/{providerId}/child
| Parámetro | Ubicación | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
providerId | path | string | Sí | ID del provider padre. |
limit | query | number | No | Cantidad de resultados por página. Si no se envía, retorna todos los hijos. |
offset | query | number | No | Cantidad de resultados a saltar. Por defecto: 0. |
Ejemplo con paginación:
GET .../child?limit=10&offset=20
Respuesta exitosa (200 OK):
{
"success": true,
"code": 200,
"response": {
"data": [
{
"id": 1042,
"name": "Clínica Hija",
"country": "Chile"
}
],
"meta": {
"total": 45,
"offset": 0,
"limit": 10
}
}
}
| Campo | Tipo | Descripción |
|---|---|---|
data | array | Listado de providers hijos. |
meta.total | number | Total de providers hijos del padre. |
meta.offset | number | Offset aplicado en la consulta. |
meta.limit | number | Límite aplicado. Si no se envió limit, coincide con total (se trajo todo). |
Suscripciones y Licencias
Consultar usuarios con sus licencias
Retorna el listado de doctores registrados en el provider junto con el estado de su licencia asignada.
👨🏻💻 QA:
GET https://api-qa.speaknosis.com/payments/v1/subscription/provider/{providerId}/user
🏥 Producción:
GET https://api-prod.speaknosis.com/payments/v1/subscription/provider/{providerId}/user
| Parámetro | Ubicación | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
providerId | path | string | Sí | ID interno del provider. |
limit | query | number | No | Cantidad de resultados por página. Por defecto: 10. |
offset | query | number | No | Cantidad de resultados a saltar. Por defecto: 0. |
search | query | string | No | Filtra por nombre o apellido del doctor (coincidencia parcial, sin distinguir mayúsculas). |
Ejemplo con paginación:
GET .../user?limit=20&offset=40
Respuesta exitosa (200 OK):
{
"success": true,
"code": 200,
"response": {
"doctors": [
{
"id": "tu-id-externo-1",
"name": "Juan",
"lastname": "Pérez",
"license": {
"type": "BASIC",
"isEnabled": true,
"currentUsage": 12,
"limit": 100,
"expiresAt": "2026-06-01T00:00:00.000Z"
}
},
{
"id": "tu-id-externo-2",
"name": "María",
"lastname": "González",
"license": null
}
],
"suspension": {
"suspended": false
},
"billing": {
"mode": "STRIPE",
"managedBy": null
},
"meta": {
"total": 152,
"offset": 0,
"limit": 20
}
}
}
| Campo | Tipo | Descripción |
|---|---|---|
doctors[].id | string | ID externo del doctor en tu sistema. |
doctors[].license | object | Licencia asignada. null si el doctor no tiene licencia. |
license.type | string | Tipo de licencia: BASIC, ELITE, TRIAL o USAGE (modalidad por consumo). |
license.isEnabled | boolean | Si la licencia está activa. Es false cuando el usuario llegó al límite de informes de un plan Basic, cuando la suscripción del provider está suspendida por falta de pago, o cuando el provider padre dio de baja el centro en self-billing (en estos dos últimos casos quedan en false todas las licencias del provider). |
license.currentUsage | number | Informes generados en el período actual, incluidas las regeneraciones. |
license.limit | number | Límite mensual de informes (100 en Basic). null para los planes ELITE, TRIAL y USAGE (sin límite). |
license.expiresAt | string | Fecha de vencimiento de la licencia. |
suspension.suspended | boolean | Si el acceso del provider está cortado por falta de pago. Solo se devuelve para providers que facturan por Stripe; ver la nota de abajo. |
suspension.reason | string | Motivo del corte. Presente solo cuando suspended es true; hoy el único valor es payment_failed. |
billing.mode | string | Régimen de facturación del provider: STRIPE si contrata y paga desde el portal de Speaknosis, PARTNER_SELF si le factura su provider padre. |
billing.managedBy | string | Nombre del provider que gestiona el cobro cuando mode es PARTNER_SELF. null en el flujo Stripe. |
meta.total | number | Total de doctores del provider, útil para calcular páginas. |
meta.offset | number | Offset aplicado en la consulta. |
meta.limit | number | Límite aplicado. Si no se envió limit, coincide con total (se trajo todo). |
ℹ️
suspensionsegún el régimen de facturación. El corte por falta de pago solo existe en el flujo Stripe, así que para un provider conbilling.mode = "PARTNER_SELF"el camposuspensionno viene. Si tu integración lo lee, trata su ausencia como "sin corte".
Ejemplo de la respuesta para un provider con facturación del padre (solo la parte que cambia):
{
"response": {
"doctors": [],
"billing": {
"mode": "PARTNER_SELF",
"managedBy": "PartnerX"
},
"meta": { "total": 0, "offset": 0, "limit": 20 }
}
}
Consultar licencias del provider
Permite consultar el resumen de licencias del provider, o filtrar por usuario específico.
👨🏻💻 QA:
GET https://api-qa.speaknosis.com/payments/v1/subscription/provider/{providerId}/license
🏥 Producción:
GET https://api-prod.speaknosis.com/payments/v1/subscription/provider/{providerId}/license
| Parámetro | Ubicación | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
providerId | path | string | Sí | ID interno del provider. |
userId | query | string | No | ID externo del usuario para filtrar sus licencias específicas. |
type | query | string | No | Filtra por plan: BASIC, ELITE, TRIAL o USAGE. Devuelve el detalle de licencias, no el resumen. |
Sin userId — Resumen general
Retorna el total de licencias contratadas, en uso y disponibles por plan, más información de downgrade programado si existe.
Respuesta exitosa (200 OK):
{
"success": true,
"code": 200,
"response": {
"summary": [
{
"type": "BASIC",
"total": 10,
"used": 7,
"available": 3
},
{
"type": "ELITE",
"total": 5,
"used": 5,
"available": 0
}
],
"scheduledDowngrade": {
"quantity": {
"8": 6,
"9": 3
},
"effectiveDate": "2026-07-01T00:00:00.000Z"
},
"suspension": {
"suspended": false
}
}
}
ℹ️
scheduledDowngradeesnullsi no hay downgrade programado.suspension.suspendedindica si el acceso del provider está cortado por falta de pago, y lleva unreason(hoy solopayment_failed) cuando estrue.
Providers con facturación del padre. Cuando el provider factura a través de su padre
(billing.mode = "PARTNER_SELF", ver Consultar usuarios con sus
licencias), el downgrade programado y el corte por falta de
pago no aplican: en su lugar la respuesta trae el estado del contrato.
{
"success": true,
"code": 200,
"response": {
"summary": [
{
"type": "BASIC",
"total": 1,
"used": 0,
"available": 1
}
],
"status": "ACTIVE"
}
}
| Campo | Tipo | Descripción |
|---|---|---|
status | string | Estado del contrato: ACTIVE si está vigente (incluido el período de prueba), INACTIVE si el padre lo dio de baja. Solo se devuelve en el flujo PARTNER_SELF. |
En ese flujo no vienen scheduledDowngrade ni suspension. Trata la ausencia de cada uno como
"no hay downgrade programado" y "sin corte", respectivamente.
summary lista los planes que tienen licencias: un plan dado de baja con quantity: 0 deja de
aparecer, y mientras el centro tenga licencias de prueba aparece también una fila TRIAL.
Centros en modalidad por consumo. Si el centro contrató por consumo (👉 ver
Self-billing), no tiene cupos: el resumen trae
un único plan USAGE, sin total ni disponibles.
{
"success": true,
"code": 200,
"response": {
"contractModel": "USAGE",
"summary": [
{
"type": "USAGE",
"total": null,
"used": 15,
"available": null
}
],
"scheduledSwitch": {
"quantity": { "9": 10 },
"effectiveDate": "2026-12-01 00:00:00"
},
"status": "ACTIVE"
}
}
| Campo | Tipo | Descripción |
|---|---|---|
contractModel | string | USAGE para un centro por consumo. Solo se devuelve en esa modalidad. |
summary[].used | number | Médicos que ya tienen su licencia de consumo. Cada médico la recibe al abrir la herramienta de grabación por primera vez. |
summary[].total / summary[].available | null | En consumo no hay cupos. |
scheduledSwitch | object | null | Paso a cupos pendiente: cantidades por plan (8 = BASIC, 9 = ELITE) y fecha en que se aplica, que es la próxima renovación (UTC, formato AAAA-MM-DD HH:MM:SS). null si no hay ninguno. |
status | string | ACTIVE o INACTIVE, igual que en el resto del flujo PARTNER_SELF. |
Con userId — Licencia de un usuario específico
Ejemplo:
GET .../license?userId=tu-id-externo-1
Respuesta exitosa (200 OK):
{
"success": true,
"code": 200,
"response": {
"licenses": [
{
"id": 301,
"type": "BASIC",
"limit": 100,
"currentUsage": 12,
"isEnabled": true,
"createdAt": "2026-01-15 10:00:00",
"updatedAt": "2026-05-20 08:30:00",
"lastUsedAt": "2026-05-20 08:30:00",
"expiresAt": "2026-06-01T00:00:00.000Z",
"userId": "tu-id-externo-1",
"name": "Juan",
"lastname": "Pérez"
}
],
"suspension": {
"suspended": false
}
}
}
ℹ️ Igual que en el resumen general,
suspensionno viene para providers conbilling.mode = "PARTNER_SELF".
Webhook del partner (self-billing)
Solo para providers padre habilitados en régimen self-billing. Permite notificar a Speaknosis las altas, los cambios de cantidad y las bajas de sus centros.
👨🏻💻 QA:
POST https://api-qa.speaknosis.com/payments/v1/partner/webhook
🏥 Producción:
POST https://api-prod.speaknosis.com/payments/v1/partner/webhook
Evento (type) | Para qué |
|---|---|
checkout.session.completed | Alta del contrato del centro, por cupos o por consumo |
customer.subscription.updated | Cambio de cantidad, cambio de modalidad (cupos ↔ consumo), cambio de consultas incluidas, actualización de datos del contrato o reactivación |
customer.subscription.deleted | Baja: corta el acceso sin borrar datos |
Para la modalidad por consumo, data.items lleva un único ítem { "planType": "USAGE", "includedConsultations": 300 }.
El contrato completo —envelope, campos, idempotencia, orden de los eventos, códigos de respuesta, formato de error y política de reintentos— está en 👉 Self-billing.
Un centro que no está configurado para self-billing responde 409.
Códigos de error comunes
| Código | Descripción |
|---|---|
400 | Cuerpo o parámetros inválidos. |
401 | Token de autenticación faltante o inválido. |
403 | Sin permisos para acceder a este recurso. |
404 | Recurso no encontrado (provider, usuario o licencia inexistente). |
409 | La operación no aplica al régimen de facturación del centro (por ejemplo, contratar por Stripe un centro en self-billing, o enviar al webhook del partner un centro que no lo está). También la devuelve la desasociación de licencias sobre un centro por consumo: en esa modalidad el acceso de un médico se corta dando de baja a su usuario. |
422 | Cuerpo válido pero inaplicable al estado actual del centro. No siempre se resuelve reintentando: en el webhook del partner, la mayoría de los casos requiere una acción (ver Self-billing — Respuestas y errores). Incluye pedir una licencia USAGE para un centro que no está por consumo o que está dado de baja. |