# Documentación de Be School · API v1 --- Referencia pública. OpenAPI: https://sch.begraffic.com/api/v1/openapi.json --- # Tu primera conexión Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. La API REST v1 conecta tu escuela con una web de inscripción, una app, un ERP, informes o automatizaciones. Puedes leer el catálogo y los datos académicos disponibles, dar de alta estudiantes, matricular con justificación, solicitar admisiones y recibir eventos. 1. Necesitas una escuela activa y un plan que incluya integraciones (desde Academia). Como propietario o administrador, abre Panel → Ajustes → API y claves (/panel/ajustes/api). Crea una clave con los permisos mínimos necesarios y guárdala: se muestra una sola vez. 2. Configura BESCHOOL_API_KEY como secreto en tu servidor. La base de los ejemplos es https://sch.begraffic.com/api/v1. Usa la dirección base indicada en el panel de tu escuela; no añadas el slug de la escuela a /api/v1. La escuela se obtiene de la clave, no de un parámetro school_id. 3. Ejecuta GET /me para comprobar la conexión. Verifica el id de escuela y los permisos antes de automatizar escrituras. El prefijo bs_live_ no implica un entorno de pruebas: una escritura con una clave real cambia datos reales. Para ensayar usa una escuela separada y sus propias claves. OpenAPI 3.1 está disponible sin autenticación en /api/v1/openapi.json. Puedes importarlo en Postman, Insomnia o un visor compatible. Esta referencia incluye todas sus operaciones; los ejemplos contienen datos ficticios y los listados pueden estar vacíos. ## Comprobar la conexión ```bash # Define BESCHOOL_API_KEY en el entorno seguro de tu servidor. curl 'https://sch.begraffic.com/api/v1/me' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" ``` --- # Autenticación y permisos Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Cada solicitud protegida requiere Authorization: Bearer . No uses cookies del panel, tokens de Firebase ni claves en parámetros URL. Las claves pertenecen a una sola escuela y no pueden consultar otra: un recurso ajeno responde 404. Mantén la clave en el servidor o en el almacén de credenciales de tu herramienta de automatización. Tu navegador llama a tu propio backend y ese backend llama a Be School. No pongas claves en variables NEXT_PUBLIC_, bundles, repositorios, capturas ni conversaciones con LLM. Escuela: Nombre, moneda, zona horaria y dirección pública. Permisos: school:read. Catálogo: Rutas con sus cursos, temarios, clases (ficha), periodos, precios y programas. Permisos: catalog:read. Contenido de clases: Lecturas, vídeos, audios, documentos y materiales de las clases, también borradores. Acceso de integración sin las restricciones de una matrícula. Permisos: content:read. Estudiantes: La lista y la ficha básica (nombre, correo, estado). Permisos: students:read, students:write. Escritura: Dar de alta e invitar. Matrículas: Quién está matriculado en qué periodo y con qué justificación. Permisos: enrollments:read, enrollments:write. Escritura: Matricular con beca, pago fuera de línea o gratis. Pedidos y cobros: Importes, estados y reembolsos. Permisos: orders:read. Certificados: Los emitidos y revocados, con su código de verificación. Permisos: certificates:read. Progreso: Las clases que ha completado cada estudiante. Permisos: progress:read. Admisiones: Solicitudes de admisión a los programas y su respuesta. Permisos: admissions:read, admissions:write. Escritura: Enviar una solicitud en nombre de un estudiante. Webhooks (Zapier, Make): Suscribir una URL a un evento y darla de baja (REST Hooks). Permisos: webhooks:write. Escritura: Suscribir y dar de baja. Escribir incluye leer el mismo recurso. GET /me acepta cualquier clave válida; si otra operación necesita un permiso ausente devuelve 403 insufficient_scope. Las claves antiguas read/write conservan sus permisos anteriores; no incluyen content:read automáticamente. Habilita explícitamente «Contenido de clases» para leer cuerpos y archivos. Los permisos nuevos seleccionados «Solo lectura» y «Acceso completo» sí incluyen content:read; «Inscripción propia» no lo necesita. Punto de partida «Solo lectura»: school:read, catalog:read, content:read, students:read, enrollments:read, orders:read, certificates:read, progress:read, admissions:read. Consulta todo y no cambia nada: informes, un ERP, un panel propio. Punto de partida «Inscripción propia»: school:read, catalog:read, students:read, students:write, enrollments:read, enrollments:write, admissions:read, admissions:write. Tu web o tu app: lee el catálogo, da de alta estudiantes, los matricula y envía admisiones. Punto de partida «Acceso completo»: school:read, catalog:read, content:read, students:read, students:write, enrollments:read, enrollments:write, orders:read, certificates:read, progress:read, admissions:read, admissions:write, webhooks:write. Todo lo que permite la API, también Zapier. Puedes crear hasta 20 claves. Caducidad: sin fecha o 30, 90 o 365 días. Rotar crea otra clave con el mismo nombre y permisos; elige una convivencia de 0 h, 1 h, 24 h o 7 días. Actualiza el secreto en tus sistemas y prueba /me antes de retirar la anterior. Caducadas o revocadas responden 401. Revocar y reducir permisos sigue disponible sin el plan; ampliar permisos o rotar requiere el plan. --- # Secciones y modelo de la plataforma Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Escuela: contiene la marca, moneda, zona horaria y dirección pública. Cada clave define una escuela. El panel administra la escuela; el campus es el espacio de aprendizaje de los estudiantes; la web pública presenta el catálogo y el checkout. Cursos: organizan contenido y clases. ready significa listo para abrirse en un periodo; no garantiza que se venda en la web. El filtro published es un alias antiguo de ready. La API de catálogo permite leer rutas, sus cursos y las fichas del temario, sin crearlos ni editar sus clases. El cuerpo y los archivos de una clase requieren content:read. Periodos: son aperturas de un curso, clase única, ruta o ciclo, con fechas, visibilidad y plazas. La matrícula se hace en un periodo, no solo en un curso. En rutas y ciclos course_id puede ser null. Un periodo activo no implica que sea visible: comprueba visible y public_from; la elegibilidad final se valida al matricular. Membresías: GET /periods/{id} incluye membership_offers con precios mensual/anual, checkout_url, pitch, description, benefits (id, text, included), badge, featured, catalog, scope y contribution. La presentación usa la misma función que la página pública: sin herencia entre planes ni exclusiones inventadas. scope.kind es period; scope.target identifica el curso, clase o ruta de ese periodo, nunca acceso general a la escuela. Los textos ausentes son cadenas vacías y yearly_price/contribution pueden ser null. contribution.monthly_amount es la parte de la cuota mensual en céntimos; text coincide con la frase pública y no representa otro cargo. Las claves de benefits identifican líneas de cada plan, no permisos. Mostrar los textos como texto, no como HTML. Ofertas: precios y condiciones de acceso asociados al catálogo. price y amount son enteros en céntimos (4900 = 49.00). En suscripciones o plazos, price corresponde a cada cuota. checkout_url puede ser null en ofertas archivadas, rutas o ediciones; utiliza su página de venta correspondiente. Estudiantes y matrículas: crear una cuenta no da acceso a un curso. La matrícula relaciona al estudiante con su periodo y la justificación de acceso. Pedidos describe los cobros; progreso contiene clases completadas y certificados registra los documentos emitidos o revocados. Programas: carreras con plan de estudios, materias y requisitos. Una solicitud de admisión no es una aceptación ni una matrícula; secretaría decide en el panel. Consulta su estado con GET /admission_requests/{id}. El catálogo actual de webhooks no incluye un evento específico de admisión. La API v1 no expone edición de cursos, subida de archivos, emisión de certificados, cambio de notas, reembolsos ni cobros directos por tarjeta. Esas tareas se realizan en sus flujos del panel, campus o proveedor. No inventes endpoints PATCH/PUT ni reutilices APIs internas con una clave v1. Las fechas date-time usan ISO 8601; date usa YYYY-MM-DD. El created del sobre de un webhook usa segundos Unix. null expresa un valor ausente o, para seats/seats_available, capacidad sin límite. Los ids son opacos: usa los devueltos por la API (rutas con letras, números, guion o guion bajo; hasta 200 caracteres). --- # Paginación y filtros Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Todos los listados devuelven object: list, data, has_more y next_cursor. limit va de 1 a 100; por defecto 25. La ordenación es por id, no por fecha de creación. Conserva los filtros y pasa next_cursor sin modificarlo como cursor para continuar; deja de paginar cuando has_more sea false. El cursor es opaco: no lo generes a partir de fechas ni lo uses para saltar páginas por número. En consultas con filtros, una página puede contener menos elementos que limit y todavía tener has_more: true. Usa has_more para decidir si continuar. La referencia de cada función enumera sus filtros admitidos. Construye los parámetros con URLSearchParams (o params en requests) para codificar correos, ids y cursores. Un filtro con un valor no válido devuelve 400 invalid_request. No se ofrece búsqueda por texto libre en los endpoints. ## Recorrer estudiantes desde Node.js ```javascript let cursor = null; do { const url = new URL("https://sch.begraffic.com/api/v1/students"); url.searchParams.set("limit", "100"); if (cursor) url.searchParams.set("cursor", cursor); const response = await fetch(url, { headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}` }, }); const page = await response.json(); if (!response.ok) throw new Error(page.error?.message); for (const student of page.data) { // Guarda o procesa cada estudiante en tu sistema. console.log(student.id); } if (!page.has_more) break; if (!page.next_cursor) throw new Error("Falta el cursor de continuación"); cursor = page.next_cursor; } while (cursor); ``` --- # Errores, límites y reintentos Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Los errores devuelven JSON con { "error": { "code": "…", "message": "…" } }. Comprueba el estado HTTP y error.code; usa message para el diagnóstico. Las respuestas de la API protegida no se almacenan en caché. 400 invalid_request / invalid_json: revisa parámetros o cuerpo. 401 unauthorized: falta la clave, no es válida, ha caducado o fue revocada. 403 insufficient_scope / plan_required / school_suspended: revisa permisos, plan o estado de la escuela. 404 not_found: recurso inexistente o fuera de tu escuela; una matrícula también puede devolver 404 enrollment_rejected si la persona no pertenece a ella. 405 method_not_allowed: consulta la cabecera Allow. 409 is_staff / suspended: no puedes dar de alta como estudiante a una persona del equipo ni readmitir por API a alguien suspendido. 413 payload_too_large: el JSON supera 64 KiB. 415 unsupported_media_type: envía Content-Type: application/json. 422 plan_limit / account_failed / enrollment_rejected / admission_rejected / invalid_target: la petición no puede aplicarse; lee message y corrige la causa. 500 internal_error y 503 unavailable: fallo o indisponibilidad del servicio. 429 rate_limited incluye Retry-After en segundos. El limitador admite una ráfaga de 120 solicitudes y repone 2/s por clave; funciona por instancia y no garantiza una cuota global. Con claves inválidas se permiten 20 intentos por IP y después uno cada 5 s. Limita concurrencia y espera Retry-After antes de volver a solicitar. Para lecturas ante un 5xx o un fallo de red, usa reintentos acotados con espera exponencial y variación aleatoria. Ante un timeout de escritura el resultado puede ser desconocido: consulta el estado antes de repetir. No hay una cabecera Idempotency-Key documentada en v1. POST /students devuelve 200 si ya era estudiante; POST /enrollments devuelve 200 si ya tenía la matrícula; POST /admission_requests puede reutilizar una solicitud con la misma nota. POST /hooks crea una suscripción nueva: no lo repitas a ciegas. Una operación exitosa puede devolver 200 o 201; comprueba created cuando exista. ## Ejemplo de permiso insuficiente ```json { "error": { "code": "insufficient_scope", "message": "Esta clave no tiene el permiso students:read. Dáselo en Ajustes → API y claves." } } ``` --- # Consultar una ruta y el contenido de sus cursos Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. 1. Lista las rutas con GET /paths (catalog:read). GET /paths/{id} devuelve course_ids en el orden configurado. Rutas y programas son recursos distintos: /programs describe carreras académicas. 2. Consulta GET /paths/{id}/courses para recibir las fichas de los cursos y clases únicas en ese mismo orden, con position. Recorre has_more/next_cursor hasta terminar; se omiten referencias eliminadas o ajenas. Los ids son ids de documento obtenidos por la API, no los códigos visibles del curso. 3. GET /courses/{id}/syllabus devuelve el temario completo: modules con lessons y unassigned_lessons al final. GET /courses/{id}/lessons es su versión paginada; admite status=published o status=draft. Sin filtro, ambas contienen fichas de borradores y publicadas. No incluyen texto ni enlaces privados. 4. Habilita el permiso específico content:read y usa GET /courses/{id}/lessons/{lessonId}. El detalle devuelve descripción saneada, lectura, objetivos, vídeo externo o media, documento, capítulos, transcripción, subtítulos, recursos, sesión en vivo y enunciado de asignación según el tipo de clase. Los campos que no aplican son null o listas/cadenas vacías según su esquema. Los archivos media, document y resources[].file traen url, expires_at y available. Las URLs duran 15 minutos: renueva consultando de nuevo el detalle antes de expires_at. Un archivo inválido o ausente es null; si no se puede firmar, available=false y url/expires_at=null. No se entregan rutas de Storage como enlaces alternativos. content:read es acceso privilegiado de tu integración, incluso a borradores; no representa a un estudiante ni aplica sus matrículas, plan o goteo. Autoriza en tu servidor quién puede ver cada contenido antes de entregarlo. No pongas la clave en una app pública. Las claves existentes, incluso las antiguas read/write, no reciben este permiso automáticamente; selecciónalo expresamente en API y claves. Los cuestionarios incluyen resumen y campus_url; las preguntas de un intento y las respuestas permanecen en el campus. Los paquetes SCORM incluyen versión, título y campus_url: no se exporta el ZIP ni se ejecuta SCORM por esta API. Los enlaces al campus requieren sesión y acceso del estudiante. Leer contenido no marca clases completadas ni crea matrículas, intentos o entregas. Las listas de rutas se paginan por id; cursos de una ruta y clases de un curso respetan el orden del panel. Mantén los mismos filtros mientras pagines. Si el cursor ya no pertenece a la lista (por retirada o cambio de filtro), recibirás 400 invalid_request: reinicia desde la primera página. Un padre o clase de otra escuela devuelve 404, aunque exista en otra escuela. ## Ruta → cursos → clases → contenido ```bash curl 'https://sch.begraffic.com/api/v1/paths?limit=25' -H "Authorization: Bearer $BESCHOOL_API_KEY" curl 'https://sch.begraffic.com/api/v1/paths/ruta_ejemplo/courses?limit=25' -H "Authorization: Bearer $BESCHOOL_API_KEY" curl 'https://sch.begraffic.com/api/v1/courses/curso_ejemplo/syllabus' -H "Authorization: Bearer $BESCHOOL_API_KEY" curl 'https://sch.begraffic.com/api/v1/courses/curso_ejemplo/lessons?status=published&limit=25' -H "Authorization: Bearer $BESCHOOL_API_KEY" # La siguiente petición requiere content:read. curl 'https://sch.begraffic.com/api/v1/courses/curso_ejemplo/lessons/clase_ejemplo' -H "Authorization: Bearer $BESCHOOL_API_KEY" ``` --- # Ofrecer compra individual o acceso con membresía Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Consulta GET /periods/{id} con catalog:read desde tu servidor. offers contiene las ofertas del periodo consultado y membership_offers, sus alternativas de membresía. content añade una lista de fichas de cursos y clases publicadas, identificadas por course_id y lesson_id (null en el curso). No incluye cuerpos ni archivos; para ellos sigue siendo necesario content:read. Cada ficha incluye independent_sale. enabled confirma que hay una configuración activa y coherente; available comprueba publicación, contenido listo, calendario de inscripción, cupo del periodo y precio. Si available=true y offer.checkout_url tiene valor, muestra Comprar este curso o Comprar esta clase con offer.price (céntimos) y offer.currency. Si no, no ofrezcas ese enlace; unavailable_reason explica el motivo. Un periodo activo puede estar oculto, pendiente de lanzamiento, cerrado o lleno. El checkout vuelve a validar condiciones, derecho de acceso y métodos de cobro; la consulta no reserva plazas ni confirma pagos. Para Acceder con membresía usa membership_offers de esa ficha: monthly_price, yearly_price (null si no hay anual), currency y checkout_url. Las tarjetas conservan sus textos y scope.id real. Una clase de un curso o un curso de una ruta pueden tener como alternativa la membresía del periodo principal. El enlace de membresía apunta a ese alcance, no al periodo individual; no lo reconstruyas sustituyendo ids. El mismo plan puede aparecer con distintos scope.id y alcances. parent_period_id relaciona una venta independiente con su origen. lesson_ids indica qué clases abre el periodo y también aparece en sus ofertas: null = curso completo; una lista = solo esas clases; [] = alcance vacío o inválido. No uses course_id para asumir acceso al curso entero. Al consultar un periodo individual, content solo enumera las clases que realmente incluye y membership_offers puede anunciar la membresía de su origen únicamente si ese origen todavía incluye su contenido. La compra individual de un curso dentro de una ruta no compra la ruta entera. Comprar una clase no abre las demás clases. Desactivar la venta impide nuevas compras y conserva los accesos ya adquiridos. No calcules el precio del curso completo tomando la oferta más barata de GET /offers: una oferta con lesson_ids limitado corresponde solo a esas clases. ## Opciones para los botones de una web externa (solo servidor) ```javascript const response = await fetch('https://sch.begraffic.com/api/v1/periods/periodo_ejemplo', { headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}` }, cache: 'no-store', }); if (!response.ok) throw new Error('No se pudo consultar el periodo'); const period = await response.json(); const cards = period.content.map(item => ({ course_id: item.course_id, lesson_id: item.lesson_id, title: item.title, buy: item.independent_sale.available && item.independent_sale.offer?.checkout_url ? { label: item.kind === 'course' ? 'Comprar este curso' : 'Comprar esta clase', price_cents: item.independent_sale.offer.price, currency: item.independent_sale.offer.currency, payment_type: item.independent_sale.offer.type, installment_count: item.independent_sale.offer.installment_count, billing_interval: item.independent_sale.offer.billing_interval, url: item.independent_sale.offer.checkout_url, } : null, memberships: item.membership_offers.map(plan => ({ label: 'Acceder con membresía', title: plan.title, monthly_price_cents: plan.monthly_price, yearly_price_cents: plan.yearly_price, currency: plan.currency, scope_period_id: plan.scope.id, url: plan.checkout_url, })), })); // Envía cards a tu frontend. La clave de API permanece en este servidor. // Para comprar el periodo completo usa sus offers, respetando lesson_ids. ``` --- # Inscripción y pagos desde tu web Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. 1. Usa school:read y catalog:read para presentar la escuela y GET /periods?status=active. Consulta GET /periods/{id} para conocer plazas y ofertas. seats_available: null significa sin límite. La disponibilidad puede cambiar entre consulta y matrícula. 2. Desde tu backend llama a POST /students con email, name y send_invite. Conserva el id devuelto. send_invite es true por defecto; invited indica si se consiguió enviar/entregar la notificación, no si el estudiante ya entró al campus. Repetir un alta existente no reenvía automáticamente la invitación. 3. Para pagos con tarjeta, redirige a checkout_url de la oferta activa. El checkout y Stripe registran el cobro y ejecutan el acceso correspondiente. Verifica order.paid y enrollment.created de manera independiente: un pago recibido no garantiza que todo el procesamiento posterior haya terminado. No marques un pedido como pagado desde una URL de éxito. 4. Para una beca, un pago externo ya confirmado o un periodo gratuito, usa POST /enrollments con students:write y enrollments:write según las operaciones que hagas. La escritura de matrícula exige un estudiante de la escuela y valida fechas, plazas y reglas académicas. Selecciona el preset «Inscripción propia» y reduce permisos si tu flujo necesita menos. Beca: justification.kind = scholarship y reason de 5 a 400 caracteres. Pago externo: kind = manual_payment, method = transfer/cash/card_terminal/other, reference de 2 a 120 caracteres y amount entero entre 1 y 100000000, en céntimos de la moneda de la escuela. No hay subida de comprobante en v1; queda registrado vía API y en auditoría. Gratuito: justification.kind = free, solo si el periodo tiene una oferta gratuita activa. No conviertas una compra pendiente en una matrícula gratuita o becada. Para programas, crea una solicitud con POST /admission_requests y consulta el resultado de secretaría. ## Matrícula con pago externo confirmado ```json { "student_id": "uid_ejemplo", "period_id": "periodo-ejemplo", "justification": { "kind": "manual_payment", "method": "transfer", "reference": "TRX-2026-001", "amount": 4900 } } ``` ## Matrícula gratuita ```json { "student_id": "uid_ejemplo", "period_id": "periodo-ejemplo", "justification": { "kind": "free" } } ``` --- # Conexiones disponibles Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Web, app, ERP o CRM propio: utiliza HTTP desde tu servidor con una clave v1. Lee catálogo, estudiantes, matrículas, cobros, progreso y certificados según sus permisos. Sincroniza con paginación inicial y eventos posteriores; conserva la relación entre tus ids y los de Be School. Zapier y Make: configura solicitudes HTTP o un conector propio. Prueba credenciales con GET /me, usa GET para búsquedas y POST para altas. Para disparadores instantáneos registra una URL con POST /hooks y un evento; guarda id y secret, y ejecuta DELETE /hooks/{id} al desconectar. Esta compatibilidad HTTP/REST Hooks no implica que exista una app publicada en sus directorios. Webhooks propios: en Panel → Integraciones → Webhooks (/panel/integraciones/webhooks) registra destinos y eventos, envía pruebas y revisa entregas. Hasta 10 destinos por escuela. Las suscripciones creadas por /hooks aparecen identificadas como Zapier / API. DELETE /hooks solo da de baja suscripciones REST Hook, no destinos manuales del panel. Be CRM: la conexión oficial se autoriza desde Panel → Integraciones → Be CRM (/panel/integraciones/becrm), con selección de cuenta y configuración de sincronización. Utiliza su flujo de autorización; una clave de Be School no es una credencial de Be CRM. Revisa qué datos y eventos habilitas y el consentimiento de comunicaciones. Be Agent: conecta la cuenta desde Panel → Integraciones → Be Agent (/panel/integraciones/be-agent). Sus funciones educativas y la revisión humana se configuran en la escuela. El protocolo de conexión y sus callbacks son del conector oficial; no se autentican con bs_live_ ni forman parte de la API v1. Stripe: autoriza tu cuenta desde Panel → Integraciones → Stripe (/panel/integraciones/stripe) y completa los requisitos del proveedor para habilitar cobros. El estudiante paga en el checkout de la escuela. La API v1 permite consultar pedidos; no sustituye Stripe Connect ni permite crear cobros o reembolsos. Acceso con Google Workspace o Microsoft: configura el inicio de sesión de tu organización y los dominios admitidos en /panel/integraciones/acceso. Esta conexión gestiona el acceso al campus; no reemplaza las claves para integraciones de servidor. Clases en vivo: /panel/integraciones/en-vivo permite usar la sala integrada o conectar Zoom y Google Meet para crear reuniones. La conexión a proveedores externos requiere la función de plan live_integrations y autorización de la cuenta correspondiente. Automatizaciones de correo: /panel/integraciones/automatizaciones configura bienvenida, recordatorios y seguimiento. Para procesos externos usa webhooks y REST Hooks; no confundas una automatización interna con una operación de la API pública. El acceso y la disponibilidad dependen del plan, los permisos de la persona y el estado de cada conexión. Una conexión guardada no garantiza que el proveedor esté listo: comprueba el estado en el panel y ensaya el recorrido completo antes de activar una automatización. --- # Botón de compra en otra web Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Para vender desde una landing sin desarrollar una integración de servidor, usa el botón oficial. En Panel → Ajustes → Ventas en otras webs autoriza cada origen HTTPS exacto (www y sin www cuentan como orígenes diferentes). En la ficha del curso o periodo, «Insertar en tu web» genera el código con el id correcto. El script muestra un botón donde lo insertas y abre la inscripción en un modal. No requiere ni debe recibir una clave de API. El periodo debe estar activo, visible y tener una oferta que pueda pagarse con tarjeta, con Stripe habilitado. Los periodos con plazas limitadas o requisitos y las opciones no admitidas por el modal deben venderse desde la web de la escuela. También puedes usar un botón propio con data-beschool-compra, data-escuela y data-periodo, cargando embed.js una sola vez; o abrirlo con BeSchoolCompra.open({ escuela, periodo }). El evento de navegador beschool:paid sirve para analítica: para confirmar operaciones en tu servidor utiliza pedidos, matrículas y webhooks firmados. ## Botón oficial ```html ``` ## Formulario en la página ```html ``` --- # Recibir y verificar webhooks Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Registra un destino HTTPS público sin credenciales en la URL; no se aceptan localhost, redes privadas ni destinos internos. Para REST Hooks usa POST /hooks con target_url y un event del catálogo. Guarda secret (whsec_…) en tu servidor en el momento de crear la suscripción: se muestra una sola vez. Es distinto a tu clave bs_live_. El cuerpo es un sobre JSON con id, type, created (segundos Unix), school y data. Los campos data usan camelCase, por ejemplo studentId, a diferencia de student_id en la API REST. El contenido depende del punto de emisión; no asumas que todos los campos ilustrativos están presentes. Cabeceras: Be-School-Signature: t=,v1=; Be-School-Event-Id; Be-School-Event. Calcula HMAC-SHA256 con el secreto y el texto exacto ".". Verifica antes de convertir el JSON, compara en tiempo constante y rechaza diferencias de más de 300 segundos en ambas direcciones. Comprueba que school coincide con la escuela esperada y type está entre tus eventos suscritos. Guarda el evento de forma duradera con una restricción única por (school, id) antes de responder; un duplicado ya guardado puede responder 2xx. Procesa el trabajo pesado en una cola. Diseña consumidores que toleren duplicados y eventos fuera de orden. Solo un 2xx cuenta como entregado; las redirecciones no se siguen. Responde en menos de 10 segundos. Hay hasta siete intentos, con esperas de 1 min, 5 min, 30 min, 2 h, 8 h y 24 h, con una pequeña variación. El despacho depende del procesador de entregas, por lo que las esperas no son un horario exacto. Tras agotar los intentos el envío queda failed y puede reenviarse manualmente. Un destino con al menos 10 fallos consecutivos y más de 24 h sin éxito se desactiva automáticamente. Consulta entregas en el panel, corrige la causa y reactívalo cuando esté listo. Los eventos actuales no incluyen uno específico para aprobar o rechazar admisiones. Consulta su endpoint. No deduzcas eventos por nombres de funciones o por ejemplos de otro producto. ## Verificador para tu servidor Node.js ```javascript import { createHmac, timingSafeEqual } from "node:crypto"; export function validSignature(header, rawBody, secret) { if (!header || header.length > 2000 || !secret) return false; let timestamp; const signatures = []; for (const part of header.split(",")) { const [key, value] = part.trim().split("=", 2); if (key === "t" && /^\d{1,12}$/.test(value ?? "")) timestamp = Number(value); if (key === "v1" && /^[a-f0-9]{64}$/.test(value ?? "")) signatures.push(value); } if (timestamp === undefined || Math.abs(Date.now() / 1000 - timestamp) > 300) return false; const expected = createHmac("sha256", secret) .update(`${timestamp}.${rawBody}`, "utf8").digest(); return signatures.some((hex) => { const received = Buffer.from(hex, "hex"); return received.length === expected.length && timingSafeEqual(received, expected); }); } // En tu receptor: lee request.text() UNA vez antes de JSON.parse. // Si !validSignature(header, rawBody, secret): responde 400. // Valida school/type; persiste y deduplica (school, id); después responde 2xx. // Si no puedes persistir, responde 5xx para que la entrega se reintente. ``` --- # Trabajar con LLM y asistentes de código Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Cada sección y cada función tienen «Copiar para LLM»: obtendrás Markdown con el contexto de autenticación, permisos, parámetros, campos y ejemplos de esa sección. Cada bloque de código tiene también su propio botón de copia. «Copiar documentación completa» reúne todas las guías, funciones y eventos. Para dar contexto por URL, comparte /documentacion/llms.txt (texto completo sin interfaz) y /api/v1/openapi.json (contrato estructurado). Ambos son públicos y no contienen datos de escuelas ni claves reales. Revisa siempre el código generado antes de ejecutarlo, especialmente las matrículas y suscripciones. Sustituye credenciales y datos personales por marcadores antes de usar un asistente. Los ejemplos no establecen que exista un estudiante, un periodo o una oferta con esos ids. Obtén los ids reales por la API autorizada y conserva los permisos mínimos. ## Prompt de integración ```text Implementa una integración de servidor con Be School API v1 usando la documentación adjunta. Usa BESCHOOL_API_KEY desde un almacén de secretos y prueba GET /me. Enumera los permisos mínimos para el flujo solicitado. Respeta los esquemas, paginación, estados HTTP y Retry-After. No inventes endpoints ni una cabecera de idempotencia. No conviertas un pago pendiente en beca o matrícula gratuita. Verifica firmas sobre el cuerpo original y deduplica los webhooks. Usa marcadores para credenciales e ids. Explica cómo validar el recorrido antes de activarlo. ``` --- # GET /me — La clave, sus permisos y su escuela (prueba de conexión de Zapier) Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: Cualquier clave válida. Cualquier clave válida. ## Parámetros Sin parámetros. ## Cuerpo JSON Sin cuerpo. ## Campos de respuesta - object ("api_key"): - prefix (string): - scopes (array): - school (object): - school.id (string): - school.name (string): ## Estados documentados - 200: La clave es válida. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request GET 'https://sch.begraffic.com/api/v1/me' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/me", { method: "GET", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, }, }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests response = requests.request( "GET", "https://sch.begraffic.com/api/v1/me", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 200 · ilustrativa ```json { "object": "api_key", "prefix": "bs_live_ejemplo", "scopes": [ "school:read" ], "school": { "id": "id_ejemplo", "name": "Ana Pérez" } } ``` --- # GET /school — La ficha pública de la escuela Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: school:read. Nombre, moneda, zona horaria y la dirección de su web (dominio propio si lo tiene). Permiso: `school:read`. ## Parámetros Sin parámetros. ## Cuerpo JSON Sin cuerpo. ## Campos de respuesta - id (string): - object ("school"): - name (string): - short_name (string): - slug (string): - tagline (string | null): - public_url (string): Su dominio propio si lo tiene activo. Formato: uri. - currency (string): Moneda de las ofertas nuevas (ISO 4217). - timezone (string): - locale (string): - support_email (string | null): - brand_color (string | null): ## Estados documentados - 200: La escuela. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request GET 'https://sch.begraffic.com/api/v1/school' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/school", { method: "GET", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, }, }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests response = requests.request( "GET", "https://sch.begraffic.com/api/v1/school", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 200 · ilustrativa ```json { "id": "id_ejemplo", "object": "school", "name": "Ana Pérez", "short_name": "ejemplo", "slug": "ejemplo", "tagline": null, "public_url": "https://sch.begraffic.com/tu-escuela", "currency": "USD", "timezone": "America/Santo_Domingo", "locale": "es", "support_email": "soporte@example.com", "brand_color": null } ``` --- # GET /courses — Cursos de la escuela Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: catalog:read. Permiso: `catalog:read`. ## Parámetros - limit (query · integer): Por defecto: 25. Mínimo: 1. Máximo: 100. - cursor (query · string): - status (query · string): Solo los de ese estado (`published` es el nombre antiguo de `ready`). Valores: draft, ready, archived, published. ## Cuerpo JSON Sin cuerpo. ## Campos de respuesta - object ("list"; obligatorio): - data (array; obligatorio): - data[].cover_url (string | null): Portada resuelta, lista para mostrar. null si no hay imagen disponible. Nunca una ruta de Storage. Formato: uri. - data[].cover_source (string | null): Origen de la imagen elegida. Valores: "period", "course", "lesson", null. - data[].cover_expires_at (string | null): Refresca la respuesta antes de esta fecha para renovar la URL firmada. null para miniaturas o portadas servidas por la aplicación sin caducidad, o si no hay imagen. Formato: date-time. - data[].id (string): - data[].object ("course"): - data[].title (string): - data[].code (string | null): - data[].status (string): Listo para abrirse en periodos; lo que se vende lo deciden sus periodos. Valores: "draft", "ready", "archived". - data[].kind (string): Valores: "course", "single". - data[].lesson_count (integer | null): - data[].created_at (string | null): Formato: date-time. - data[].updated_at (string | null): Formato: date-time. - has_more (boolean; obligatorio): - next_cursor (string | null; obligatorio): Pásalo como `cursor` para la página siguiente. ## Estados documentados - 200: Una página de resultados. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request GET 'https://sch.begraffic.com/api/v1/courses?limit=25' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/courses?limit=25", { method: "GET", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, }, }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests response = requests.request( "GET", "https://sch.begraffic.com/api/v1/courses?limit=25", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 200 · ilustrativa ```json { "object": "list", "data": [ { "cover_url": null, "cover_source": "period", "cover_expires_at": "2026-10-21T12:00:00.000Z", "id": "id_ejemplo", "object": "course", "title": "Introducción al liderazgo", "code": "LID-101", "status": "draft", "kind": "course", "lesson_count": 1, "created_at": "2026-10-21T12:00:00.000Z", "updated_at": "2026-10-21T12:00:00.000Z" } ], "has_more": false, "next_cursor": null } ``` --- # GET /courses/{id} — Un curso, con su presentación Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: catalog:read. Permiso: `catalog:read`. ## Parámetros - id (path · string; obligatorio): ## Cuerpo JSON Sin cuerpo. ## Campos de respuesta - cover_url (string | null): Portada resuelta, lista para mostrar. null si no hay imagen disponible. Nunca una ruta de Storage. Formato: uri. - cover_source (string | null): Origen de la imagen elegida. Valores: "period", "course", "lesson", null. - cover_expires_at (string | null): Refresca la respuesta antes de esta fecha para renovar la URL firmada. null para miniaturas o portadas servidas por la aplicación sin caducidad, o si no hay imagen. Formato: date-time. - id (string): - object ("course"): - title (string): - code (string | null): - status (string): Listo para abrirse en periodos; lo que se vende lo deciden sus periodos. Valores: "draft", "ready", "archived". - kind (string): Valores: "course", "single". - lesson_count (integer | null): - created_at (string | null): Formato: date-time. - updated_at (string | null): Formato: date-time. - description (string | null): Presentación: HTML saneado. - outcomes (array): ## Estados documentados - 200: Encontrado. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 404: No existe. - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request GET 'https://sch.begraffic.com/api/v1/courses/id_ejemplo' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/courses/id_ejemplo", { method: "GET", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, }, }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests response = requests.request( "GET", "https://sch.begraffic.com/api/v1/courses/id_ejemplo", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 200 · ilustrativa ```json { "cover_url": null, "cover_source": "period", "cover_expires_at": "2026-10-21T12:00:00.000Z", "id": "id_ejemplo", "object": "course", "title": "Introducción al liderazgo", "code": "LID-101", "status": "draft", "kind": "course", "lesson_count": 1, "created_at": "2026-10-21T12:00:00.000Z", "updated_at": "2026-10-21T12:00:00.000Z", "description": null, "outcomes": [ "ejemplo" ] } ``` --- # GET /paths — Rutas de aprendizaje Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: catalog:read. Incluye course_ids en el orden de la ruta y course_count. Los cursos deben existir dentro de la escuela de la clave. Permiso: `catalog:read`. ## Parámetros - limit (query · integer): Por defecto: 25. Mínimo: 1. Máximo: 100. - cursor (query · string): - status (query · string): Estado del contenido; published es alias de ready. Valores: draft, ready, archived, published. ## Cuerpo JSON Sin cuerpo. ## Campos de respuesta - object ("list"; obligatorio): - data (array; obligatorio): - data[].id (string): - data[].object ("path"): - data[].title (string): - data[].code (string | null): - data[].tagline (string): - data[].status (string): Valores: "draft", "ready", "archived". - data[].course_ids (array): Cursos existentes de esta escuela, en el orden de la ruta. Se omiten referencias eliminadas o ajenas y duplicados. - data[].course_count (integer): - data[].created_at (string | null): - data[].updated_at (string | null): - has_more (boolean; obligatorio): - next_cursor (string | null; obligatorio): Pásalo como `cursor` para la página siguiente. ## Estados documentados - 200: Una página de resultados. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request GET 'https://sch.begraffic.com/api/v1/paths?limit=25' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/paths?limit=25", { method: "GET", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, }, }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests response = requests.request( "GET", "https://sch.begraffic.com/api/v1/paths?limit=25", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 200 · ilustrativa ```json { "object": "list", "data": [ { "id": "id_ejemplo", "object": "path", "title": "Introducción al liderazgo", "code": "LID-101", "tagline": "ejemplo", "status": "draft", "course_ids": [ "ejemplo" ], "course_count": 1, "created_at": null, "updated_at": null } ], "has_more": false, "next_cursor": null } ``` --- # GET /paths/{id} — Una ruta y sus cursos en orden Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: catalog:read. Permiso: `catalog:read`. ## Parámetros - id (path · string; obligatorio): ## Cuerpo JSON Sin cuerpo. ## Campos de respuesta - id (string): - object ("path"): - title (string): - code (string | null): - tagline (string): - status (string): Valores: "draft", "ready", "archived". - course_ids (array): Cursos existentes de esta escuela, en el orden de la ruta. Se omiten referencias eliminadas o ajenas y duplicados. - course_count (integer): - created_at (string | null): - updated_at (string | null): ## Estados documentados - 200: Encontrado. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 404: No existe. - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request GET 'https://sch.begraffic.com/api/v1/paths/id_ejemplo' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/paths/id_ejemplo", { method: "GET", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, }, }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests response = requests.request( "GET", "https://sch.begraffic.com/api/v1/paths/id_ejemplo", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 200 · ilustrativa ```json { "id": "id_ejemplo", "object": "path", "title": "Introducción al liderazgo", "code": "LID-101", "tagline": "ejemplo", "status": "draft", "course_ids": [ "ejemplo" ], "course_count": 1, "created_at": null, "updated_at": null } ``` --- # GET /paths/{id}/courses — Los cursos de una ruta, en orden Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: catalog:read. Incluye fichas de cursos y clases únicas con position. Paginación en el orden definido en la ruta. Si el último curso del cursor fue retirado, devuelve 400 invalid_request: reinicia la lista. 404 si la ruta no existe en esta escuela. Permiso: `catalog:read`. ## Parámetros - id (path · string; obligatorio): - limit (query · integer): Por defecto: 25. Mínimo: 1. Máximo: 100. - cursor (query · string): ## Cuerpo JSON Sin cuerpo. ## Campos de respuesta - object ("list"; obligatorio): - data (array; obligatorio): - data[].cover_url (string | null): Portada resuelta, lista para mostrar. null si no hay imagen disponible. Nunca una ruta de Storage. Formato: uri. - data[].cover_source (string | null): Origen de la imagen elegida. Valores: "period", "course", "lesson", null. - data[].cover_expires_at (string | null): Refresca la respuesta antes de esta fecha para renovar la URL firmada. null para miniaturas o portadas servidas por la aplicación sin caducidad, o si no hay imagen. Formato: date-time. - data[].id (string): - data[].object ("course"): - data[].title (string): - data[].code (string | null): - data[].status (string): Listo para abrirse en periodos; lo que se vende lo deciden sus periodos. Valores: "draft", "ready", "archived". - data[].kind (string): Valores: "course", "single". - data[].lesson_count (integer | null): - data[].created_at (string | null): Formato: date-time. - data[].updated_at (string | null): Formato: date-time. - data[].description (string | null): Presentación: HTML saneado. - data[].outcomes (array): - data[].position (integer): Posición 1-based dentro de la ruta. - has_more (boolean; obligatorio): - next_cursor (string | null; obligatorio): Pásalo como `cursor` para la página siguiente. ## Estados documentados - 200: Una página de resultados. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 404: No existe. - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request GET 'https://sch.begraffic.com/api/v1/paths/id_ejemplo/courses?limit=25' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/paths/id_ejemplo/courses?limit=25", { method: "GET", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, }, }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests response = requests.request( "GET", "https://sch.begraffic.com/api/v1/paths/id_ejemplo/courses?limit=25", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 200 · ilustrativa ```json { "object": "list", "data": [ { "cover_url": null, "cover_source": "period", "cover_expires_at": "2026-10-21T12:00:00.000Z", "id": "id_ejemplo", "object": "course", "title": "Introducción al liderazgo", "code": "LID-101", "status": "draft", "kind": "course", "lesson_count": 1, "created_at": "2026-10-21T12:00:00.000Z", "updated_at": "2026-10-21T12:00:00.000Z", "description": null, "outcomes": [ "ejemplo" ], "position": 1 } ], "has_more": false, "next_cursor": null } ``` --- # GET /courses/{id}/syllabus — El temario completo de un curso Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: catalog:read. Módulos en su orden, con sus clases, y unassigned_lessons al final. Incluye fichas de clases publicadas y borradores, sin texto, enlaces de archivos ni preguntas. No requiere paginación; devuelve todo el temario. Una clase única aparece en unassigned_lessons. Permiso: `catalog:read`. ## Parámetros - id (path · string; obligatorio): ## Cuerpo JSON Sin cuerpo. ## Campos de respuesta - object ("syllabus"): - course_id (string): - title (string): - lesson_count (integer): - modules (array): - modules[].id (string): - modules[].title (string): - modules[].position (integer): - modules[].lessons (array): - modules[].lessons[].id (string): - modules[].lessons[].object ("lesson"): - modules[].lessons[].course_id (string): - modules[].lessons[].module_id (string | null): - modules[].lessons[].title (string): - modules[].lessons[].summary (string): - modules[].lessons[].kind (string): Valores: "video", "audio", "text", "document", "live", "quiz", "assignment", "scorm". - modules[].lessons[].status (string): Valores: "draft", "published". - modules[].lessons[].position (integer): Posición 1-based en el temario completo, antes de filtrar por estado. - modules[].lessons[].duration_seconds (number | null): - modules[].lessons[].free_preview (boolean): - modules[].lessons[].has_quiz (boolean): - modules[].lessons[].has_assignment (boolean): - unassigned_lessons (array): - unassigned_lessons[].id (string): - unassigned_lessons[].object ("lesson"): - unassigned_lessons[].course_id (string): - unassigned_lessons[].module_id (string | null): - unassigned_lessons[].title (string): - unassigned_lessons[].summary (string): - unassigned_lessons[].kind (string): Valores: "video", "audio", "text", "document", "live", "quiz", "assignment", "scorm". - unassigned_lessons[].status (string): Valores: "draft", "published". - unassigned_lessons[].position (integer): Posición 1-based en el temario completo, antes de filtrar por estado. - unassigned_lessons[].duration_seconds (number | null): - unassigned_lessons[].free_preview (boolean): - unassigned_lessons[].has_quiz (boolean): - unassigned_lessons[].has_assignment (boolean): ## Estados documentados - 200: Encontrado. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 404: No existe. - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request GET 'https://sch.begraffic.com/api/v1/courses/id_ejemplo/syllabus' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/courses/id_ejemplo/syllabus", { method: "GET", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, }, }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests response = requests.request( "GET", "https://sch.begraffic.com/api/v1/courses/id_ejemplo/syllabus", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 200 · ilustrativa ```json { "object": "syllabus", "course_id": "curso-ejemplo", "title": "Introducción al liderazgo", "lesson_count": 1, "modules": [ { "id": "id_ejemplo", "title": "Introducción al liderazgo", "position": 1, "lessons": [ { "id": "id_ejemplo", "object": "lesson", "course_id": "curso-ejemplo", "module_id": null, "title": "Introducción al liderazgo", "summary": "ejemplo", "kind": "video", "status": "draft", "position": 1, "duration_seconds": 1, "free_preview": true, "has_quiz": true, "has_assignment": true } ] } ], "unassigned_lessons": [ { "id": "id_ejemplo", "object": "lesson", "course_id": "curso-ejemplo", "module_id": null, "title": "Introducción al liderazgo", "summary": "ejemplo", "kind": "video", "status": "draft", "position": 1, "duration_seconds": 1, "free_preview": true, "has_quiz": true, "has_assignment": true } ] } ``` --- # GET /courses/{id}/lessons — Clases de un curso en el orden del temario Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: catalog:read. Solo fichas; el cuerpo de la clase se consulta en su detalle con content:read. Orden: módulos en su orden, clases por order y desempate por id, y clases sin módulo al final. Un cursor de una clase retirada o fuera del filtro devuelve 400 invalid_request. 404 si el curso no existe en esta escuela. Permiso: `catalog:read`. ## Parámetros - id (path · string; obligatorio): - limit (query · integer): Por defecto: 25. Mínimo: 1. Máximo: 100. - cursor (query · string): - status (query · string): Sin filtro: draft y published. Valores: draft, published. ## Cuerpo JSON Sin cuerpo. ## Campos de respuesta - object ("list"; obligatorio): - data (array; obligatorio): - data[].id (string): - data[].object ("lesson"): - data[].course_id (string): - data[].module_id (string | null): - data[].title (string): - data[].summary (string): - data[].kind (string): Valores: "video", "audio", "text", "document", "live", "quiz", "assignment", "scorm". - data[].status (string): Valores: "draft", "published". - data[].position (integer): Posición 1-based en el temario completo, antes de filtrar por estado. - data[].duration_seconds (number | null): - data[].free_preview (boolean): - data[].has_quiz (boolean): - data[].has_assignment (boolean): - has_more (boolean; obligatorio): - next_cursor (string | null; obligatorio): Pásalo como `cursor` para la página siguiente. ## Estados documentados - 200: Una página de resultados. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 404: No existe. - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request GET 'https://sch.begraffic.com/api/v1/courses/id_ejemplo/lessons?limit=25' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/courses/id_ejemplo/lessons?limit=25", { method: "GET", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, }, }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests response = requests.request( "GET", "https://sch.begraffic.com/api/v1/courses/id_ejemplo/lessons?limit=25", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 200 · ilustrativa ```json { "object": "list", "data": [ { "id": "id_ejemplo", "object": "lesson", "course_id": "curso-ejemplo", "module_id": null, "title": "Introducción al liderazgo", "summary": "ejemplo", "kind": "video", "status": "draft", "position": 1, "duration_seconds": 1, "free_preview": true, "has_quiz": true, "has_assignment": true } ], "has_more": false, "next_cursor": null } ``` --- # GET /courses/{id}/lessons/{lessonId} — Contenido de una clase Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: content:read. Lectura privilegiada de integración, también de borradores: no aplica matrículas, planes ni goteo de un estudiante. Tu servidor debe autorizar quién recibe este contenido. La clave y las URLs firmadas nunca se publican en documentación ni se comparten indiscriminadamente. Archivos de esta escuela con URL de 15 minutos; vuelve a pedir el detalle para renovar. No incluye respuestas, preguntas de intentos, entregas, notas, chats ni credenciales de anfitrión. Cuestionarios y SCORM se abren en campus_url con sesión de estudiante. No registra progreso ni reproduce la experiencia completa del campus. Permiso: `content:read`. ## Parámetros - id (path · string; obligatorio): - lessonId (path · string; obligatorio): ## Cuerpo JSON Sin cuerpo. ## Campos de respuesta - id (string): - object ("lesson"): - course_id (string): - module_id (string | null): - title (string): - summary (string): - kind (string): Valores: "video", "audio", "text", "document", "live", "quiz", "assignment", "scorm". - status (string): Valores: "draft", "published". - position (integer): Posición 1-based en el temario completo, antes de filtrar por estado. - duration_seconds (number | null): - free_preview (boolean): - has_quiz (boolean): - has_assignment (boolean): - description (string): Descripción HTML saneada. - objectives (string): - key_points (string): - campus_url (string): Enlace al campus; requiere la sesión y el acceso del estudiante. Formato: uri. - text_content (string): Texto plano de la lectura; vacío en otros tipos. - media (object | null): Archivo de contenido con enlace temporal. null si no aplica o su referencia no es válida. Si la firma falla, available=false y url/expires_at=null; no se entrega una ruta de Storage como sustituto. - media.url (string | null): Formato: uri. - media.expires_at (string | null): Vuelve a consultar el detalle antes de esta fecha para renovar el enlace (15 minutos). Formato: date-time. - media.available (boolean): - video_url (string | null): Vídeo externo por HTTP(S), cuando existe; no caduca por la API. Formato: uri. - document (object | null): Archivo de contenido con enlace temporal. null si no aplica o su referencia no es válida. Si la firma falla, available=false y url/expires_at=null; no se entrega una ruta de Storage como sustituto. - document.url (string | null): Formato: uri. - document.expires_at (string | null): Vuelve a consultar el detalle antes de esta fecha para renovar el enlace (15 minutos). Formato: date-time. - document.available (boolean): - chapters (array): - chapters[].at (number): - chapters[].title (string): - transcript (array): - transcript[].at (number | null): - transcript[].text (string): - captions (string): - resources (array): - resources[].name (string): - resources[].description (string): - resources[].content_type (string | null): - resources[].size_bytes (number | null): - resources[].file (object | null): Archivo de contenido con enlace temporal. null si no aplica o su referencia no es válida. Si la firma falla, available=false y url/expires_at=null; no se entrega una ruta de Storage como sustituto. - resources[].file.url (string | null): Formato: uri. - resources[].file.expires_at (string | null): Vuelve a consultar el detalle antes de esta fecha para renovar el enlace (15 minutos). Formato: date-time. - resources[].file.available (boolean): - live (object | null): - live.starts_at (string | null): - live.join_url (string | null): Enlace del participante; no incluye credenciales de anfitrión. Formato: uri. - live.duration_minutes (number | null): - assignment (object | null): - assignment.brief (string): - assignment.criteria (string): - assignment.allow_attachments (boolean): - assignment.due_at (string | null): - assignment.due (object | null): - assignment.due.from (string): Valores: "start", "opened". - assignment.due.days (integer): - assignment.rubric (array): - assignment.rubric[].key (string): - assignment.rubric[].label (string): - assignment.rubric[].weight (number): - quiz (object | null): Resumen del cuestionario. Los intentos, sus preguntas, corrección y respuestas se gestionan en el campus con sesión de estudiante. - quiz.intro (string): - quiz.question_count (integer): - quiz.pass_score (number): - quiz.time_limit_minutes (number): - quiz.campus_url (string): Formato: uri. - scorm (object | null): Información del paquete. La ejecución y el progreso SCORM se realizan en el campus, con sesión de estudiante; no se expone el ZIP ni las rutas internas. - scorm.version (string): Valores: "1.2", "2004". - scorm.title (string): - scorm.campus_url (string): Formato: uri. ## Estados documentados - 200: Encontrado. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 404: No existe. - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request GET 'https://sch.begraffic.com/api/v1/courses/id_ejemplo/lessons/clase_ejemplo' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/courses/id_ejemplo/lessons/clase_ejemplo", { method: "GET", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, }, }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests response = requests.request( "GET", "https://sch.begraffic.com/api/v1/courses/id_ejemplo/lessons/clase_ejemplo", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 200 · ilustrativa ```json { "id": "id_ejemplo", "object": "lesson", "course_id": "curso-ejemplo", "module_id": null, "title": "Introducción al liderazgo", "summary": "ejemplo", "kind": "video", "status": "draft", "position": 1, "duration_seconds": 1, "free_preview": true, "has_quiz": true, "has_assignment": true, "description": "ejemplo", "objectives": "ejemplo", "key_points": "ejemplo", "campus_url": "ejemplo", "text_content": "ejemplo", "media": { "url": null, "expires_at": "2026-10-21T12:00:00.000Z", "available": true }, "video_url": null, "document": { "url": null, "expires_at": "2026-10-21T12:00:00.000Z", "available": true }, "chapters": [ { "at": 1, "title": "Introducción al liderazgo" } ], "transcript": [ { "at": 1, "text": "ejemplo" } ], "captions": "ejemplo", "resources": [ { "name": "Ana Pérez", "description": "ejemplo", "content_type": null, "size_bytes": 1, "file": { "url": null, "expires_at": "2026-10-21T12:00:00.000Z", "available": true } } ], "live": { "starts_at": null, "join_url": null, "duration_minutes": 1 }, "assignment": { "brief": "ejemplo", "criteria": "ejemplo", "allow_attachments": true, "due_at": null, "due": { "from": "start", "days": 1 }, "rubric": [ { "key": "ejemplo", "label": "ejemplo", "weight": 1 } ] }, "quiz": { "intro": "ejemplo", "question_count": 1, "pass_score": 1, "time_limit_minutes": 1, "campus_url": "ejemplo" }, "scorm": { "version": "1.2", "title": "Introducción al liderazgo", "campus_url": "ejemplo" } } ``` --- # GET /periods — Periodos (aperturas) Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: catalog:read. Lo que se vende y se matricula: un curso, una clase única o una ruta abiertos por una temporada (o sin fecha). `visible` y `public_from` dicen si se ve en la web. Permiso: `catalog:read`. ## Parámetros - limit (query · integer): Por defecto: 25. Mínimo: 1. Máximo: 100. - cursor (query · string): - status (query · string): Activos o archivados. Valores: active, archived. - course_id (query · string): Los que abren ese curso. ## Cuerpo JSON Sin cuerpo. ## Campos de respuesta - object ("list"; obligatorio): - data (array; obligatorio): - data[].cover_url (string | null): Portada resuelta, lista para mostrar. null si no hay imagen disponible. Nunca una ruta de Storage. Formato: uri. - data[].cover_source (string | null): Origen de la imagen elegida. Valores: "period", "course", "lesson", null. - data[].cover_expires_at (string | null): Refresca la respuesta antes de esta fecha para renovar la URL firmada. null para miniaturas o portadas servidas por la aplicación sin caducidad, o si no hay imagen. Formato: date-time. - data[].cover_mode (string): auto: primera portada del contenido en orden, curso y luego clases publicadas; custom: portada propia del periodo. Valores: "auto", "custom". - data[].id (string): - data[].object ("period"): - data[].title (string): - data[].target (object): - data[].target.kind (string): Valores: "course", "single", "path", "cycle". - data[].target.id (string): - data[].course_id (string | null): null en un periodo de ruta o de ciclo. - data[].parent_period_id (string | null): Periodo de origen de una venta independiente; null en aperturas principales. - data[].independent_purchase (boolean): Este periodo abre una compra independiente, sin conceder acceso a la ruta o carrera completa. - data[].lesson_ids (array | null): Clases originales que abre; null = curso completo, [] = alcance vacío o inválido. Nunca asumir curso entero por course_id si esta lista está limitada. - data[].kind (string): Valores: "open", "dated". - data[].status (string): Valores: "active", "archived". - data[].visible (boolean): Se ve y se vende en la web. - data[].public_from (string | null): Formato: date-time. - data[].starts_at (string | null): Formato: date. - data[].ends_at (string | null): Formato: date. - data[].enroll_from (string | null): Formato: date. - data[].enroll_until (string | null): Formato: date. - data[].seats (integer | null): null = sin límite. - data[].certificate (boolean): - data[].created_at (string | null): Formato: date-time. - has_more (boolean; obligatorio): - next_cursor (string | null; obligatorio): Pásalo como `cursor` para la página siguiente. ## Estados documentados - 200: Una página de resultados. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request GET 'https://sch.begraffic.com/api/v1/periods?limit=25' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/periods?limit=25", { method: "GET", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, }, }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests response = requests.request( "GET", "https://sch.begraffic.com/api/v1/periods?limit=25", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 200 · ilustrativa ```json { "object": "list", "data": [ { "cover_url": null, "cover_source": "period", "cover_expires_at": "2026-10-21T12:00:00.000Z", "cover_mode": "auto", "id": "id_ejemplo", "object": "period", "title": "Introducción al liderazgo", "target": { "kind": "course", "id": "id_ejemplo" }, "course_id": "curso-ejemplo", "parent_period_id": null, "independent_purchase": true, "lesson_ids": [ "ejemplo" ], "kind": "open", "status": "active", "visible": true, "public_from": "2026-10-21T12:00:00.000Z", "starts_at": "2026-10-21", "ends_at": "2026-10-21", "enroll_from": "2026-10-21", "enroll_until": "2026-10-21", "seats": 30, "certificate": true, "created_at": "2026-10-21T12:00:00.000Z" } ], "has_more": false, "next_cursor": null } ``` --- # GET /periods/{id} — Un periodo con contenido, compras y membresías Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: catalog:read. Lo que necesita una web de venta: `offers` para comprar el periodo completo y `content` con fichas de sus cursos y clases publicadas. Cada ficha incluye `independent_sale` (habilitación, disponibilidad, periodo individual, precio en céntimos y checkout propio) y `membership_offers` para presentar Comprar este curso/clase o Acceder con membresía. `available=false` deja el enlace individual en null. Las membresías incluyen presentación, beneficios, insignia, destacado y aporte social; `scope` conserva su periodo real, incluso si es el padre de una venta individual. No implica acceso general ni al curso entero si lesson_ids limita las clases. `seats_available` null significa sin límite. El checkout valida de nuevo acceso y cobro. Permiso: `catalog:read`. ## Parámetros - id (path · string; obligatorio): ## Cuerpo JSON Sin cuerpo. ## Campos de respuesta - cover_url (string | null): Portada resuelta, lista para mostrar. null si no hay imagen disponible. Nunca una ruta de Storage. Formato: uri. - cover_source (string | null): Origen de la imagen elegida. Valores: "period", "course", "lesson", null. - cover_expires_at (string | null): Refresca la respuesta antes de esta fecha para renovar la URL firmada. null para miniaturas o portadas servidas por la aplicación sin caducidad, o si no hay imagen. Formato: date-time. - cover_mode (string): auto: primera portada del contenido en orden, curso y luego clases publicadas; custom: portada propia del periodo. Valores: "auto", "custom". - id (string): - object ("period"): - title (string): - target (object): - target.kind (string): Valores: "course", "single", "path", "cycle". - target.id (string): - course_id (string | null): null en un periodo de ruta o de ciclo. - parent_period_id (string | null): Periodo de origen de una venta independiente; null en aperturas principales. - independent_purchase (boolean): Este periodo abre una compra independiente, sin conceder acceso a la ruta o carrera completa. - lesson_ids (array | null): Clases originales que abre; null = curso completo, [] = alcance vacío o inválido. Nunca asumir curso entero por course_id si esta lista está limitada. - kind (string): Valores: "open", "dated". - status (string): Valores: "active", "archived". - visible (boolean): Se ve y se vende en la web. - public_from (string | null): Formato: date-time. - starts_at (string | null): Formato: date. - ends_at (string | null): Formato: date. - enroll_from (string | null): Formato: date. - enroll_until (string | null): Formato: date. - seats (integer | null): null = sin límite. - certificate (boolean): - created_at (string | null): Formato: date-time. - seats_taken (integer): - seats_available (integer | null): null = sin límite. - offers (array): - offers[].id (string): - offers[].object ("offer"): - offers[].title (string | null): - offers[].period_id (string | null): - offers[].course_id (string | null): - offers[].parent_period_id (string | null): Periodo de origen cuando esta oferta vende un curso o clase de forma independiente. - offers[].lesson_ids (array | null): Alcance por clase del periodo de esta oferta; null abre el curso entero. No presentar una oferta con clases limitadas como precio del curso completo. - offers[].type (string): Valores: "one_time", "subscription", "installments", "cohort", "free". - offers[].price (integer): Céntimos (de cada cuota en suscripciones y plazos). - offers[].currency (string): - offers[].access_kind (string): - offers[].access_days (integer | null): - offers[].installment_count (integer | null): - offers[].billing_interval (string | null): Valores: "weekly", "biweekly", "monthly", "quarterly", null. - offers[].seats (integer | null): - offers[].status (string): Valores: "active", "archived". - offers[].checkout_url (string | null): Formato: uri. - offers[].created_at (string | null): Formato: date-time. - membership_offers (array): - membership_offers[].id (string; obligatorio): - membership_offers[].title (string; obligatorio): - membership_offers[].pitch (string; obligatorio): Frase de presentación en texto plano; cadena vacía si no se configuró. - membership_offers[].description (string; obligatorio): Descripción pública en texto plano; cadena vacía si no se configuró. - membership_offers[].benefits (array; obligatorio): - membership_offers[].benefits[].id (string; obligatorio): Identificador de la línea en este plan (tipo y posición); apto como clave de renderizado, no como permiso de acceso ni identificador permanente si se reordenan beneficios. - membership_offers[].benefits[].text (string; obligatorio): Texto público listo para mostrar como texto, no como HTML. Incluye los detalles configurados. - membership_offers[].benefits[].included (boolean; obligatorio): Si está incluido. Hoy, como en la página pública, solo se enumeran los beneficios incluidos (true); no se inventan exclusiones ni se heredan beneficios. - membership_offers[].badge (string; obligatorio): Insignia de comunidad; cadena vacía si no se configuró. No es la etiqueta del plan destacado. - membership_offers[].featured (boolean; obligatorio): Misma regla de la página: segundo de dos planes, central con tres o más; false si solo hay uno. Depende del orden de los planes ofrecidos en este periodo. - membership_offers[].catalog (string; obligatorio): Texto público del alcance que se muestra en la tarjeta; no implica acceso al catálogo general. - membership_offers[].scope (object; obligatorio): - membership_offers[].scope.kind ("period"; obligatorio): Estas ofertas se contratan para un periodo, nunca como membresía general. - membership_offers[].scope.id (string; obligatorio): ID del periodo donde se contrata esta membresía; puede ser el periodo de origen de una venta individual. - membership_offers[].scope.target (object; obligatorio): - membership_offers[].scope.target.kind (string; obligatorio): Valores: "course", "single", "path". - membership_offers[].scope.target.id (string; obligatorio): ID público del contenido de ese periodo; path abre esa ruta dentro del periodo, no toda la escuela. - membership_offers[].contribution (oneOf; obligatorio): - membership_offers[].contribution.[opción 1].monthly_amount (integer; obligatorio): Parte de la cuota mensual destinada al aporte, en céntimos de currency. Mínimo: 1. - membership_offers[].contribution.[opción 1].currency (string; obligatorio): Moneda ISO 4217 del plan. - membership_offers[].contribution.[opción 1].label (string; obligatorio): Destino público del aporte. - membership_offers[].contribution.[opción 1].text (string; obligatorio): Frase completa con dinero formateado en el locale de la escuela, idéntica a la tarjeta pública. - membership_offers[].monthly_price (integer; obligatorio): Precio mensual en céntimos. - membership_offers[].yearly_price (integer | null; obligatorio): Precio anual en céntimos; null si solo se paga al mes. - membership_offers[].currency (string; obligatorio): - membership_offers[].checkout_url (string; obligatorio): Página de membresía del periodo, con ancla al plan y selector mensual/anual. Formato: uri. - content (array): - content[].kind (string; obligatorio): Valores: "course", "single", "lesson". - content[].course_id (string; obligatorio): - content[].lesson_id (string | null; obligatorio): null en la ficha del curso; ID original en una clase. - content[].title (string; obligatorio): - content[].position (integer; obligatorio): Posición del curso en su ruta o de la clase en su curso, empezando por 1. - content[].lesson_kind (string | null; obligatorio): Tipo de clase (video, text, quiz, live…); null en un curso. - content[].independent_sale (object; obligatorio): - content[].independent_sale.enabled (boolean; obligatorio): Existe una venta independiente activa y coherente para este curso o clase dentro del periodo. - content[].independent_sale.available (boolean; obligatorio): La venta está publicada, su contenido está listo, admite inscripciones y el precio es válido. Es disponibilidad de catálogo; el checkout vuelve a validar acceso, cupo y métodos de cobro. - content[].independent_sale.period_id (string | null; obligatorio): Periodo individual que abre únicamente este contenido; null si no existe una configuración coherente. - content[].independent_sale.unavailable_reason (string | null; obligatorio): Motivo legible de indisponibilidad; null si available=true. - content[].independent_sale.offer (oneOf; obligatorio): Oferta propia con precio. null cuando la venta está desactivada o su alcance no coincide. - content[].independent_sale.offer.[opción 1].id (string; obligatorio): - content[].independent_sale.offer.[opción 1].title (string; obligatorio): - content[].independent_sale.offer.[opción 1].price (integer; obligatorio): Precio de la compra independiente, en céntimos de currency; por cuota si type es installments o subscription. - content[].independent_sale.offer.[opción 1].type (string; obligatorio): one_time en la configuración inicial de venta independiente; conserva el tipo real si se modifica desde las ofertas del periodo. Valores: "one_time", "subscription", "installments", "cohort", "free". - content[].independent_sale.offer.[opción 1].installment_count (integer | null; obligatorio): Cantidad de cuotas si type=installments; null en otro caso. - content[].independent_sale.offer.[opción 1].billing_interval (string | null; obligatorio): Frecuencia si type=subscription (monthly por defecto); null en otro caso. - content[].independent_sale.offer.[opción 1].currency (string; obligatorio): Moneda ISO 4217. - content[].independent_sale.offer.[opción 1].checkout_url (string | null; obligatorio): Enlace de compra de esta oferta; null si available=false. No usar el checkout del periodo principal para comprar una sola clase. Formato: uri. - content[].membership_offers (array; obligatorio): Planes que incluyen este contenido por el periodo solicitado, su periodo de curso o el de origen de una venta individual. scope.id y checkout_url conservan el alcance real; el mismo plan puede aparecer para distintos alcances. - content[].membership_offers[].id (string; obligatorio): - content[].membership_offers[].title (string; obligatorio): - content[].membership_offers[].pitch (string; obligatorio): Frase de presentación en texto plano; cadena vacía si no se configuró. - content[].membership_offers[].description (string; obligatorio): Descripción pública en texto plano; cadena vacía si no se configuró. - content[].membership_offers[].benefits (array; obligatorio): - content[].membership_offers[].benefits[].id (string; obligatorio): Identificador de la línea en este plan (tipo y posición); apto como clave de renderizado, no como permiso de acceso ni identificador permanente si se reordenan beneficios. - content[].membership_offers[].benefits[].text (string; obligatorio): Texto público listo para mostrar como texto, no como HTML. Incluye los detalles configurados. - content[].membership_offers[].benefits[].included (boolean; obligatorio): Si está incluido. Hoy, como en la página pública, solo se enumeran los beneficios incluidos (true); no se inventan exclusiones ni se heredan beneficios. - content[].membership_offers[].badge (string; obligatorio): Insignia de comunidad; cadena vacía si no se configuró. No es la etiqueta del plan destacado. - content[].membership_offers[].featured (boolean; obligatorio): Misma regla de la página: segundo de dos planes, central con tres o más; false si solo hay uno. Depende del orden de los planes ofrecidos en este periodo. - content[].membership_offers[].catalog (string; obligatorio): Texto público del alcance que se muestra en la tarjeta; no implica acceso al catálogo general. - content[].membership_offers[].scope (object; obligatorio): - content[].membership_offers[].scope.kind ("period"; obligatorio): Estas ofertas se contratan para un periodo, nunca como membresía general. - content[].membership_offers[].scope.id (string; obligatorio): ID del periodo donde se contrata esta membresía; puede ser el periodo de origen de una venta individual. - content[].membership_offers[].scope.target (object; obligatorio): - content[].membership_offers[].scope.target.kind (string; obligatorio): Valores: "course", "single", "path". - content[].membership_offers[].scope.target.id (string; obligatorio): ID público del contenido de ese periodo; path abre esa ruta dentro del periodo, no toda la escuela. - content[].membership_offers[].contribution (oneOf; obligatorio): - content[].membership_offers[].contribution.[opción 1].monthly_amount (integer; obligatorio): Parte de la cuota mensual destinada al aporte, en céntimos de currency. Mínimo: 1. - content[].membership_offers[].contribution.[opción 1].currency (string; obligatorio): Moneda ISO 4217 del plan. - content[].membership_offers[].contribution.[opción 1].label (string; obligatorio): Destino público del aporte. - content[].membership_offers[].contribution.[opción 1].text (string; obligatorio): Frase completa con dinero formateado en el locale de la escuela, idéntica a la tarjeta pública. - content[].membership_offers[].monthly_price (integer; obligatorio): Precio mensual en céntimos. - content[].membership_offers[].yearly_price (integer | null; obligatorio): Precio anual en céntimos; null si solo se paga al mes. - content[].membership_offers[].currency (string; obligatorio): - content[].membership_offers[].checkout_url (string; obligatorio): Página de membresía del periodo, con ancla al plan y selector mensual/anual. Formato: uri. ## Estados documentados - 200: Encontrado. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 404: No existe. - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request GET 'https://sch.begraffic.com/api/v1/periods/id_ejemplo' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/periods/id_ejemplo", { method: "GET", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, }, }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests response = requests.request( "GET", "https://sch.begraffic.com/api/v1/periods/id_ejemplo", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 200 · ilustrativa ```json { "cover_url": null, "cover_source": "period", "cover_expires_at": "2026-10-21T12:00:00.000Z", "cover_mode": "auto", "id": "id_ejemplo", "object": "period", "title": "Introducción al liderazgo", "target": { "kind": "course", "id": "id_ejemplo" }, "course_id": "curso-ejemplo", "parent_period_id": null, "independent_purchase": true, "lesson_ids": [ "ejemplo" ], "kind": "open", "status": "active", "visible": true, "public_from": "2026-10-21T12:00:00.000Z", "starts_at": "2026-10-21", "ends_at": "2026-10-21", "enroll_from": "2026-10-21", "enroll_until": "2026-10-21", "seats": 30, "certificate": true, "created_at": "2026-10-21T12:00:00.000Z", "seats_taken": 12, "seats_available": 18, "offers": [ { "id": "id_ejemplo", "object": "offer", "title": "Introducción al liderazgo", "period_id": "periodo-ejemplo", "course_id": "curso-ejemplo", "parent_period_id": null, "lesson_ids": [ "ejemplo" ], "type": "one_time", "price": 4900, "currency": "USD", "access_kind": "ejemplo", "access_days": 1, "installment_count": 1, "billing_interval": "weekly", "seats": 30, "status": "active", "checkout_url": "https://sch.begraffic.com/tu-escuela/checkout?curso=lid-101&oferta=oferta-ejemplo", "created_at": "2026-10-21T12:00:00.000Z" } ], "membership_offers": [ { "id": "id_ejemplo", "title": "Introducción al liderazgo", "pitch": "ejemplo", "description": "ejemplo", "benefits": [ { "id": "id_ejemplo", "text": "ejemplo", "included": true } ], "badge": "ejemplo", "featured": true, "catalog": "ejemplo", "scope": { "kind": "period", "id": "id_ejemplo", "target": { "kind": "course", "id": "id_ejemplo" } }, "contribution": { "monthly_amount": 1, "currency": "USD", "label": "ejemplo", "text": "ejemplo" }, "monthly_price": 1, "yearly_price": 1, "currency": "USD", "checkout_url": "https://sch.begraffic.com/tu-escuela/checkout?curso=lid-101&oferta=oferta-ejemplo" } ], "content": [ { "kind": "course", "course_id": "curso-ejemplo", "lesson_id": null, "title": "Introducción al liderazgo", "position": 1, "lesson_kind": null, "independent_sale": { "enabled": true, "available": true, "period_id": "periodo-ejemplo", "unavailable_reason": null, "offer": { "id": "id_ejemplo", "title": "Introducción al liderazgo", "price": 4900, "type": "one_time", "installment_count": 1, "billing_interval": null, "currency": "USD", "checkout_url": "https://sch.begraffic.com/tu-escuela/checkout?curso=lid-101&oferta=oferta-ejemplo" } }, "membership_offers": [ { "id": "id_ejemplo", "title": "Introducción al liderazgo", "pitch": "ejemplo", "description": "ejemplo", "benefits": [ { "id": "id_ejemplo", "text": "ejemplo", "included": true } ], "badge": "ejemplo", "featured": true, "catalog": "ejemplo", "scope": { "kind": "period", "id": "id_ejemplo", "target": { "kind": "course", "id": "id_ejemplo" } }, "contribution": { "monthly_amount": 1, "currency": "USD", "label": "ejemplo", "text": "ejemplo" }, "monthly_price": 1, "yearly_price": 1, "currency": "USD", "checkout_url": "https://sch.begraffic.com/tu-escuela/checkout?curso=lid-101&oferta=oferta-ejemplo" } ] } ] } ``` --- # GET /offers — Precios (ofertas) Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: catalog:read. `price` en céntimos. `checkout_url` lleva al pago de la escuela (con tarjeta y lo que tenga activo); es `null` en las archivadas y en las de una ruta o edición, que se venden desde su página. Permiso: `catalog:read`. ## Parámetros - limit (query · integer): Por defecto: 25. Mínimo: 1. Máximo: 100. - cursor (query · string): - period_id (query · string): Las de ese periodo. - course_id (query · string): Las de ese curso. - status (query · string): Activas o archivadas. Valores: active, archived. ## Cuerpo JSON Sin cuerpo. ## Campos de respuesta - object ("list"; obligatorio): - data (array; obligatorio): - data[].id (string): - data[].object ("offer"): - data[].title (string | null): - data[].period_id (string | null): - data[].course_id (string | null): - data[].parent_period_id (string | null): Periodo de origen cuando esta oferta vende un curso o clase de forma independiente. - data[].lesson_ids (array | null): Alcance por clase del periodo de esta oferta; null abre el curso entero. No presentar una oferta con clases limitadas como precio del curso completo. - data[].type (string): Valores: "one_time", "subscription", "installments", "cohort", "free". - data[].price (integer): Céntimos (de cada cuota en suscripciones y plazos). - data[].currency (string): - data[].access_kind (string): - data[].access_days (integer | null): - data[].installment_count (integer | null): - data[].billing_interval (string | null): Valores: "weekly", "biweekly", "monthly", "quarterly", null. - data[].seats (integer | null): - data[].status (string): Valores: "active", "archived". - data[].checkout_url (string | null): Formato: uri. - data[].created_at (string | null): Formato: date-time. - has_more (boolean; obligatorio): - next_cursor (string | null; obligatorio): Pásalo como `cursor` para la página siguiente. ## Estados documentados - 200: Una página de resultados. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request GET 'https://sch.begraffic.com/api/v1/offers?limit=25' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/offers?limit=25", { method: "GET", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, }, }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests response = requests.request( "GET", "https://sch.begraffic.com/api/v1/offers?limit=25", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 200 · ilustrativa ```json { "object": "list", "data": [ { "id": "id_ejemplo", "object": "offer", "title": "Introducción al liderazgo", "period_id": "periodo-ejemplo", "course_id": "curso-ejemplo", "parent_period_id": null, "lesson_ids": [ "ejemplo" ], "type": "one_time", "price": 4900, "currency": "USD", "access_kind": "ejemplo", "access_days": 1, "installment_count": 1, "billing_interval": "weekly", "seats": 30, "status": "active", "checkout_url": "https://sch.begraffic.com/tu-escuela/checkout?curso=lid-101&oferta=oferta-ejemplo", "created_at": "2026-10-21T12:00:00.000Z" } ], "has_more": false, "next_cursor": null } ``` --- # GET /programs — Programas (carreras) Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: catalog:read. Permiso: `catalog:read`. ## Parámetros - limit (query · integer): Por defecto: 25. Mínimo: 1. Máximo: 100. - cursor (query · string): - status (query · string): Solo los de ese estado. Valores: draft, published, archived. ## Cuerpo JSON Sin cuerpo. ## Campos de respuesta - object ("list"; obligatorio): - data (array; obligatorio): - data[].id (string): - data[].object ("program"): - data[].title (string): - data[].faculty_name (string | null): - data[].version (string | null): - data[].status (string): Valores: "draft", "published", "archived". - data[].pass_score (number | null): - data[].cycle_kind (string): Valores: "trimester", "semester", "other". - data[].enrollment_mode (string): Valores: "flexible", "full_load". - data[].subjects (array): - data[].subjects[].course_id (string | null): - data[].subjects[].cycle (integer | null): - data[].subjects[].credits (number | null): - data[].subjects[].hours (number | null): - data[].subjects[].required (boolean): - data[].subjects[].prerequisites (array): - data[].updated_at (string | null): Formato: date-time. - has_more (boolean; obligatorio): - next_cursor (string | null; obligatorio): Pásalo como `cursor` para la página siguiente. ## Estados documentados - 200: Una página de resultados. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request GET 'https://sch.begraffic.com/api/v1/programs?limit=25' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/programs?limit=25", { method: "GET", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, }, }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests response = requests.request( "GET", "https://sch.begraffic.com/api/v1/programs?limit=25", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 200 · ilustrativa ```json { "object": "list", "data": [ { "id": "id_ejemplo", "object": "program", "title": "Introducción al liderazgo", "faculty_name": null, "version": null, "status": "draft", "pass_score": 1, "cycle_kind": "trimester", "enrollment_mode": "flexible", "subjects": [ { "course_id": "curso-ejemplo", "cycle": 1, "credits": 1, "hours": 1, "required": true, "prerequisites": [ "ejemplo" ] } ], "updated_at": "2026-10-21T12:00:00.000Z" } ], "has_more": false, "next_cursor": null } ``` --- # GET /programs/{id} — Un programa con su plan de estudios Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: catalog:read. Permiso: `catalog:read`. ## Parámetros - id (path · string; obligatorio): ## Cuerpo JSON Sin cuerpo. ## Campos de respuesta - id (string): - object ("program"): - title (string): - faculty_name (string | null): - version (string | null): - status (string): Valores: "draft", "published", "archived". - pass_score (number | null): - cycle_kind (string): Valores: "trimester", "semester", "other". - enrollment_mode (string): Valores: "flexible", "full_load". - subjects (array): - subjects[].course_id (string | null): - subjects[].cycle (integer | null): - subjects[].credits (number | null): - subjects[].hours (number | null): - subjects[].required (boolean): - subjects[].prerequisites (array): - updated_at (string | null): Formato: date-time. ## Estados documentados - 200: Encontrado. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 404: No existe. - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request GET 'https://sch.begraffic.com/api/v1/programs/id_ejemplo' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/programs/id_ejemplo", { method: "GET", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, }, }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests response = requests.request( "GET", "https://sch.begraffic.com/api/v1/programs/id_ejemplo", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 200 · ilustrativa ```json { "id": "id_ejemplo", "object": "program", "title": "Introducción al liderazgo", "faculty_name": null, "version": null, "status": "draft", "pass_score": 1, "cycle_kind": "trimester", "enrollment_mode": "flexible", "subjects": [ { "course_id": "curso-ejemplo", "cycle": 1, "credits": 1, "hours": 1, "required": true, "prerequisites": [ "ejemplo" ] } ], "updated_at": "2026-10-21T12:00:00.000Z" } ``` --- # GET /students — Estudiantes de la escuela Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: students:read. Permiso: `students:read`. ## Parámetros - limit (query · integer): Por defecto: 25. Mínimo: 1. Máximo: 100. - cursor (query · string): - email (query · string): El que tiene ese correo (sin distinguir mayúsculas). ## Cuerpo JSON Sin cuerpo. ## Campos de respuesta - object ("list"; obligatorio): - data (array; obligatorio): - data[].id (string): - data[].object ("student"): - data[].email (string | null): Formato: email. - data[].name (string | null): - data[].status (string): Valores: "active", "invited", "suspended". - data[].created_at (string | null): Formato: date-time. - has_more (boolean; obligatorio): - next_cursor (string | null; obligatorio): Pásalo como `cursor` para la página siguiente. ## Estados documentados - 200: Una página de resultados. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request GET 'https://sch.begraffic.com/api/v1/students?limit=25' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/students?limit=25", { method: "GET", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, }, }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests response = requests.request( "GET", "https://sch.begraffic.com/api/v1/students?limit=25", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 200 · ilustrativa ```json { "object": "list", "data": [ { "id": "id_ejemplo", "object": "student", "email": "ana@example.com", "name": "Ana Pérez", "status": "active", "created_at": "2026-10-21T12:00:00.000Z" } ], "has_more": false, "next_cursor": null } ``` --- # POST /students — Dar de alta (e invitar) a un estudiante Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: students:write. Crea la cuenta si no existe y la membresía de estudiante. Si ya era estudiante, devuelve 200 sin cambiar nada. No matricula: para eso, `POST /enrollments`. Permiso: `students:write`. ## Parámetros Sin parámetros. ## Cuerpo JSON - email (string; obligatorio): Formato: email. - name (string; obligatorio): Longitud máxima: 120. - send_invite (boolean): Por defecto: true. ## Campos de respuesta - id (string): - object ("student"): - email (string): - name (string): - created (boolean): - invited (boolean): ## Estados documentados - 200: Ya era estudiante de la escuela. - 201: Dado de alta. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 409: Esa persona es del equipo o está suspendida. - 422: Válido, pero no se puede (sin plaza, fuera de plazo, límite del plan…). - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request POST 'https://sch.begraffic.com/api/v1/students' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" \ --header 'Content-Type: application/json' \ --data '{ "email": "ana@example.com", "name": "Ana Pérez", "send_invite": true }' ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/students", { method: "POST", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ "email": "ana@example.com", "name": "Ana Pérez", "send_invite": true }), }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests import json response = requests.request( "POST", "https://sch.begraffic.com/api/v1/students", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, json=json.loads('''{ "email": "ana@example.com", "name": "Ana Pérez", "send_invite": true }'''), timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 201 · ilustrativa ```json { "id": "id_ejemplo", "object": "student", "email": "ana@example.com", "name": "Ana Pérez", "created": true, "invited": true } ``` --- # GET /students/{id} — Un estudiante Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: students:read. Permiso: `students:read`. ## Parámetros - id (path · string; obligatorio): ## Cuerpo JSON Sin cuerpo. ## Campos de respuesta - id (string): - object ("student"): - email (string | null): Formato: email. - name (string | null): - status (string): Valores: "active", "invited", "suspended". - created_at (string | null): Formato: date-time. ## Estados documentados - 200: Encontrado. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 404: No existe. - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request GET 'https://sch.begraffic.com/api/v1/students/id_ejemplo' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/students/id_ejemplo", { method: "GET", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, }, }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests response = requests.request( "GET", "https://sch.begraffic.com/api/v1/students/id_ejemplo", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 200 · ilustrativa ```json { "id": "id_ejemplo", "object": "student", "email": "ana@example.com", "name": "Ana Pérez", "status": "active", "created_at": "2026-10-21T12:00:00.000Z" } ``` --- # GET /enrollments — Matrículas Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: enrollments:read. Permiso: `enrollments:read`. ## Parámetros - limit (query · integer): Por defecto: 25. Mínimo: 1. Máximo: 100. - cursor (query · string): - student_id (query · string): De ese estudiante. - course_id (query · string): De ese curso. - period_id (query · string): De ese periodo. ## Cuerpo JSON Sin cuerpo. ## Campos de respuesta - object ("list"; obligatorio): - data (array; obligatorio): - data[].id (string): - data[].object ("enrollment"): - data[].student_id (string | null): - data[].course_id (string | null): - data[].period_id (string | null): - data[].status (string): - data[].source (string | null): - data[].justification (string): Valores: "stripe", "manual_payment", "scholarship", "free", "unverified". - data[].starts_at (string | null): Formato: date-time. - data[].expires_at (string | null): Formato: date-time. - data[].closed_at (string | null): Formato: date-time. - data[].created_at (string | null): Formato: date-time. - has_more (boolean; obligatorio): - next_cursor (string | null; obligatorio): Pásalo como `cursor` para la página siguiente. ## Estados documentados - 200: Una página de resultados. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request GET 'https://sch.begraffic.com/api/v1/enrollments?limit=25' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/enrollments?limit=25", { method: "GET", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, }, }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests response = requests.request( "GET", "https://sch.begraffic.com/api/v1/enrollments?limit=25", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 200 · ilustrativa ```json { "object": "list", "data": [ { "id": "id_ejemplo", "object": "enrollment", "student_id": "uid_ejemplo", "course_id": "curso-ejemplo", "period_id": "periodo-ejemplo", "status": "ejemplo", "source": null, "justification": "scholarship", "starts_at": "2026-10-21T12:00:00.000Z", "expires_at": "2026-10-21T12:00:00.000Z", "closed_at": "2026-10-21T12:00:00.000Z", "created_at": "2026-10-21T12:00:00.000Z" } ], "has_more": false, "next_cursor": null } ``` --- # POST /enrollments — Matricular en un periodo, con justificación Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: enrollments:write. Las mismas reglas que en el Panel: plazas, fechas del periodo y requisitos académicos. La justificación es obligatoria: beca (con motivo), pago fuera de línea (método, referencia e importe en céntimos) o gratis (solo si el periodo tiene una oferta gratuita activa). Un pago con tarjeta no se registra aquí: manda al estudiante al `checkout_url` de la oferta. Permiso: `enrollments:write`. ## Parámetros Sin parámetros. ## Cuerpo JSON - student_id (string; obligatorio): - period_id (string; obligatorio): - justification (oneOf; obligatorio): - justification.[opción 1].kind ("scholarship"; obligatorio): - justification.[opción 1].reason (string; obligatorio): Longitud mínima: 5. Longitud máxima: 400. - justification.[opción 2].kind ("manual_payment"; obligatorio): - justification.[opción 2].method (string; obligatorio): Valores: "transfer", "cash", "card_terminal", "other". - justification.[opción 2].reference (string; obligatorio): Longitud mínima: 2. Longitud máxima: 120. - justification.[opción 2].amount (integer; obligatorio): En céntimos de la moneda de la escuela. Mínimo: 1. - justification.[opción 3].kind ("free"; obligatorio): ## Campos de respuesta - id (string): - object ("enrollment"): - student_id (string): - period_id (string): - justification (string): - created (boolean): ## Estados documentados - 200: Ya tenía matrícula en ese periodo. - 201: Matriculado. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 404: No existe. - 422: Válido, pero no se puede (sin plaza, fuera de plazo, límite del plan…). - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request POST 'https://sch.begraffic.com/api/v1/enrollments' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" \ --header 'Content-Type: application/json' \ --data '{ "student_id": "uid_ejemplo", "period_id": "periodo-ejemplo", "justification": { "kind": "scholarship", "reason": "Beca por convenio con la empresa" } }' ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/enrollments", { method: "POST", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ "student_id": "uid_ejemplo", "period_id": "periodo-ejemplo", "justification": { "kind": "scholarship", "reason": "Beca por convenio con la empresa" } }), }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests import json response = requests.request( "POST", "https://sch.begraffic.com/api/v1/enrollments", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, json=json.loads('''{ "student_id": "uid_ejemplo", "period_id": "periodo-ejemplo", "justification": { "kind": "scholarship", "reason": "Beca por convenio con la empresa" } }'''), timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 201 · ilustrativa ```json { "id": "id_ejemplo", "object": "enrollment", "student_id": "uid_ejemplo", "period_id": "periodo-ejemplo", "justification": "scholarship", "created": true } ``` --- # GET /enrollments/{id} — Una matrícula Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: enrollments:read. Permiso: `enrollments:read`. ## Parámetros - id (path · string; obligatorio): ## Cuerpo JSON Sin cuerpo. ## Campos de respuesta - id (string): - object ("enrollment"): - student_id (string | null): - course_id (string | null): - period_id (string | null): - status (string): - source (string | null): - justification (string): Valores: "stripe", "manual_payment", "scholarship", "free", "unverified". - starts_at (string | null): Formato: date-time. - expires_at (string | null): Formato: date-time. - closed_at (string | null): Formato: date-time. - created_at (string | null): Formato: date-time. ## Estados documentados - 200: Encontrado. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 404: No existe. - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request GET 'https://sch.begraffic.com/api/v1/enrollments/id_ejemplo' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/enrollments/id_ejemplo", { method: "GET", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, }, }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests response = requests.request( "GET", "https://sch.begraffic.com/api/v1/enrollments/id_ejemplo", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 200 · ilustrativa ```json { "id": "id_ejemplo", "object": "enrollment", "student_id": "uid_ejemplo", "course_id": "curso-ejemplo", "period_id": "periodo-ejemplo", "status": "ejemplo", "source": null, "justification": "scholarship", "starts_at": "2026-10-21T12:00:00.000Z", "expires_at": "2026-10-21T12:00:00.000Z", "closed_at": "2026-10-21T12:00:00.000Z", "created_at": "2026-10-21T12:00:00.000Z" } ``` --- # GET /orders — Pedidos y cobros Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: orders:read. Permiso: `orders:read`. ## Parámetros - limit (query · integer): Por defecto: 25. Mínimo: 1. Máximo: 100. - cursor (query · string): - status (query · string): Solo los de ese estado. Valores: paid, pending, pending_verification, failed, refunded, needs_review, rejected, expired, disputed_lost. - student_id (query · string): De ese estudiante. ## Cuerpo JSON Sin cuerpo. ## Campos de respuesta - object ("list"; obligatorio): - data (array; obligatorio): - data[].id (string): - data[].object ("order"): - data[].student_id (string | null): - data[].course_id (string | null): - data[].offer_id (string | null): - data[].email (string | null): - data[].amount (integer): Céntimos cobrados. - data[].refunded_amount (integer): - data[].currency (string | null): - data[].status (string | null): - data[].provider (string | null): Valores: "stripe", "scholarship", "transfer", null. - data[].created_at (string | null): Formato: date-time. - data[].paid_at (string | null): Formato: date-time. - data[].refunded_at (string | null): Formato: date-time. - has_more (boolean; obligatorio): - next_cursor (string | null; obligatorio): Pásalo como `cursor` para la página siguiente. ## Estados documentados - 200: Una página de resultados. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request GET 'https://sch.begraffic.com/api/v1/orders?limit=25' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/orders?limit=25", { method: "GET", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, }, }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests response = requests.request( "GET", "https://sch.begraffic.com/api/v1/orders?limit=25", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 200 · ilustrativa ```json { "object": "list", "data": [ { "id": "id_ejemplo", "object": "order", "student_id": "uid_ejemplo", "course_id": "curso-ejemplo", "offer_id": null, "email": "ana@example.com", "amount": 4900, "refunded_amount": 0, "currency": "USD", "status": null, "provider": "stripe", "created_at": "2026-10-21T12:00:00.000Z", "paid_at": "2026-10-21T12:00:00.000Z", "refunded_at": "2026-10-21T12:00:00.000Z" } ], "has_more": false, "next_cursor": null } ``` --- # GET /orders/{id} — Un pedido Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: orders:read. Permiso: `orders:read`. ## Parámetros - id (path · string; obligatorio): ## Cuerpo JSON Sin cuerpo. ## Campos de respuesta - id (string): - object ("order"): - student_id (string | null): - course_id (string | null): - offer_id (string | null): - email (string | null): - amount (integer): Céntimos cobrados. - refunded_amount (integer): - currency (string | null): - status (string | null): - provider (string | null): Valores: "stripe", "scholarship", "transfer", null. - created_at (string | null): Formato: date-time. - paid_at (string | null): Formato: date-time. - refunded_at (string | null): Formato: date-time. ## Estados documentados - 200: Encontrado. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 404: No existe. - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request GET 'https://sch.begraffic.com/api/v1/orders/id_ejemplo' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/orders/id_ejemplo", { method: "GET", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, }, }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests response = requests.request( "GET", "https://sch.begraffic.com/api/v1/orders/id_ejemplo", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 200 · ilustrativa ```json { "id": "id_ejemplo", "object": "order", "student_id": "uid_ejemplo", "course_id": "curso-ejemplo", "offer_id": null, "email": "ana@example.com", "amount": 4900, "refunded_amount": 0, "currency": "USD", "status": null, "provider": "stripe", "created_at": "2026-10-21T12:00:00.000Z", "paid_at": "2026-10-21T12:00:00.000Z", "refunded_at": "2026-10-21T12:00:00.000Z" } ``` --- # GET /certificates — Certificados emitidos Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: certificates:read. Permiso: `certificates:read`. ## Parámetros - limit (query · integer): Por defecto: 25. Mínimo: 1. Máximo: 100. - cursor (query · string): - student_id (query · string): De ese estudiante. - course_id (query · string): De ese curso. ## Cuerpo JSON Sin cuerpo. ## Campos de respuesta - object ("list"; obligatorio): - data (array; obligatorio): - data[].id (string): - data[].object ("certificate"): - data[].code (string | null): - data[].student_id (string | null): - data[].student_name (string | null): - data[].course_id (string | null): - data[].course_title (string | null): - data[].period_id (string | null): - data[].status (string): Valores: "issued", "revoked". - data[].issued_at (string | null): Formato: date-time. - data[].revoked_at (string | null): Formato: date-time. - has_more (boolean; obligatorio): - next_cursor (string | null; obligatorio): Pásalo como `cursor` para la página siguiente. ## Estados documentados - 200: Una página de resultados. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request GET 'https://sch.begraffic.com/api/v1/certificates?limit=25' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/certificates?limit=25", { method: "GET", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, }, }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests response = requests.request( "GET", "https://sch.begraffic.com/api/v1/certificates?limit=25", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 200 · ilustrativa ```json { "object": "list", "data": [ { "id": "id_ejemplo", "object": "certificate", "code": "LID-101", "student_id": "uid_ejemplo", "student_name": "Ana Pérez", "course_id": "curso-ejemplo", "course_title": "Introducción al liderazgo", "period_id": "periodo-ejemplo", "status": "issued", "issued_at": "2026-10-21T12:00:00.000Z", "revoked_at": "2026-10-21T12:00:00.000Z" } ], "has_more": false, "next_cursor": null } ``` --- # GET /certificates/{id} — Un certificado Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: certificates:read. Permiso: `certificates:read`. ## Parámetros - id (path · string; obligatorio): ## Cuerpo JSON Sin cuerpo. ## Campos de respuesta - id (string): - object ("certificate"): - code (string | null): - student_id (string | null): - student_name (string | null): - course_id (string | null): - course_title (string | null): - period_id (string | null): - status (string): Valores: "issued", "revoked". - issued_at (string | null): Formato: date-time. - revoked_at (string | null): Formato: date-time. ## Estados documentados - 200: Encontrado. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 404: No existe. - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request GET 'https://sch.begraffic.com/api/v1/certificates/id_ejemplo' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/certificates/id_ejemplo", { method: "GET", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, }, }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests response = requests.request( "GET", "https://sch.begraffic.com/api/v1/certificates/id_ejemplo", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 200 · ilustrativa ```json { "id": "id_ejemplo", "object": "certificate", "code": "LID-101", "student_id": "uid_ejemplo", "student_name": "Ana Pérez", "course_id": "curso-ejemplo", "course_title": "Introducción al liderazgo", "period_id": "periodo-ejemplo", "status": "issued", "issued_at": "2026-10-21T12:00:00.000Z", "revoked_at": "2026-10-21T12:00:00.000Z" } ``` --- # GET /progress — Clases completadas Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: progress:read. Permiso: `progress:read`. ## Parámetros - limit (query · integer): Por defecto: 25. Mínimo: 1. Máximo: 100. - cursor (query · string): - student_id (query · string): De ese estudiante. - course_id (query · string): De ese curso. - period_id (query · string): De ese periodo. ## Cuerpo JSON Sin cuerpo. ## Campos de respuesta - object ("list"; obligatorio): - data (array; obligatorio): - data[].id (string): - data[].object ("lesson_completion"): - data[].student_id (string | null): - data[].course_id (string | null): - data[].lesson_id (string | null): - data[].period_id (string | null): - data[].via (string | null): Valores: "manual", "video", "quiz", "assignment", "reading", null. - data[].completed_at (string | null): Formato: date-time. - has_more (boolean; obligatorio): - next_cursor (string | null; obligatorio): Pásalo como `cursor` para la página siguiente. ## Estados documentados - 200: Una página de resultados. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request GET 'https://sch.begraffic.com/api/v1/progress?limit=25' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/progress?limit=25", { method: "GET", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, }, }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests response = requests.request( "GET", "https://sch.begraffic.com/api/v1/progress?limit=25", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 200 · ilustrativa ```json { "object": "list", "data": [ { "id": "id_ejemplo", "object": "lesson_completion", "student_id": "uid_ejemplo", "course_id": "curso-ejemplo", "lesson_id": null, "period_id": "periodo-ejemplo", "via": "manual", "completed_at": "2026-10-21T12:00:00.000Z" } ], "has_more": false, "next_cursor": null } ``` --- # GET /admission_requests — Solicitudes de admisión Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: admissions:read. Permiso: `admissions:read`. ## Parámetros - limit (query · integer): Por defecto: 25. Mínimo: 1. Máximo: 100. - cursor (query · string): - status (query · string): Solo las de ese estado. Valores: pending, approved, rejected. - student_id (query · string): De ese estudiante. - program_id (query · string): A ese programa. ## Cuerpo JSON Sin cuerpo. ## Campos de respuesta - object ("list"; obligatorio): - data (array; obligatorio): - data[].id (string): - data[].object ("admission_request"): - data[].program_id (string | null): - data[].program_title (string | null): - data[].program_version (string | null): - data[].student_id (string | null): - data[].student_name (string | null): - data[].note (string): - data[].status (string): Valores: "pending", "approved", "rejected". - data[].cohort_id (string | null): - data[].created_at (string | null): Formato: date-time. - data[].resolved_at (string | null): Formato: date-time. - has_more (boolean; obligatorio): - next_cursor (string | null; obligatorio): Pásalo como `cursor` para la página siguiente. ## Estados documentados - 200: Una página de resultados. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request GET 'https://sch.begraffic.com/api/v1/admission_requests?limit=25' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/admission_requests?limit=25", { method: "GET", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, }, }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests response = requests.request( "GET", "https://sch.begraffic.com/api/v1/admission_requests?limit=25", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 200 · ilustrativa ```json { "object": "list", "data": [ { "id": "id_ejemplo", "object": "admission_request", "program_id": "programa-ejemplo", "program_title": "Formación en liderazgo", "program_version": null, "student_id": "uid_ejemplo", "student_name": "Ana Pérez", "note": "Me interesa el programa.", "status": "pending", "cohort_id": null, "created_at": "2026-10-21T12:00:00.000Z", "resolved_at": "2026-10-21T12:00:00.000Z" } ], "has_more": false, "next_cursor": null } ``` --- # POST /admission_requests — Solicitar admisión a un programa en nombre de un estudiante Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: admissions:write. El estudiante tiene que estar activo en la escuela (`POST /students`) y el programa publicado. No admite a nadie: la decide secretaría en el Panel y llega el evento correspondiente. Repetirla con la misma nota devuelve la misma solicitud (200). Permiso: `admissions:write`. ## Parámetros Sin parámetros. ## Cuerpo JSON - student_id (string; obligatorio): - program_id (string; obligatorio): - note (string): Por defecto: "". Longitud máxima: 2000. ## Campos de respuesta - id (string): - object ("admission_request"): - program_id (string): - program_title (string): - student_id (string): - status (string): Valores: "pending", "approved", "rejected". - created_at (string): Formato: date-time. - created (boolean): ## Estados documentados - 200: Ya existía esa solicitud. - 201: Solicitud enviada. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 422: Válido, pero no se puede (sin plaza, fuera de plazo, límite del plan…). - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request POST 'https://sch.begraffic.com/api/v1/admission_requests' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" \ --header 'Content-Type: application/json' \ --data '{ "student_id": "uid_ejemplo", "program_id": "programa-ejemplo", "note": "Me interesa el programa." }' ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/admission_requests", { method: "POST", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ "student_id": "uid_ejemplo", "program_id": "programa-ejemplo", "note": "Me interesa el programa." }), }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests import json response = requests.request( "POST", "https://sch.begraffic.com/api/v1/admission_requests", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, json=json.loads('''{ "student_id": "uid_ejemplo", "program_id": "programa-ejemplo", "note": "Me interesa el programa." }'''), timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 201 · ilustrativa ```json { "id": "id_ejemplo", "object": "admission_request", "program_id": "programa-ejemplo", "program_title": "Formación en liderazgo", "student_id": "uid_ejemplo", "status": "pending", "created_at": "2026-10-21T12:00:00.000Z", "created": true } ``` --- # GET /admission_requests/{id} — Una solicitud de admisión Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: admissions:read. Permiso: `admissions:read`. ## Parámetros - id (path · string; obligatorio): ## Cuerpo JSON Sin cuerpo. ## Campos de respuesta - id (string): - object ("admission_request"): - program_id (string | null): - program_title (string | null): - program_version (string | null): - student_id (string | null): - student_name (string | null): - note (string): - status (string): Valores: "pending", "approved", "rejected". - cohort_id (string | null): - created_at (string | null): Formato: date-time. - resolved_at (string | null): Formato: date-time. ## Estados documentados - 200: Encontrado. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 404: No existe. - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request GET 'https://sch.begraffic.com/api/v1/admission_requests/id_ejemplo' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/admission_requests/id_ejemplo", { method: "GET", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, }, }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests response = requests.request( "GET", "https://sch.begraffic.com/api/v1/admission_requests/id_ejemplo", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 200 · ilustrativa ```json { "id": "id_ejemplo", "object": "admission_request", "program_id": "programa-ejemplo", "program_title": "Formación en liderazgo", "program_version": null, "student_id": "uid_ejemplo", "student_name": "Ana Pérez", "note": "Me interesa el programa.", "status": "pending", "cohort_id": null, "created_at": "2026-10-21T12:00:00.000Z", "resolved_at": "2026-10-21T12:00:00.000Z" } ``` --- # POST /hooks — Suscribirse a un evento (REST Hook) Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: webhooks:write. Permiso: `webhooks:write`. ## Parámetros Sin parámetros. ## Cuerpo JSON - target_url (string; obligatorio): https:// pública. Formato: uri. - event (string; obligatorio): Valores: "student.created", "enrollment.created", "enrollment.closed", "order.paid", "order.refunded", "submission.created", "submission.graded", "certificate.issued", "lesson.completed", "course.completed", "transfer.approved", "coupon.redeemed", "installment.paid", "installment.overdue", "invoice.issued". ## Campos de respuesta - id (string): - object ("hook"): - event (string): - target_url (string): - secret (string): Para verificar la firma. Solo se enseña aquí. ## Estados documentados - 201: Suscrito. Guarda el `id` para darte de baja. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 422: Válido, pero no se puede (sin plaza, fuera de plazo, límite del plan…). - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request POST 'https://sch.begraffic.com/api/v1/hooks' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" \ --header 'Content-Type: application/json' \ --data '{ "target_url": "https://tu-servidor.example/webhooks/beschool", "event": "enrollment.created" }' ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/hooks", { method: "POST", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ "target_url": "https://tu-servidor.example/webhooks/beschool", "event": "enrollment.created" }), }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests import json response = requests.request( "POST", "https://sch.begraffic.com/api/v1/hooks", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, json=json.loads('''{ "target_url": "https://tu-servidor.example/webhooks/beschool", "event": "enrollment.created" }'''), timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 201 · ilustrativa ```json { "id": "id_ejemplo", "object": "hook", "event": "enrollment.created", "target_url": "https://tu-servidor.example/webhooks/beschool", "secret": "whsec_EJEMPLO_GUARDAR_EN_EL_SERVIDOR" } ``` --- # DELETE /hooks/{id} — Darse de baja de un REST Hook Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Permiso: webhooks:write. Permiso: `webhooks:write`. ## Parámetros - id (path · string; obligatorio): ## Cuerpo JSON Sin cuerpo. ## Campos de respuesta - id (string): - object ("hook"): - deleted (true): ## Estados documentados - 200: Baja hecha. - 400: Petición no válida. - 401: Falta la clave o no es válida. - 403: La clave no tiene el permiso de esa operación, la escuela está suspendida o su plan no incluye la API y no tiene una gracia de API vigente. - 404: No existe. - 429: Demasiadas peticiones. Mira `Retry-After`. ## cURL ```bash curl --request DELETE 'https://sch.begraffic.com/api/v1/hooks/hook_ejemplo' \ --header "Authorization: Bearer $BESCHOOL_API_KEY" ``` ## JavaScript ```javascript // Ejecutar en tu servidor (Node.js). const response = await fetch("https://sch.begraffic.com/api/v1/hooks/hook_ejemplo", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.BESCHOOL_API_KEY}`, }, }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${result.error?.message}`); console.log(result); ``` ## Python ```python # Servidor. Requiere requests: pip install requests import os import requests response = requests.request( "DELETE", "https://sch.begraffic.com/api/v1/hooks/hook_ejemplo", headers={"Authorization": "Bearer " + os.environ["BESCHOOL_API_KEY"]}, timeout=30, ) response.raise_for_status() print(response.json()) ``` ## Respuesta 200 · ilustrativa ```json { "id": "id_ejemplo", "object": "hook", "deleted": true } ``` --- # student.created Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Estudiante dado de alta Sobre ilustrativo. data puede variar según el punto de emisión; valida los campos que usa tu integración. Verifica la firma sobre el cuerpo original y deduplica por (school, id) antes de procesarlo. ## Payload ilustrativo ```json { "id": "evt_ejemplo", "type": "student.created", "created": 1792584000, "school": "tu-escuela", "data": { "studentId": "uid_ejemplo", "email": "ana@example.com", "name": "Ana Pérez" } } ``` --- # enrollment.created Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Matrícula nueva Sobre ilustrativo. data puede variar según el punto de emisión; valida los campos que usa tu integración. Verifica la firma sobre el cuerpo original y deduplica por (school, id) antes de procesarlo. ## Payload ilustrativo ```json { "id": "evt_ejemplo", "type": "enrollment.created", "created": 1792584000, "school": "tu-escuela", "data": { "studentId": "uid_ejemplo", "email": "ana@example.com", "name": "Ana Pérez", "courseId": "curso-ejemplo", "courseTitle": "Curso de ejemplo", "periodId": "periodo-ejemplo", "enrollmentId": "uid_ejemplo__periodo-ejemplo" } } ``` --- # enrollment.closed Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Matrícula cerrada o retirada Sobre ilustrativo. data puede variar según el punto de emisión; valida los campos que usa tu integración. Verifica la firma sobre el cuerpo original y deduplica por (school, id) antes de procesarlo. ## Payload ilustrativo ```json { "id": "evt_ejemplo", "type": "enrollment.closed", "created": 1792584000, "school": "tu-escuela", "data": { "studentId": "uid_ejemplo", "email": "ana@example.com", "name": "Ana Pérez", "courseId": "curso-ejemplo", "courseTitle": "Curso de ejemplo", "periodId": "periodo-ejemplo", "enrollmentId": "uid_ejemplo__periodo-ejemplo" } } ``` --- # order.paid Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Pago recibido Sobre ilustrativo. data puede variar según el punto de emisión; valida los campos que usa tu integración. Verifica la firma sobre el cuerpo original y deduplica por (school, id) antes de procesarlo. ## Payload ilustrativo ```json { "id": "evt_ejemplo", "type": "order.paid", "created": 1792584000, "school": "tu-escuela", "data": { "studentId": "uid_ejemplo", "email": "ana@example.com", "name": "Ana Pérez", "courseId": "curso-ejemplo", "courseTitle": "Curso de ejemplo", "orderId": "pedido-ejemplo", "amount": 4900, "currency": "USD" } } ``` --- # order.refunded Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Pago reembolsado Sobre ilustrativo. data puede variar según el punto de emisión; valida los campos que usa tu integración. Verifica la firma sobre el cuerpo original y deduplica por (school, id) antes de procesarlo. ## Payload ilustrativo ```json { "id": "evt_ejemplo", "type": "order.refunded", "created": 1792584000, "school": "tu-escuela", "data": { "studentId": "uid_ejemplo", "email": "ana@example.com", "name": "Ana Pérez", "courseId": "curso-ejemplo", "courseTitle": "Curso de ejemplo", "orderId": "pedido-ejemplo", "amount": 4900, "currency": "USD" } } ``` --- # submission.created Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Entrega recibida Sobre ilustrativo. data puede variar según el punto de emisión; valida los campos que usa tu integración. Verifica la firma sobre el cuerpo original y deduplica por (school, id) antes de procesarlo. ## Payload ilustrativo ```json { "id": "evt_ejemplo", "type": "submission.created", "created": 1792584000, "school": "tu-escuela", "data": { "studentId": "uid_ejemplo", "email": "ana@example.com", "name": "Ana Pérez", "courseId": "curso-ejemplo", "courseTitle": "Curso de ejemplo", "lessonId": "clase-ejemplo", "submissionId": "entrega-ejemplo", "grade": 90 } } ``` --- # submission.graded Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Entrega calificada Sobre ilustrativo. data puede variar según el punto de emisión; valida los campos que usa tu integración. Verifica la firma sobre el cuerpo original y deduplica por (school, id) antes de procesarlo. ## Payload ilustrativo ```json { "id": "evt_ejemplo", "type": "submission.graded", "created": 1792584000, "school": "tu-escuela", "data": { "studentId": "uid_ejemplo", "email": "ana@example.com", "name": "Ana Pérez", "courseId": "curso-ejemplo", "courseTitle": "Curso de ejemplo", "lessonId": "clase-ejemplo", "submissionId": "entrega-ejemplo", "grade": 90 } } ``` --- # certificate.issued Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Certificado emitido Sobre ilustrativo. data puede variar según el punto de emisión; valida los campos que usa tu integración. Verifica la firma sobre el cuerpo original y deduplica por (school, id) antes de procesarlo. ## Payload ilustrativo ```json { "id": "evt_ejemplo", "type": "certificate.issued", "created": 1792584000, "school": "tu-escuela", "data": { "studentId": "uid_ejemplo", "email": "ana@example.com", "name": "Ana Pérez", "courseId": "curso-ejemplo", "courseTitle": "Curso de ejemplo", "certificateId": "cert-ejemplo", "code": "ABCD-1234" } } ``` --- # lesson.completed Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Clase completada Sobre ilustrativo. data puede variar según el punto de emisión; valida los campos que usa tu integración. Verifica la firma sobre el cuerpo original y deduplica por (school, id) antes de procesarlo. ## Payload ilustrativo ```json { "id": "evt_ejemplo", "type": "lesson.completed", "created": 1792584000, "school": "tu-escuela", "data": { "studentId": "uid_ejemplo", "email": "ana@example.com", "name": "Ana Pérez", "courseId": "curso-ejemplo", "courseTitle": "Curso de ejemplo", "lessonId": "clase-ejemplo" } } ``` --- # course.completed Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Curso completado Sobre ilustrativo. data puede variar según el punto de emisión; valida los campos que usa tu integración. Verifica la firma sobre el cuerpo original y deduplica por (school, id) antes de procesarlo. ## Payload ilustrativo ```json { "id": "evt_ejemplo", "type": "course.completed", "created": 1792584000, "school": "tu-escuela", "data": { "studentId": "uid_ejemplo", "email": "ana@example.com", "name": "Ana Pérez", "courseId": "curso-ejemplo", "courseTitle": "Curso de ejemplo" } } ``` --- # transfer.approved Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Transferencia aprobada Sobre ilustrativo. data puede variar según el punto de emisión; valida los campos que usa tu integración. Verifica la firma sobre el cuerpo original y deduplica por (school, id) antes de procesarlo. ## Payload ilustrativo ```json { "id": "evt_ejemplo", "type": "transfer.approved", "created": 1792584000, "school": "tu-escuela", "data": { "studentId": "uid_ejemplo", "email": "ana@example.com", "name": "Ana Pérez", "courseId": "curso-ejemplo", "courseTitle": "Curso de ejemplo", "orderId": "pedido-ejemplo", "amount": 4900, "currency": "USD" } } ``` --- # coupon.redeemed Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Cupón canjeado Sobre ilustrativo. data puede variar según el punto de emisión; valida los campos que usa tu integración. Verifica la firma sobre el cuerpo original y deduplica por (school, id) antes de procesarlo. ## Payload ilustrativo ```json { "id": "evt_ejemplo", "type": "coupon.redeemed", "created": 1792584000, "school": "tu-escuela", "data": { "studentId": "uid_ejemplo", "orderId": "pedido-ejemplo", "couponId": "BIENVENIDA10" } } ``` --- # installment.paid Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Cuota pagada Sobre ilustrativo. data puede variar según el punto de emisión; valida los campos que usa tu integración. Verifica la firma sobre el cuerpo original y deduplica por (school, id) antes de procesarlo. ## Payload ilustrativo ```json { "id": "evt_ejemplo", "type": "installment.paid", "created": 1792584000, "school": "tu-escuela", "data": { "studentId": "uid_ejemplo", "orderId": "pedido-ejemplo", "planId": "plan-ejemplo", "n": 2, "count": 3 } } ``` --- # installment.overdue Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Cuota vencida Sobre ilustrativo. data puede variar según el punto de emisión; valida los campos que usa tu integración. Verifica la firma sobre el cuerpo original y deduplica por (school, id) antes de procesarlo. ## Payload ilustrativo ```json { "id": "evt_ejemplo", "type": "installment.overdue", "created": 1792584000, "school": "tu-escuela", "data": { "studentId": "uid_ejemplo", "planId": "plan-ejemplo", "n": 2, "count": 3 } } ``` --- # invoice.issued Be School API v1. Base: https://sch.begraffic.com/api/v1. Autenticación desde el servidor: Authorization: Bearer . Cada clave pertenece a una escuela y requiere un plan con integraciones (desde Academia). Los ejemplos son ilustrativos: sustituye los ids y consulta los estados reales. Nunca expongas claves en el navegador ni en prompts. Factura o recibo emitido Sobre ilustrativo. data puede variar según el punto de emisión; valida los campos que usa tu integración. Verifica la firma sobre el cuerpo original y deduplica por (school, id) antes de procesarlo. ## Payload ilustrativo ```json { "id": "evt_ejemplo", "type": "invoice.issued", "created": 1792584000, "school": "tu-escuela", "data": { "studentId": "uid_ejemplo", "orderId": "pedido-ejemplo", "number": "F-000042", "kind": "invoice" } } ```