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
Todos los endpoints requieren autenticación mediante una API Key, tanto en Sandbox como en Producción. La API Key se obtiene contactando al equipo de CBus y está asociada a la instancia del transportista.
Puedes enviar la API Key de cualquiera de estas tres formas (elige una):
Opción 1 — Header Authorization: Bearer (recomendado)
Authorization: Bearer TU_API_KEY
Accept: application/json
Opción 2 — Header X-API-Key
X-API-Key: TU_API_KEY
Accept: application/json
Opción 3 — Query parameter api_key
GET /api/dev/v1/origenes?api_key=TU_API_KEY
Modo Sandbox vs Modo Producción
Cada API Key tiene un flag is_production. Las keys en modo producción tienen
una restricción adicional: el endpoint /confirmar exige que la key tenga configuradas
restricciones de IP o dominio permitido en el panel de CBus. Sin esas restricciones, /confirmar
responderá con 403. Las keys en modo sandbox no aplican esta restricción.
Errores de autenticación
| Código | Mensaje | Causa |
|---|---|---|
| 401 | Unauthorized |
No se envió API Key o la key no existe. |
| 403 | IP address is not allowed. |
La IP del cliente no está en la lista blanca de la key. |
| 403 | Origin is not allowed. |
El header Origin de la solicitud no está permitido. |
| 429 | Rate limit exceeded. |
Se superó el límite de solicitudes por minuto del plan. |
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.POST /api/dev/v1/boletos/cancelar-> Cancela un boleto ya pagado.
- (Opcional) Facturación CFDI 4.0:
POST /api/dev/v1/facturacion/emitir-> Timbra una factura para los boletos de unaclave_global.POST /api/dev/v1/facturacion/cancelar-> Marca una factura como cancelada localmente.
- (Opcional) Paquetería:
POST /api/dev/v1/envios/preregistrar-> Pre-registra un envío.GET /api/dev/v1/envios/rastreo-> Consulta el estado y seguimiento de un envío por clave.
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,
"...": "..."
}
]
}
GET /api/dev/v1/sucursales
Obtiene el listado completo de sucursales con su información detallada.
Parámetros de Query
Ninguno.
Respuesta Exitosa (200 OK)
{
"success": true,
"data": [
{
"id": 1,
"nombre": "Terminal Central",
"direccion": "Av. Principal 123",
"estado": "Jalisco",
"colonia": "Centro",
"localidad": "Guadalajara",
"municipio": "Guadalajara",
"pais": "MX",
"codigo_postal": "44100",
"telefono_1": "3312345678",
"telefono_2": null,
"telefono_3": null,
"lat": "20.659698",
"lng": "-103.349609",
"descripcion": "Terminal principal de autobuses",
"datos_ticket_id": null,
"pais_nombre": "México"
}
],
"total": 1
}
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)
GET /api/dev/v1/render
Obtiene una representación visual (HTML) del autobús con la disposición de sus asientos y su estado de disponibilidad en tiempo real.
Este endpoint es ideal para ser mostrado en un <iframe> dentro de la aplicación del
socio comercial.
Parámetros de Consulta (Query Params)
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
api_key |
string | Sí | Tu clave de API (necesaria si se usa en iframe sin headers). |
corrida |
int | Sí | ID de la corrida. |
horario |
int | Sí | ID del horario. |
origen |
int | Sí | ID de la escala de origen. |
destino |
int | Sí | ID de la escala de destino. |
fecha |
string | Sí | Fecha del viaje (YYYY-MM-DD). |
Ejemplo de Uso (Iframe)
<iframe src="https://cbus.com.mx/api/dev/v1/render?api_key=TU_API_KEY&corrida=2&horario=3&origen=5&destino=3&fecha=2025-10-31" width="100%" height="600" frameborder="0"></iframe>
Respuesta
Devuelve un documento HTML completo con estilos embebidos que renderiza el mapa del autobús.
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",
"precio": 350.00
}
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. |
generar_checkout_url |
boolean | No | Si es true, el sistema intentará generar un enlace de pago (Stripe/MercadoPago) y lo devolverá en checkout_url. Por defecto false. |
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
},
"total": 1400.00,
"total_original": 1552.00,
"checkout_url": null,
"checkout_status": null,
"checkout_requested": false,
"checkout_error": null,
"enviar_link_pago": false
}
Errores Posibles (422)
| Mensaje | Causa |
|---|---|
Has excedido el tiempo de reservación o las claves no existen, inicia de nuevo. |
Las claves ya expiraron o no existen en el sistema. |
Existen claves duplicadas entre salida y regreso. |
Una misma clave de asiento aparece en el array de ida y de regreso. |
Tu acceso ha sido restringido... |
El teléfono o email del cliente está en la lista negra del sistema. |
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 | No | El monto total de la venta con descuentos de comisión. Si se envía, el servidor lo valida contra su cálculo propio (tolerancia de ±$1). Si difiere, retorna error 422. |
total_original |
numeric | No | El monto total de la venta sin descuentos de comisión. Si se envía, se valida igual que total. |
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,
"boleto_url": "https://demo.cbus.com.mx/boleto/compartir/YGBD2D"
}
// ... 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"
}
Errores Posibles
| Código | Mensaje | Causa |
|---|---|---|
| 401 | Acceso denegado. API key no válida. |
La API Key no se encontró en el sistema. |
| 403 | Acceso denegado. Tu API Key está en MODO PRODUCCIÓN y requiere que configures restricciones... |
La key está en modo producción pero no tiene restricciones de IP/dominio configuradas en el panel. |
| 422 | Tus boletos han sido cancelados, ya no es posible continuar. |
Los boletos de la clave_global ya no están en estatus apartado (expiraron o fueron cancelados). |
| 422 | El total enviado no coincide con el monto calculado en el servidor. |
El campo total enviado difiere en más de $1 del total calculado por el servidor. |
| 422 | El total original enviado no coincide con el monto calculado en el servidor. |
El campo total_original enviado difiere en más de $1 del calculado por el servidor. |
POST /api/dev/v1/boletos/actualizar-nombre
Permite asignar el nombre del pasajero a un boleto que ya fue pagado. Este endpoint está diseñado para flujos donde el pago se confirma antes de que el nombre del pasajero esté disponible. Solo puede ejecutarse una sola vez por boleto y únicamente si la fecha de viaje aún no ha pasado.
- El boleto debe tener estatus
pagado. - La fecha de viaje no debe haber pasado.
- El nombre solo puede asignarse una vez. Intentos posteriores serán rechazados.
Cuerpo de la Solicitud (JSON)
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
clave |
string | Sí | La clave única del boleto individual (obtenida en /confirmar dentro del array reservaciones). |
nombre |
string | Sí | Nombre completo del pasajero. Máximo 100 caracteres. |
Ejemplo de Solicitud
{
"clave": "YGBD2D",
"nombre": "Juan Pérez García"
}
Respuesta Exitosa (200 OK)
{
"message": "Nombre actualizado correctamente.",
"clave": "YGBD2D",
"nombre": "Juan Pérez García"
}
Errores Posibles (422)
| Mensaje | Causa |
|---|---|
El boleto no está pagado. |
El estatus del boleto no es pagado. |
La fecha de viaje ya pasó, no es posible actualizar el nombre. |
La fecha de viaje registrada en el boleto es anterior a hoy. |
El nombre del pasajero ya fue asignado y no puede modificarse nuevamente. |
El cambio de nombre ya fue realizado previamente en este boleto. |
POST /api/dev/v1/boletos/cancelar
Permite cancelar un boleto que ya fue pagado o confirmado. Al ejecutar este endpoint, se cambiará el estatus del boleto a cancelado, y si el pago fue realizado por tarjeta (Stripe), se procesará la cancelación en la pasarela. Además se hará la devolución pertinente de puntos premia si el pasajero contaba con ellos.
- El sistema validará que la hora de salida de la corrida no haya expirado para poder cancelar.
- Solo boletos con estatus
pagadopueden ser cancelados con este endpoint. (Para boletos en estatusapartadousar/deseleccionar-asiento).
Cuerpo de la Solicitud (JSON)
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
clave |
string | Sí | La clave única del boleto individual. |
motivo |
string | Sí | Motivo por el cual se cancela el boleto. Máximo 255 caracteres. |
Ejemplo de Solicitud
{
"clave": "YGBD2D",
"motivo": "Cancelación solicitada por el cliente."
}
Respuesta Exitosa (200 OK)
{
"message": "Boleto cancelado correctamente.",
"clave": "YGBD2D"
}
Errores Posibles (422)
| Mensaje | Causa |
|---|---|
El boleto ya se encuentra cancelado. |
El boleto tiene estatus cancelado previamente. |
Solo se pueden cancelar boletos con estatus pagado. |
El boleto no está pagado. |
Ya no puedes cancelar esta reservación (El tiempo de la corrida ha expirado). |
El tiempo límite para cancelar este boleto ha expirado debido a la hora de salida. |
POST /api/dev/v1/envios/preregistrar
Permite a socios comerciales dar de alta un envío en estado de "Pre-registro". Esta modalidad no requiere conocer escalas ni costos, ya que el sistema administrativo lo completará cuando el paquete sea entregado físicamente en una sucursal.
Nota: Este endpoint no genera ticket digital ni PDF.
Cuerpo de la Solicitud (JSON)
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
remitente |
string | Sí | Nombre completo de quien envía. |
telefono_remitente |
string | Sí | Teléfono de contacto del remitente. |
destinatario |
string | Sí | Nombre completo de quien recibe. |
telefono_destinatario |
string | Sí | Teléfono de contacto del destinatario. |
descripcion |
string | Sí | Contenido del paquete. |
modalidad_pago |
string | Sí | por_pagar (el cliente paga al dejar) o por_cobrar (paga al
recibir). |
Ejemplo de Solicitud
{
"remitente": "Juan Pérez",
"telefono_remitente": "9511234567",
"destinatario": "María López",
"telefono_destinatario": "9519876543",
"descripcion": "Caja con 5kg de café",
"modalidad_pago": "por_pagar"
}
Respuesta Exitosa (201 Created)
{
"success": true,
"clave": "ENV-784521",
"mensaje": "Pre-registro guardado. Use esta clave al entregar el paquete en sucursal."
}
GET /api/dev/v1/envios/rastreo
Consulta el estado actual y el historial de seguimiento de un envío (paquetería) a partir de su clave única. Devuelve los datos del envío, las sucursales de origen y destino, y todos los eventos de rastreo registrados en orden cronológico.
Parámetros de Query
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
clave |
string | Sí | Clave única del envío (formato PAQ-XXXXXX), obtenida al crear el preregistro o al registrar el paquete en sucursal. |
Ejemplo de Solicitud
GET /api/dev/v1/envios/rastreo?clave=PAQ-784521
Authorization: X-API-KEY tu_api_key
Respuesta Exitosa (200 OK)
{
"clave": "PAQ-784521",
"estatus": "en_transito",
"remitente": "Juan Pérez",
"destinatario": "María López",
"sucursal_origen": "Terminal Oaxaca Centro",
"sucursal_destino": "Terminal Ciudad de México",
"fecha_envio": "2026-06-01",
"fecha_entrega": null,
"seguimiento": [
{
"descripcion": "Paquete registrado en sucursal origen",
"fecha": "2026-06-01 09:15:00"
},
{
"descripcion": "En viaje a sucursal destino PAQ-784521",
"fecha": "2026-06-01 14:30:00"
},
{
"descripcion": "Recepción en sucursal destino PAQ-784521",
"fecha": "2026-06-02 08:45:00"
}
]
}
Valores posibles de estatus
| Valor | Significado |
|---|---|
preregistrado |
Registrado en el sistema, aún no entregado en sucursal. |
en_transito |
El paquete está en camino a la sucursal destino. |
en_sucursal_destino |
El paquete llegó a la sucursal destino y está listo para recoger. |
en_intermedio |
El paquete está en una sucursal intermedia. |
entregado |
El paquete fue entregado al destinatario. |
Errores Posibles
| Código | Mensaje | Causa |
|---|---|---|
| 422 | The clave field is required. |
No se envió el parámetro clave. |
| 404 | No se encontró ningún envío con esa clave. |
La clave no corresponde a ningún envío en el sistema. |
POST /api/dev/v1/facturacion/emitir
Genera y timbra una factura CFDI 4.0 para un grupo de boletos ya pagados, identificados por su
clave_global. El CFDI se emite ante el SAT en tiempo real a través de Soicont y
devuelve las URLs del PDF y XML firmados.
- Las reservaciones deben tener estatus
pagado. - No debe existir ya una factura emitida para la
clave_global. - La
clave_globalno debe estar incluida en una factura global mensual. - El RFC del receptor debe ser válido según el SAT (formato correcto para persona física o moral).
Cuerpo de la Solicitud (JSON)
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
clave_global |
string | Sí | Clave del grupo de boletos (obtenida en /confirmar). |
rfc |
string | Sí | RFC del receptor. Persona física: 13 caracteres. Moral: 12 caracteres. |
nombre |
string | Sí | Razón social o nombre completo del receptor tal como está registrado en el SAT. |
email |
string | Sí | Correo electrónico donde se enviará la notificación de la factura. |
uso_cfdi |
string | Sí | Clave de uso del CFDI según catálogo SAT. Ej: G03 (Gastos en general), D01 (Honorarios médicos), S01 (Sin efectos fiscales). |
forma_pago |
string | Sí | Clave de forma de pago SAT. Ej: 01 (Efectivo), 03 (Transferencia), 04 (Tarjeta de crédito), 28 (Tarjeta de débito), 99 (Por definir). |
regimen_fiscal |
string | Sí | Régimen fiscal del receptor según catálogo SAT. Ej: 626 (Simplificado de Confianza), 612 (Personas Físicas con Act. Empresariales), 601 (General de Ley Personas Morales). |
domicilio_fiscal |
string | Sí | Código postal del domicilio fiscal del receptor (5 dígitos), tal como aparece en la Constancia de Situación Fiscal del SAT. |
Ejemplo de Solicitud
{
"clave_global": "YBR7HRGL",
"rfc": "BAZO900101ABC",
"nombre": "Juan Pérez García",
"email": "juan.perez@ejemplo.com",
"uso_cfdi": "G03",
"forma_pago": "03",
"regimen_fiscal": "626",
"domicilio_fiscal": "63170"
}
Respuesta Exitosa (200 OK)
{
"uuid": "6128a3f1-4e2c-4d1a-9b3e-7c8d2f1e0a45",
"folio": "1042",
"pdf": "https://storage.cbus.com.mx/reservaciones/YBR7HRGL/factura.pdf",
"xml": "https://storage.cbus.com.mx/reservaciones/YBR7HRGL/factura.xml"
}
Errores Posibles (422)
| Mensaje | Causa |
|---|---|
No se encontraron reservaciones pagadas para esta clave_global. |
La clave_global no existe o los boletos no están en estatus pagado. |
Ya existe una factura emitida para esta clave_global. |
Ya se timbró un CFDI para este grupo de boletos. |
Ya no puedes generar factura; estas reservaciones ya están incluidas en una factura global. |
Las reservaciones ya fueron facturadas de forma global al público en general. |
Error al timbrar CFDI: ... |
El PAC (Soicont) rechazó el comprobante. El mensaje incluye el detalle del error del SAT. |
POST /api/dev/v1/facturacion/cancelar
Marca una factura como cancelada en el sistema y libera los boletos asociados para que puedan refacturarse. La cancelación fiscal ante el SAT debe completarse manualmente desde el panel de Soicont (soicont.com.mx), ya que el proveedor no expone un endpoint de cancelación por API.
- Llama a este endpoint para liberar los boletos en el sistema CBus.
- Ingresa al panel de Soicont y cancela el CFDI usando el UUID proporcionado para que la cancelación tenga efecto fiscal ante el SAT.
Cuerpo de la Solicitud (JSON)
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
uuid |
string | Sí | UUID del CFDI a cancelar (obtenido en la respuesta de /facturacion/emitir). |
Ejemplo de Solicitud
{
"uuid": "6128a3f1-4e2c-4d1a-9b3e-7c8d2f1e0a45"
}
Respuesta Exitosa (200 OK)
{
"message": "Factura marcada como cancelada localmente. Completa la cancelación fiscal desde el panel de Soicont (soicont.com.mx) usando el UUID proporcionado.",
"uuid": "6128a3f1-4e2c-4d1a-9b3e-7c8d2f1e0a45"
}
Errores Posibles
| Código | Mensaje | Causa |
|---|---|---|
| 404 | No se encontró ninguna factura con ese UUID. |
El UUID no coincide con ninguna factura en el sistema. |
| 422 | La factura ya está marcada como cancelada. |
La cancelación local ya fue aplicada previamente. |
| 422 | No puedes cancelar esta factura; forma parte de una factura global. |
El CFDI fue incluido en una factura global y no puede cancelarse de forma individual. |
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"
}