Documentation API Manifst
Bienvenue dans la documentation de l'API REST Manifst v1. Elle est incluse à partir du plan Portefeuille, et s'évalue sur l'abonnement du compte propriétaire de la clé. Les comptes souscrits avant la grille actuelle conservent leur accès.
baseUrl et apiKey dans les variables de collection.Introduction
URL de base :
https://manifst.net/api/v1/
Toutes les réponses sont au format JSON avec la structure suivante :
// Succès
{
"data": { ... }, // objet ou tableau
"meta": { "total": 42 } // présent sur les listes
}
// Erreur
{
"error": {
"code": "INSUFFICIENT_SCOPE",
"message": "This API key does not have the required scope: 'projects:write'."
}
}
Authentification
Toutes les requêtes doivent inclure votre clé API dans le header Authorization :
Authorization: Bearer mfst_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Générer une clé
Rendez-vous dans Paramètres → Clés API de votre dashboard Manifst. Les clés sont affichées une seule fois à la création.
Exemple de requête
curl https://manifst.net/api/v1/projects \
-H "Authorization: Bearer mfst_live_xxxxx"
Rate limiting
| Fenêtre | Limite |
|---|---|
| Par heure | 1 000 requêtes |
| Par mois | 10 000 requêtes |
Chaque réponse inclut ces headers :
X-RateLimit-Limit-Hour: 1000
X-RateLimit-Remaining-Hour: 847
X-RateLimit-Limit-Month: 10000
X-RateLimit-Remaining-Month: 9231
X-RateLimit-Reset-Hour: 1711234567
En cas de dépassement, l'API retourne HTTP 429 avec le header Retry-After (secondes avant le prochain reset).
Pagination & rafraîchissement incrémental
Les listes volumineuses : /tickets et /time-entries : acceptent une pagination optionnelle et un filtre incrémental. Sans ces paramètres, la réponse reste identique aux versions précédentes (rétro-compatible).
| Paramètre | Description |
|---|---|
page | Numéro de page (défaut 1). Active la pagination. |
per_page | Taille de page (défaut 100, max 500). Active la pagination. |
updated_since | Ne renvoie que les éléments modifiés depuis cette date (YYYY-MM-DD ou datetime). Idéal pour un refresh incrémental Power BI. |
Le bloc meta renvoie toujours le total réel avant pagination :
GET /api/v1/tickets?project=abc-123&page=2&per_page=50&updated_since=2026-07-01
{
"data": [ ... ],
"meta": { "total": 320, "count": 50, "page": 2, "per_page": 50 }
}
Codes d'erreur
| Code | HTTP | Description |
|---|---|---|
| MISSING_API_KEY | 401 | Header Authorization absent |
| INVALID_API_KEY | 401 | Clé introuvable |
| REVOKED_API_KEY | 401 | Clé révoquée |
| EXPIRED_API_KEY | 401 | Clé expirée |
| PLAN_REQUIRED | 403 | Plan Portefeuille requis |
| ACCOUNT_INACTIVE | 403 | Compte inactif |
| IP_NOT_ALLOWED | 403 | IP non autorisée (whitelist) |
| INSUFFICIENT_SCOPE | 403 | Scope manquant sur la clé |
| RATE_LIMIT_EXCEEDED | 429 | Quota dépassé |
| NOT_FOUND | 404 | Ressource introuvable |
| EXTERNAL_ACCESS_DENIED | 403 | Projet dont vous n’êtes ni owner ni admin, et dont le owner n’a pas ouvert l’accès par assistant IA ou API à votre rôle (permission use_ai_api, Paramètres → Accès → Assistants IA et API ; aucun rôle par défaut). Ces projets n’apparaissent pas non plus dans GET /projects, le portefeuille, les métriques ni les notifications. |
| FORBIDDEN | 403 | Accès interdit : projet dont vous n’êtes pas membre, ou action que votre rôle dans le projet n’autorise pas (la clé a les droits de son titulaire, pas davantage) |
| VALIDATION_ERROR | 422 | Paramètre manquant ou invalide |
| METHOD_NOT_ALLOWED | 405 | Méthode HTTP non supportée |
Scopes
Chaque clé API dispose d'un ensemble de scopes qui définissent ses permissions. Les scopes disponibles :
| Scope | Description |
|---|---|
| projects:read | Lire les projets et leurs membres |
| projects:write | Créer, modifier, supprimer des projets |
| super-epics:read | Lire les super-épics |
| super-epics:write | Créer, modifier, supprimer les super-épics |
| epics:read | Lire les épics |
| epics:write | Créer, modifier, supprimer les épics |
| sprints:read | Lire les sprints |
| sprints:write | Créer et modifier les sprints |
| tickets:read | Lire les tickets |
| tickets:write | Créer, modifier, supprimer les tickets |
| comments:read | Lire les commentaires d'un ticket |
| comments:write | Créer, modifier, supprimer des commentaires |
| team:read | Lire les membres d'un projet |
| time:read | Lire les entrées de temps |
| time:write | Enregistrer des entrées de temps |
| statuses:read | Lire les statuts d'un projet |
| finance:read | Lire les indicateurs finance / EVM (projet & portefeuille) |
| roadmap:read | Lire les dépendances et jalons de roadmap |
| roadmap:write | Créer, modifier, supprimer les jalons et dépendances de roadmap |
| governance:read | Lire le registre des risques et la matrice RACI |
| governance:write | Créer, modifier, supprimer les risques et la matrice RACI |
| notifications:read | Lire les notifications du titulaire de la clé |
| notifications:write | Marquer lues, supprimer les notifications du titulaire de la clé |
| docs:read | Lire les espaces, pages et fichiers MyDoc |
| docs:write | Créer, modifier, supprimer des pages MyDoc |
Le référentiel d'un projet suit les scopes existants : les tags relèvent de tickets:read / tickets:write, les types de ticket de projects:read / projects:write, les règles de transition de projects:read, l'historique d'un ticket de tickets:read. La recherche demande projects:read et ne cherche que les types dont la clé a le scope de lecture.
Projets
Scope requis : projects:read
curl /api/v1/projects -H "Authorization: Bearer mfst_live_xxx"
// Réponse 200
{
"data": [
{
"uuid": "abc-123",
"name": "Mon projet",
"description": "Description",
"color": "#6366f1",
"default_dod": ["Code review faite", "Tests passants"],
"contingency_pct": 10.0,
"budget": 350000.00,
"user_role": "owner",
"member_count": 5,
"created_at": "2026-01-15 10:00:00",
"updated_at": "2026-03-01 14:22:00"
}
],
"meta": { "total": 1 }
}
Scope requis : projects:read
curl /api/v1/projects/abc-123 -H "Authorization: Bearer mfst_live_xxx"
Scope requis : team:read
Scope requis : projects:write
La création initialise automatiquement les 6 statuts par défaut (Backlog, À faire, En cours, En revue, Terminé, Bloqué) et les 2 types de tickets par défaut (Story, Bug).
Body
{
"name": "Mon projet", // requis, 1 - 80 caractères
"description": "...", // optionnel
"color": "#6366f1", // optionnel, hex #rrggbb
"default_dod": ["Tests OK", "Code review"], // optionnel : Definition of Done (tableau)
"contingency_pct": 10.00, // optionnel : aléas finance, 0 - 100 % (défaut: 10)
"budget": 350000.00 // optionnel : BAC / budget à terminaison (≥ 0, null pour effacer)
}
Réponse 201
{
"data": {
"uuid": "...",
"name": "Mon projet",
"description": null,
"color": "#6366f1",
"default_dod": null,
"contingency_pct": 10.0,
"budget": 350000.00,
"user_role": "owner",
"created_at": "2026-03-24 20:00:00",
"updated_at": "2026-03-24 20:00:00"
}
}
Scope requis : projects:write : owner uniquement
Body
{
"name": "Nouveau nom", // optionnel
"description": "...", // optionnel
"color": "#f59e0b", // optionnel
"default_dod": ["item 1", "item 2"], // optionnel : null ou [] pour vider
"contingency_pct": 15.00, // optionnel : 0 - 100 %
"budget": 420000.00 // optionnel : BAC (≥ 0, null pour effacer)
}
Réponse 200
{
"data": {
"uuid": "...",
"name": "Nouveau nom",
"description": null,
"color": "#f59e0b",
"default_dod": ["item 1", "item 2"],
"contingency_pct": 15.0,
"budget": 420000.00,
"user_role": "owner",
"created_at": "2026-03-24 20:00:00",
"updated_at": "2026-03-24 20:00:00"
}
}
Scope requis : projects:write : owner uniquement
// Réponse 200
{ "data": { "deleted": true } }
Super-épics
Scope requis : super-epics:read
Scope requis : super-epics:read
Scope requis : super-epics:write
Body
{
"project_uuid": "abc-123",
"title": "Titre",
"description": "...", // optionnel
"status": "backlog", // optionnel : backlog | in_progress | done (défaut: backlog)
"tags": ["tag-uuid-1"] // optionnel : array d'UUID de tags
}
Réponse 201
{
"data": {
"uuid": "...",
"project_id": 1,
"title": "Titre",
"description": null,
"status": "backlog",
"created_at": "2026-03-24 20:00:00",
"updated_at": "2026-03-24 20:00:00",
"tags": [{ "uuid": "tag-uuid", "name": "bug" }]
}
}
Scope requis : super-epics:write
Body
{
"title": "Nouveau titre", // optionnel
"description": "...", // optionnel
"status": "in_progress", // optionnel : backlog | in_progress | done
"tags": ["tag-uuid-1"] // optionnel : remplace l'ensemble des tags
}
Réponse 200
{
"data": {
"uuid": "...",
"project_id": 1,
"title": "Nouveau titre",
"description": null,
"status": "in_progress",
"created_at": "2026-03-24 20:00:00",
"updated_at": "2026-03-24 20:00:00",
"tags": [{ "uuid": "tag-uuid", "name": "bug" }]
}
}
Scope requis : super-epics:write : les épics liés sont détachés, pas supprimés.
Épics
Scope requis : epics:read
Scope requis : epics:read
Scope requis : epics:write
Body
{
"project_uuid": "abc-123",
"title": "Titre",
"description": "...", // optionnel
"super_epic_uuid": "se-uuid", // optionnel
"status": "backlog", // optionnel : backlog | todo | in_progress | in_review | done | blocked (défaut: backlog)
"tags": ["tag-uuid-1"] // optionnel : array d'UUID de tags
}
Réponse 201
{
"data": {
"uuid": "...",
"project_id": 1,
"super_epic_id": null,
"super_epic_uuid": null,
"title": "Titre",
"description": null,
"status": "backlog",
"created_at": "2026-03-24 20:00:00",
"updated_at": "2026-03-24 20:00:00",
"tags": [{ "uuid": "tag-uuid", "name": "bug" }]
}
}
Scope requis : epics:write
Body
{
"title": "...", // optionnel
"description": "...", // optionnel
"super_epic_uuid": null, // optionnel : uuid ou null pour détacher
"status": "done", // optionnel : backlog | todo | in_progress | in_review | done | blocked
"tags": ["tag-uuid-1"] // optionnel : remplace l'ensemble des tags
}
Réponse 200
{
"data": {
"uuid": "...",
"project_id": 1,
"super_epic_id": null,
"super_epic_uuid": null,
"title": "...",
"description": null,
"status": "done",
"created_at": "2026-03-24 20:00:00",
"updated_at": "2026-03-24 20:00:00",
"tags": [{ "uuid": "tag-uuid", "name": "bug" }]
}
}
Scope requis : epics:write : les tickets liés sont détachées.
Sprints
Scope requis : sprints:read
Scope requis : sprints:read
Scope requis : sprints:write
Body
{
"project_uuid": "abc-123",
"title": "Sprint 1",
"goal": "...", // optionnel
"status": "planning", // optionnel : planning | active | completed (défaut: planning)
"start_date": "2026-04-01", // optionnel
"end_date": "2026-04-14", // optionnel
"dod": ["item 1", "item 2"] // optionnel : Definition of Done du sprint (tableau)
}
Réponse 201
{
"data": {
"uuid": "...",
"project_id": 1,
"title": "Sprint 1",
"goal": null,
"status": "planning",
"start_date": "2026-04-01",
"end_date": "2026-04-14",
"dod": null,
"created_at": "2026-03-24 20:00:00"
}
}
Scope requis : sprints:write
Body
{
"title": "Sprint 1", // optionnel
"status": "active", // optionnel : planning | active | completed
"goal": "...", // optionnel
"start_date": "2026-04-01", // optionnel
"end_date": "2026-04-14", // optionnel
"dod": ["item 1"] // optionnel : null ou [] pour vider
}
Réponse 200
{
"data": {
"uuid": "...",
"project_id": 1,
"title": "Sprint 1",
"goal": null,
"status": "active",
"start_date": "2026-04-01",
"end_date": "2026-04-14",
"dod": ["item 1"],
"created_at": "2026-03-24 20:00:00"
}
}
Tickets
Scope requis : tickets:read
Filtres optionnels : ?sprint={uuid} et ?epic={uuid}
Scope requis : tickets:read
{
"data": {
"uuid": "...", "title": "...", "description": "...",
"priority": "haute", "status": "todo", "story_points": 5,
"epic_id": 1, "sprint_id": 2, "assignee": "Alice Martin",
"project_uuid": "abc-123", // UUID du projet : clé de jointure
"epic_uuid": "epic-uuid", // null si la story n'a pas d'épic
"sprint_uuid": "sprint-uuid", // null si la story n'est dans aucun sprint
"acceptance_criteria": ["Critère 1", "Critère 2"],
"dependencies": [{ "uuid": "...", "type": "story", "title": "Autre story" }], // tickets à terminer AVANT celui-ci (bloqueurs)
"blocked_by": [{ "uuid": "...", "number": 12, "key": "US-12", "title": "Autre story", "status": "todo" }],
"blocks": [], "duplicates": [], "duplicated_by": [], "relates": [], // cf. Liens entre tickets
"tags": [{ "uuid": "tag-uuid", "name": "bug" }],
"subtasks": [
{ "uuid": "...", "title": "Tâche technique", "status": "todo",
"sort_order": 1, "assignee_id": 3, "assignee_name": "Alice Martin",
"created_at": "2026-03-25 10:00:00", "updated_at": "2026-03-25 10:00:00" }
],
"subtasks_total": 1,
"subtasks_done": 0
}
}
Scope requis : tickets:write
Body
{
"project_uuid": "abc-123",
"title": "En tant qu'utilisateur…",
"description": "...",
"priority": "haute", // critique | haute | normale | faible
"status": "backlog", // backlog | todo | in_progress | in_review | done | blocked
"story_points": 5, // optionnel
"epic_uuid": "epic-uuid", // optionnel
"sprint_uuid": "sprint-uuid",// optionnel
"assignee_id": 3, // optionnel
"acceptance_criteria": ["Critère 1"], // optionnel
"dependencies": ["us-uuid-1"], // optionnel
"tags": ["tag-uuid-1"] // optionnel : array d'UUID de tags
}
Réponse 201
{
"data": {
"uuid": "...",
"project_id": 1,
"epic_id": null,
"sprint_id": null,
"assignee_id": null,
"assignee": null,
"title": "En tant qu'utilisateur…",
"description": null,
"priority": "haute",
"status": "backlog",
"story_points": 5,
"created_at": "2026-03-24 20:00:00",
"updated_at": "2026-03-24 20:00:00",
"acceptance_criteria": ["Critère 1"],
"dependencies": [],
"tags": [{ "uuid": "tag-uuid", "name": "bug" }]
}
}
Scope requis : tickets:write
Body
{
"title": "...", // optionnel
"description": "...", // optionnel
"priority": "haute", // optionnel : critique | haute | normale | faible
"status": "in_progress", // optionnel : backlog | todo | in_progress | in_review | done | blocked
"story_points": 8, // optionnel
"epic_uuid": "epic-uuid", // optionnel : uuid ou null pour détacher
"sprint_uuid": "sprint-uuid", // optionnel : uuid ou null pour détacher
"assignee_id": 3, // optionnel : id ou null
"acceptance_criteria": ["Critère 1"], // optionnel : remplace les critères existants
"dependencies": ["us-uuid-1"], // optionnel : remplace les tickets dont celui-ci dépend. Boucle refusée : 409
"tags": ["tag-uuid-1"] // optionnel : remplace l'ensemble des tags
}
Réponse 200
{
"data": {
"uuid": "...",
"project_id": 1,
"epic_id": null,
"sprint_id": null,
"assignee_id": null,
"assignee": null,
"title": "...",
"description": null,
"priority": "haute",
"status": "in_progress",
"story_points": 8,
"created_at": "2026-03-24 20:00:00",
"updated_at": "2026-03-24 20:00:00",
"acceptance_criteria": ["Critère 1"],
"dependencies": [],
"tags": [{ "uuid": "tag-uuid", "name": "bug" }]
}
}
Scope requis : tickets:write
Liens entre tickets
Cinq relations, lues depuis le ticket de l'URL : blocked_by (l'autre ticket doit être terminé avant), blocks (l'autre attend celui-ci), duplicates (celui-ci est un doublon de l'autre, qui est l'original), duplicated_by et relates (lien sans sens). Un lien s'enregistre immédiatement et apparaît des deux côtés.
Refus : un lien qui créerait une boucle de blocage (409 CONFLICT), deux tickets qui se dupliquent mutuellement (409), un ticket lié à lui-même ou une relation inconnue (422), un ticket d'un autre projet (404).
Scope requis : tickets:read
{
"data": {
"blocked_by": [{ "uuid": "...", "type": "story", "number": 12, "key": "US-12", "title": "Prérequis", "status": "todo" }],
"blocks": [],
"duplicates": [],
"duplicated_by": [],
"relates": []
}
}
Scope requis : tickets:write · permission edit_us dans le projet
Body
{
"other_uuid": "us-uuid", // requis : ticket du même projet
"kind": "duplicates" // requis : blocked_by | blocks | duplicates | duplicated_by | relates
}
Réponse 201 : les cinq listes à jour, comme le GET. Rejouer un lien existant ne fait rien.
Scope requis : tickets:write · permission edit_us dans le projet
Même body que le POST (ou ?other_uuid=…&kind=… en query). Réponse 200 : les cinq listes à jour.
Historique d'un ticket
Scope requis : tickets:read
Changements du plus récent au plus ancien. Un changement regroupe les champs modifiés par une même action. source vaut app, api, mcp, import ou le tracker / fournisseur Git qui a poussé le changement. Pagination facultative (?page, ?per_page, 200 au plus).
{
"data": [
{
"change_id": "c0a8…",
"created_at": "2026-10-27 14:02:11",
"source": "api",
"actor": { "user_id": 42, "uuid": "user-uuid", "name": "Camille Martin" },
"items": [
{ "field": "status", "old_value": "todo", "new_value": "in_progress",
"old_label": "À faire", "new_label": "En cours" }
]
}
],
"meta": { "total": 1, "count": 1 }
}
Sous-tâches
Les sous-tâches ont des statuts fixes (todo, in_progress, done), indépendants des statuts personnalisés du projet. Elles représentent une liste de contrôle technique attaché à un ticket.
Scope requis : tickets:read
{
"data": [
{
"uuid": "...",
"title": "Tâche technique",
"status": "todo", // todo | in_progress | done
"sort_order": 1,
"assignee_id": 3,
"assignee_name": "Alice Martin",
"created_at": "2026-03-25 10:00:00",
"updated_at": "2026-03-25 10:00:00"
}
],
"meta": { "total": 1 }
}
Scope requis : tickets:write
Body
{
"ticket_uuid": "us-uuid", // requis
"title": "Tâche technique", // requis
"status": "todo", // optionnel : todo | in_progress | done (défaut: todo)
"assignee_id": 3 // optionnel
}
Réponse 201
{
"data": {
"uuid": "...", "title": "Tâche technique", "status": "todo",
"sort_order": 1, "assignee_id": null, "assignee_name": null,
"created_at": "...", "updated_at": "..."
}
}
Scope requis : tickets:write
Body
{
"title": "...", // optionnel
"status": "in_progress", // optionnel : todo | in_progress | done
"assignee_id": 3, // optionnel : id ou null pour désassigner
"sort_order": 2 // optionnel
}
Réponse 200
{
"data": {
"uuid": "...", "title": "...", "status": "in_progress",
"sort_order": 2, "assignee_id": 3, "assignee_name": "Alice Martin",
"created_at": "...", "updated_at": "..."
}
}
Scope requis : tickets:write
Commentaires
Les commentaires sont attachés à un ticket. Seul l'auteur (ou un admin) peut modifier ou supprimer ses propres commentaires. Le contenu est du HTML sanitisé (scripts et handlers JS retirés).
Scope requis : comments:read
| Paramètre | Type | Description |
|---|---|---|
us | string | UUID du ticket : requis |
Réponse 200
{
"data": [
{
"uuid": "...",
"author_id": 3,
"author_name": "Alice Martin",
"author_initials": "AM",
"content": "<p>Texte du commentaire</p>",
"mentions": [4, 7],
"ticket_uuid": "...",
"created_at": "2026-04-02 10:00:00",
"updated_at": "2026-04-02 10:00:00"
}
],
"meta": { "total": 1 }
}
Scope requis : comments:read
Scope requis : comments:write
Body
{
"ticket_uuid": "...", // requis : UUID du ticket
"content": "<p>Mon commentaire</p>", // requis : HTML sanitisé
"mentions": [4, 7] // optionnel : IDs utilisateurs mentionnés
}
Réponse 201
{
"data": {
"uuid": "...",
"author_id": 3,
"author_name": "Alice Martin",
"author_initials": "AM",
"content": "<p>Mon commentaire</p>",
"mentions": [4],
"ticket_uuid": "...",
"created_at": "2026-04-02 10:00:00",
"updated_at": "2026-04-02 10:00:00"
}
}
Scope requis : comments:write : auteur ou admin uniquement.
Body
{
"content": "<p>Contenu modifié</p>", // optionnel
"mentions": [4, 7] // optionnel : remplace la liste complète
}
Scope requis : comments:write : auteur ou admin uniquement.
{ "data": { "deleted": true } }
Équipe
Scope requis : team:read
{
"data": [
{
"uuid": "...", "firstname": "Alice", "lastname": "Martin",
"email": "alice@example.com", "scrum_role": "dev",
"project_role": "member", "status": "active",
"tjm": 550.00, "service": "Delivery"
}
],
"meta": { "total": 4 }
}
tjm = taux journalier moyen (coût/jour) du membre sur ce projet, base des indicateurs finance ; null si non renseigné.
Backlog en une transaction
Scopes requis : tickets:write, plus epics:write si l'appel crée des epics et super-epics:write s'il crée un Super Epic. Le rôle doit avoir les droits de création correspondants dans le projet.
Le moteur de Launchpad, ouvert à l'API : tout est créé dans une seule transaction. Au moindre problème (type de ticket inconnu, points hors de l'échelle du Planning Poker 0, 1, 2, 3, 5, 8, 13, 21, UUID absent du projet, titre vide…), rien n'est créé et la réponse 422 liste tous les problèmes. Les tickets arrivent au statut backlog, hors sprint. Limites : 40 epics, 300 tickets et 30 critères par ticket par appel.
Chaque epic est soit existant (epic_uuid : les tickets y sont ajoutés), soit nouveau (title) : il est alors rattaché au Super Epic créé par l'appel s'il y en a un, ou au Super Epic existant indiqué par super_epic_uuid.
Idempotence : envoyez un commit_token (UUID). Le même jeton renvoyé ne crée rien une seconde fois et répond 200 avec "idempotent": true. Aucune synchro vers Jira, Azure Boards ou Linear (comme Launchpad) : tracker_not_synced le signale quand le projet est relié à un tracker.
Body
{
"project_uuid": "abc-123", // requis
"commit_token": "3f2b…-uuid", // optionnel, recommandé
"super_epic": { "title": "Portail client", "description": "<p>…</p>" }, // optionnel
"epics": [
{ "title": "Authentification",
"tickets": [
{ "title": "Se connecter par e-mail", "type": "story", "priority": "haute",
"story_points": 3, "description": "<p>…</p>",
"acceptance_criteria": ["Lien magique envoyé", "Expire en 15 min"] }
] },
{ "epic_uuid": "epic-existant-uuid",
"tickets": [ { "title": "Connexion SSO", "story_points": 8 } ] }
]
}
Réponse 201
{
"data": {
"super_epic_uuid": "…",
"n_epics": 1,
"n_tickets": 2,
"n_epics_touched": 2,
"created": {
"super_epic": { "uuid": "…", "title": "Portail client" },
"epics": [
{ "uuid": "…", "title": "Authentification", "tickets": [ { "uuid": "…", "title": "Se connecter par e-mail" } ] },
{ "uuid": "epic-existant-uuid", "existing": true, "tickets": [ { "uuid": "…", "title": "Connexion SSO" } ] }
]
},
"commit_token": "3f2b…-uuid"
}
}
Réponse 422
{ "error": { "code": "VALIDATION_ERROR",
"message": "2 problem(s), nothing was created: epics[0].tickets[1].type: unknown type 'task' (valid: story, bug); epics[1].epic_uuid: no epic with this UUID in the project." } }
Entrées de temps
Scope requis : time:read
Filtres optionnels : ?story={uuid} pour une story spécifique, ?user={user_id} pour les saisies d’un membre, ?from=YYYY-MM-DD et ?to=YYYY-MM-DD pour le jour du travail (bornes incluses).
Scope requis : time:write
Body
{
"ticket_uuid": "story-uuid", // requis
"duration_minutes": 90, // requis
"logged_at": "2026-03-24", // requis, format YYYY-MM-DD
"comment": "Revue de code" // optionnel
}
Réponse 201
{
"data": {
"uuid": "...",
"ticket_id": 12,
"user_id": 3,
"user_name": "Alice Martin",
"ticket_uuid": "...",
"ticket_title": "En tant qu'utilisateur…",
"duration_minutes": 90,
"comment": "Revue de code",
"logged_at": "2026-03-24 00:00:00",
"created_at": "2026-03-24 20:00:00"
}
}
Statuts
Scope requis : statuses:read
Retourne tous les statuts du projet (par défaut et personnalisés), triés par sort_order.
Réponse 200
[
{
"key": "backlog",
"label": "Backlog",
"color": "#64748b",
"bg_color": "#f1f5f9",
"sort_order": 1,
"is_done": false,
"is_default": true
},
{
"key": "in_validation",
"label": "En validation",
"color": "#7c3aed",
"bg_color": "#f5f3ff",
"sort_order": 7,
"is_done": false,
"is_default": false
}
]
Tags
Étiquettes réutilisables d'un projet. Pour poser des tags sur un ticket, un épic ou un super-épic, envoyez leur liste d'UUID dans le champ tags de ces ressources. Écriture : owner / admin du projet, ou rôle disposant de la permission manage_tags (tous les rôles par défaut).
Scope requis : tickets:read · GET /api/v1/tags/{uuid} pour un seul tag
{
"data": [ { "uuid": "tag-uuid", "name": "Front-end", "slug": "front-end", "usage_count": 12 } ],
"meta": { "total": 1 }
}
Scope requis : tickets:write
{ "project_uuid": "proj-uuid", "name": "Front-end" } // 50 caractères au plus
Réponse 201 : le tag. 409 CONFLICT si un tag de même nom (casse et ponctuation ignorées) existe déjà.
Scope requis : tickets:write · body { "name": "…" }
Scope requis : tickets:write · le tag est retiré de tous les tickets, épics et super-épics qui le portaient.
Types de ticket
Le slug d'un type est la valeur du champ type d'un ticket ; l'abbreviation préfixe sa clé (US-42). Écriture : owner / admin du projet, ou rôle disposant de la permission manage_ticket_types.
Scope requis : projects:read
{
"data": [
{ "uuid": "…", "name": "User Story", "slug": "story", "abbreviation": "US", "is_default": true, "sort_order": 1 }
],
"meta": { "total": 1 }
}
Scope requis : projects:write
{ "project_uuid": "proj-uuid", "name": "Spike", "abbreviation": "SPK" }
L'abréviation est mise en majuscules, 10 caractères au plus, unique dans le projet (409) ; SE, EP et SP sont réservées (422).
Scope requis : projects:write · body partiel { "name": "…", "abbreviation": "…" }. Le slug ne change pas.
Scope requis : projects:write · les tickets de ce type repassent en story. Un type par défaut ne se supprime pas (403).
Règles de transition
Scope requis : projects:read · &entity_type=ticket|epic|super_epic pour un seul type d'entité
forbidden_transitions liste, pour le titulaire de la clé, les changements de statut que les règles lui refusent : { statut_départ: { statut_arrivée: motif } }. Un client sait ainsi d'avance ce qu'il ne pourra pas faire, au lieu de le découvrir par un 403 WORKFLOW_BLOCKED. Les validations qui dépendent d'un objet précis (sous-tâches terminées, champs requis, verdict de recette) n'y figurent pas : elles ne se vérifient qu'au moment du PUT. Les owner / admin du projet ne sont pas soumis aux conditions (bypasses_conditions). La configuration des règles se fait dans l'application.
{
"data": {
"rules": [
{ "uuid": "…", "entity_type": "ticket", "status_from": null, "status_to": "done",
"rule_type": "condition", "action_type": "require_role",
"config": { "roles": ["product_owner"] }, "sort_order": 1, "enabled": true }
],
"forbidden_transitions": {
"ticket": { "review": { "done": { "reason": "role", "roles": ["product_owner"] } } },
"epic": {},
"super_epic": {}
},
"bypasses_conditions": false
},
"meta": { "total": 1 }
}
Dépendances
Liens finish-to-start entre items de roadmap (épic ou super-épic). Chaque nœud est exposé par son UUID (joignable avec /epics et /super-epics) pour reconstruire le graphe et, côté consommateur, en dériver le chemin critique.
Scope requis : roadmap:read
{
"data": [
{
"uuid": "dep-uuid",
"type": "FS", // finish-to-start
"pred_type": "epic", // epic | super_epic
"pred_uuid": "epic-uuid-1",
"succ_type": "epic",
"succ_uuid": "epic-uuid-2"
}
],
"meta": { "total": 1 }
}
Scope requis : roadmap:write · owner / admin du projet
{
"project_uuid": "proj-uuid",
"pred_type": "epic", // epic | super_epic : doit finir avant…
"pred_uuid": "epic-uuid-1",
"succ_type": "epic", // …que celui-ci commence
"succ_uuid": "epic-uuid-2"
}
Réponse 201 : la dépendance. Refus : lien sur soi-même (422), doublon ou cycle (409 CONFLICT), épic d'un autre projet (404). Les liens « bloque / est bloqué par » entre tickets passent par /tickets/{uuid}/links.
Scope requis : roadmap:write · owner / admin du projet
Jalons
Scope requis : roadmap:read
{
"data": [
{ "uuid": "...", "name": "Go-live", "date": "2026-09-30", "color": "#e11d48" }
],
"meta": { "total": 1 }
}
Scope requis : roadmap:write · owner / admin du projet
{
"project_uuid": "proj-uuid",
"name": "Go-live", // requis, 120 caractères au plus
"date": "2026-12-01", // requis, YYYY-MM-DD
"color": "#e11d48" // facultatif, #rgb ou #rrggbb
}
Réponse 201 : le jalon.
Scope requis : roadmap:write · body partiel : seuls les champs envoyés (name, date, color) changent.
Scope requis : roadmap:write · owner / admin du projet
Pièces jointes des tickets
Même barrière que le formulaire de l'interface : 10 Mo au plus par fichier, quota de stockage du projet, type réel détecté sur le contenu (jamais le nom ni l'en-tête envoyés). SVG, HTML, scripts et exécutables sont refusés. Écrire demande la permission edit_us ou create_us (owner / admin toujours).
Scope requis : tickets:read
{
"data": [
{ "uuid": "...", "ticket_uuid": "...", "name": "export.csv", "size": 2048,
"type": "text/csv", "downloadable": true, "created_at": "..." }
],
"meta": { "total": 1 }
}
downloadable: false : pièce jointe restée chez le tracker connecté (Jira, Azure Boards), sans copie locale.
Scope requis : tickets:write · permission edit_us ou create_us
En multipart/form-data, champ file :
curl -X POST https://manifst.net/api/v1/tickets/{uuid}/attachments \
-H "Authorization: Bearer mfst_live_..." \
-F "file=@export.csv"
Ou en JSON, contenu en base64 (une data URL est acceptée) :
{ "name": "capture.png", "content_base64": "iVBORw0KGgo..." }
Réponse 201 : la pièce jointe. 413 si le fichier dépasse 10 Mo ou si le quota du projet est atteint (STORAGE_QUOTA_REACHED).
Scope requis : tickets:read
Scope requis : tickets:read · renvoie le fichier lui-même (Content-Disposition: attachment), pas du JSON.
Scope requis : tickets:write · le fichier est effacé du disque avec la ligne.
Documentation (MyDoc)
Espaces et pages MyDoc, avec les droits de l'interface : la section MyDoc doit être ouverte au rôle du titulaire de la clé dans le projet, puis sa permission sur l'espace s'applique. L'owner du projet est admin partout. Une permission explicite fait foi. Un espace public donne edit à tout membre, un espace privé rien. Lire demande read, écrire edit.
Le contenu s'envoie en HTML (content, nettoyé par le sanitiseur de l'appli) ou en Markdown (content_markdown). Il se lit en HTML, en Markdown ou les deux (?format=html|markdown|both).
Scope requis : docs:read
{
"data": [
{ "uuid": "...", "name": "Spécifications", "description": "", "icon": "bi-journal-text",
"color": "#6366f1", "is_public": true, "page_count": 12, "my_permission": "edit", "updated_at": "..." }
],
"meta": { "total": 1 }
}
Scope requis : docs:read · liste à plat : les pages de premier niveau d'abord, puis les sous-pages (parent_uuid). Les pages archivées sont exclues, sauf avec ?include_archived=1.
{ "data": [ { "uuid": "...", "parent_uuid": null, "title": "Export comptable", "emoji": null,
"status": "published", "sort_order": 0, "word_count": 240, "rev": 7,
"updated_at": "...", "updated_by_name": "Jeanne Martin" } ] }
Scope requis : docs:read
{
"data": {
"uuid": "...", "space_uuid": "...", "space_name": "Spécifications", "project_uuid": "...",
"parent_uuid": null, "title": "Export comptable", "emoji": null, "status": "published",
"rev": 7,
"content_markdown": "### Objectif\n\nProduire un **CSV** mensuel…",
"word_count": 240, "labels": ["finance"],
"created_by": { "uuid": "...", "name": "Jeanne Martin" },
"updated_by": { "uuid": "...", "name": "Bob Dev" },
"created_at": "...", "updated_at": "...", "my_permission": "edit"
}
}
Gardez rev : il est exigé pour réécrire le contenu.
Scope requis : docs:write · permission edit sur l'espace
{
"space_uuid": "space-uuid",
"parent_uuid": "page-uuid", // facultatif : sous-page (2 niveaux au plus)
"title": "Export comptable", // requis, 500 caractères au plus
"emoji": "📄", // facultatif
"status": "published", // published (défaut), draft, archived
"content_markdown": "## Objectif\n\nProduire un **CSV** mensuel." // ou "content" en HTML
}
Réponse 201 : la page, avec son rev.
Scope requis : docs:write · body partiel : title, emoji, status, content ou content_markdown.
{ "content_markdown": "…", "base_rev": 7 }
Réécrire le contenu exige base_rev, le rev de la page lue (ou l'en-tête If-Match: 7). Sans lui : 428. Si la page a changé entre-temps (un humain en co-édition, un autre agent) : 409 CONFLICT et rien n'est écrit. Il faut alors relire la page, refusionner et renvoyer avec le nouveau rev. Une écriture acceptée archive la version précédente dans l'historique de la page, et les onglets ouverts dessus se rechargent.
Si quelqu'un co-édite la page et que ses dernières modifications ne sont pas encore enregistrées, la réponse est 409 PAGE_BEING_EDITED et rien n'est écrit : réessayer une minute plus tard. Ses frappes ne sont jamais écrasées.
Titre, emoji et statut seuls ne demandent pas de base_rev.
Scope requis : docs:write · supprime aussi ses sous-pages et les fichiers joints. Pour la retirer sans la perdre, préférer "status": "archived".
Scope requis : docs:read · liste (uuid, name, type, size, created_at, created_by_name), puis téléchargement. Le dépôt de fichiers dans une page reste réservé à l'interface.
Recette (QA)
Le cadre de recette des tickets, avec les règles de l'écran :
- un passage ne s'ouvre que pour un ticket dans une colonne de type
review; - les critères d'acceptation (
ac) et la Definition of Done du sprint (dod) y sont recopiés comme points à contrôler, et tenus à jour tant que le passage est ouvert ; - accepter demande qu'aucun point ne soit « à contrôler » ni « refusé » ; refuser demande au moins un point refusé.
Écrire demande les permissions perform_qa et edit_us (owner / admin toujours). Le verdict ne déplace pas le ticket : changez ensuite son statut par PUT /tickets/{uuid}. C'est là que s'appliquent les règles de transition, dont « recette acceptée exigée ».
Scope requis : tickets:read
{
"data": {
"ticket_uuid": "...", "status": "in_review", "column_type": "review",
"can_perform": true, "accepted": false,
"sessions": [
{ "uuid": "...", "round": 1, "verdict": "open", "tester_name": "Jeanne Martin",
"version_label": "1.4.0", "environment": "recette",
"counts": { "pending": 1, "passed": 1, "failed": 0, "untestable": 0 },
"checks": [
{ "uuid": "...", "source": "ac", "label": "Les montants sont en euros",
"result": "pending", "note": null, "orphan": false, "...": "..." }
] }
]
}
}
result : pending, passed, failed, untestable. orphan : point dont le critère a été retiré du ticket depuis.
Scope requis : tickets:write · body facultatif { "version_label": "1.4.0", "environment": "recette" }.
Idempotent : si un passage est déjà ouvert, il est rendu tel quel (200) ; sinon un nouveau tour est ouvert (201). Deux agents qui démarrent en même temps travaillent sur le même passage.
Scope requis : tickets:write
{
"checks": [
{ "uuid": "check-1", "result": "passed" },
{ "uuid": "check-2", "result": "failed", "note": "Montant en dollars, ligne 12" }
]
}
200 points au plus, tout ou rien. Un point seul : PUT /qa/checks/{uuid} avec { "result", "note" }.
Scope requis : tickets:write · { "label": "Export de plus de 10 000 lignes" }. Seuls les points libres se suppriment (DELETE /qa/checks/{uuid}) ; un critère ou un point de DoD se marque untestable.
Scope requis : tickets:write · version_label, environment, tester_uuid (membre du projet).
Scope requis : tickets:write · { "verdict": "accepted" } ou "rejected".
422 si la règle du verdict n'est pas remplie (le message donne le nombre de points à contrôler et refusés) ; 409 si le passage est déjà clos. Un passage clos ne se modifie plus : démarrer un nouveau tour.
Rituels du sprint
Les mêmes calculs que les écrans Sprint Review et Daily : aucun second calcul, les chiffres sont ceux que l'équipe voit.
Scope requis : sprints:read
Engagé / livré, vélocité des sprints précédents, burndown, périmètre ajouté ou retiré, tickets reportés, avancement des epics, jalons, risques liés, coût EVM du sprint. Les blocs finance et risks valent null si le titulaire de la clé n'y a pas accès (view_financial, section Risques), comme à l'écran ; access dit lesquels.
Scope requis : sprints:write · permission run_review (Product Owner et Scrum Master par défaut ; can_review dans la lecture dit si la clé l'a)
{
"goal_met": "partial", // yes, partial, no, ou null (non évalué)
"goal_comment": "Export livré sans les avoirs",
"review_notes": "Démo validée par le client."
}
Partiel : enregistrer le verdict n'efface pas les notes.
Scope requis : sprints:read · section Sprints ouverte au rôle
Avancement du sprint, points à régler (tickets bloqués, en cours depuis plus de 3 jours sans mouvement, file de recette, charge concentrée), tour de table par personne (ce qui a bougé depuis le dernier jour ouvré, ce qui est en cours). closed_today : le daily déjà clôturé aujourd'hui, ou null. La clôture (compte rendu envoyé à l'équipe) reste un geste de l'interface.
Scope requis : sprints:read · section Sprints + permission edit_sprint, comme l'écran de rétro
{
"data": [ { "uuid": "...", "category": "action", "content": "Ajouter les avoirs", "done": false, "created_at": "..." } ],
"meta": { "total": 1, "by_category": { "went_well": 0, "problem": 0, "action": 1 } }
}
Scope requis : sprints:write · permission edit_sprint
// POST /sprints/{uuid}/retro
{ "category": "problem", "content": "Recette tardive" } // went_well, problem, action
// PUT /retro-items/{uuid}
{ "done": true } // ou "content"
Indicateurs de flux
Scope requis : tickets:read
Le calcul des blocs Flux, Qualité et Périmètre du tableau de bord, sur 90 jours : débit par semaine, cycle time et lead time, travail en cours et son âge, taux de défauts, résultats de recette. available: false tant que l'historique des tickets n'existe pas.
| Paramètre | Description |
|---|---|
cfd=1 | Flux cumulé jour par jour |
sprints=1 | Périmètre des derniers sprints (ajouté, retiré, livré) |
forecast=1 | Prévision de fin par simulation Monte-Carlo (palier Portefeuille) |
projects=uuid1,uuid2 | Consolidation de plusieurs projets (palier Portefeuille). Les projets dont le titulaire n'est pas membre sont ignorés. Ni cfd, ni sprints, ni forecast : ils sont propres à une équipe. |
Risques
Registre des risques du projet. La sévérité est calculée (probabilité × impact, de 1 à 25) : low jusqu'à 4, medium jusqu'à 9, high jusqu'à 14, critical au-delà. Lecture : la section Risques doit être ouverte au rôle (la même règle que le bloc risques de la Sprint Review). Écriture : palier Team et plus, owner / admin du projet ou permission manage_risks. Une valeur hors domaine est refusée (422), jamais bornée en silence.
Scope requis : governance:read
| Paramètre | Description |
|---|---|
status | open, mitigating, monitored, closed, occurred (plusieurs séparés par des virgules) |
category | technique, delai, budget, ressources, externe, securite, qualite |
severity | low, medium, high, critical |
entity_type + entity_uuid | Risques liés à un ticket, une epic ou une super_epic |
updated_since | Modifiés depuis cette date (YYYY-MM-DD ou YYYY-MM-DD HH:MM:SS) |
{
"data": [
{
"uuid": "...", "title": "Retard API Stripe", "description": "",
"category": "externe", "probability": 4, "impact": 5,
"severity_score": 20, "severity_level": "critical",
"status": "open", "owner_uuid": "...", "owner_name": "Jeanne Martin", "owner_initials": "JM",
"mitigation": "Plan B : bouchon", "contingency": "", "due_date": "2026-12-01",
"links": [ { "entity_type": "ticket", "uuid": "...", "title": "Migration paiement" } ],
"created_at": "...", "updated_at": "..."
}
],
"meta": { "total": 1, "categories": ["technique", "delai", "..."] }
}
Scope requis : governance:read
Scope requis : governance:write · permission manage_risks
{
"project_uuid": "proj-uuid",
"title": "Retard API Stripe", // requis, 160 caractères au plus
"category": "externe", // requis
"probability": 4, // entier 1 à 5, défaut 3
"impact": 5, // entier 1 à 5, défaut 3
"status": "open", // défaut open
"owner_uuid": "user-uuid", // membre du projet, facultatif
"due_date": "2026-12-01",
"description": "...", // 5 000 caractères au plus
"mitigation": "...", // 2 000 caractères au plus
"contingency": "...", // 2 000 caractères au plus
"links": [ { "entity_type": "ticket", "uuid": "ticket-uuid" } ]
}
Réponse 201 : le risque. Une cible de links introuvable dans le projet refuse tout l'appel.
Scope requis : governance:write · body partiel. links remplace la liste entière ([] les retire) ; owner_uuid: null retire le porteur.
Scope requis : governance:write · permission manage_risks
Matrice RACI
Phases → tâches en lignes, participants en colonnes (membres du projet ou personnes externes sans compte), un rôle par case : R, A, C, I ou C/I. Mêmes droits que les risques : section RACI ouverte au rôle, et permission manage_raci pour écrire.
Scope requis : governance:read
{
"data": {
"participants": [
{ "participant_uuid": "p1", "user_uuid": "...", "is_external": false, "name": "Jeanne Martin", "initials": "JM", "...": "..." },
{ "participant_uuid": "p2", "user_uuid": null, "is_external": true, "name": "Client", "initials": "CL", "...": "..." }
],
"phases": [
{ "uuid": "...", "name": "Cadrage", "color": "#6366f1", "position": 0,
"tasks": [ { "uuid": "t1", "title": "Valider le budget", "position": 0,
"cells": { "p1": "A", "p2": "C/I" } } ] }
],
"can_edit": true
}
}
cells est indexé par participant_uuid ; une case vide n'y figure pas. can_edit dit si le titulaire de la clé peut écrire.
Scope requis : governance:write · permission manage_raci
// POST /raci/phases
{ "project_uuid": "proj-uuid", "name": "Cadrage", "color": "#6366f1", "position": 0 }
// POST /raci/tasks
{ "phase_uuid": "phase-uuid", "title": "Valider le budget", "position": 0 }
// POST /raci/participants : un membre…
{ "project_uuid": "proj-uuid", "user_uuid": "user-uuid" }
// …ou une personne externe
{ "project_uuid": "proj-uuid", "name": "Jeanne Client" }
Réponse 201 : l'élément créé. position (0 = en tête) est facultatif : par défaut, à la fin. Un membre déjà présent renvoie 409.
Scope requis : governance:write · phase : name, color, position ; tâche : title, position ; participant : name, pour une personne externe seulement (un membre garde le nom de son compte).
Scope requis : governance:write · permission manage_raci
{
"cells": [
{ "task_uuid": "t1", "participant_uuid": "p1", "role": "A" },
{ "task_uuid": "t1", "participant_uuid": "p2", "role": "C/I" },
{ "task_uuid": "t2", "participant_uuid": "p1", "role": null } // null ou "" vide la case
]
}
500 cases au plus, toutes du même projet. Tout ou rien : une case invalide refuse le lot entier. Réponse : la matrice à jour, et meta.updated. Une case seule peut aussi s'envoyer à plat : { "task_uuid", "participant_uuid", "role" }.
Scope requis : governance:write · supprimer une phase supprime ses tâches et leurs cases ; retirer un participant supprime sa colonne.
Recherche
Scope requis : projects:read, plus le scope de lecture de chaque type cherché.
Le moteur de la recherche de l'interface : titres, descriptions, clés (US-12, EP-3, SE-1, y compris l'ancien numéro global d'un ticket), objectifs de sprint, pages MyDoc (dans les espaces lisibles), noms et e-mails des membres.
| Paramètre | Description |
|---|---|
q | 2 à 200 caractères |
types | super_epic (super-epics:read), epic (epics:read), sprint (sprints:read), ticket (tickets:read), doc (docs:read), user (team:read). Défaut : tous. |
limit | Résultats par type, 1 à 50 (défaut 10) |
{
"data": [
{ "type": "ticket", "uuid": "...", "key": "US-12", "title": "Migration paiement", "status": "in_progress", "matched_legacy_key": false },
{ "type": "user", "uuid": "...", "user_id": 42, "title": "Jeanne Martin", "email": "jeanne@exemple.fr" }
],
"meta": { "total": 2, "types_searched": ["super_epic", "epic", "sprint", "ticket", "user"] }
}
Un type dont la clé n'a pas le scope de lecture n'est pas cherché : il figure dans meta.types_skipped. user_id est l'identifiant attendu par mentions dans les commentaires.
Notifications
La cloche du titulaire de la clé, tous projets confondus : mentions, assignations, désignations de responsable, changements de statut… Ce sont les notifications de l'interface, aux mêmes conditions (périmées et projets quittés exclus). Une clé ne voit jamais les notifications d'un autre compte.
Scope requis : notifications:read
| Paramètre | Description |
|---|---|
unread | 1 : non lues seulement |
type | Un ou plusieurs types séparés par des virgules : mention, doc_mention, story_assigned, lot_owner_assigned, ticket_updated, poker_started… |
project | UUID d'un projet |
since | Arrivées depuis cette date (heure du serveur) |
lang | fr ou en pour message ; défaut : la langue du compte |
page, per_page | Pagination, 50 par défaut, 200 au plus |
{
"data": [
{
"uuid": "...", "type": "mention",
"message": "Bob Dev vous a mentionné sur « Migration paiement »",
"params": { "actor_name": "Bob Dev", "ticket_title": "Migration paiement" },
"project_uuid": "...", "project_name": "Refonte",
"subject_type": "ticket", "subject_uuid": "ticket-uuid",
"is_read": false, "read_at": null,
"created_at": "2026-10-27 09:12:00", "age_seconds": 340
}
],
"meta": { "total": 1, "count": 1, "unread": 3, "page": 1, "per_page": 50 }
}
Pour un agent qui surveille sa boîte : ?unread=1&per_page=1 donne le compteur dans meta.unread pour un seul appel. subject_uuid se lit avec l'endpoint du subject_type (par exemple GET /tickets/{uuid}).
Scope requis : notifications:write · body { "read": true } (défaut) ou { "read": false }.
Scope requis : notifications:write
{ "uuids": ["notif-uuid-1", "notif-uuid-2"] } // 500 au plus
{ "all": true } // tout ce qui est arrivé
Réponse : { "marked": 2, "unread": 1 }. Préférer uuids : all marque aussi ce qui est arrivé depuis la dernière lecture de l'agent.
Scope requis : notifications:write
Finance / EVM
Indicateurs de gestion par la valeur acquise (Earned Value Management) d'un projet, calculés côté serveur à partir des story points, des temps enregistrés (× TJM) et du BAC déclaré (projects.budget).
bac, pv, ev, cpi, cv, sv, eac, etc, vac, tcpi) valent null tant qu'aucun BAC n'est déclaré sur le projet. Les métriques d'avancement et de coût (ac, ac_total, no_sprint_cost, engaged_charge, spi, progress_pct, planned_pct) restent calculées.ac est le coût du périmètre sprint : c'est la seule assiette comparable à l'ev, donc la seule admissible au dénominateur du cpi et dans le cv. ac_total = ac + no_sprint_cost est la dépense réelle totale : le temps pointé hors sprint ne reçoit aucune part de BAC (celui-ci est réparti au prorata des SP de sprint) donc n'acquiert aucune valeur, mais l'argent est bel et bien sorti. C'est donc ac_total qui sert aux questions d'atterrissage (eac, tcpi). Sur un projet sans pointage hors sprint, ac_total == ac et eac retombe exactement sur BAC ÷ CPI.Scope requis : finance:read
Le titulaire de la clé doit aussi pouvoir lire les finances du projet (droit « Finance » de son rôle, ou owner/admin du projet) ; sinon 403 FORBIDDEN.
{
"data": {
"project_uuid": "abc-123",
"bac": 350000.00, // budget à terminaison (déclaré)
"base_budget": 315000.00, // BAC − réserve d'aléa
"reserve": 35000.00, // réserve d'aléa (incluse dans le BAC)
"contingency_pct": 10.00,
"pv": 326341.95, // valeur planifiée = % SP planifié à date × BAC
"ev": 257455.27, // valeur acquise = % SP réalisé × BAC
"ac": 186573.75, // coût du périmètre sprint = Σ (temps ÷ 480) × TJM en vigueur à la date du travail
"no_sprint_cost": 8250.00, // temps pointé sur des tickets sans sprint
"ac_total": 194823.75, // dépense réelle totale = ac + no_sprint_cost
"engaged_charge": 137253.75, // coût réel des épics non terminées
"cpi": 1.38, // EV ÷ ac (> 1 = sous le budget)
"spi": 0.79, // EV ÷ PV (> 1 = en avance)
"tcpi": 0.60, // (BAC − EV) ÷ (BAC − ac_total)
"eac": 261889.45, // coût final estimé = ac_total + etc
"etc": 67065.70, // reste à terminaison = (BAC − EV) ÷ CPI
"vac": 88110.55, // écart à terminaison = BAC − EAC
"cv": 70881.52, // écart de coût = EV − ac
"sv": -68886.68, // écart de délai = EV − PV
"total_sp": 503,
"done_sp": 374, // SP physiquement terminés (avancement brut)
"earned_sp": 370, // SP acquis = Σ min(faits, engagés) par sprint : base de l'EV
"planned_sp": 469.10,
"undated_sp": 12, // SP engagés dans des sprints sans dates : ils comptent
// dans l'EV mais contribuent 0 au PV — ils gonflent le SPI
"progress_pct": 73.56, // earned_sp ÷ total_sp
"planned_pct": 93.24 // % SP planifiés à date
}
}
Portefeuille
Consolidation EVM sur tous les projets accessibles au porteur de la clé : une seule requête là où il fallait boucler projet par projet. Les indices consolidés sont calculés sur la somme des valeurs (EV, PV, AC), pas sur une moyenne des ratios.
Scope requis : finance:read
Filtre optionnel : ?projects=uuid1,uuid2 pour cibler une équipe / un programme. Seuls les projets dont vous pouvez lire les finances sont consolidés ; les autres sont comptés dans totals.excluded_no_financial_access.
{
"data": {
"totals": {
"projects": 12, "with_bac": 4, "without_bac": 8,
"bac": 1250000.00, "pv": 910000.00, "ev": 742000.00, "ac": 690000.00,
"ac_total": 712000.00,
"engaged_charge": 305000.00,
"cpi": 1.08, "spi": 0.82,
"etc": 472398.92, "eac": 1184398.92, "vac": 65601.08,
"cv": 52000.00, "sv": -168000.00,
"ac_all": 812000.00, // dépense réelle totale sur TOUS les projets (BAC ou non)
"engaged_all": 410000.00 // charge engagée sur tous les projets
},
"projects": [
{ "project_uuid": "abc-123", "project_name": "CMA-CGM", "bac": 350000.00, "ev": 257455.27, "ac": 186573.75, "cpi": 1.38, "spi": 0.79, "vac": 96360.55, "engaged_charge": 137253.75, "...": "..." }
]
},
"meta": { "total": 12 }
}
Documentation Manifst API v1 · Retour au site