CRM API — Référence des endpoints

Arc OS — The Orchestration System for AI Teams

Informations générales

Paramètre Valeur
Base URL https://arc-os.co/api/crm
Autorisation Authorization: Bearer <JWT> ou ?token=<JWT> (pour SSE/WebSocket)
Content-Type application/json
Algorithme JWT HMAC-SHA256
TTL JWT 24 heures

Authentification

Tous les endpoints (sauf /docs/*) nécessitent un token JWT dans l'en-tête Authorization: Bearer <token>.

Pour les connexions SSE et WebSocket, le token est transmis via le query param ?token=<JWT>.

Erreurs d'autorisation

Code Description
401 Token absent ou invalide
403 Accès au projet refusé (multi-tenancy)

Endpoints par catégorie

Compte et paramètres

Méthode Chemin Description
GET /account/settings Récupérer les paramètres du compte
PUT /account/settings Mettre à jour les paramètres du compte

Onboarding + Trial Credits (Phase 50.1)

Méthode Chemin Description
POST /onboarding/setup Crée le premier projet. Body multipart : config (JSON) + files. Le champ anthropicKey est désormais optionnel — si vide + user avec email_verified + n'ayant jamais eu d'essai gratuit, le projet est créé en trial_mode=1 avec 100K free tokens. Response : { ok, project, trial_activated }. Phase 51 : retourne 402 avec {error:"plan_limit_reached", reason, current, limit, plan} quand l'utilisateur dépasse la limite de projets pour son plan.
GET /account/trial-status Statut de l'essai gratuit pour le bandeau UI. Response : { email, email_verified, trial_granted, has_trial_active, total_remaining, total_granted, projects: [...] }

Onboarding Checklist (Phase 54.1, issue #56)

Checklist d'engagement post-wizard en 5 étapes. Chaque étape (workers, cli, skill, bot, issue) accepte le statut completed ou skipped. Les mutations sont idempotentes : un POST identique répété retourne le même état, sans écrire de doublon dans activity_log. Replay ne réinitialise pas l'état, il efface seulement dismissed_at — l'UI réaffiche le panneau avec le même progression.

Méthode Chemin Description
GET /onboarding/progress État actuel pour l'utilisateur authentifié. Response : { steps:["workers","cli","skill","bot","issue"], state:{<step>:<status>}, completed_count, total_steps:5, completed_at, dismissed_at, source, started_at, updated_at }. Utilisateur non touché → zéros/null sans création de ligne.
POST /onboarding/event Enregistre une transition d'étape. Body : { step: "workers"|"cli"|"skill"|"bot"|"issue", status: "completed"|"skipped", source?: "web"|"cli" }. Validation whitelist → 400 sur étape/statut inconnu. Response : même shape que GET. Émet onboarding_step_completed/onboarding_step_skipped dans activity_log uniquement sur changed ; lors de la transition à 5/5, émet aussi onboarding_completed avec duration_ms.
POST /onboarding/dismiss Ferme le panneau (dismissed_at = now). Idempotent. Émet onboarding_dismissed au premier appel avec payload {completed_count}.
POST /onboarding/replay Rouvre le panneau fermé (dismissed_at = NULL). L'état des étapes n'est pas affecté. Émet onboarding_replayed lors du clear-event.
POST /projects/:name/active-issue Issue #115. Lie la session web actuelle à une issue. Body : { issue_id: number, title?: string }. Écrit un événement session_active_issue dans activity_log (source=web).
GET /projects/:name/active-issue Issue #115. Dernière issue liée pour ce propriétaire dans les 7 derniers jours. Response : { active_issue_id, title, ts }.
GET /onboarding/cli-status Phase 54.3 (issue #58). L'utilisateur s'est-il connecté via arc login ces 30 derniers jours ? Response : { installed: boolean, last_cli_at: string|null }. SSOT — lignes dans activity_log avec event_type='cli_invocation' et actor=chatId. Le checklist d'onboarding frontend poll cet endpoint toutes les 10s tant que l'étape CLI est pending ; quand installed=true — marque automatiquement l'étape cli comme completed.
GET /analytics/onboarding-funnel Phase 54.6 (issue #61). Stats de funnel agrégées sur une fenêtre glissante. Query : hours=168 (1-720, défaut 7j). Response : { hours, total_steps:5, started_users, completed_users, completion_rate, per_step: [{step, completed, skipped}…], duration_p50_ms, duration_p90_ms, ttfc_p50_ms, ttfc_sample_size }. SSOT — événements activity_log onboarding_step_* + onboarding_completed + cli_invocation. TTFC = time-to-first-arc (delta julianday entre le premier onboarding-step et le premier cli_invocation par actor).

Le SSOT pour les métriques de funnel (Phase 54.6 / issue #61) correspond aux événements dans activity_log (event_type LIKE 'onboarding_%'). La table onboarding_progress est un cache dérivé : l'UI se rend en une seule requête plutôt qu'en agrégeant les événements.

Beta Feedback (Phase 53.3)

Méthode Chemin Description
POST /feedback Envoyer un feedback bêta. Body : {type: "bug"|"feature"|"other", title, description, project?, browser?}. Écrit dans activity_log (event_type=feedback_report) et envoie un ping CEO sur Telegram.
GET /admin/feedback Liste des dernières soumissions (admin uniquement). Query : limit=50 (max 500). Response : {items: [...], count}.

POST /feedback — validation du body : type ∈ {bug,feature,other}, title ≤200 chars, description ≤5000 chars. Succès → {ok: true, type, title}. Le ping Telegram est formaté comme 🐞/💡/📝 New <type> feedback ... From: <user> Title: <title> + les 400 premiers caractères de la description.

Le widget flottant dans FeedbackWidget.jsx (CRM dashboard) transmet automatiquement browser (UA + viewport + locale) et project (technical_name du projet actif).

Beta Invites (Phase 52.1, admin uniquement)

Méthode Chemin Description
GET /admin/invites Liste de tous les codes d'invitation + compteurs (total_active, total_used). Admin uniquement.
POST /admin/invites Générer N codes. Body : {count: N, note?: string}. Admin uniquement. Response : {ok, codes, count}.
DELETE /admin/invites/:code Révoquer un code d'invitation non utilisé.

Mise à jour du flux auth : POST /api/auth/register requiert désormais le champ invite_code (bêta fermée Phase 52.1). Sans code → 403 {error: "invite_required"}. Code invalide/utilisé → 403 {error: "invalid_invite"}.

Billing (Phase 51)

Méthode Chemin Description
GET /account/usage Historique d'utilisation des tokens pour l'utilisateur autorisé (Phase 63, #148). Réponse : { rows: [ { project_name, worker_id, input_tokens, output_tokens, cache_tokens, total_tokens, created_at } × jusqu'à 200 ], totals: { total, input, output } }. Lit token_usage_log par owner_id. Affiché dans UserDropdown (UsageCard) et BillingPage (section Token Usage).
GET /billing/status Plan actuel, limites, usage, features. Crée automatiquement une ligne Free au premier appel. Response : { plan, status, current_period_end, limits, usage, features, pricing, can_upgrade, stripe_ready }
POST /billing/checkout-session Stripe Checkout (Stage 2 — retourne 501 tant que le SDK Stripe n'est pas intégré)

Limites du plan (sémantique OR) :

Réponse 402 sur POST /onboarding/setup ou POST /projects/:name/workers quand la limite est dépassée : { error: "plan_limit_reached", reason: "projects_limit"|"workers_limit", current, limit, plan, message }

Les utilisateurs admin (role=admin) contournent entièrement la vérification des limites de plan — ce sont des opérateurs, pas des tenants payants.

Les bêta-testeurs (subscriptions.plan='beta', Phase 52 F&F) contournent aussi — nombre illimité de projets/workers plus toutes les features Max. Assigné manuellement : UPDATE subscriptions SET plan='beta' WHERE user_id=?.

Bugfix (issue #25) : POST /projects/create (Quick Start, Phase 50.2) plantait avec ownerChatId is not defined à cause d'une typo — corrigé, l'audit-actor est désormais correctement enregistré.

Bugfix (issue #26) : allocatePort() pour les nouveaux projets sonde désormais les vrais bindings TCP (ss -tln), et non plus uniquement le registry. Auparavant, il pouvait retourner un port occupé par un service hors-registry (NotebookLM bridge :19213, internal bridges) → le workspace bot plantait sur EADDRINUSE.

Flux auth (Phase 50.1) : /api/auth/register et /api/auth/login retournent désormais un JWT même pour un email non vérifié + flag needs_verification: true. Les actions sensibles (trial grant, billing, invites) vérifient email_verified séparément. Rate limit sur l'inscription : 3 / IP / 24h.


Projets (9 endpoints)

Méthode Chemin Description
GET /projects Liste des projets de l'utilisateur
POST /projects/create Crée un projet
GET /projects/:name Détails du projet
GET /projects/:name/config Configuration du projet
PUT /projects/:name/config Mettre à jour la configuration
GET /projects/:name/protocol Protocole du projet
PUT /projects/:name/protocol Mettre à jour le protocole
GET /projects/:name/logs Logs du projet
GET /projects/:name/metrics Métriques du projet

POST /projects/create — body :

{
  "technical_name": "string",
  "displayName": "string",
  "description": "string",
  "icon": "string",
  "color": "string"
}

GET /projects/:name/logs — query : category, lines

GET /projects/:name/metrics — query : since, until


Workers (11 endpoints)

Méthode Chemin Description
GET /projects/:name/workers Liste des workers
POST /projects/:name/workers Crée un worker
POST /projects/:name/workers/reorder Phase 53.8 — réordonner les workers. Body : {order: [id1, id2, ...]}. Réécrit workers_registry.json de façon atomique. Les workers absents de order sont ajoutés à la fin (protection contre la perte). Response : {ok, count, order}.
PUT /projects/:name/workers/:id Mettre à jour un worker
DELETE /projects/:name/workers/:id Supprimer un worker
POST /projects/:name/workers/generate-prompt Générer un prompt système
GET /projects/:name/workers/:id/telegram-token Récupérer le token Telegram
POST /projects/:name/workers/:id/telegram-token Phase 53.4 — valide le token via Telegram getMe, stocke bot_username dans le vault, refuse si le même bot est déjà lié à un autre worker (409). Response : {ok, started, bot_username}.
DELETE /projects/:name/workers/:id/telegram-token Supprimer le token Telegram
POST /projects/:name/workers/:id/notify Phase 53.2 — envoyer un ping d'événement TG ({event?, text, buttons?}). Silent no-op si aucun token lié ou CRM_DISABLE_TG_NOTIFY=1.
POST /projects/:name/workers/:id/suggest-bot-username 53.11.1 (issue #48) — retourne 5 candidats de username TG pour le wizard de création de bot au format <project>_<worker>_bot + fallbacks numérotés. Slugify supprime les tirets, troncature à 32 chars (la partie worker est tronquée en premier). Response : {candidates: string[]}.
POST /metrics/wizard 53.11.1 (issue #48) — sink de télémétrie pour le wizard de création de bot. Body : {action, duration_ms?, attempts?, success?, project?, worker_id?}. Écrit dans activity_log (event_type=wizard_metric), best-effort.
GET /analytics/wizard-metrics?hours=168 53.11.1 (issue #48) — résumé du funnel : {starts, completions, abandons, success_rate, avg_duration_ms_completed, avg_attempts_completed, by_action}. Défaut 7 jours, clamp 1-720h.
POST /projects/:name/restart Redémarrer un worker
GET /projects/:name/active-role Rôle actif actuel
POST /projects/:name/active-role Changer le rôle actif

POST /projects/:name/workers — body :

{
  "label": "string",
  "icon": "string",
  "type": "terminal | telegram",
  "model": "string",
  "max_turns": 20,
  "tools": ["Read", "Write", "Bash"],
  "system_prompt": "string",
  "focus_dirs": ["src/", "docs/"]
}

max_turns vaut 20 par défaut (auparavant 5, ce qui provoquait l'erreur "Reached max turns" dans les dialogues multi-étapes avec tool calls).

POST /projects/:name/restart — query : worker_id


Fichiers et stockage (8 endpoints)

Méthode Chemin Description
GET /projects/:name/files Arborescence des fichiers
POST /projects/:name/files/upload Envoyer un fichier (multipart, max 100 Mo)
POST /projects/:name/files/mkdir Crée un répertoire
POST /projects/:name/files/create Crée un fichier
GET /projects/:name/files/read Lire un fichier
PUT /projects/:name/files/save Enregistre un fichier
DELETE /projects/:name/files/delete Supprimer un fichier
POST /projects/:name/files/clone Git clone d'un dépôt

GET /projects/:name/files — query : path

GET /projects/:name/files/read — query : path, raw


Skills (18 endpoints)

Skills du projet

Méthode Chemin Description
GET /projects/:name/skills Liste des skills du projet
POST /projects/:name/skills Crée une skill
PUT /projects/:name/skills/:id Mettre à jour une skill
DELETE /projects/:name/skills/:id Supprimer une skill

Marketplace global

Méthode Chemin Description
GET /skills Liste des skills globales
POST /skills Publier une skill
GET /skills/:id Détails d'une skill
PUT /skills/:id Mettre à jour une skill
DELETE /skills/:id Supprimer une skill

Évolution et mises à jour

Méthode Chemin Description
GET /skills/:id/evolution Historique d'évolution d'une skill
GET /skill-updates Liste des mises à jour disponibles
POST /skill-updates/:id/approve Accepter une mise à jour
POST /skill-updates/:id/reject Rejeter une mise à jour

Forks de skills

Méthode Chemin Description
GET /projects/:name/skill-forks Liste des forks
POST /projects/:name/skill-forks Crée un fork
PUT /projects/:name/skill-forks/:id Mettre à jour un fork
DELETE /projects/:name/skill-forks/:id Supprimer un fork

Chat et messages

Méthode Chemin Description
POST /projects/:name/chat Envoyer un message dans le chat
GET /projects/:name/chat/history Historique du chat
POST /projects/:name/message Envoyer un message à un worker (Phase 48.6 : wake-up automatique du worker idle-killed, ~2-4s cold start ; Phase 48.6.1 : le wake-up fonctionne aussi dans les projets single-mode, pas seulement parallel)
GET /projects/:name/pins Liste des notes (pins)
POST /projects/:name/pins Crée une note
DELETE /projects/:name/pins/:id Supprimer une note

Wiki (4 endpoints)

Méthode Chemin Description
GET /projects/:name/wiki/tree Arborescence des pages wiki
GET /projects/:name/wiki/file Lire une page wiki
PUT /projects/:name/wiki/save Enregistre une page wiki
GET /projects/:name/wiki/download Télécharger le wiki en archive ZIP

Analytique (4 endpoints)

Méthode Chemin Description
GET /analytics/activity Fil d'activité
GET /analytics/sidebar Données pour le panneau latéral
GET /analytics/phases Liste des phases du projet
POST /analytics/phases Mettre à jour les phases du projet

Marketplace et Sage (8 endpoints)

Méthode Chemin Description
GET /sage/scout/categories Catégories du marketplace
POST /sage/scout Rechercher des skills
POST /sage/scout/quick-scan Scan rapide
POST /sage/scout/analyze Analyse approfondie d'une skill
POST /sage/scout/install Installer une skill
POST /sage/analyze Analyse Sage
GET /sage/status Statut du service Sage
POST /sage/benchmark Lancer un benchmark

Mémoire et Knowledge

Méthode Chemin Description
POST /projects/:name/memory/refresh Mettre à jour la mémoire neurale
POST /projects/:name/memory/fetch-artifact Télécharger un artefact
GET /projects/:name/learnings Liste des learnings
POST /projects/:name/learnings Ajouter un learning
GET /projects/:name/knowledge-graph Graphe de connaissances du projet

Documentation (globale, sans auth)

Méthode Chemin Description
GET /docs/tree?lang=<lang> Arborescence de la documentation ; lang optionnel (en/uk), défaut en
GET /docs/file?path=<p>&lang=<lang> Lire un fichier de documentation avec language fallback

GET /docs/tree — query : lang (optionnel)

GET /docs/file — query : path (obligatoire), lang (optionnel)


Système

Méthode Chemin Description
GET /system/configs Récupérer les configurations système
PUT /system/configs Mettre à jour les configurations système

Codes d'erreur

Code Signification
200 Succès
201 Créé
400 Requête invalide
401 Non autorisé
403 Interdit (multi-tenancy)
404 Non trouvé
409 Conflit (doublon)
429 Trop de requêtes
500 Erreur serveur

GitHub Integration (Phase 49.3)

Endpoint Méthode Description
/api/crm/projects/:name/github GET Liste des repos GitHub liés au projet
/api/crm/projects/:name/github POST Lier un repo (body : {owner, repo}) — retourne webhook URL + secret + instructions de setup
/api/crm/projects/:name/github/:id DELETE Délier un repo
/api/crm/projects/:name/github/events GET Liste des derniers événements GitHub (Phase 49.3.1, query : ?limit=50)
/api/webhooks/github POST Récepteur de webhook public (validé HMAC-SHA256, rate-limit 100/min)

Événements supportés : push, pull_request, workflow_run, issues. Notifications routées vers le Telegram du propriétaire du projet.

Account Security (Phase 45.4)

Endpoint Méthode Description
/api/crm/account/recovery GET Liste des recovery keys actives
/api/crm/account/recovery POST Crée une recovery key (body : encryptedKey, keyHint)
/api/crm/account/recovery DELETE Révoquer une ou plusieurs recovery keys (body : { id } ou {} pour toutes)
/api/crm/account/recovery/restore GET Récupérer la clé maître chiffrée pour la restauration

Sécurité


Phase 53.13 — baseline type-safety (2026-05-10)

Aucun changement de comportement des endpoints — uniquement des types internes. tsc --noEmit bloque désormais push/CI :


Phase 53.15 — Sentinel Sprint 1 (2026-05-10)

Changements de comportement des endpoints auth + admin (corrections P0 de l'audit Sentinel) :


Phase 53.21 — Sentinel P2 batch 2 (2026-05-12)

Phase 53.18 — correction fuite secret tmux (2026-05-11)

Aucun changement de comportement des endpoints — uniquement un refactor des chemins de spawn internes.

Phase 53.16 — Sentinel Sprint 2 (2026-05-10)

Changements de comportement des endpoints après le hardening 13 × P1 :

Phase 55 — Cosmic Editorial login (2026-05-13)

Nouveaux endpoints pour la connexion via magic-link :

L'union EphemeralTokenType est étendue : elle contient désormais "magic_link" aux côtés des types existants oauth_state / password_reset / email_verification / tfa_challenge.

Le frontend (CosmicCard.jsx) gère l'état magic (countdown de renvoi de 60 s) et le paramètre URL ?magic_token= (auto-consume → login → animation de succès).

Phase 56 — AI Interop / Project Context Export (2026-05-13)

Export réservé au propriétaire d'un snapshot sanitisé du projet en .md pour transmission à un AI externe (Gemini / ChatGPT / Perplexity / Claude.ai).

Alerte : quand le propriétaire dépasse 3 exports en 24h ET prefs.notify_on_export = true (défaut OFF) — logActivity("export_alert", ...) transite par le pipeline de notification TG Phase 53.10 existant (alertFired: true dans le corps de la réponse).

Scanner multi-niveaux (shared/secret-scanner.ts) — Tier 1 regex (PATTERN_REGISTRY depuis le sanitiseur PII), Tier 2 entropie Shannon ≥4,5 bits/char sur des séquences ≥20 chars, Tier 3 heuristiques contextuelles (key=/token:/secret=/password=). Whitelist : UUID / git SHA / SHA-256 / caractères répétés / hex court / base58 basse entropie. Niveaux de sévérité (critical/high/medium/low). Performance : <500 ms / 1 Mo.

Migration DB 024 — tables export_audit_log + export_preferences.


Phase 57 — Platform Settings (suivi Sentinel #103, 2026-05-15)

Gestion des secrets super-admin via l'UI CRM au lieu de ssh/edit-.env/paste-in-chat. MVP backend (Stage 1 sur 4 stages). Tous les endpoints sont gateés par requireAdmin (Phase 53.15) — retournent 403 Forbidden — admin only pour les non-admin, 401 Unauthorized sans JWT.

Liste d'exclusion stricte NEVER_EXPOSE : CRM_SECRET (signature JWT) + SECRET_ENCRYPTION_KEY (méta-clé vault) — même une requête admin avec un token valide retourne 400 "not managed". Le log d'audit est en append-only (pas de handler UPDATE/DELETE), chaque action (y compris les échecs) écrit une ligne avec IP + UA + email.

Migration DB 026 — table platform_audit_log. Stage 2 (frontend PlatformSettings.jsx) — livré 2026-05-15 (cbc8bac) : grille de cartes admin uniquement + modal de rotation (<input type="password"> + confirmation de saisie) + drawer d'audit ; entrée dans la sidebar filtrée par userRole === "admin" récupéré depuis /api/auth/me.

Polish (2026-05-15, commit 56191b0) — Restructuration UI Platform Settings. Les items de la réponse GET /api/crm/platform/settings gagnent 5 nouveaux champs : category (anthropic|oauth|telegram|email), usedIn (string[] — fichiers/flux qui consomment la clé), getFromUrl (où obtenir une nouvelle valeur), effectAfterRotate, riskIfLeaked. Utilisés par le frontend pour afficher 4 groupes de cartes par section + panneau d'aide repliable par carte avec contexte structuré (Used in / Get from / Effect / Risk). Aucun changement de comportement des endpoints mutateurs (PUT/POST/restart/test).

Refactor (2026-05-16) — nettoyage interne de shared/routes/platform.ts. 39 lignes supprimées (16 ajoutées), aucun changement de surface API publique. Les signatures et réponses des endpoints PUT/POST/restart/test/audit sont inchangées. Documenté ici uniquement parce que le hook pre-push de couverture doc se déclenche sur tout diff shared/routes/*.ts.

Activité antidatée (#117, 2026-05-16)POST /api/mcp/issues/:project/:id/log accepte désormais un champ ts optionnel (chaîne ISO-8601). Utilisé par la reconstruction arc retro pour que les entrées historiques atterrissent à leur timestamp d'origine. Les valeurs à date future sont silencieusement plafonnées à now dans addActivity() (défense contre les erreurs de saisie). ISO invalide → 400.

Stage 3 (2026-05-15) — hot-reload des secrets OAuth + Resend sans restart. shared/auth.ts loadOAuthConfig() lit désormais getSecret("GITHUB_CLIENT_ID/SECRET" | "GOOGLE_CLIENT_ID/SECRET") à chaque appel au lieu de process.env. Les callsites dans master-bot/routes/auth.ts appelaient déjà getOAuthConfig() par requête → 0 changement de callsite. RESEND_API_KEY était déjà hot-reload via shared/email.ts:47. Changement de comportement : PUT /api/crm/platform/settings/{GITHUB_CLIENT_ID|GITHUB_CLIENT_SECRET|GOOGLE_CLIENT_ID|GOOGLE_CLIENT_SECRET|RESEND_API_KEY} prend désormais effet dès la prochaine requête, sans restart requis. restartTargets pour ces 5 clés est vide → le bouton Restart est masqué dans l'UI. Cas limite : un flux OAuth avec un state-token émis avant la rotation peut recevoir un 400 au callback lors du code-exchange — un retry utilisateur résout le problème. ANTHROPIC_API_KEY, PLATFORM_ANTHROPIC_KEY, MASTER_BOT_TOKEN, CITADEL_BOT_TOKEN restent restart-required (lus lors du spawn du child-bot / initialisation du long-poll TG).

Nettoyage Phase 57.3.5 (2026-05-16) — l'allowlist MANAGED_KEYS réduite de 9 à 6. Supprimés : ANTHROPIC_API_KEY (les opérateurs utilisent désormais PLATFORM_ANTHROPIC_KEY pour les trial-credits et l'inférence plateforme ; le fallback .env fonctionne toujours pour les chemins de code legacy jusqu'à la migration de Sage/Karpathy), CITADEL_BOT_TOKEN (le bot par projet appartient aux entrées vault child:<name>:token, géré par le flux d'onboarding worker — pas les Platform Settings). MASTER_BOT_TOKEN remis à jour : label → "Telegram — System Monitor Bot", description → "Server health alerts + on-demand status probes (admin-only, not a chat bot)". La Phase 58 ajoutera la boucle de monitoring (push alerts pour crash worker / disque / RAM / brute-force SSH / bypass CF + commandes /status, /health, /errors, /restart). Set final : PLATFORM_ANTHROPIC_KEY + GITHUB×2 + GOOGLE×2 + MASTER_BOT_TOKEN + RESEND_API_KEY (refs #103).

Phase 63 — Consolidation UI/UX + Suivi de l'utilisation des tokens (2026-05-21, #148)

Nouvel endpoint :

Modifications dans claude-runner.ts :

Modifications UI (pas API) :