Referencia de la API
Cada ruta que sirve la API, quién puede llamarla, y cómo funcionan el inicio de sesión, los espacios de trabajo, los permisos, la facturación, las claves de API, los errores, las listas y los límites.
Para el paquete Full Stack
URL base y formato de respuesta
Cada ruta se sirve bajo el prefijo /api. En local la URL base es http://localhost:8000/api. En un servidor es la dirección de tu API seguida de /api, el mismo valor que leen el sitio web y el panel de NEXT_PUBLIC_API_BASE_URL (Variables de entorno). Los archivos se sirven fuera del prefijo: los archivos de marca del kit en /media/… y los archivos subidos en /media/uploads/….
Los cuerpos de las peticiones son JSON, salvo las subidas de archivos. Cada respuesta llega en el mismo envoltorio:
{
"success": true,
"data": { },
"message": ""
}data contiene el resultado. message es un código corto que rellenan algunas escrituras, como project_created o invitation_sent, y está vacío en los demás casos. Los espacios de trabajo, proyectos y tareas se nombran por sus ids públicos (org_forma, prj_01); los miembros, invitaciones, claves y archivos por su número. Los textos de proyectos y tareas llevan ambos idiomas como { "en": { … }, "ar": { … } }.
La comprobación de estado no necesita token:
curl http://localhost:8000/api/health{"success":true,"data":{"status":"ok","database":"up"},"message":""}Hasta que la base de datos responde y tiene sus tablas, responde 503 con "status":"unavailable" y "database":"down". La comprobación de estado de la imagen de Docker lee ese estado, y Docker Compose inicia el sitio web y el panel solo cuando pasa.
Los navegadores solo pueden llamar a la API desde las direcciones de CORS_ORIGIN. Sin definir, permite http://localhost:3030 y http://localhost:3031, y en producción la API se niega a iniciar sin él (Problemas de CORS).
Errores
Un fallo conserva el envoltorio, con success: false, data: null y el estado HTTP:
{
"success": false,
"data": null,
"message": "seat_limit_reached",
"errors": { "email": ["…"] }
}errorsaparece en un fallo de validación (400), con los problemas por campo.- Un campo desconocido en un cuerpo se rechaza con
400: cada cuerpo acepta solo los campos listados abajo. - Cuando la API tiene una frase para el error,
messagees esa frase en el idioma de la petición (Accept-Language: arpara árabe): un inicio de sesión rechazado, todo429, los registros cerrados, una cuenta protegida frente a un cambio de contraseña o correo, un secreto de webhook ausente, una suscripción que Stripe no pudo cancelar, un rol o permiso de plataforma que quien llama no tiene, y el modo de mantenimiento, entre otros. - En los demás casos, en
401,403y404elmessagees una frase general del estado, y en400,402,409,410y503es el motivo como código corto (seat_limit_reached,slug_taken,over_plan_limit,payment_declined,preview_expired,invitation_expired,payment_unavailable), que un cliente convierte en su propia frase. Un cambio a un plan inferior rechazado por el uso lleva ademásblockers. 402significa un cambio a un plan superior que el proveedor de pago no quiso cobrar:payment_declined,payment_needs_action(el banco quiere que se confirme el pago) opayment_method_missing. El plan se queda como estaba.- Mientras el personal de la plataforma tiene activado el modo de mantenimiento, toda ruta fuera de la lista corta de Modo de mantenimiento responde
503a cualquiera que no sea personal de la plataforma, con la frase enmessageycode: "maintenance_mode". 401significa sin token, un token caducado, o credenciales incorrectas.403significa sesión iniciada sin el permiso, o una escritura en un espacio de trabajo suspendido.404también responde por un espacio de trabajo del que quien llama no es miembro, para que un extraño no pueda saber qué espacios existen.- Una petición por encima de un límite responde
429(Límites por dirección).
Inicio de sesión
Inicio de sesión
Terminalcurl -X POST http://localhost:8000/api/auth/login \ -H "Content-Type: application/json" \ -d '{"email":"owner@northstack.example","password":"YOUR_PASSWORD"}'Pon la contraseña de ejemplo de Instalación en lugar de
YOUR_PASSWORD, o usa una cuenta propia.data.access_tokenes el token, contoken_typeBearer,expires_inyadmin, la cuenta con sus roles de plataforma, permisos y espacios de trabajo.Llama a una ruta protegida
Terminalcurl http://localhost:8000/api/workspaces \ -H "Authorization: Bearer YOUR_TOKEN"Eso lista los espacios de trabajo de la cuenta con su rol en cada uno. Cada ruta de espacio de trabajo recibe entonces el id público del espacio en su ruta:
Terminalcurl "http://localhost:8000/api/workspaces/org_forma/projects?limit=20" \ -H "Authorization: Bearer YOUR_TOKEN"
remember: truefirma un token de 30 días yremember: falseuno de 12 horas; si se omite, el token duraJWT_EXPIRATION,7dpor defecto.- Un correo desconocido y una contraseña incorrecta responden ambos
401con el mismo mensaje, tras el mismo trabajo de hash de contraseña, de modo que ni la respuesta ni su tiempo indican qué correos tienen cuenta. - Diez inicios de sesión fallidos desde una misma dirección en 15 minutos responden
429hasta que el fallo más antiguo cumple 15 minutos (RATE_LIMIT_LOGIN). Los inicios de sesión correctos no cuentan. - Los tokens no guardan estado:
POST /api/auth/logoutresponde al instante, y cerrar sesión es que el cliente olvide su token.
| Método | Ruta | Quién puede llamarla | Qué hace |
|---|---|---|---|
POST | /api/auth/login | Cualquiera | Inicio de sesión: email, password, opcionalmente remember |
POST | /api/auth/signup | Cualquiera | Una cuenta nueva, con sesión iniciada al instante y lista para el alta inicial: name (de 2 a 80 caracteres), email, password (de 8 a 128) y acceptTerms (true). Un correo ya usado responde 409 con email_taken; mientras el ajuste signups_enabled de la plataforma está desactivado responde 403 |
POST | /api/auth/forgot-password | Cualquiera | email, opcionalmente locale (en, ar). Envía por correo un enlace de restablecimiento que funciona una vez durante una hora cuando la cuenta existe, y responde igual en ambos casos |
POST | /api/auth/reset-password | Cualquiera | token (del enlace) y la nueva password (de 8 a 128). Un enlace usado o caducado responde 401 |
POST | /api/auth/logout | Cualquiera | Responde { signedOut: true } |
GET | /api/auth/me | Cualquier cuenta con la sesión iniciada | La cuenta, sus roles y permisos de plataforma, y sus espacios de trabajo |
PATCH | /api/auth/me | Cualquier cuenta con la sesión iniciada | Cambia firstName, lastName, email o phone |
PATCH | /api/auth/me/preferences | Cualquier cuenta con la sesión iniciada | theme (system, light, dark), reducedMotion, locale (en, ar) y lastWorkspaceId, el espacio de trabajo que abre el siguiente inicio de sesión |
PATCH | /api/admins/profile/password | Cualquier cuenta con la sesión iniciada | Cambia la contraseña de la propia cuenta: current_password, password, password_confirmation |
Espacios de trabajo y permisos
- Las rutas de espacio de trabajo empiezan por
/api/workspaces/:workspaceId, el id público del espacio. En cada petición la API comprueba que quien llama tiene una membresía activa allí (si no,404) y tiene el permiso que nombra la ruta (si no,403). El rol es el de la membresía: una persona puede ser propietaria de un espacio y administradora de otro. - Un espacio de trabajo tiene cuatro roles (
owner,admin,member,billing) y su propia matriz de 14 permisos:workspace.read,workspace.update,workspace.delete,projects.read,projects.write,members.invite,members.remove,roles.manage,billing.read,billing.manage,keys.manage,audit.read,ai.readyai.apply. El propietario los tiene todos, yworkspace.deleteyroles.managequedan con el propietario diga lo que diga la matriz. Un cambio en la matriz se aplica a la siguiente petición. - Un espacio de trabajo suspendido conserva todo
GETy rechaza cualquier otro método con403. - Un espacio de trabajo con pago vencido lo conserva todo hasta que pasan
BILLING_GRACE_DAYS(7 por defecto); entonces no puede crear proyectos, invitaciones ni claves de API (403), mientras toda lectura y toda otra escritura siguen funcionando. - Las rutas de plataforma (
/api/platform,/api/users,/api/admins,/api/roles,/api/settings) comprueban en su lugar los permisos de plataforma de la cuenta, concedidos mediante roles de plataforma. Los datos de ejemplo crean Platform admin, con los 21, y Support, que lee todas las páginas de la plataforma y exporta informes. Una ruta que lista varios permisos acepta cualquiera de ellos.
Listas y paginación
{ "data": [ ], "total": 12, "page": 1, "limit": 7, "totalPages": 2 }- Proyectos, proyectos archivados, actividad, conversaciones del chat y sus mensajes, y la página de organizaciones y usuarios de la plataforma con
pageylimit, como máximo 100 por página. Los valores por defecto son 7 proyectos, 12 eventos, 20 conversaciones y 10 organizaciones o usuarios. - La página de personal, de ajustes de la plataforma y de roles de plataforma usa en su lugar
pageypage_count(10 por defecto, como máximo 100). La página de roles de plataforma solo cuando se dan ambos; si no, se devuelven todos los roles. - Las listas vienen de la más reciente a la más antigua (proyectos, usuarios y organizaciones por su último cambio), con el id como desempate, salvo que se dé un
sort; los roles de plataforma vienen por nombre. La lista de proyectos devuelve tambiéncountspor pestaña. - Lo que pertenece a un solo espacio de trabajo y se muestra completo responde sin paginar: las tareas de un tablero, los miembros, las invitaciones, las claves de API, los archivos de un proyecto, las últimas 36 facturas y las últimas 8 notificaciones.
Espacios de trabajo, miembros y la matriz de roles
| Método | Ruta | Quién puede llamarla | Qué hace |
|---|---|---|---|
GET | /api/workspaces | Cualquier cuenta con la sesión iniciada | Los espacios de trabajo de la cuenta con su rol en cada uno, para el selector |
GET | /api/workspaces/slug-availability | Cualquier cuenta con la sesión iniciada | ?slug=: si una dirección de espacio de trabajo está libre, con una suggestion cuando no lo está |
POST | /api/workspaces | Cualquier cuenta con la sesión iniciada | Alta inicial: name (de 2 a 80), slug (de 3 a 48 letras minúsculas, dígitos y guiones), region (europe, north_america, asia_pacific) y opcionalmente invitations, hasta 20 { email, role }. Quien llama pasa a ser el propietario, en una prueba de Starter tan larga como el trial_days de la plataforma. Un slug ocupado responde 409 con slug_taken, y mientras signups_enabled está desactivado responde 403 |
GET | /api/workspaces/:workspaceId | Un miembro con workspace.read | El espacio de trabajo: nombre, dirección, plan, uso frente a los límites del plan y sus ajustes. billingEmail es null salvo que quien llama tenga billing.read o billing.manage |
PATCH | /api/workspaces/:workspaceId | Un miembro con workspace.update | Cambia name, slug, locale (en, ar), timeZone (una zona IANA como Europe/Berlin), domainConfigured, emailNotifications (los correos de facturación) o weeklyDigest |
DELETE | /api/workspaces/:workspaceId | El propietario del espacio de trabajo | confirmName (el nombre del espacio de trabajo, escrito completo) y la password del propietario. El espacio se elimina de forma lógica, y su suscripción de Stripe se cancela primero; si Stripe la rechaza, responde 409 y el espacio se conserva |
GET | /api/workspaces/:workspaceId/members | Un miembro con workspace.read | Los miembros con sus roles. search, role |
PATCH | /api/workspaces/:workspaceId/members/:memberId | Un miembro con roles.manage | Cambia el role de un miembro (admin, member, billing). La fila del propietario responde 403 |
DELETE | /api/workspaces/:workspaceId/members/:memberId | Un miembro con members.remove | Elimina a un miembro. El propietario no se puede eliminar, y un administrador no puede eliminar a otro administrador |
POST | /api/workspaces/:workspaceId/members/transfer-ownership | El propietario del espacio de trabajo | memberId y la password del propietario. El miembro pasa a ser el propietario y el propietario anterior un administrador |
GET | /api/workspaces/:workspaceId/roles | Un miembro con workspace.read | La matriz de roles: cada permiso, en seis grupos, y los roles que lo tienen |
PUT | /api/workspaces/:workspaceId/roles | El propietario del espacio de trabajo | Guarda la matriz: matrix, cada permiso asignado a los roles que lo tienen. Un permiso o rol desconocido responde 400 |
Invitaciones
| Método | Ruta | Quién puede llamarla | Qué hace |
|---|---|---|---|
GET | /api/workspaces/:workspaceId/invitations | Un miembro con workspace.read | status (pending, el valor por defecto, o all). Responde invitations y seats, los puestos usados y restantes del plan |
POST | /api/workspaces/:workspaceId/invitations | Un miembro con members.invite | Invita a email como role (admin, member, billing). El enlace se envía por correo y funciona 7 días. Un plan lleno responde 409 con seat_limit_reached; already_member y already_invited se rechazan del mismo modo |
POST | /api/workspaces/:workspaceId/invitations/:id/resend | Un miembro con members.invite | Un enlace nuevo, válido otros 7 días. Una vez por minuto como máximo (resend_too_soon), y cinco veces por invitación (resend_limit_reached) |
DELETE | /api/workspaces/:workspaceId/invitations/:id | Un miembro con members.invite | Revocar una invitación |
GET | /api/invitations/:token | Cualquiera | Lo que abre el enlace de una invitación: el nombre y la dirección del espacio de trabajo, el rol, el correo invitado en parte oculto, y si sigue abierta |
POST | /api/invitations/:token/accept | La cuenta invitada, con sesión iniciada | Se une al espacio de trabajo. Un enlace caducado o revocado responde 410; una cuenta cuyo correo no es el invitado responde 403; un plan lleno responde 409 con seat_limit_reached |
Los correos de invitación pasan por los ajustes SMTP de la API, o van a su registro, con el enlace, mientras SMTP_HOST está vacío.
Proyectos y archivos
| Método | Ruta | Quién puede llamarla | Qué hace |
|---|---|---|---|
GET | /api/workspaces/:workspaceId/projects | Un miembro con projects.read | Proyectos con counts por pestaña. status (all, active, completed o un estado), state (in_progress, review, blocked, completed), search, ownerId, dueBefore (YYYY-MM-DD), sort (updated_at, due_date, name, progress), order (asc, desc), page, limit |
GET | /api/workspaces/:workspaceId/projects/archived | Un miembro con projects.read | Los proyectos archivados, con los mismos filtros |
GET | /api/workspaces/:workspaceId/projects/owners | Un miembro con projects.read | Los miembros que son responsables de proyectos, para el filtro de responsable |
GET | /api/workspaces/:workspaceId/projects/:projectId | Un miembro con projects.read | Un proyecto con su responsable, fecha de entrega, progreso y recuentos de tareas |
POST | /api/workspaces/:workspaceId/projects | Un miembro con projects.write | Crea un proyecto: translations (en obligatorio, ar opcional, cada name hasta 120 caracteres y description hasta 2000), y opcionalmente status, ownerId (un miembro), dueDate (YYYY-MM-DD) y progress (de 0 a 100) |
PATCH | /api/workspaces/:workspaceId/projects/:projectId | Un miembro con projects.write | Cambia cualquiera de esos campos |
DELETE | /api/workspaces/:workspaceId/projects/:projectId | Un miembro con projects.write | Archiva un proyecto. No se elimina nada |
POST | /api/workspaces/:workspaceId/projects/:projectId/restore | Un miembro con projects.write | Devuelve un proyecto archivado |
GET | /api/workspaces/:workspaceId/projects/:projectId/activity | Un miembro con projects.read | Los últimos 100 eventos del proyecto |
GET | /api/workspaces/:workspaceId/projects/:projectId/files | Un miembro con projects.read | Los archivos del proyecto, el más reciente primero |
POST | /api/workspaces/:workspaceId/projects/:projectId/files | Un miembro con projects.write | Adjunta un archivo: multipart file, 10 MB como máximo. Su tamaño cuenta para el almacenamiento del plan |
GET | /api/workspaces/:workspaceId/projects/:projectId/files/:fileId/download | Un miembro con projects.read | El url, name y mime del archivo |
Crear un proyecto en un espacio de trabajo con pago vencido cuyo periodo de gracia ha terminado responde 403.
Tareas y el tablero
Una tarea se lista y se crea bajo su proyecto, y se cambia por su propio id bajo el espacio de trabajo.
| Método | Ruta | Quién puede llamarla | Qué hace |
|---|---|---|---|
GET | /api/workspaces/:workspaceId/projects/:projectId/tasks | Un miembro con projects.read | Las tareas del tablero en el orden de las columnas. assigneeId, priority, search |
POST | /api/workspaces/:workspaceId/projects/:projectId/tasks | Un miembro con projects.write | Crea una tarea: translations (en obligatorio, cada title hasta 160 caracteres y description hasta 4000), y opcionalmente status (backlog, el valor por defecto, in_progress, review, done), priority (low, normal, high), assigneeId (un miembro) y dueDate |
PATCH | /api/workspaces/:workspaceId/tasks/:taskId | Un miembro con projects.write | Cambia cualquiera de esos campos |
POST | /api/workspaces/:workspaceId/tasks/:taskId/move | Un miembro con projects.write | Mueve una tarjeta: status (la columna) y beforeId, la tarjeta sobre la que cae, o nada para el final de la columna |
DELETE | /api/workspaces/:workspaceId/tasks/:taskId | Un miembro con projects.write | Archiva una tarea |
Una persona asignada que no es miembro responde 400 con assignee_not_member. Cada cambio escribe un evento en el rastro de actividad, como task.moved con las columnas de origen y destino.
Resumen, actividad, notificaciones y búsqueda
| Método | Ruta | Quién puede llamarla | Qué hace |
|---|---|---|---|
GET | /api/workspaces/:workspaceId/overview | Un miembro con workspace.read | El resumen: proyectos por estado, miembros e invitaciones pendientes, eventos de tareas por día de los últimos siete días, la lista de lanzamiento, hasta tres proyectos con vencimiento dentro del periodo y los últimos seis eventos. period (7d, 30d, el valor por defecto, 90d). Sin projects.read, los recuentos de proyectos, el gráfico y los proyectos con vencimiento vuelven a cero o vacíos |
GET | /api/workspaces/:workspaceId/activity | Un miembro con audit.read | El rastro de actividad, con las personas que aparecen en él como actors. action (una acción como task.moved, o una familia que termina en punto como task.), actorId, from y to (YYYY-MM-DD), search, page, limit |
GET | /api/workspaces/:workspaceId/activity/export | Un miembro con audit.read | Los mismos filtros como archivo CSV (date, actor, action, subject, details), hasta 5.000 filas. Las celdas que se ejecutarían como fórmula de hoja de cálculo se escapan |
GET | /api/workspaces/:workspaceId/notifications | Un miembro con workspace.read | La campana: los últimos 8 eventos y cuántos son más recientes que la última vez que quien lee miró |
POST | /api/workspaces/:workspaceId/notifications/read | Un miembro con workspace.read | Marca todo hasta ahora como leído |
GET | /api/workspaces/:workspaceId/search | Un miembro con workspace.read | q (de 2 a 100 caracteres): hasta cinco proyectos, tareas y miembros cada uno. Los proyectos y las tareas aparecen solo para un miembro con projects.read |
Los últimos eventos del resumen y la campana muestran todo el rastro a quien llama con projects.read o audit.read, solo los eventos billing. a quien tiene billing.read y ninguno de esos dos, y nada a nadie más.
Facturación, su webhook y el pago de prueba
| Método | Ruta | Quién puede llamarla | Qué hace |
|---|---|---|---|
GET | /api/workspaces/:workspaceId/billing | Un miembro con billing.read | La suscripción: plan, estado, precio, renovación, una cancelación o un cambio a un plan inferior a la espera de la renovación, el periodo de gracia del pago vencido, el uso y cualquier pago en curso |
GET | /api/workspaces/:workspaceId/billing/invoices | Un miembro con billing.read | Las últimas 36 facturas |
GET | /api/workspaces/:workspaceId/billing/invoices/:invoiceId/download-link | Un miembro con billing.read | Un enlace firmado al PDF de la factura, válido cinco minutos, construido a partir de PUBLIC_API_URL |
POST | /api/workspaces/:workspaceId/billing/preview | Un miembro con billing.manage | Lo que cuesta cambiar a planId (starter, pro, scale): cambio a un plan superior o inferior, cuándo surte efecto, el cargo de hoy (chargeTodayCents), la siguiente factura y cualquier blockers (puestos, solicitudes de API o almacenamiento por encima de los límites del plan más pequeño). Un cambio a un plan superior de una suscripción de Stripe en marcha lo cotiza Stripe y responde chargesCardOnFile: true con un quote que mantiene ese precio 30 minutos; el pago de prueba prorratea la diferencia sobre el resto del periodo. El plan actual responde 400 con same_plan, salvo en un primer pago (una prueba, una prueba terminada o un espacio de trabajo cancelado), donde cualquier plan se cobra a su precio completo |
POST | /api/workspaces/:workspaceId/billing/checkout | Un miembro con billing.manage | planId, opcionalmente locale y el quote de la vista previa. Un cambio a un plan superior de una suscripción de Stripe en marcha se hace al instante y se cobra al método de pago guardado (upgraded: true), con la fecha de renovación sin cambios; un cobro rechazado responde 402 con payment_declined, payment_needs_action o payment_method_missing, y una cotización caducada o ajena 409 con preview_expired. Cualquier otro cambio a un plan superior responde con el url del pago alojado y su session. Un cambio a un plan inferior se programa para la renovación (scheduled: true), en Stripe mediante un calendario de suscripción, o se rechaza con 409 over_plan_limit y los blockers |
POST | /api/workspaces/:workspaceId/billing/checkout/:sessionId/sync | Un miembro con billing.read | Tras el retorno del pago: lee la sesión de vuelta desde el proveedor y responde con la sesión y el resumen. El plan cambia solo cuando el proveedor dice que está pagado |
POST | /api/workspaces/:workspaceId/billing/keep-plan | Un miembro con billing.manage | Deshace un cambio a un plan inferior programado para la renovación; responde con el resumen. Si no hay nada programado responde 409 con no_scheduled_change |
POST | /api/workspaces/:workspaceId/billing/portal | Un miembro con billing.manage | El url del portal de clientes de Stripe, para el método de pago; url: null para un espacio de trabajo que no se factura mediante Stripe |
POST | /api/workspaces/:workspaceId/billing/cancel | Un miembro con billing.manage | Cancela al final del periodo. Un cambio a un plan inferior programado para la renovación se descarta |
POST | /api/workspaces/:workspaceId/billing/resume | Un miembro con billing.manage | Conserva una suscripción que estaba configurada para cancelarse |
PATCH | /api/workspaces/:workspaceId/billing/email | Un miembro con billing.manage | Cambia el email de facturación |
GET | /api/billing/invoices/download | Cualquiera que tenga el enlace firmado | ?token=: la factura como PDF. Un token incorrecto responde 401 y uno caducado 403 |
POST | /api/billing/webhooks/stripe | Stripe | Los eventos de Stripe, verificados con STRIPE_WEBHOOK_SECRET contra la cabecera Stripe-Signature; una firma ausente o incorrecta responde 400, y también cada entrega mientras STRIPE_WEBHOOK_SECRET está vacío, con un mensaje que lo nombra. Las suscripciones usan checkout.session.completed, invoice.paid, invoice.payment_failed y customer.subscription.deleted, con cada id de evento aplicado una vez. Los pedidos de licencias llegan por la misma ruta (El pago de licencias) |
GET | /api/billing/simulator/:sessionId | Un miembro con billing.manage en el espacio de trabajo de la sesión | La sesión del pago de prueba, para la propia página de pago del panel |
POST | /api/billing/simulator/:sessionId/complete | Un miembro con billing.manage en el espacio de trabajo de la sesión | Pago de prueba: el plan cambia como si el proveedor lo hubiera confirmado |
POST | /api/billing/simulator/:sessionId/cancel | Un miembro con billing.manage en el espacio de trabajo de la sesión | Sale del pago de prueba sin pagar |
- El pago usa Stripe Checkout cuando
STRIPE_SECRET_KEYestá definido, y el pago de prueba integrado en caso contrario, o siempre queBILLING_PROVIDER=simulator. El pago de prueba no cobra a nadie. - La dirección de retorno por sí sola nunca cambia un plan: solo lo hace el webhook firmado, o la sesión leída de vuelta en
sync. - El precio de Stripe de cada plan se crea en el primer uso, con la clave de búsqueda
northstack_<plan>_usd_<cents>_month, así que no hay que configurar nada antes en Stripe. - Un pago de Stripe completado para un espacio de trabajo que ya tiene una suscripción de Stripe cancela la antigua, con su tiempo sin usar abonado.
invoice.paidguarda la factura de Stripe una vez (las facturas son únicas por id de factura del proveedor), con su importe y periodo, lleva el periodo del espacio de trabajo al que inicia una factura, y en una renovación aplica un cambio a un plan inferior programado para ella. - En el pago de prueba la API renueva cada periodo al terminar (una factura pagada, los siguientes 30 días, un cambio a un plan inferior programado aplicado) y termina uno configurado para cancelarse. Una prueba que llega a su fin pasa a pago vencido, con el periodo de gracia. Los contadores de uso vuelven a cero en cada periodo. Esto se ejecuta en cada lectura de facturación y una vez por hora.
- Apunta tu webhook de Stripe a
https://<your API>/api/billing/webhooks/stripecon los eventos de arriba máscheckout.session.expired.
Claves de API y la API pública
| Método | Ruta | Quién puede llamarla | Qué hace |
|---|---|---|---|
GET | /api/workspaces/:workspaceId/api-keys | Un miembro con keys.manage | Las claves del espacio de trabajo: nombre, prefijo, alcances, entorno, último uso y quién la creó. Nunca el secreto |
POST | /api/workspaces/:workspaceId/api-keys | Un miembro con keys.manage | name (de 2 a 60), scopes (uno o más de projects:read, projects:write, tasks:read, tasks:write) y environment (production, el valor por defecto, o demo). Responde con la clave y su secret, mostrado esta única vez; solo se guarda su hash SHA-256 |
DELETE | /api/workspaces/:workspaceId/api-keys/:id | Un miembro con keys.manage | Revoca una clave. Deja de funcionar al instante |
GET | /api/v1/projects | Una clave de API con projects:read | Los 100 proyectos actualizados más recientemente del espacio de trabajo |
POST | /api/v1/projects | Una clave de API con projects:write | Crea un proyecto, con el mismo cuerpo que el del panel |
PATCH | /api/v1/projects/:projectId | Una clave de API con projects:write | Cambia un proyecto, con el mismo cuerpo que el del panel |
GET | /api/v1/projects/:projectId/tasks | Una clave de API con tasks:read | Las tareas de un proyecto |
POST | /api/v1/projects/:projectId/tasks | Una clave de API con tasks:write | Crea una tarea, con el mismo cuerpo que el del tablero |
curl http://localhost:8000/api/v1/projects \
-H "Authorization: Bearer nst_live_…"- El secreto de una clave de producción empieza por
nst_live_, el de una clave de demostración pornst_demo_. Una clave de demostración funciona solo mientras la API funciona en modo demostración. - Sin clave, una desconocida o revocada responde
401; una clave sin el alcance de la ruta responde403; un espacio de trabajo suspendido rechaza la escritura. - Toda ruta salvo la lista de proyectos se ejecuta como el creador de la clave: lo que crea queda registrado como hecho por él, con la validación del panel, los eventos de actividad y los límites del plan. Responde
404cuando el creador ha dejado el espacio de trabajo, y las tres escrituras responden403cuando el rol del creador ya no tieneprojects.write. - Cada llamada cuenta para las solicitudes de API del plan, que leen la página de facturación y los límites del plan.
Resumen de la plataforma y organizaciones
| Método | Ruta | Quién puede llamarla | Qué hace |
|---|---|---|---|
GET | /api/platform/overview | Personal con platform.view | Los ingresos recurrentes mensuales y su historial, la mezcla de planes y las organizaciones por estado. range (30d, 90d, 6m, el valor por defecto, 12m) |
GET | /api/platform/overview/export | Personal con reports.export | Todas las organizaciones como archivo CSV: organization, id, plan, status, seats, mrr_usd, created |
GET | /api/platform/organizations | Personal con organizations.view | Organizaciones. search (nombre o dirección), plan (starter, pro, scale), status (trialing, active, past_due, canceling, canceled, suspended), page, limit |
GET | /api/platform/organizations/:id | Personal con organizations.view | Una organización, por su id público |
POST | /api/platform/organizations/:id/suspend | Personal con organizations.manage | reason (de 3 a 300 caracteres). El espacio de trabajo conserva sus lecturas y rechaza toda escritura. Uno suspendido responde 409 |
POST | /api/platform/organizations/:id/reactivate | Personal con organizations.manage | Levanta una suspensión |
Usuarios, personal, roles de plataforma y ajustes
| Método | Ruta | Quién puede llamarla | Qué hace |
|---|---|---|---|
GET | /api/users | Personal con users.view | Todas las cuentas. search, role (owner, admin, member o billing para un rol de espacio de trabajo tenido en cualquier parte, staff para un rol de plataforma, none para cuentas sin espacio de trabajo), page, limit |
GET | /api/users/deleted | Personal con users.view | Las cuentas eliminadas, con los mismos filtros |
GET | /api/users/statistic | Personal con users.view | Totales: cuentas, eliminadas, personal, sin espacio de trabajo, nuevas en los últimos 30 días |
GET | /api/users/deleted/:username | Personal con users.view | Una cuenta eliminada |
GET | /api/users/:username | Personal con users.view | Una cuenta |
POST | /api/users | Personal con users.create | Crea una cuenta: first_name, last_name, email, password (de 8 a 128), opcionalmente phone |
PATCH | /api/users/:username | Personal con users.update | Cambia first_name, last_name, email o phone. Un correo nuevo está protegido como se indica abajo |
PATCH | /api/users/:username/change-password | Personal con users.update | Define una password nueva (de 8 a 128), protegida como se indica abajo |
DELETE | /api/users/:username | Personal con users.delete | Mueve una cuenta a la lista de eliminadas. Una cuenta que es propietaria de un espacio de trabajo responde 409 con user_owns_workspace hasta que la propiedad se traspasa |
POST | /api/users/deleted/:username/restore | Personal con users.restore | Devuelve una cuenta eliminada |
GET | /api/admins | Personal con admins.view | Todas las cuentas con sus roles de plataforma. email, name (nombre, apellido o nombre de usuario), phone, una fila que coincida con cualquiera de los dados, page, page_count |
GET | /api/admins/statistics | Personal con admins.view | Recuentos para la página de personal |
GET | /api/admins/:id | Personal con admins.view | Una cuenta, por id o nombre de usuario |
POST | /api/admins | Personal con admins.create | Añade una cuenta de personal: first_name, last_name, email, password y password_confirmation (6 caracteres o más), opcionalmente phone, phone_code, profile_picture |
PATCH | /api/admins/:id | Personal con admins.edit | Cambia esos campos; una contraseña o un correo nuevos están protegidos como se indica abajo |
PATCH | /api/admins/:id/roles | Personal con admins.assign_roles | Define los roles de plataforma de una cuenta: role_ids. Cada rol concedido o quitado debe tener solo permisos que quien llama tiene él mismo, o responde 403 (role_outranks_caller). Su panel abierto recibe el aviso al instante |
DELETE | /api/admins/:id | Personal con admins.delete | Elimina una cuenta. Una que es propietaria de un espacio de trabajo responde 409 |
PATCH | /api/admins/profile | Personal con admins.edit | Cambia el propio perfil del miembro del personal con sesión iniciada |
GET | /api/roles | Personal con roles.view | Roles de plataforma. name, guard_name, created_from, created_to, page, page_count |
GET | /api/roles/statistics | Personal con roles.view | Recuentos para la página de roles |
GET | /api/roles/select | Personal con roles.view | El id y el nombre de cada rol, para un selector |
GET | /api/roles/permissions | Personal con roles.view | Todos los permisos de plataforma, agrupados por área |
GET | /api/roles/:id | Personal con roles.view | Un rol con sus permisos |
POST | /api/roles | Personal con roles.create | Añade un rol: name. Un nombre en uso responde 409 |
PUT | /api/roles/:id | Personal con roles.edit | Renombra un rol: name |
POST | /api/roles/:id/permissions | Personal con roles.assign_permissions | Define los permissions de un rol, por nombre. Quien llama solo puede dar permisos que tiene, y cambiar solo un rol que no tenga ninguno que le falte, o responde 403 (permission_outranks_caller). Todos los que lo tienen reciben el aviso al instante |
DELETE | /api/roles/:id | Personal con roles.delete | Elimina un rol. Uno que alguien tiene responde 400 |
GET | /api/settings | Personal con settings.view | Los ajustes de la plataforma. search, category, page, page_count |
GET | /api/settings/:key | Personal con settings.view | Un ajuste |
PATCH | /api/settings/:key | Personal con settings.edit | Cambia un ajuste: value |
DELETE | /api/settings/:key | Personal con settings.edit | Eliminar un ajuste |
La protección de contraseñas y correos: quien llama y no es la propia cuenta puede definir su contraseña o cambiar su correo solo cuando es Platform admin o tiene todos los permisos de plataforma que tiene la cuenta; en caso contrario la API responde 403. El correo también está protegido, porque un restablecimiento de contraseña se envía a él.
La protección de roles: nadie puede conceder, quitar ni elevar un rol de plataforma más allá de sus propios permisos, así que admins.assign_roles o roles.assign_permissions por sí solos nunca llevan al rol Platform admin.
Los 21 permisos de plataforma son platform.view, reports.export, organizations.view, organizations.manage, users.view, users.create, users.update, users.delete, users.restore, admins.view, admins.create, admins.edit, admins.delete, admins.assign_roles, roles.view, roles.create, roles.edit, roles.delete, roles.assign_permissions, settings.view y settings.edit.
Ajustes públicos y modo de mantenimiento
| Método | Ruta | Quién puede llamarla | Qué hace |
|---|---|---|---|
GET | /api/public-settings | Cualquiera | maintenanceMode y trialDays (el trial_days de la plataforma, 14 si no está definido): lo que el alta inicial indica como duración de la prueba, y si el panel muestra su pantalla de mantenimiento. Abierto durante el mantenimiento |
Mientras el ajuste de plataforma maintenance_mode es true, toda petición de quien no es personal de la plataforma (una cuenta con platform.view) responde 503, con code: "maintenance_mode" y el mensaje "The platform is closed for maintenance. Please try again shortly." Un token ausente o incorrecto recibe el mismo 503. Estas siguen abiertas para todos:
GET /api/healthyGET /api/public-settings;POST /api/auth/loginyPOST /api/auth/logout, donde el inicio de sesión sigue rechazando con el503a quien no es personal una vez que las credenciales son correctas;POST /api/billing/webhooks/stripey la descarga firmada de facturas;- el endpoint MCP, que conserva su propia clave, y
GET /api/demo/status; - el
POST /api/contactdel sitio web y todas las rutas/api/licenses, para que el formulario de contacto y el pago de licencias sigan funcionando.
Todo lo demás está cerrado para quien no es personal, incluidos el registro, la recuperación de contraseña, las invitaciones, el alta inicial, la API pública bajo /api/v1, los archivos subidos y el asistente. La lista es back-end/src/common/constants/maintenance.constants.ts.
El formulario de contacto
| Método | Ruta | Quién puede llamarla | Qué hace |
|---|---|---|---|
POST | /api/contact | Cualquiera | name (de 2 a 120), email, topic (licensing, setup, product), message (de 20 a 4000 caracteres), clientToken (de 8 a 64 letras, dígitos, _ o -) y el campo oculto website. Responde { id } |
- Un
clientTokenrepetido devuelve el id del primer mensaje en lugar de guardar un segundo, así que reintentar es seguro. - Un campo
websiterelleno se trata como un bot: la respuesta es igual, y no se guarda nada. - Cada dirección puede enviar cinco por hora; el siguiente responde
429. - Los mensajes se guardan en la tabla
contact_messagesy se reenvían aCONTACT_INBOXcuando está definido.
El pago de licencias
| Método | Ruta | Quién puede llamarla | Qué hace |
|---|---|---|---|
GET | /api/licenses/checkout/options | Cualquiera | Los métodos de pago (card, paypal), el provider que cobra cada uno (stripe, paypal o simulator) y testMode, verdadero cuando ninguno mueve dinero real |
POST | /api/licenses/checkout | Cualquiera | license (single, studio), email, name (de 2 a 120), method (card, paypal), opcionalmente locale (en, ar) y clientToken. Responde order, el id del pedido, y url, la página donde pagar. Una página de pago que no se puede abrir responde 503 con payment_unavailable |
GET | /api/licenses/orders/:id | Cualquiera que tenga el id del pedido | El pedido: referencia, licencia, importe, correo, nombre, método, proveedor, status (pending, paid, cancelled, failed), test y licenseKey una vez pagado |
POST | /api/licenses/orders/:id/confirm | Cualquiera que tenga el id del pedido | El comprador vuelve de Stripe o PayPal: pregunta al proveedor si el pago se completó y luego responde con el pedido |
POST | /api/licenses/orders/:id/cancel | Cualquiera que tenga el id del pedido | El comprador salió de la página de pago sin pagar |
POST | /api/licenses/orders/:id/simulate | Cualquiera que tenga el id del pedido | El Pay o Cancel del pago de prueba: outcome (pay, cancel). Un pedido tomado por Stripe o PayPal responde 404 |
- El precio es el propio del servidor, de
back-end/src/modules/licenses/constants/license-offers.ts; el navegador nunca envía uno. - Una tarjeta va a Stripe Checkout cuando
STRIPE_SECRET_KEYestá definido, PayPal a PayPal cuandoPAYPAL_CLIENT_IDyPAYPAL_CLIENT_SECRETestán definidos, y cualquiera de los dos al pago de prueba en caso contrario. En el modo demostración nunca se ofrece un proveedor con claves en vivo. - Un pedido se paga una sola vez, por lo que llegue primero entre el
checkout.session.completedde Stripe enPOST /api/billing/webhooks/stripe, elconfirmdel retorno o el Pay del pago de prueba. Solo ese emite la clave de licencia y envía el recibo por correo.checkout.session.expiredmarca el pedido como fallido. - El id del pedido es una cadena larga y aleatoria que solo llevan los enlaces de retorno del comprador. Un
clientTokenrepetido responde con el primer pedido. Cada dirección puede iniciar diez pedidos por hora.
Archivos subidos y multimedia
| Método | Ruta | Quién puede llamarla | Qué hace |
|---|---|---|---|
POST | /api/helpers/upload | Cualquier cuenta con la sesión iniciada | Guarda una imagen: multipart file (10 MB como máximo), path para la carpeta y for para el tamaño predefinido (profile, cover, logo o default). Responde con las direcciones por tamaño: original, de 1920 píxeles de ancho como máximo, y una copia redimensionada, todas en JPEG. Un archivo que no es una imagen legible responde 400 |
Las imágenes y los archivos de proyecto van al bucket de Cloudflare R2 cuando están definidas sus cinco variables R2_*, y en caso contrario a MEDIA_UPLOAD_DIR, y se sirven en PUBLIC_MEDIA_URL seguido de /uploads/….
Todo lo que la API sirve bajo /media lleva X-Content-Type-Options: nosniff, y un archivo que no es una imagen rasterizada, un PDF, texto plano, audio o vídeo (un archivo HTML o SVG, por ejemplo) se envía con Content-Disposition: attachment, de modo que se descarga en lugar de abrirse como una página en la dirección de la API. Los archivos de un bucket R2 los sirve el bucket, con el tipo con el que se subieron.
Límites por dirección
| Ruta | Límite | Variable |
|---|---|---|
POST /api/auth/login | 10 inicios de sesión fallidos cada 15 minutos | RATE_LIMIT_LOGIN |
POST /api/auth/signup | 10 por hora | Ninguno |
POST /api/auth/forgot-password | 5 cada 15 minutos | Ninguno |
POST /api/auth/reset-password | 10 cada 15 minutos | Ninguno |
POST /api/contact | 5 por hora | Ninguno |
POST /api/licenses/checkout | 10 por hora | Ninguno |
POST …/invitations/:id/resend | Una vez por minuto, cinco veces por invitación | Ninguno |
Una petición por encima de un límite responde 429, con una frase que nombra lo que se limitó y cuánto esperar, como "Too many sign-ups from this address. Wait an hour, then try again." Los recuentos viven en la memoria de la API, así que un reinicio los borra. Detrás de un proxy inverso, define TRUST_PROXY con el número de proxies delante de la API, o cada visitante se cuenta como la única dirección del proxy.
El asistente de IA, sus conversaciones y propuestas
Incluido con tu compra. Inicia sesión para leerlo o ábrelo en tu descarga.
La ruta de streaming del asistente, su lista de modelos, las conversaciones guardadas y las propuestas de tareas.
Servidor MCP
Incluido con tu compra. Inicia sesión para leerlo o ábrelo en tu descarga.
El endpoint MCP para agentes de código, cómo se autoriza y qué espacio de trabajo lee.
Actualizaciones de permisos en vivo mediante socket.io
Incluido con tu compra. Inicia sesión para leerlo o ábrelo en tu descarga.
El espacio de nombres de socket.io que escucha el panel, su evento y cómo se autoriza una conexión.
Rutas del modo demostración
Incluido con tu compra. Inicia sesión para leerlo o ábrelo en tu descarga.
Las rutas que existen solo para una compilación de demostración pública.