CBus API for Developers v1
Bienvenido a la documentación oficial de la CBus API for Developers v1. CBus es una plataforma líder en la gestión de flotas y venta de boletos para empresas de autotransporte.
Esta API RESTful proporciona a los desarrolladores un conjunto de endpoints para interactuar directamente con el sistema de ventas de CBus. El objetivo es permitir la integración de sistemas de terceros, agencias de viaje en línea (OTAs) y aplicaciones personalizadas para:
- Consultar orígenes, destinos y horarios en tiempo real.
- Ver la disponibilidad y precios de asientos en corridas específicas.
- Gestionar el ciclo de vida completo de una reservación, desde el apartado temporal hasta la confirmación del pago.
Sigue esta guía para entender el flujo de autenticación, los entornos disponibles y cada uno de los endpoints.
Autenticación
La autenticación de la API difiere entre los entornos de Sandbox y Producción.
Entorno de Sandbox (Pruebas)
Para el entorno de Sandbox, no es necesario enviar el header Authorization. Las peticiones funcionarán de forma abierta para facilitar las pruebas. Solo se requiere el header Accept.
Accept: application/json
Entorno de Producción
Para el entorno de Producción, el header Authorization es obligatorio. Esta Api_key (Bearer Token) deberá ser solicitada directamente al equipo de CBus y estará asociada al plan de API de la instancia.
Authorization: Bearer
Accept: application/json
Entorno Sandbox
Todas las URL de los endpoints listados a continuación deben usar la siguiente URL base correspondiente al entorno de pruebas:
- URL Base:
https://demo.cbus.com.mx - Prefijo:
/api/dev/v1 - URL Completa de Ejemplo:
https://demo.cbus.com.mx/api/dev/v1/origenes
Flujo General de Venta
El proceso de reserva y confirmación de un boleto sigue un flujo específico de varios pasos. No puedes confirmar un boleto sin antes haberlo seleccionado y reservado.
- Búsqueda:
GET /api/dev/v1/origenes-> Obtiene la lista de todas las ciudades de origen.GET /api/dev/v1/destinos-> Obtiene los destinos disponibles desde un origen.GET /api/dev/v1/horarios-> Obtiene las corridas y horarios para una ruta y fecha.
- Selección:
POST /api/dev/v1/asientos-> Muestra el mapa de asientos disponibles para un horario.POST /api/dev/v1/seleccionar-asiento-> Aparta temporalmente un asiento. Devuelve una clave única.
- Reserva:
POST /api/dev/v1/reservar-> Agrupa todas lasclavesde asientos (ida y regreso) bajo los datos de un cliente. Devuelve una clave_global.
- Confirmación:
POST /api/dev/v1/confirmar-> Finaliza la compra usando laclave_globaly marca los boletos como "pagados".
- (Opcional) Cancelación:
POST /api/dev/v1/deseleccionar-asiento-> Libera un asiento que fue apartado temporalmente.
Planes de API y Límites de Tasa (Rate Limiting)
El acceso a la API está sujeto a límites de tasa (Rate Limiting) que dependen del plan de API adquirido para la instancia de CBus WEB. La Api_key asociada a la instancia de la empresa de transporte tendrá el límite de solicitudes por minuto (x-ratelimit-limit) correspondiente a su plan.
| Plan de API | Límite por Minuto (x-ratelimit-limit) |
Costo Mensual (USD) |
|---|---|---|
| Plan Básico (Default) | 8 | Incluido en la instancia |
| API Plan 1 | 15 | $10.00 |
| API Plan 2 | 30 | $18.00 |
| API Plan 3 | 90 | $30.00 |
Si se excede el límite correspondiente al plan de la instancia, la API comenzará a responder con un código de error 429 Too Many Requests. Para adquirir o cambiar de plan, es necesario contactar al equipo de ventas de CBus.
Cuotas de Uso (Quotas)
Además de los límites de tasa por minuto, el uso de la API está sujeto a una **cuota general** basada en el **número total de claves/folios emitidos** (incluyendo apartados, cancelados, reservados y confirmados/pagados) permitidas por el plan de suscripción de la empresa de transporte con CBus.
Cada intento de seleccion de asiento emite una clave/folio
Si se excede la cuota total de reservaciones activas permitida por el plan, la API **rechazará cualquier nueva conexión o solicitud** que intente crear o confirmar una reservación, independientemente de los límites de tasa por minuto.
Es responsabilidad del cliente (la empresa de transporte) y del desarrollador estar al tanto de los límites específicos de su plan contratado.
Responsabilidades del Desarrollador
- Crear (imagen/pdf) el boleto (comprobante o confirmación de compra) al cliente final.
- Notificar al cliente final sobre el estado de su reservación o compra (ej. por email, SMS, WhatsApp).
- Enviar notificaciones activas (push, email, etc.) a la empresa de transporte sobre nuevas ventas o reservaciones confirmadas.
Sin embargo, toda reservación creada o confirmada a través de la API queda registrada en el sistema administrativo de CBus y puede ser consultada por el personal de la empresa de transporte a través de los reportes y módulos correspondientes.
Es responsabilidad del desarrollador que integra esta API implementar la lógica necesaria para:
- Generar y enviar el comprobante de compra (boleto) al cliente final.
- Establecer los mecanismos de notificación al cliente que considere necesarios.
GET /api/dev/v1/origenes
Obtiene una lista de todas las escalas (orígenes) disponibles.
Parámetros de Query
Ninguno.
Respuesta Exitosa (200 OK)
[
{
"id": 5,
"nombre": "CDMX",
"lat": null,
"lng": null,
"abordajes": []
},
{
"id": 1,
"nombre": "GPE",
"lat": null,
"lng": null,
"abordajes": []
},
{
"id": 3,
"nombre": "OAXACA",
"lat": null,
"lng": null,
"abordajes": []
},
{
"id": 4,
"nombre": "PUEBLA",
"lat": null,
"lng": null,
"abordajes": []
},
{
"id": 2,
"nombre": "TEPIC",
"lat": null,
"lng": null,
"abordajes": []
}
]
GET /api/dev/v1/destinos
Obtiene los destinos disponibles basado en un nombre de origen.
Parámetros de Query
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
q |
string | Sí | Nombre exacto del origen. Ej: DMX. |
Respuesta Exitosa (200 OK)
[
{
"id": 3,
"nombre": "OAXACA",
"lat": null,
"lng": null,
"abordajes": []
},
{
"id": 4,
"nombre": "PUEBLA",
"lat": null,
"lng": null,
"abordajes": []
}
]
GET /api/dev/v1/horarios
Obtiene los horarios disponibles para una ruta y fechas específicas.
Parámetros de Query
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
origen |
string | Sí | Nombre de la escala de origen (Ej: CDMX). |
destino |
string | Sí | Nombre de la escala de destino (Ej: OAXACA). |
fecha_ida |
date | Sí | Fecha de salida (Formato: YYYY-MM-DD). |
fecha_regreso |
date | No | Fecha de regreso (Formato: YYYY-MM-DD). |
Respuesta Exitosa (200 OK)
{
"ida": [
{
"id": 3,
"corrida_id": 2,
"ab_ba": "ba",
"ab": 1,
"hora_salida": "14:00:00",
"fcomp": 0,
"fecha": "2025-10-30",
"minutos_trayecto": 480,
"activo": 1,
"aplicar_venta_linea": 1,
"corrida": {
"id": 2,
"nombre": "Oaxaca-CDMX",
"mostrar_pv": 1,
"aplicar_en_reservaciones": 1,
"aplicar_venta_linea": 1,
"extraordinaria": 0,
"fechas_deshabilitadas": [],
"activo": 1
},
"origen": {
"id": 5,
"nombre": "CDMX",
"hora": "2:00 pm"
},
"destino": {
"id": 3,
"nombre": "OAXACA",
"hora": "10:00 pm"
},
"disponibilidad": {
"asientos_disponibles": 20,
"asientos_ocupados": 0,
"total_asientos": 20,
"ultima_actualizacion": "2025-10-29T00:00:00.000000Z"
}
}
],
"regreso": [
{
"id": 3,
"corrida_id": 2,
"ab_ba": "ab",
"ab": 1,
"hora_salida": "14:00:00",
"fcomp": 0,
"fecha": "2025-11-01",
"minutos_trayecto": 480,
"...": "..."
}
]
}
POST /api/dev/v1/asientos
Obtiene el mapa de asientos (layout) de un autobús para un horario específico, indicando cuáles están disponibles, ocupados o bloqueados.
Cuerpo de la Solicitud (JSON)
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
ab |
integer | Sí | Dirección del viaje (Usar 1 o 0). |
corrida |
integer | Sí | ID de la corrida (obtenido de /horarios). |
origen |
integer | Sí | ID de la escala de origen. |
destino |
integer | Sí | ID de la escala de destino. |
horario |
integer | Sí | ID del horario (obtenido de /horarios). |
redondo |
integer | Sí | 0 para viaje sencillo. |
fecha |
date | Sí | Fecha del viaje (Formato: YYYY-MM-DD). |
Ejemplo de Solicitud
{
"ab": 1,
"corrida": 2,
"origen": 5,
"destino": 3,
"horario": 3,
"redondo": 0,
"fecha": "2025-10-31"
}
Respuesta Exitosa (200 OK)
{
"escalas": {
"origen": "CDMX",
"destino": "OAXACA"
},
"precio": [ ... ],
"hora": {
"salida": "2:00 pm",
"llegada": "10:00 pm"
},
"asientos": [
{
"id": 21,
"numero": 1,
"corrida_id": 2,
"categoria": null,
"categoria_abreviacion": null,
"forma_calculo_valor": null,
"valor": null,
"reservaciones": [],
"disponible": true,
"fcomp": 0,
"horario_id": 3,
"origen_id": 5,
"destino_id": 3,
"ab": false,
"fecha_reservacion": "2025-10-31",
"prefix": "000"
},
{
"id": 22,
"numero": 2,
"corrida_id": 2,
"..." : "..."
}
],
"total_asientos": 20,
"abordajes": [],
"fecha_reservacion": "31-10-2025",
"fcomp": 0
}
Nota Importante sobre la Respuesta
La respuesta de este endpoint es crucial. Cada objeto dentro del array asientos contiene los IDs exactos que necesitarás para el siguiente paso (/api/dev/v1/seleccionar-asiento).
Debes capturar y utilizar los siguientes valores del asiento que elija el usuario:
id(Se usará comoasiento)corrida_id(Se usará comocorrida)horario_id(Se usará comohorario)origen_id(Se usará comoorigen)destino_id(Se usará comodestino)ab(Se usará tal cual, es fundamental)
POST /api/dev/v1/seleccionar-asiento
Aparta temporalmente un asiento específico. Este asiento se bloqueará por un tiempo limitado (ej. 10 minutos) y se generará una clave única para esta selección.
Cuerpo de la Solicitud (JSON)
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
corrida |
string | Sí | ID de la corrida (obtenido de /asientos). |
origen |
string | Sí | ID de la escala de origen (obtenido de /asientos). |
destino |
string | Sí | ID de la escala de destino (obtenido de /asientos). |
horario |
string | Sí | ID del horario (obtenido de /asientos). |
asiento |
string | Sí | ID del asiento a seleccionar (obtenido de /asientos). |
tipo_persona |
string | Sí | adulto o nino. |
ab |
integer | Sí | Dirección del viaje (1 o 0). Importante: Debes usar el valor ab (true/false o 1/0) que se obtuvo del objeto asiento en la respuesta de /api/dev/v1/asientos. |
fecha |
date | Sí | Fecha del viaje (Formato: YYYY-MM-DD). |
canal_venta |
string | Sí | Identificador del canal (Ej: "TU CANAL"). |
observaciones |
string | No | Notas adicionales para este asiento. |
Ejemplo de Solicitud
{
"corrida":"2",
"origen":"5",
"destino":"3",
"horario":"3",
"asiento":"23",
"tipo_persona":"adulto",
"ab": 0,
"fecha": "2025-10-31",
"canal_venta":"TU CANAL",
"observaciones": "Texto que saldrá en los reportes y boleto"
}
Respuesta Exitosa (200 OK)
{
"cliente": "N/A",
"asiento_id": "23",
"horario_id": "3",
"corrida_id": "2",
"redondo": null,
"fecha_reservacion": "2025-10-31",
"fcomp": 0,
"origen_id": "5",
"destino_id": "3",
"estatus": "apartado",
"tipo_persona": "adulto",
"clave": "YGBD2D",
"ab": false,
"is_plt_reservacion": true,
"canal_venta": "TU CANAL",
"updated_at": "2025-10-30T02:29:45.000000Z",
"created_at": "2025-10-30T02:29:45.000000Z",
"id": 3,
"abordajes": [],
"expiracion": {
"fecha_expira": "2025-10-29T20:39:45.666850Z",
"fecha_expira_diff_h": "en 9 minutos",
"fecha_expira_mins": 9
},
"observaciones": "Texto que saldrá en los reportes y boleto"
}
Nota Importante sobre la clave
La clave (ej: "YGBD2D") devuelta por este endpoint es **temporal** y se usa para agrupar el asiento en el siguiente paso (/api/dev/v1/reservar).
Al ser usada en /reservar, la API generará una nueva clave final (con caracteres diferentes). Además, si se trata de un viaje redondo, las claves finales tendrán un sufijo: -1 para el viaje de ida y -2 para el viaje de regreso.
POST /api/dev/v1/deseleccionar-asiento
Libera un asiento que fue apartado (con /seleccionar-asiento) antes de que expire su tiempo. Esto cancela la pre-reserva de ese asiento individual.
Cuerpo de la Solicitud (JSON)
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
clave |
string | Sí | La clave única del asiento que se obtuvo en /seleccionar-asiento. |
Ejemplo de Solicitud
{
"clave": "YGBD2D"
}
Respuesta Exitosa (200 OK)
{
"message": "Clave cancelada"
}
POST /api/dev/v1/reservar
Asigna los datos de un cliente a uno o más asientos apartados (claves). Esto agrupa todas las selecciones de asientos bajo una clave_global y prepara la orden para el pago.
Cuerpo de la Solicitud (JSON)
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
lang |
string | No | Idioma (ej: "es"). |
cliente |
object | Sí | Objeto que contiene la información del cliente. |
cliente.nombre |
string | Sí | Nombre completo del cliente. |
cliente.celular |
string | No | Número de celular del cliente. |
cliente.email |
string | No | Email del cliente (debe ser un email válido si se envía). |
reservaciones |
object | Sí | Objeto que contiene los arrays de asientos de ida y (opcionalmente) regreso. |
reservaciones.ida |
array | Sí | Array de objetos, cada uno con la clave temporal de un asiento (obtenida de /seleccionar-asiento). |
...ida[].clave |
string | Sí | Clave temporal del asiento. |
...ida[].observaciones |
string | No | Observaciones específicas para este asiento/pasajero. |
reservaciones.regreso |
array | No | Array de objetos para el viaje de regreso. Requerido si es viaje redondo. |
...regreso[].clave |
string | Sí | Clave temporal del asiento de regreso. |
...regreso[].observaciones |
string | No | Observaciones específicas para este asiento/pasajero. |
abordajes |
object | No | Objeto con los IDs de los puntos de abordaje (puede ser requerido según la configuración). |
abordajes.ida |
integer | No | ID del punto de abordaje para el viaje de ida. |
abordajes.regreso |
integer | No | ID del punto de abordaje para el viaje de regreso. |
Ejemplo de Solicitud (Viaje Redondo)
{
"cliente": {
"nombre": "Fulanito Pérez",
"celular": "9585846497",
"email": "alejandro@cbus.com.mx"
},
"reservaciones": {
"ida": [
{
"clave": "YGBD2D",
"observaciones": "Pasajero adulto, ventana"
}
],
"regreso": [
{
"clave": "HJD8K1",
"observaciones": "Pasajero adulto, pasillo"
}
]
},
"abordajes": {
"ida": 1,
"regreso": 3
}
}
Respuesta Exitosa (200 OK)
{
"clave_global": "YBR7HRGL",
"divisa": "MXN",
"cliente": {
"nombre": "Fulanito Pérez",
"telefono": "9585846497"
},
"primer_viaje": false,
"reservaciones": {
"ida": [
{
"id": 3,
"cliente": "Fulanito Pérez",
"celular": "9585846497",
"email": "alejandro@cbus.com.mx",
"redondo": true,
"clave": "YGBD2D-1",
"observaciones": "Pasajero adulto, ventana"
}
],
"regreso": [
{
"id": 4,
"cliente": "Fulanito Pérez",
"celular": "9585846497",
"email": "alejandro@cbus.com.mx",
"redondo": true,
"clave": "YGBD2D-2",
"observaciones": "Pasajero adulto, pasillo"
}
]
},
"expiracion": {
"fecha_expira": "2025-10-30T06:31:37.360615Z",
"fecha_expira_diff_h": "en 3 horas",
"fecha_expira_mins": 120
}
}
POST /api/dev/v1/confirmar
Confirma la reservación y marca todos los boletos asociados a la clave_global como "pagados". Este es el último paso del flujo. La respuesta incluye detalles de los boletos confirmados, información de las sucursales/terminales relevantes al origen del viaje, y los datos fiscales de la empresa transportista.
Cuerpo de la Solicitud (JSON)
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
clave_global |
string | Sí | La clave obtenida en /reservar. |
tipo_pago |
string | Sí | efectivo, TD, TC, deposito, transferencia. |
total |
numeric | Sí | El monto total de la venta con descentos de comisión. |
total_original |
numeric | Sí | El monto total de la venta sin descuentos de comisi´pn. |
observaciones |
string | No | Observaciones generales de la venta que aplicarán para cada una de las claves. (Se recomienda colocar aquí los folios de transacción o de control interno para aclaraciones). Ten en cuenta que al registrar nuevas observaciones, estas sobrescribirán las observaciones previas asignadas desde otros endpoints. Esta información sale en reportes y boletos del cliente. |
Ejemplo de Solicitud
{
"clave_global": "YBR7HRGL",
"tipo_pago": "TD",
"total": 700,
"total_original": 776
"observaciones": "Folio de transacción MTI1MzU0Ng | Venta relizada por desarrollador independiente"
}
Respuesta Exitosa (200 OK)
La respuesta contiene un array reservaciones con los boletos confirmados, un array sucursales con la información de las terminales asociadas al origen del primer boleto (útil para mostrar dirección, teléfonos, ubicación), y un objeto empresa con los datos fiscales del transportista.
{
"reservaciones": [
{
"id": 3,
"cliente": "Fulanito Pérez",
"celular": "9585846497",
"email": "alejandro@cbus.com.mx",
"redondo": false,
"costo_boleto": 700,
"clave": "YGBD2D", // Clave final del boleto
"fecha_reservacion": "2025-10-31",
"fcomp": 0,
"estatus": "pagado",
"costo_boleto_original": 776,
"is_plt_reservacion": 1,
"tipo_persona": "adulto",
"corrida_id": 2,
"asiento_id": 23,
"horario_id": 3,
"origen_id": 5,
"destino_id": 3,
"ab": 0,
"created_at": "2025-10-30T02:31:37.000000Z",
"updated_at": "2025-10-30T02:31:46.000000Z",
"abordaje_id": null,
"canal_venta": "TU CANAL",
"observaciones": "Folio de transacción MTI1MzU0Ng | Venta relizada por desarrollador independiente",
"origen": {
"id": 5,
"nombre": "CDMX"
},
"destino": {
"id": 3,
"nombre": "OAXACA"
},
"abordaje": null
}
// ... más boletos si la clave_global agrupaba varios
],
"sucursales": [ // Información de terminales/sucursales asociadas al origen
{
"nombre": "Terminal Central CDMX",
"direccion": "Av. Principal 123",
"estado": "Ciudad de México",
"comision": null, // Campo interno, puede ignorarse
"codigo_postal": "01234",
"telefono_1": "5512345678",
"telefono_2": null,
"telefono_3": null,
"descripcion": "Terminal principal en CDMX",
"pais": "México",
"colonia": "Centro",
"localidad": null,
"municipio": "Cuauhtémoc",
"lat": "19.4326",
"lng": "-99.1332"
}
// ... puede haber más sucursales relacionadas al origen
],
"empresa": { // Datos fiscales de la empresa transportista
"nombre": "CBus DEMO",
"rfc": "ALEJANDRO TONATIUH BERNAL ZAMORANO",
"razon_social": null,
"calle": "Jilguero 57",
"tel_1": "",
"tel_2": "",
"tel_3": "",
"cp": "63170"
},
"clave_global": "YBR7HRGL"
}
Manejo de Errores
La API utiliza códigos de estado HTTP estándar para indicar el éxito o fracaso de una solicitud.
422 Unprocessable Entity (Entidad no procesable)
Este es el error más común. Se devuelve cuando la solicitud es sintácticamente correcta, pero no se puede procesar debido a errores de validación o de lógica de negocio.
A) Error de Validación de Campos (Formato Laravel)
Ocurre cuando faltan campos requeridos o los datos no tienen el formato correcto (ej. un email inválido, una fecha mal formada).
{
"message": "The given data was invalid.",
"errors": {
"corrida": [
"El campo corrida es obligatorio."
],
"horario": [
"El campo horario es obligatorio."
],
"fecha": [
"El campo fecha debe ser una fecha posterior o igual a hoy."
]
}
}
B) Error de Lógica de Negocio (Formato Personalizado)
Ocurre cuando los datos son válidos, pero rompen una regla de negocio (ej. el asiento ya está ocupado, el origen y destino son el mismo, la clave de reservación expiró).
{
"message": "El asiento ya no está disponible"
}
{
"errors": {
"message": "Has execedido el tiempo de reservación o las claves no existen, inicia de nuevo."
}
}
404 Not Found (No Encontrado)
Este error ocurre cuando se intenta acceder a un recurso que no existe, como una URL de endpoint incorrecta.
{
"message": "Not Found"
}
500 Internal Server Error (Error Interno del Servidor)
Este error indica que algo salió mal en el servidor de forma inesperada (un error en el código, fallo de la base de datos). Este error debe ser reportado al equipo de desarrollo.
{
"message": "Server Error"
}