Permisos
read: lectura; read write: lectura y gestión. Los clientes de API los gestiona un usuario con sesión iniciada. El rol de facturación ve las facturas y la suscripción, pero no los empleados ni las redes.
Gestiona empresas, miembros, acceso a VPN y rutas mediante la misma API que usa el panel de empresa.
Primera versión: 169 métodos del contrato actual, no 169 de 191 requisitos cumplidos. El borrador usa otras rutas y procesos. Matriz completa de la primera versión: quedan excluidos SSO/SCIM, la transferencia de propiedad, el despliegue de firmware en routers, la exportación de configuraciones y los datos legales de las facturas. notification-channels es solo un inventario heredado: su contrato y la entrega por email/webhook/in_app quedan fuera de la primera versión. Siete tipos de alertas están disponibles en la monitorización, la auditoría y la API; la entrega mediante la API de webhooks firmados independiente sigue dentro del alcance. Por ahora, los routers y los recursos son solo inventario; billing-profile solo almacena metadatos sin vínculo con las facturas.
Ruta base: https://meduzacore.com/__api/api/business/v1. La interfaz usa la sesión de usuario actual. Para automatizar, crea un cliente de API en el panel de empresa y guarda el secreto en el momento de su emisión.
curl -X POST https://meduzacore.com/__api/api/business/v1/oauth/token \ -d grant_type=client_credentials \ -d client_id=YOUR_CLIENT_ID \ -d client_secret=YOUR_SECRET curl https://meduzacore.com/__api/api/business/v1/organizations/ORGANIZATION_ID/members \ -H "Authorization: Bearer TOKEN"
Envía el token obtenido en Authorization: Bearer TOKEN. El token es válido durante una hora y solo dentro de la empresa que emitió el cliente. El acceso se revoca al desactivar el cliente o al retirar los permisos del administrador que lo creó.
read: lectura; read write: lectura y gestión. Los clientes de API los gestiona un usuario con sesión iniciada. El rol de facturación ve las facturas y la suscripción, pero no los empleados ni las redes.
Para los cambios, usa Idempotency-Key. Al editar, envía If-Match: "3" con la versión actual del objeto. Ante un conflicto 412, vuelve a leer el objeto.
POST /organizations desde una sesión de usuario.subscription/quotes: número de VPN, periodo y operación purchase, renewal o upgrade.quote_id y payment_method: stripe_card a subscription/checkout.invoices hasta que aparezca payment_url y paga con ese enlace.pricing_id de catalog/vpn-locations y un Idempotency-Key obligatorio.El paquete empieza en 5 VPN. Precio base por VPN: $5.99 al mes o $54.99 al año. Descuento por paquete: 5–9 VPN — 5 %, 10–99 — 10 %, desde 100 — 20 %. Volver al navegador después de pagar no da acceso sin verificar el pago.
La renovación mantiene la cantidad y el periodo. Ampliar el paquete calcula un pago adicional por el tiempo restante. Para eliminar un servidor de forma definitiva, usa POST /organizations/{organization_id}/vpns/{id}/retire con confirm: true e If-Match. La plaza se libera tras confirmarse la eliminación; el precio de la suscripción no cambia. La asignación a un empleado se envía como member_id; sin él, la VPN está disponible para la empresa según las asignaciones de acceso.
El campo access de PATCH /organizations/{organization_id}/vpns/{id}, con If-Match, define quién puede usar una VPN de la empresa. company la abre a todos los miembros de la empresa cuyo rol permite conectarse; las listas member_ids y group_ids se vacían. restricted la limita a los empleados indicados y a los miembros de los grupos indicados; las listas vacías retiran el acceso a todos. company junto con listas no vacías se rechaza con 422. Las respuestas GET y /me/vpn-grants siempre incluyen data.access, también para las VPN creadas antes de que existiera el campo. Una solicitud sin access pero con member_ids o group_ids pasa la VPN a restricted, como antes. La VPN dedicada de un empleado siempre es restricted. Los cambios de modo se registran en la auditoría como vpn_access_changed; al restringir el acceso, se revocan las credenciales de los dispositivos que lo perdieron.
curl -X PATCH https://meduzacore.com/__api/api/business/v1/organizations/ORGANIZATION_ID/vpns/VPN_ID \
-H "Authorization: Bearer TOKEN" \
-H 'If-Match: "3"' \
-H "Idempotency-Key: vpn-access-1" \
-H "Content-Type: application/json" \
-d '{"access":"company"}'Un conjunto describe sitios o redes; una regla elige la acción: una VPN concreta, directo o bloqueo. Usa los países y regiones de catalog/traffic-presets. En el modo first-match las reglas se comprueban de arriba abajo, hasta 128; para «Resto del tráfico» no se puede bloquear.
Guardar crea un borrador. Ejecuta la validación y después publica con If-Match. Comprueba policy-deployments: estar en cola y estar aplicado con confirmación del dispositivo son estados distintos. La conexión a la VPN la realiza la aplicación cliente.
GET /organizations/{id}/roles devuelve los roles integrados y personalizados, las definiciones de permisos y el indicador can_manage. El propietario crea un rol personalizado a partir del de administrador, empleado o finanzas, dejando solo los permisos necesarios. La asignación se hace con PATCH members/{id}: role y custom_role_id. Los cambios de permisos también afectan a los clientes de API creados por ese usuario.
El rol permite acciones en el panel. group_ids asigna al empleado las VPN y un paquete de enrutamiento; pertenecer a un grupo no otorga permisos de administración. El rol de propietario no se puede copiar en uno personalizado. Un rol asignado a empleados no se puede eliminar.
Los dispositivos reciben un display_name, un propietario y datos de la VPN. PATCH devices/{id} solo cambia el nombre, con comprobación de If-Match. POST devices/{id}/revocations solicita la revocación; el estado final lo confirma el nodo. Tus propios dispositivos están disponibles en /me/devices. El campo last_api_ip es la dirección de transporte de la API, no la de la conexión VPN. El nombre y el modelo que comunica la aplicación no se usan para la autorización.
GET /organizations/{id}/employee-analytics muestra las conexiones observadas, los cambios de estado y el historial por horas de 7 días. Una medición que falta no se sustituye por cero; el número de sesiones no es el tiempo de trabajo del empleado.
GET /organizations/{id}/activity devuelve las solicitudes de conexión registradas por el nodo: dominio o IP solicitado, puerto, protocolo, hora, decisión, dispositivo y empleado. Filtros: from, to, member_id, action, search; páginas: cursor y limit. El dominio lo envía el cliente en la solicitud de conexión; no se obtiene descifrando HTTPS.
La recopilación está desactivada por defecto. El propietario cambia activity-settings con PATCH, {"enabled": true} e If-Match. La lectura requiere activity.read. Con la recopilación activada, la aplicación muestra un aviso al empleado; para emitir la clave se necesita una confirmación vigente de X-Corporate-Activity-Version tras verificar al propietario de la clave. Un cliente antiguo no recibe clave para este modo.
El almacenamiento está limitado a 7 días y 100 000 eventos por empresa; el búfer del nodo guarda hasta 24 horas. coverage informa de la actualidad, los dispositivos no disponibles y las pérdidas del búfer. No se registran las VPN personales, el tráfico directo del dispositivo, los bloqueos locales antes de llegar al nodo, las rutas HTTPS, el contenido de las solicitudes ni la evaluación de productividad. Desactivar la recopilación no recupera el historial perdido ni borra al instante los registros ya recopilados: se aplica el plazo de conservación.
La respuesta del registro contiene analysis: distribución por categorías, empleados y horas, destinos principales y el número de active_minutes. Los resúmenes se calculan sobre todos los eventos guardados del periodo elegido, independientemente de la página del registro original. El filtro category permite ir a los eventos concretos.
Un minuto activo es un minuto único de un empleado en el que el nodo observó una solicitud. No es el tiempo de visita de un sitio ni de trabajo: cuenta el tráfico en segundo plano y las categorías pueden coincidir en el mismo minuto. El tiempo total se calcula por separado y no es la suma de las categorías.
El propietario define hasta 64 reglas classification_rules en los ajustes de registro: dominio y categoría. Se aplican a los subdominios y tienen prioridad sobre el directorio integrado. Los destinos desconocidos quedan en una categoría aparte. Las reglas actuales clasifican todo el periodo elegido; la respuesta indica la versión de la clasificación.
Se procesan los destinos de las conexiones corporativas, no el contenido HTTPS, las URL completas ni el tráfico personal. La confirmación del aviso se guarda una sola vez por empleado en la empresa; cambiar las categorías, los grupos o el modo de recopilación no exige volver a confirmar el mismo aviso. Los indicadores de cobertura incompleta y truncated no deben interpretarse como ausencia de actividad.
Crea webhooks con una URL HTTPS pública en el puerto 443 y los eventos audit.event, * o groups.created, members.updated y otros cambios de recursos. El secreto solo se muestra al crearlo o rotarlo.
Meduza-Signature: t=UNIX_SECONDS,v1=HEX_SIGNATURE Meduza-Event-Id: EVENT_ID Meduza-Delivery-Id: DELIVERY_ID HEX_SIGNATURE = HMAC-SHA256(secret, timestamp + "." + RAW_JSON_BODY)
Verifica la firma sobre los bytes originales del cuerpo, la antigüedad admitida del timestamp y la repetición del event ID. Durante la rotación, conserva el secreto anterior para los eventos ya en cola. Cualquier 2xx es un éxito; las demás respuestas se reintentan con intervalos crecientes, hasta ocho intentos. El historial y el reenvío manual están disponibles en deliveries. Las entregas correctas y canceladas se guardan hasta 30 días, como máximo 2000 por webhook. La cola y los errores se limitan a 1000 eventos; si se desborda, se eliminan los más antiguos y el contador dropped_deliveries informa de la pérdida. En ese caso, sincroniza el estado mediante la API.
La entrega es al menos una vez. Además de los cambios hechos por la API, se envían los cambios confirmados de facturas (invoices.updated), suscripciones (subscriptions.updated), disponibilidad de VPN (vpns.updated) y las alertas (alerts.created). Volver a consultar sin cambio de estado no crea un evento nuevo. No es un flujo de tráfico de red. Están prohibidas las IP privadas, las redirecciones y las llamadas a servicios internos.
Cargando el contrato…
En las listas de inventario, envía limit=1..200 y después cursor de next_cursor, manteniendo los demás parámetros. Sin limit/cursor, los clientes antiguos reciben la lista completa: los datos nunca se recortan sin aviso. Están disponibles status, member_id, user_id, vpn_id, search y created_from/created_to (RFC3339, límite inferior incluido, superior excluido). El acceso se comprueba antes de filtrar y en cada página. Los parámetros desconocidos o repetidos devuelven 422. Las listas del registro de actividad usan un contrato aparte. Parámetros de cada método.
La auditoría administrativa se guarda hasta 90 días, como máximo los últimos 10 000 eventos por empresa; los registros de idempotencia caducados se limpian. Esta limpieza no elimina facturas ni suscripciones. Las listas de inventario devuelven items, total y next_cursor; la continuación aparece con un limit/cursor explícito. Sin ellos, la respuesta completa sigue siendo compatible con los clientes antiguos. El registro activity admite por separado un cursor y los filtros documentados. Los secretos no se repiten en las respuestas de la caché de idempotencia. La revocación confirmada de una clave llega del servidor VPN; desactivar una membresía impide de inmediato nuevas emisiones.
La monitorización muestra las conexiones y la carga reales. El historial de destinos está disponible mediante un registro aparte que se activa de forma explícita. El contenido HTTPS y la evaluación de productividad de los empleados no forman parte de estos datos. SSO/SCIM y otros métodos del borrador ampliado no forman parte de este contrato implementado.
Los errores de gestión devuelven application/problem+json con status, detail y request_id. Los errores de emisión del token OAuth usan el campo error.