Référence de l'API
Chaque route que sert l'API, qui peut l'appeler, et comment fonctionnent la connexion, les espaces de travail, les permissions, la facturation, les clés d'API, les erreurs, les listes et les limites.
Pour le pack Full Stack
URL de base et format des réponses
Chaque route est servie sous le préfixe /api. En local, l'URL de base est http://localhost:8000/api. Sur un serveur, c'est l'adresse de votre API suivie de /api, la même valeur que le site et le tableau de bord lisent dans NEXT_PUBLIC_API_BASE_URL (Variables d'environnement). Les fichiers sont servis hors du préfixe : les fichiers de marque du kit sur /media/… et les fichiers téléversés sur /media/uploads/….
Les corps de requête sont en JSON, sauf les téléversements de fichiers. Chaque réponse arrive dans la même enveloppe :
{
"success": true,
"data": { },
"message": ""
}data contient le résultat. message est un court code que remplissent certaines écritures, comme project_created ou invitation_sent, et est vide sinon. Les espaces de travail, projets et tâches sont désignés par leurs identifiants publics (org_forma, prj_01) ; les membres, invitations, clés et fichiers par leur numéro. Les textes des projets et des tâches portent les deux langues sous la forme { "en": { … }, "ar": { … } }.
La vérification de santé ne demande aucun jeton :
curl http://localhost:8000/api/health{"success":true,"data":{"status":"ok","database":"up"},"message":""}Tant que la base ne répond pas et ne contient pas ses tables, elle répond 503 avec "status":"unavailable" et "database":"down". La vérification de santé de l'image Docker lit ce statut, et Docker Compose ne démarre le site et le tableau de bord qu'une fois qu'elle passe.
Les navigateurs ne peuvent appeler l'API que depuis les adresses de CORS_ORIGIN. Non défini, il autorise http://localhost:3030 et http://localhost:3031, et en production l'API refuse de démarrer sans lui (problèmes de CORS).
Erreurs
Un échec garde l'enveloppe, avec success: false, data: null et le statut HTTP :
{
"success": false,
"data": null,
"message": "seat_limit_reached",
"errors": { "email": ["…"] }
}errorsapparaît lors d'un échec de validation (400), avec les problèmes par champ.- Un champ inconnu dans un corps est refusé avec
400: chaque corps n'accepte que les champs listés ci-dessous. - Quand l'API a une phrase pour l'erreur,
messageest cette phrase dans la langue de la requête (Accept-Language: arpour l'arabe) : une connexion refusée, chaque429, les inscriptions fermées, un compte protégé contre un changement de mot de passe ou d'e-mail, un secret de webhook manquant, un abonnement que Stripe n'a pas pu annuler, un rôle ou une permission de plateforme que l'appelant ne détient pas, et le mode maintenance en font partie. - Sinon, sur
401,403et404,messageest une phrase générale pour le statut, et sur400,402,409,410et503c'est la raison sous forme de court code (seat_limit_reached,slug_taken,over_plan_limit,payment_declined,preview_expired,invitation_expired,payment_unavailable), qu'un client transforme en sa propre phrase. Un retour à une formule inférieure refusé pour cause d'usage porte aussiblockers. 402signifie un passage à une formule supérieure que le fournisseur de paiement n'a pas voulu débiter :payment_declined,payment_needs_action(la banque veut confirmer le paiement) oupayment_method_missing. La formule reste ce qu'elle était.- Tant que le personnel de la plateforme a le mode maintenance activé, chaque route hors de la courte liste de Maintenance mode répond
503à quiconque n'est pas du personnel de la plateforme, avec la phrase dansmessageetcode: "maintenance_mode". 401signifie pas de jeton, un jeton expiré, ou de mauvais identifiants.403signifie connecté sans la permission, ou une écriture dans un espace de travail suspendu.404répond aussi pour un espace de travail dont l'appelant n'est pas membre, si bien qu'un inconnu ne peut pas savoir quels espaces existent.- Une requête au-delà d'une limite répond
429(Limites par adresse).
Connexion
Connexion
Terminalcurl -X POST http://localhost:8000/api/auth/login \ -H "Content-Type: application/json" \ -d '{"email":"owner@northstack.example","password":"YOUR_PASSWORD"}'Mettez le mot de passe du seed de l'installation à la place de
YOUR_PASSWORD, ou utilisez un compte à vous.data.access_tokenest le jeton, avectoken_typeBearer,expires_in, etadmin, le compte avec ses rôles de plateforme, ses permissions et ses espaces de travail.Appeler une route protégée
Terminalcurl http://localhost:8000/api/workspaces \ -H "Authorization: Bearer YOUR_TOKEN"Cela liste les espaces de travail du compte avec son rôle dans chacun. Chaque route d'espace de travail prend ensuite l'identifiant public de l'espace dans son chemin :
Terminalcurl "http://localhost:8000/api/workspaces/org_forma/projects?limit=20" \ -H "Authorization: Bearer YOUR_TOKEN"
remember: truesigne un jeton pour 30 jours etremember: falseun jeton pour 12 heures ; omis, le jeton dureJWT_EXPIRATION,7dpar défaut.- Un e-mail inconnu et un mauvais mot de passe répondent tous deux
401avec le même message, après le même travail de hachage du mot de passe, si bien que ni la réponse ni son délai ne disent quels e-mails ont un compte. - Dix connexions échouées depuis une même adresse en 15 minutes répondent
429jusqu'à ce que le plus ancien échec ait 15 minutes (RATE_LIMIT_LOGIN). Les connexions réussies ne sont pas comptées. - Les jetons sont sans état :
POST /api/auth/logoutrépond aussitôt, et se déconnecter revient à ce que le client oublie son jeton.
| Méthode | Chemin | Qui peut l'appeler | Rôle |
|---|---|---|---|
POST | /api/auth/login | N'importe qui | Connexion : email, password, en option remember |
POST | /api/auth/signup | N'importe qui | Un nouveau compte, connecté aussitôt et prêt pour l'onboarding : name (2 à 80 caractères), email, password (8 à 128) et acceptTerms (true). Un e-mail déjà pris répond 409 avec email_taken ; tant que le paramètre signups_enabled de la plateforme est désactivé, il répond 403 |
POST | /api/auth/forgot-password | N'importe qui | email, en option locale (en, ar). Envoie par e-mail un lien de réinitialisation valable une fois pendant une heure quand le compte existe, et répond pareil dans les deux cas |
POST | /api/auth/reset-password | N'importe qui | token (issu du lien) et le nouveau password (8 à 128). Un lien utilisé ou expiré répond 401 |
POST | /api/auth/logout | N'importe qui | Répond { signedOut: true } |
GET | /api/auth/me | Tout compte connecté | Le compte, ses rôles et permissions de plateforme, et ses espaces de travail |
PATCH | /api/auth/me | Tout compte connecté | Modifie firstName, lastName, email ou phone |
PATCH | /api/auth/me/preferences | Tout compte connecté | theme (system, light, dark), reducedMotion, locale (en, ar) et lastWorkspaceId, l'espace de travail que la prochaine connexion ouvre |
PATCH | /api/admins/profile/password | Tout compte connecté | Modifie le mot de passe du compte lui-même : current_password, password, password_confirmation |
Espaces de travail et permissions
- Les routes d'espace de travail commencent par
/api/workspaces/:workspaceId, l'identifiant public de l'espace. À chaque requête, l'API vérifie que l'appelant y a une appartenance active (sinon404) et détient la permission que nomme la route (sinon403). Le rôle est celui de l'appartenance : une même personne peut être propriétaire d'un espace et admin d'un autre. - Un espace de travail contient quatre rôles (
owner,admin,member,billing) et sa propre matrice de 14 permissions :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.readetai.apply. Le propriétaire les détient toutes, etworkspace.deleteetroles.managerestent au propriétaire quoi que dise la matrice. Un changement de la matrice s'applique à la requête suivante. - Un espace de travail suspendu garde chaque
GETet refuse toute autre méthode avec403. - Un espace de travail en retard de paiement garde tout jusqu'à ce que
BILLING_GRACE_DAYS(7 par défaut) soient écoulés ; ensuite il ne peut ni créer de projets, ni d'invitations ou de clés d'API (403), tandis que chaque lecture et chaque autre écriture continue de fonctionner. - Les routes de plateforme (
/api/platform,/api/users,/api/admins,/api/roles,/api/settings) vérifient plutôt les permissions de plateforme du compte, accordées par les rôles de plateforme. Le seed crée Platform admin, avec les 21, et Support, qui lit chaque page de la plateforme et exporte les rapports. Une route qui liste plusieurs permissions en accepte n'importe laquelle.
Listes et pagination
{ "data": [ ], "total": 12, "page": 1, "limit": 7, "totalPages": 2 }- Les projets, les projets archivés, l'activité, les conversations du chat et leurs messages, ainsi que les organisations et les utilisateurs de la plateforme, se paginent avec
pageetlimit, 100 par page au plus. Les valeurs par défaut sont 7 projets, 12 événements, 20 conversations et 10 organisations ou utilisateurs. - Le personnel, les paramètres de la plateforme et les rôles de la plateforme se paginent avec
pageetpage_countà la place (10 par défaut, 100 au plus). Les rôles de la plateforme ne se paginent que si les deux sont fournis ; sinon chaque rôle est renvoyé. - Les listes arrivent de la plus récente à la plus ancienne (projets, utilisateurs et organisations par leur dernière modification), avec l'identifiant pour départager, sauf si un
sortest donné ; les rôles de la plateforme arrivent par nom. La liste des projets renvoie aussicountspar onglet. - Ce qui appartient à un seul espace de travail et s'affiche en entier répond sans pagination : les tâches d'un tableau, les membres, les invitations, les clés d'API, les fichiers d'un projet, les 36 dernières factures et les 8 dernières notifications.
Espaces de travail, membres et matrice de rôles
| Méthode | Chemin | Qui peut l'appeler | Rôle |
|---|---|---|---|
GET | /api/workspaces | Tout compte connecté | Les espaces de travail du compte avec son rôle dans chacun, pour le sélecteur |
GET | /api/workspaces/slug-availability | Tout compte connecté | ?slug= : si l'adresse d'un espace de travail est libre, avec une suggestion quand elle ne l'est pas |
POST | /api/workspaces | Tout compte connecté | Onboarding : name (2 à 80), slug (3 à 48 lettres minuscules, chiffres et tirets), region (europe, north_america, asia_pacific) et en option invitations, jusqu'à 20 { email, role }. L'appelant devient le propriétaire, sur un essai Starter aussi long que le trial_days de la plateforme. Un slug déjà pris répond 409 avec slug_taken, et tant que signups_enabled est désactivé, il répond 403 |
GET | /api/workspaces/:workspaceId | Un membre avec workspace.read | L'espace de travail : nom, adresse, formule, usage par rapport aux limites de la formule et ses paramètres. billingEmail vaut null sauf si l'appelant détient billing.read ou billing.manage |
PATCH | /api/workspaces/:workspaceId | Un membre avec workspace.update | Modifie name, slug, locale (en, ar), timeZone (une zone IANA comme Europe/Berlin), domainConfigured, emailNotifications (les e-mails de facturation) ou weeklyDigest |
DELETE | /api/workspaces/:workspaceId | Le propriétaire de l'espace de travail | confirmName (le nom de l'espace de travail, saisi en entier) et le password du propriétaire. L'espace de travail est supprimé logiquement, et son abonnement Stripe est d'abord annulé ; si Stripe refuse, elle répond 409 et l'espace est conservé |
GET | /api/workspaces/:workspaceId/members | Un membre avec workspace.read | Les membres avec leurs rôles. search, role |
PATCH | /api/workspaces/:workspaceId/members/:memberId | Un membre avec roles.manage | Modifie le role d'un membre (admin, member, billing). La ligne du propriétaire répond 403 |
DELETE | /api/workspaces/:workspaceId/members/:memberId | Un membre avec members.remove | Retire un membre. Le propriétaire ne peut pas être retiré, et un admin ne peut pas retirer un autre admin |
POST | /api/workspaces/:workspaceId/members/transfer-ownership | Le propriétaire de l'espace de travail | memberId et le password du propriétaire. Le membre devient le propriétaire et l'ancien propriétaire un admin |
GET | /api/workspaces/:workspaceId/roles | Un membre avec workspace.read | La matrice de rôles : chaque permission, en six groupes, et les rôles qui la détiennent |
PUT | /api/workspaces/:workspaceId/roles | Le propriétaire de l'espace de travail | Enregistre la matrice : matrix, chaque permission associée aux rôles qui la détiennent. Une permission ou un rôle inconnu répond 400 |
Invitations
| Méthode | Chemin | Qui peut l'appeler | Rôle |
|---|---|---|---|
GET | /api/workspaces/:workspaceId/invitations | Un membre avec workspace.read | status (pending, par défaut, ou all). Répond invitations et seats, les places utilisées et restantes sur la formule |
POST | /api/workspaces/:workspaceId/invitations | Un membre avec members.invite | Invite email comme role (admin, member, billing). Le lien est envoyé par e-mail et fonctionne 7 jours. Une formule pleine répond 409 avec seat_limit_reached ; already_member et already_invited sont refusés de la même façon |
POST | /api/workspaces/:workspaceId/invitations/:id/resend | Un membre avec members.invite | Un nouveau lien, valable 7 jours de plus. Une fois par minute au plus (resend_too_soon), et cinq fois par invitation (resend_limit_reached) |
DELETE | /api/workspaces/:workspaceId/invitations/:id | Un membre avec members.invite | Révoque une invitation |
GET | /api/invitations/:token | N'importe qui | Ce qu'ouvre un lien d'invitation : le nom et l'adresse de l'espace de travail, le rôle, l'e-mail invité en partie masqué, et s'il est encore ouvert |
POST | /api/invitations/:token/accept | Le compte invité, connecté | Rejoint l'espace de travail. Un lien expiré ou révoqué répond 410 ; un compte dont l'e-mail n'est pas celui invité répond 403 ; une formule pleine répond 409 avec seat_limit_reached |
Les e-mails d'invitation passent par les paramètres SMTP de l'API, ou dans son journal tant que SMTP_HOST est vide, lien compris.
Projets et fichiers
| Méthode | Chemin | Qui peut l'appeler | Rôle |
|---|---|---|---|
GET | /api/workspaces/:workspaceId/projects | Un membre avec projects.read | Les projets avec counts par onglet. status (all, active, completed, ou un statut), 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 membre avec projects.read | Les projets archivés, avec les mêmes filtres |
GET | /api/workspaces/:workspaceId/projects/owners | Un membre avec projects.read | Les membres qui possèdent des projets, pour le filtre de responsable |
GET | /api/workspaces/:workspaceId/projects/:projectId | Un membre avec projects.read | Un projet avec son responsable, son échéance, sa progression et le nombre de ses tâches |
POST | /api/workspaces/:workspaceId/projects | Un membre avec projects.write | Crée un projet : translations (en obligatoire, ar facultatif, chaque name jusqu'à 120 caractères et description jusqu'à 2000), et en option status, ownerId (un membre), dueDate (YYYY-MM-DD) et progress (0 à 100) |
PATCH | /api/workspaces/:workspaceId/projects/:projectId | Un membre avec projects.write | Modifier n'importe lequel de ces champs |
DELETE | /api/workspaces/:workspaceId/projects/:projectId | Un membre avec projects.write | Archive un projet. Rien n'est supprimé |
POST | /api/workspaces/:workspaceId/projects/:projectId/restore | Un membre avec projects.write | Remet un projet archivé |
GET | /api/workspaces/:workspaceId/projects/:projectId/activity | Un membre avec projects.read | Les 100 derniers événements du projet |
GET | /api/workspaces/:workspaceId/projects/:projectId/files | Un membre avec projects.read | Les fichiers du projet, du plus récent au plus ancien |
POST | /api/workspaces/:workspaceId/projects/:projectId/files | Un membre avec projects.write | Joint un fichier : file multipart, 10 Mo au plus. Sa taille compte dans le stockage de la formule |
GET | /api/workspaces/:workspaceId/projects/:projectId/files/:fileId/download | Un membre avec projects.read | L'url, le name et le mime du fichier |
Créer un projet dans un espace de travail en retard de paiement dont le délai de grâce est terminé répond 403.
Tâches et tableau
Une tâche est listée et créée sous son projet, et modifiée par son propre identifiant sous l'espace de travail.
| Méthode | Chemin | Qui peut l'appeler | Rôle |
|---|---|---|---|
GET | /api/workspaces/:workspaceId/projects/:projectId/tasks | Un membre avec projects.read | Les tâches du tableau dans l'ordre des colonnes. assigneeId, priority, search |
POST | /api/workspaces/:workspaceId/projects/:projectId/tasks | Un membre avec projects.write | Crée une tâche : translations (en obligatoire, chaque title jusqu'à 160 caractères et description jusqu'à 4000), et en option status (backlog, par défaut, in_progress, review, done), priority (low, normal, high), assigneeId (un membre) et dueDate |
PATCH | /api/workspaces/:workspaceId/tasks/:taskId | Un membre avec projects.write | Modifier n'importe lequel de ces champs |
POST | /api/workspaces/:workspaceId/tasks/:taskId/move | Un membre avec projects.write | Déplace une carte : status (la colonne) et beforeId, la carte au-dessus de laquelle elle arrive, ou rien pour la fin de la colonne |
DELETE | /api/workspaces/:workspaceId/tasks/:taskId | Un membre avec projects.write | Archive une tâche |
Une personne assignée qui n'est pas membre répond 400 avec assignee_not_member. Chaque changement écrit un événement dans la trace d'activité, comme task.moved avec les colonnes d'origine et d'arrivée.
Vue d'ensemble, activité, notifications et recherche
| Méthode | Chemin | Qui peut l'appeler | Rôle |
|---|---|---|---|
GET | /api/workspaces/:workspaceId/overview | Un membre avec workspace.read | La vue d'ensemble : les projets par statut, les membres et les invitations en attente, les événements de tâches par jour sur les sept derniers jours, la checklist de lancement, jusqu'à trois projets dont l'échéance tombe dans la période et les six derniers événements. period (7d, 30d, par défaut, 90d). Sans projects.read, le nombre de projets, le graphique et les projets à échéance reviennent à zéro ou vides |
GET | /api/workspaces/:workspaceId/activity | Un membre avec audit.read | La trace d'activité, avec les personnes qui y figurent sous actors. action (une action comme task.moved, ou une famille se terminant par un point comme task.), actorId, from et to (YYYY-MM-DD), search, page, limit |
GET | /api/workspaces/:workspaceId/activity/export | Un membre avec audit.read | Les mêmes filtres sous forme de fichier CSV (date, actor, action, subject, details), jusqu'à 5 000 lignes. Les cellules qui s'exécuteraient comme une formule de tableur sont échappées |
GET | /api/workspaces/:workspaceId/notifications | Un membre avec workspace.read | La cloche : les 8 derniers événements et combien sont plus récents que le dernier passage du lecteur |
POST | /api/workspaces/:workspaceId/notifications/read | Un membre avec workspace.read | Marque tout comme lu jusqu'à maintenant |
GET | /api/workspaces/:workspaceId/search | Un membre avec workspace.read | q (2 à 100 caractères) : jusqu'à cinq projets, tâches et membres chacun. Les projets et les tâches n'apparaissent que pour un membre avec projects.read |
Les derniers événements de la vue d'ensemble et la cloche montrent toute la trace à un appelant avec projects.read ou audit.read, seulement les événements billing. à un appelant avec billing.read et aucun des deux, et rien à tous les autres.
Facturation, son webhook et le paiement test
| Méthode | Chemin | Qui peut l'appeler | Rôle |
|---|---|---|---|
GET | /api/workspaces/:workspaceId/billing | Un membre avec billing.read | L'abonnement : formule, statut, prix, renouvellement, une annulation ou un retour à une formule inférieure en attente du renouvellement, le délai de grâce du retard de paiement, l'usage et tout paiement en cours |
GET | /api/workspaces/:workspaceId/billing/invoices | Un membre avec billing.read | Les 36 dernières factures |
GET | /api/workspaces/:workspaceId/billing/invoices/:invoiceId/download-link | Un membre avec billing.read | Un lien signé vers le PDF de la facture, valable cinq minutes, construit à partir de PUBLIC_API_URL |
POST | /api/workspaces/:workspaceId/billing/preview | Un membre avec billing.manage | Ce que coûte le passage à planId (starter, pro, scale) : passage à une formule supérieure ou inférieure, quand il prend effet, le débit du jour (chargeTodayCents), la prochaine facture et d'éventuels blockers (places, requêtes d'API ou stockage au-dessus des limites de la formule plus petite). Le passage à une formule supérieure d'un abonnement Stripe en cours est tarifé par Stripe et répond chargesCardOnFile: true avec un quote qui garantit ce prix 30 minutes ; le paiement test calcule la différence au prorata du reste de la période. La formule actuelle répond 400 avec same_plan, sauf pour un premier paiement (un essai, un essai terminé ou un espace de travail annulé), où n'importe quelle formule est facturée à son prix plein |
POST | /api/workspaces/:workspaceId/billing/checkout | Un membre avec billing.manage | planId, en option locale et le quote de l'aperçu. Le passage à une formule supérieure d'un abonnement Stripe en cours est fait tout de suite et débité sur le moyen de paiement enregistré (upgraded: true), la date de renouvellement inchangée ; un débit refusé répond 402 avec payment_declined, payment_needs_action ou payment_method_missing, et un devis expiré ou étranger 409 avec preview_expired. Tout autre passage à une formule supérieure répond l'url du paiement hébergé et sa session. Un retour à une formule inférieure est planifié pour le renouvellement (scheduled: true), sur Stripe via un échéancier d'abonnement, ou refusé avec 409 over_plan_limit et les blockers |
POST | /api/workspaces/:workspaceId/billing/checkout/:sessionId/sync | Un membre avec billing.read | Après le retour du paiement : relit la session chez le fournisseur et répond la session et le récapitulatif. La formule ne change qu'une fois que le fournisseur dit qu'elle est payée |
POST | /api/workspaces/:workspaceId/billing/keep-plan | Un membre avec billing.manage | Annule un retour à une formule inférieure planifié pour le renouvellement ; répond le récapitulatif. Si rien n'est planifié, répond 409 avec no_scheduled_change |
POST | /api/workspaces/:workspaceId/billing/portal | Un membre avec billing.manage | L'url du portail client de Stripe, pour le moyen de paiement ; url: null pour un espace de travail non facturé par Stripe |
POST | /api/workspaces/:workspaceId/billing/cancel | Un membre avec billing.manage | Annule à la fin de la période. Un retour à une formule inférieure planifié pour le renouvellement est abandonné |
POST | /api/workspaces/:workspaceId/billing/resume | Un membre avec billing.manage | Garde un abonnement qui avait été réglé pour être annulé |
PATCH | /api/workspaces/:workspaceId/billing/email | Un membre avec billing.manage | Modifie l'email de facturation |
GET | /api/billing/invoices/download | Toute personne qui détient le lien signé | ?token= : la facture en PDF. Un mauvais jeton répond 401 et un jeton expiré 403 |
POST | /api/billing/webhooks/stripe | Stripe | Les événements de Stripe, vérifiés avec STRIPE_WEBHOOK_SECRET contre l'en-tête Stripe-Signature ; une signature manquante ou fausse répond 400, de même que chaque envoi tant que STRIPE_WEBHOOK_SECRET est vide, avec un message qui le nomme. Les abonnements utilisent checkout.session.completed, invoice.paid, invoice.payment_failed et customer.subscription.deleted, chaque identifiant d'événement n'étant appliqué qu'une fois. Les commandes de licences arrivent sur la même route (Le paiement des licences) |
GET | /api/billing/simulator/:sessionId | Un membre avec billing.manage dans l'espace de travail de la session | La session du paiement test, pour la page de paiement propre au tableau de bord |
POST | /api/billing/simulator/:sessionId/complete | Un membre avec billing.manage dans l'espace de travail de la session | Test payment : la formule change comme si le fournisseur l'avait confirmée |
POST | /api/billing/simulator/:sessionId/cancel | Un membre avec billing.manage dans l'espace de travail de la session | Quitte le paiement test sans payer |
- Le paiement utilise Stripe Checkout quand
STRIPE_SECRET_KEYest défini, et le paiement test intégré sinon, ou chaque fois queBILLING_PROVIDER=simulator. Le paiement test ne débite personne. - L'adresse de retour seule ne change jamais une formule : seuls le webhook signé, ou la session relue via
sync, le font. - Le prix Stripe de chaque formule est créé à la première utilisation, avec la clé de recherche
northstack_<plan>_usd_<cents>_month: rien n'est donc configuré d'abord dans Stripe. - Un paiement Stripe terminé pour un espace de travail qui a déjà un abonnement Stripe annule l'ancien, son temps inutilisé étant crédité.
invoice.paidstocke une fois la facture de Stripe (les factures sont uniques par identifiant de facture du fournisseur), avec son montant et sa période, déplace la période de l'espace de travail vers celle qu'ouvre une facture, et lors d'un renouvellement applique un retour à une formule inférieure planifié pour lui. - Sur le paiement test, l'API renouvelle chaque période à son terme (une facture payée, les 30 jours suivants, un retour à une formule inférieure planifié appliqué) et met fin à celle réglée pour être annulée. Un essai qui arrive à son terme passe en retard de paiement, avec le délai de grâce. Les compteurs d'usage repartent de zéro à chaque période. Cela s'exécute à chaque lecture de la facturation et une fois par heure.
- Pointez votre webhook Stripe vers
https://<your API>/api/billing/webhooks/stripeavec les événements ci-dessus pluscheckout.session.expired.
Clés d'API et API publique
| Méthode | Chemin | Qui peut l'appeler | Rôle |
|---|---|---|---|
GET | /api/workspaces/:workspaceId/api-keys | Un membre avec keys.manage | Les clés de l'espace de travail : nom, préfixe, portées, environnement, dernier usage et qui l'a créée. Jamais le secret |
POST | /api/workspaces/:workspaceId/api-keys | Un membre avec keys.manage | name (2 à 60), scopes (un ou plusieurs parmi projects:read, projects:write, tasks:read, tasks:write) et environment (production, par défaut, ou demo). Répond la clé avec son secret, montré cette seule fois ; seul son hachage SHA-256 est conservé |
DELETE | /api/workspaces/:workspaceId/api-keys/:id | Un membre avec keys.manage | Révoque une clé. Elle cesse aussitôt de fonctionner |
GET | /api/v1/projects | Une clé d'API avec projects:read | Les 100 projets de l'espace de travail les plus récemment modifiés |
POST | /api/v1/projects | Une clé d'API avec projects:write | Crée un projet, avec le même corps que celui du tableau de bord |
PATCH | /api/v1/projects/:projectId | Une clé d'API avec projects:write | Modifie un projet, avec le même corps que celui du tableau de bord |
GET | /api/v1/projects/:projectId/tasks | Une clé d'API avec tasks:read | Les tâches d'un projet |
POST | /api/v1/projects/:projectId/tasks | Une clé d'API avec tasks:write | Crée une tâche, avec le même corps que celui du tableau |
curl http://localhost:8000/api/v1/projects \
-H "Authorization: Bearer nst_live_…"- Le secret d'une clé de production commence par
nst_live_, celui d'une clé de démo parnst_demo_. Une clé de démo ne fonctionne que tant que l'API tourne en mode démo. - Aucune clé, une clé inconnue ou révoquée répond
401; une clé sans la portée de la route répond403; un espace de travail suspendu refuse l'écriture. - Chaque route sauf la liste des projets s'exécute au nom du créateur de la clé : ce qu'elle crée est enregistré comme fait par lui, avec la validation, les événements d'activité et les limites de formule du tableau de bord. Elle répond
404une fois que le créateur a quitté l'espace de travail, et les trois écritures répondent403une fois que le rôle du créateur ne détient plusprojects.write. - Chaque appel compte dans les requêtes d'API de la formule, que lisent la page de facturation et les limites de la formule.
Vue d'ensemble de la plateforme et organisations
| Méthode | Chemin | Qui peut l'appeler | Rôle |
|---|---|---|---|
GET | /api/platform/overview | Le personnel avec platform.view | Les revenus mensuels récurrents et leur historique, la répartition des formules et les organisations par statut. range (30d, 90d, 6m, par défaut, 12m) |
GET | /api/platform/overview/export | Équipe avec reports.export | Chaque organisation sous forme de fichier CSV : organization, id, plan, status, seats, mrr_usd, created |
GET | /api/platform/organizations | Le personnel avec organizations.view | Les organisations. search (nom ou adresse), plan (starter, pro, scale), status (trialing, active, past_due, canceling, canceled, suspended), page, limit |
GET | /api/platform/organizations/:id | Le personnel avec organizations.view | Une organisation, par son identifiant public |
POST | /api/platform/organizations/:id/suspend | Le personnel avec organizations.manage | reason (3 à 300 caractères). L'espace de travail garde ses lectures et refuse toute écriture. Un espace déjà suspendu répond 409 |
POST | /api/platform/organizations/:id/reactivate | Le personnel avec organizations.manage | Lève une suspension |
Utilisateurs, personnel, rôles et paramètres de la plateforme
| Méthode | Chemin | Qui peut l'appeler | Rôle |
|---|---|---|---|
GET | /api/users | Personnel avec users.view | Chaque compte. search, role (owner, admin, member ou billing pour un rôle d'espace de travail détenu quelque part, staff pour un rôle de plateforme, none pour les comptes sans espace de travail), page, limit |
GET | /api/users/deleted | Personnel avec users.view | Les comptes supprimés, avec les mêmes filtres |
GET | /api/users/statistic | Personnel avec users.view | Les totaux : comptes, supprimés, personnel, sans espace de travail, nouveaux ces 30 derniers jours |
GET | /api/users/deleted/:username | Personnel avec users.view | Un compte supprimé |
GET | /api/users/:username | Personnel avec users.view | Un compte |
POST | /api/users | Personnel avec users.create | Crée un compte : first_name, last_name, email, password (8 à 128), en option phone |
PATCH | /api/users/:username | Personnel avec users.update | Modifie first_name, last_name, email ou phone. Un nouvel e-mail est protégé comme ci-dessous |
PATCH | /api/users/:username/change-password | Personnel avec users.update | Définit un nouveau password (8 à 128), protégé comme ci-dessous |
DELETE | /api/users/:username | Personnel avec users.delete | Déplace un compte vers la liste des supprimés. Un compte qui possède un espace de travail répond 409 avec user_owns_workspace tant que la propriété n'a pas changé de main |
POST | /api/users/deleted/:username/restore | Personnel avec users.restore | Ramène un compte supprimé |
GET | /api/admins | Personnel avec admins.view | Chaque compte avec ses rôles de plateforme. email, name (prénom, nom ou nom d'utilisateur), phone, une ligne correspondant à l'un quelconque des critères donnés, page, page_count |
GET | /api/admins/statistics | Personnel avec admins.view | Les totaux de la page du personnel |
GET | /api/admins/:id | Personnel avec admins.view | Un compte, par identifiant ou nom d'utilisateur |
POST | /api/admins | Personnel avec admins.create | Ajoute un compte du personnel : first_name, last_name, email, password et password_confirmation (6 caractères ou plus), en option phone, phone_code, profile_picture |
PATCH | /api/admins/:id | Personnel avec admins.edit | Modifie ces champs ; un nouveau mot de passe ou e-mail est protégé comme ci-dessous |
PATCH | /api/admins/:id/roles | Personnel avec admins.assign_roles | Définit les rôles de plateforme d'un compte : role_ids. Chaque rôle accordé ou retiré ne doit contenir que des permissions que l'appelant détient lui-même, sinon la réponse est 403 (role_outranks_caller). Son tableau de bord ouvert en est informé aussitôt |
DELETE | /api/admins/:id | Personnel avec admins.delete | Retire un compte. Un compte qui possède un espace de travail répond 409 |
PATCH | /api/admins/profile | Personnel avec admins.edit | Modifie le profil du membre du personnel connecté lui-même |
GET | /api/roles | Personnel avec roles.view | Les rôles de plateforme. name, guard_name, created_from, created_to, page, page_count |
GET | /api/roles/statistics | Personnel avec roles.view | Les totaux de la page des rôles |
GET | /api/roles/select | Personnel avec roles.view | L'identifiant et le nom de chaque rôle, pour un sélecteur |
GET | /api/roles/permissions | Personnel avec roles.view | Chaque permission de plateforme, groupée par domaine |
GET | /api/roles/:id | Personnel avec roles.view | Un rôle avec ses permissions |
POST | /api/roles | Personnel avec roles.create | Ajoute un rôle : name. Un nom déjà utilisé répond 409 |
PUT | /api/roles/:id | Personnel avec roles.edit | Renommer un rôle : name |
POST | /api/roles/:id/permissions | Personnel avec roles.assign_permissions | Définit les permissions d'un rôle, par nom. L'appelant ne peut donner que des permissions qu'il détient, et ne modifier qu'un rôle qui n'en contient aucune qui lui manque, sinon la réponse est 403 (permission_outranks_caller). Tous ceux qui le détiennent en sont informés aussitôt |
DELETE | /api/roles/:id | Personnel avec roles.delete | Supprime un rôle. Un rôle que quelqu'un détient répond 400 |
GET | /api/settings | Personnel avec settings.view | Les paramètres de la plateforme. search, category, page, page_count |
GET | /api/settings/:key | Personnel avec settings.view | Un paramètre |
PATCH | /api/settings/:key | Personnel avec settings.edit | Modifie un paramètre : value |
DELETE | /api/settings/:key | Personnel avec settings.edit | Supprimer un paramètre |
La protection des mots de passe et des e-mails : un appelant autre que le compte lui-même ne peut définir son mot de passe ou changer son e-mail que s'il est Platform admin ou détient chaque permission de plateforme que détient le compte ; sinon l'API répond 403. L'e-mail est aussi protégé, car une réinitialisation du mot de passe y est envoyée.
La protection des rôles : personne ne peut accorder, retirer ou relever un rôle de plateforme au-delà de ses propres permissions, si bien que admins.assign_roles ou roles.assign_permissions seul ne mène jamais au rôle Platform admin.
Les 21 permissions de plateforme sont 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 et settings.edit.
Paramètres publics et mode maintenance
| Méthode | Chemin | Qui peut l'appeler | Rôle |
|---|---|---|---|
GET | /api/public-settings | N'importe qui | maintenanceMode et trialDays (le trial_days de la plateforme, 14 quand il n'est pas défini) : ce que l'onboarding indique comme durée d'essai, et si le tableau de bord affiche son écran de maintenance. Ouvert pendant la maintenance |
Tant que le paramètre de plateforme maintenance_mode vaut true, chaque requête de quiconque n'est pas du personnel de la plateforme (un compte détenant platform.view) répond 503, avec code: "maintenance_mode" et le message « The platform is closed for maintenance. Please try again shortly. » Un jeton absent ou faux reçoit le même 503. Ces routes restent ouvertes à tous :
GET /api/healthetGET /api/public-settings;POST /api/auth/loginetPOST /api/auth/logout, où la connexion refuse encore quiconque n'est pas du personnel avec le503une fois les identifiants corrects ;POST /api/billing/webhooks/stripeet le téléchargement signé des factures ;- le point de terminaison MCP, qui garde sa propre clé, et
GET /api/demo/status; - le
POST /api/contactdu site et chaque route/api/licenses, pour que le formulaire de contact et le paiement des licences continuent de fonctionner.
Tout le reste est fermé aux non-membres du personnel, y compris l'inscription, la récupération du mot de passe, les invitations, l'onboarding, l'API publique sous /api/v1, les fichiers téléversés et l'assistant. La liste est back-end/src/common/constants/maintenance.constants.ts.
Le formulaire de contact
| Méthode | Chemin | Qui peut l'appeler | Rôle |
|---|---|---|---|
POST | /api/contact | N'importe qui | name (2 à 120), email, topic (licensing, setup, product), message (20 à 4000 caractères), clientToken (8 à 64 lettres, chiffres, _ ou -) et le champ caché website. Répond { id } |
- Un
clientTokenrépété renvoie l'identifiant du premier message au lieu d'en stocker un second : une nouvelle tentative est donc sans risque. - Un champ
websiterempli est traité comme un robot : la réponse a le même aspect, et rien n'est stocké. - Chaque adresse peut en envoyer cinq par heure ; le suivant répond
429. - Les messages sont stockés dans la table
contact_messageset transférés àCONTACT_INBOXquand il est défini.
Le paiement des licences
| Méthode | Chemin | Qui peut l'appeler | Rôle |
|---|---|---|---|
GET | /api/licenses/checkout/options | N'importe qui | Les moyens de paiement (card, paypal), le provider qui prend chacun (stripe, paypal ou simulator), et testMode, vrai quand aucun ne déplace d'argent réel |
POST | /api/licenses/checkout | N'importe qui | license (single, studio), email, name (2 à 120), method (card, paypal), en option locale (en, ar), et clientToken. Répond order, l'identifiant de la commande, et url, la page où payer. Une page de paiement qui ne peut pas s'ouvrir répond 503 avec payment_unavailable |
GET | /api/licenses/orders/:id | Quiconque détient l'identifiant de la commande | La commande : référence, licence, montant, e-mail, nom, moyen, fournisseur, status (pending, paid, cancelled, failed), test, et licenseKey une fois payée |
POST | /api/licenses/orders/:id/confirm | Quiconque détient l'identifiant de la commande | L'acheteur revient de Stripe ou de PayPal : demande au fournisseur si le paiement a abouti, puis répond la commande |
POST | /api/licenses/orders/:id/cancel | Quiconque détient l'identifiant de la commande | L'acheteur a quitté la page de paiement sans payer |
POST | /api/licenses/orders/:id/simulate | Quiconque détient l'identifiant de la commande | Le Pay ou le Cancel du paiement test : outcome (pay, cancel). Une commande prise par Stripe ou PayPal répond 404 |
- Le prix est celui du serveur lui-même, issu de
back-end/src/modules/licenses/constants/license-offers.ts; le navigateur n'en envoie jamais un. - Une carte va vers Stripe Checkout quand
STRIPE_SECRET_KEYest défini, PayPal vers PayPal quandPAYPAL_CLIENT_IDetPAYPAL_CLIENT_SECRETsont définis, et l'un comme l'autre vers le paiement test sinon. En mode démo, un fournisseur disposant de clés live n'est jamais proposé. - Une commande est payée une seule fois, par celui qui arrive le premier entre le
checkout.session.completedde Stripe surPOST /api/billing/webhooks/stripe, leconfirmdu retour, ou le Pay du paiement test. Celui-là seul émet la clé de licence et envoie le reçu par e-mail.checkout.session.expiredmarque la commande comme échouée. - L'identifiant de la commande est une longue chaîne aléatoire que seuls portent les liens de retour de l'acheteur. Un
clientTokenrépété répond la première commande. Chaque adresse peut démarrer dix commandes par heure.
Fichiers téléversés et médias
| Méthode | Chemin | Qui peut l'appeler | Rôle |
|---|---|---|---|
POST | /api/helpers/upload | Tout compte connecté | Stocke une image : file multipart (10 Mo au plus), path pour le dossier et for pour le préréglage de taille (profile, cover, logo, ou default). Répond les adresses par taille : original, de 1920 pixels de large au plus, et une copie redimensionnée, toutes en JPEG. Un fichier qui n'est pas une image lisible répond 400 |
Les images et les fichiers de projet vont dans le bucket Cloudflare R2 quand ses cinq variables R2_* sont définies, et dans MEDIA_UPLOAD_DIR sinon, servis à PUBLIC_MEDIA_URL suivi de /uploads/….
Tout ce que l'API sert sous /media porte X-Content-Type-Options: nosniff, et un fichier qui n'est ni une image raster, ni un PDF, ni du texte brut, ni de l'audio ou de la vidéo (un fichier HTML ou SVG, par exemple) est envoyé avec Content-Disposition: attachment, si bien qu'il se télécharge au lieu de s'ouvrir comme une page à l'adresse de l'API. Les fichiers d'un bucket R2 sont servis par le bucket, avec le type qu'ils avaient au téléversement.
Limites par adresse
| Route | Limite | Variable |
|---|---|---|
POST /api/auth/login | 10 connexions échouées par 15 minutes | RATE_LIMIT_LOGIN |
POST /api/auth/signup | 10 par heure | Aucune |
POST /api/auth/forgot-password | 5 par 15 minutes | Aucune |
POST /api/auth/reset-password | 10 par 15 minutes | Aucune |
POST /api/contact | 5 par heure | Aucune |
POST /api/licenses/checkout | 10 par heure | Aucune |
POST …/invitations/:id/resend | Une fois par minute, cinq fois par invitation | Aucune |
Une requête au-delà d'une limite répond 429, avec une phrase qui nomme ce qui a été limité et combien de temps attendre, comme « Too many sign-ups from this address. Wait an hour, then try again. » Les compteurs vivent dans la mémoire de l'API : un redémarrage les efface. Derrière un reverse proxy, définissez TRUST_PROXY avec le nombre de proxies devant l'API, sinon chaque visiteur est compté comme l'unique adresse du proxy.
L'assistant IA, ses conversations et ses propositions
Inclus avec votre achat. Connectez-vous pour le lire, ou ouvrez-le dans votre téléchargement.
La route de streaming de l'assistant, sa liste de modèles, les conversations enregistrées et les propositions de tâches.
Serveur MCP
Inclus avec votre achat. Connectez-vous pour le lire, ou ouvrez-le dans votre téléchargement.
Le point de terminaison MCP pour les agents de code, comment il est autorisé et quel espace de travail il lit.
Mises à jour de permissions en direct via socket.io
Inclus avec votre achat. Connectez-vous pour le lire, ou ouvrez-le dans votre téléchargement.
Le namespace socket.io que le tableau de bord écoute, son événement et comment une connexion est autorisée.
Routes du mode démo
Inclus avec votre achat. Connectez-vous pour le lire, ou ouvrez-le dans votre téléchargement.
Les routes qui n'existent que pour un build de démo publique.