Référence de l’API REST
DocPlatform expose une API JSON RESTful à /api/v1/. Tous les endpoints nécessitent une authentification sauf indication contraire.
URL de base
http://localhost:3000/api/v1
Authentification
La plupart des endpoints nécessitent un token d’accès JWT dans l’en-tête Authorization :
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
Obtenez des tokens via les endpoints de connexion ou OIDC.
Cycle de vie des tokens
| Token | Durée de vie | Objectif |
|---|---|---|
| Token d’accès | 15 minutes | Authentification API |
| Token de rafraîchissement | 30 jours | Obtenir de nouveaux tokens d’accès |
Endpoints d’authentification
Inscription
POST /api/v1/auth/register
Créer un nouveau compte utilisateur. Le premier utilisateur devient Super Admin.
Requête :
{
"name": "Jane Smith",
"email": "[email protected]",
"password": "secure-password-here"
}
Réponse : 201 Created
{
"user": {
"id": "01HJK...",
"name": "Jane Smith",
"email": "[email protected]",
"role": "superadmin"
},
"access_token": "eyJhbG...",
"refresh_token": "eyJhbG...",
"expires_in": 900
}
Connexion
POST /api/v1/auth/login
S’authentifier avec e-mail et mot de passe.
Requête :
{
"email": "[email protected]",
"password": "secure-password-here"
}
Réponse : 200 OK
{
"access_token": "eyJhbG...",
"refresh_token": "eyJhbG...",
"expires_in": 900
}
Erreurs :
| Code | Description |
|---|---|
401 |
Identifiants invalides |
429 |
Trop de tentatives de connexion (limitation de débit) |
Rafraîchir le token
POST /api/v1/auth/refresh
Échanger un token de rafraîchissement contre un nouveau token d’accès. Le token de rafraîchissement est roté (l’ancien est invalidé).
Requête :
{
"refresh_token": "eyJhbG..."
}
Réponse : 200 OK
{
"access_token": "eyJhbG...",
"refresh_token": "eyJhbG...",
"expires_in": 900
}
Demande de réinitialisation de mot de passe
POST /api/v1/auth/password-reset
Demander un token de réinitialisation de mot de passe. Avec SMTP configuré, un e-mail est envoyé. Sans SMTP, le token est enregistré sur stdout.
Requête :
{
"email": "[email protected]"
}
Réponse : 200 OK (toujours, que l’e-mail existe ou non — empêche l’énumération)
Confirmation de réinitialisation de mot de passe
POST /api/v1/auth/password-reset/confirm
Définir un nouveau mot de passe à l’aide d’un token de réinitialisation.
Requête :
{
"token": "reset-token-here",
"new_password": "new-secure-password"
}
Réponse : 200 OK
Endpoints de contenu
Tous les endpoints de contenu sont limités à un espace de travail.
Lister les pages
GET /api/v1/workspaces/{workspace_id}/pages
Renvoie toutes les pages que l’utilisateur courant a la permission de consulter.
Paramètres de requête :
| Paramètre | Type | Description |
|---|---|---|
parent_id |
string | Filtrer par page parente (pour la navigation en arbre) |
tag |
string | Filtrer par tag |
published |
boolean | Filtrer par statut de publication |
limit |
int | Résultats max (par défaut : 100) |
offset |
int | Décalage de pagination |
Réponse : 200 OK
{
"pages": [
{
"id": "01HJK...",
"title": "Getting Started",
"slug": "getting-started",
"description": "Install and configure DocPlatform.",
"path": "getting-started.md",
"tags": ["guide"],
"published": true,
"access": "public",
"created_at": "2026-03-08T10:00:00Z",
"updated_at": "2025-01-16T14:30:00Z",
"author_id": "01HJK..."
}
],
"total": 42,
"limit": 100,
"offset": 0
}
Obtenir une page
GET /api/v1/workspaces/{workspace_id}/pages/{page_id}
Réponse : 200 OK
{
"id": "01HJK...",
"title": "Getting Started",
"slug": "getting-started",
"description": "Install and configure DocPlatform.",
"path": "getting-started.md",
"content": "# Getting Started\n\nThis guide walks you through...",
"content_hash": "sha256:abc123...",
"tags": ["guide"],
"published": true,
"access": "public",
"parent_id": null,
"created_at": "2026-03-08T10:00:00Z",
"updated_at": "2025-01-16T14:30:00Z",
"author_id": "01HJK..."
}
Erreurs :
| Code | Description |
|---|---|
403 |
Permissions insuffisantes |
404 |
Page non trouvée |
Créer une page
POST /api/v1/workspaces/{workspace_id}/pages
Requête :
{
"title": "New Page",
"slug": "new-page",
"content": "# New Page\n\nContent here...",
"description": "Description for search and SEO.",
"tags": ["guide"],
"published": false,
"parent_id": null
}
Réponse : 201 Created — renvoie l’objet page complet.
Mettre à jour une page
PUT /api/v1/workspaces/{workspace_id}/pages/{page_id}
Requête :
{
"title": "Updated Title",
"content": "# Updated Title\n\nUpdated content...",
"content_hash": "sha256:abc123..."
}
Le champ content_hash active la concurrence optimiste. Si le hash ne correspond pas à la version actuelle, le serveur renvoie 409 Conflict.
Réponse : 200 OK — renvoie l’objet page mis à jour.
Erreurs :
| Code | Description |
|---|---|
409 |
Le hash du contenu ne correspond pas (modification concurrente détectée) |
Supprimer une page
DELETE /api/v1/workspaces/{workspace_id}/pages/{page_id}
Réponse : 204 No Content
Endpoints d’espace de travail
Lister les espaces de travail
GET /api/v1/workspaces
Renvoie les espaces de travail dont l’utilisateur courant est membre.
Créer un espace de travail
POST /api/v1/workspaces
Nécessite le rôle Super Admin.
Requête :
{
"name": "API Docs",
"slug": "api-docs",
"git_remote": "[email protected]:org/api-docs.git",
"git_branch": "main"
}
Membres de l’espace de travail
GET /api/v1/workspaces/{workspace_id}/members
POST /api/v1/workspaces/{workspace_id}/invitations
DELETE /api/v1/workspaces/{workspace_id}/members/{user_id}
PUT /api/v1/workspaces/{workspace_id}/members/{user_id}/role
Recherche
GET /api/v1/workspaces/{workspace_id}/search?q={query}
Paramètres de requête :
| Paramètre | Type | Description |
|---|---|---|
q |
string | Requête de recherche (obligatoire) |
tag |
string | Filtrer par tag |
limit |
int | Résultats max (par défaut : 20) |
Réponse : 200 OK
{
"results": [
{
"page_id": "01HJK...",
"title": "Git Integration",
"description": "Bidirectional git sync...",
"path": "guides/git-integration.md",
"score": 0.95,
"highlights": [
"...bidirectional <mark>git sync</mark> lets your team..."
]
}
],
"total": 5,
"query": "git sync",
"took_ms": 12
}
Les résultats sont filtrés par permissions — les utilisateurs ne voient que les pages auxquelles ils ont accès.
Synchronisation git
Déclencher la synchronisation
POST /api/v1/workspaces/{workspace_id}/sync
Déclencher manuellement un pull git + réconciliation. Nécessite le rôle Admin.
Réponse : 200 OK
{
"status": "completed",
"changes": {
"added": 2,
"updated": 1,
"deleted": 0
}
}
Endpoints webhook
POST /api/v1/webhooks/github
POST /api/v1/webhooks/gitlab
POST /api/v1/webhooks/bitbucket
Ces endpoints reçoivent les payloads d’événements push des hébergeurs git. Aucun en-tête d’authentification requis — ils valident en utilisant le secret partagé GIT_WEBHOOK_SECRET.
Santé
Ces endpoints ne nécessitent pas d’authentification.
GET /health → 200 OK { "status": "ok" }
GET /ready → 200 OK { "status": "ready", "db": "ok", "search": "ok" }
Format d’erreur
Toutes les réponses d’erreur utilisent un format cohérent :
{
"error": {
"code": "CONFLICT",
"message": "Content hash mismatch. The page was modified by another user.",
"details": {
"current_hash": "sha256:def456...",
"provided_hash": "sha256:abc123..."
}
}
}
Codes d’erreur courants
| HTTP | Code | Description |
|---|---|---|
400 |
BAD_REQUEST |
Corps de requête ou paramètres invalides |
401 |
UNAUTHORIZED |
Authentification manquante ou invalide |
403 |
FORBIDDEN |
Permissions insuffisantes |
404 |
NOT_FOUND |
Ressource non trouvée |
409 |
CONFLICT |
Modification concurrente détectée |
429 |
RATE_LIMITED |
Trop de requêtes |
500 |
INTERNAL_ERROR |
Erreur serveur (vérifiez les journaux) |
Pagination
Les endpoints de listage de contenu utilisent une pagination par curseur avec des ULIDs pour des résultats stables même lorsque du contenu est ajouté ou supprimé.
Paramètres de requête :
| Paramètre | Type | Par défaut | Description |
|---|---|---|---|
cursor |
string | — | ULID du dernier élément de la page précédente. Omettez pour la première page. |
limit |
int | 20 | Nombre de résultats par page (max : 100) |
Métadonnées de réponse :
{
"data": [...],
"next_cursor": "01HJK...",
"has_more": true
}
Passez next_cursor comme paramètre cursor dans la requête suivante. Lorsque has_more est false, vous avez atteint la fin.
Upload d’assets
POST /api/v1/workspaces/{workspace_id}/assets
Télécharger des images et fichiers vers un espace de travail. Les assets sont stockés dans le répertoire assets/ de l’espace de travail et commités dans git si la synchronisation est activée.
Requête : multipart/form-data avec un champ file.
Limites :
| Contrainte | Valeur |
|---|---|
| Taille maximale du fichier | 10 Mo |
| Types acceptés | PNG, JPG, GIF, SVG, WebP, PDF |
Réponse : 201 Created
{
"path": "assets/screenshot-2025-01-15.png",
"url": "/api/v1/workspaces/{workspace_id}/assets/screenshot-2025-01-15.png",
"size": 245760,
"content_type": "image/png"
}
Résolution des conflits
Lister les conflits
GET /api/v1/workspaces/{workspace_id}/conflicts
Réponse : 200 OK
{
"workspace_id": "01HJK...",
"sync_status": "conflict",
"conflicts": [
{
"path": "guides/editor.md",
"ours_hash": "abc123...",
"theirs_hash": "def456...",
"page_id": "01HJK...",
"timestamp": "20250115T103045Z"
}
]
}
Télécharger une version de conflit
GET /api/v1/conflicts/{page_id}/{timestamp}/{version}
Le paramètre version est soit ours (local) soit theirs (distant).
Réponse : 200 OK avec Content-Type: text/markdown — contenu brut du fichier.
Résoudre un conflit
POST /api/v1/conflicts/{page_id}/{timestamp}/resolve
Supprime les artefacts de conflit après résolution manuelle.
Réponse : 200 OK
{
"message": "Conflict resolved",
"page_id": "01HJK..."
}
WebSocket
Obtenir un ticket de connexion
POST /api/v1/auth/ws-ticket
Les connexions WebSocket utilisent un système de ticket à usage unique pour éviter d’exposer les tokens JWT dans les URLs.
Réponse : 200 OK
{
"ticket": "random-ticket-value",
"expires_in": 30
}
Le ticket est valide pendant 30 secondes et ne peut être utilisé qu’une seule fois. Connectez-vous via :
ws://localhost:3000/ws?ticket={ticket}
Événements serveur
| Type d’événement | Payload | Quand |
|---|---|---|
page-created |
{workspace_id, path, actor} |
Une nouvelle page est créée |
page-updated |
{workspace_id, path, actor} |
Une page est modifiée |
page-deleted |
{workspace_id, path, actor} |
Une page est supprimée |
presence-join |
{workspace_id, user_id} |
Un utilisateur se connecte |
presence-leave |
{workspace_id, user_id} |
Un utilisateur se déconnecte (timeout de 90s) |
sync-status |
{workspace_id, status} |
Changement du statut de synchronisation git |
conflict-detected |
{workspace_id, path} |
Conflit de merge git détecté |
bulk-sync |
{workspace_id, changed_count, paths[]} |
Plusieurs fichiers synchronisés (>20 fichiers) |
Messages client
{"type": "subscribe", "workspace_id": "01HJK..."}
{"type": "unsubscribe", "workspace_id": "01HJK..."}
En-têtes de sécurité
DocPlatform définit les en-têtes suivants sur toutes les réponses :
| En-tête | Valeur |
|---|---|
X-Content-Type-Options |
nosniff |
X-Frame-Options |
DENY |
Content-Security-Policy |
default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline' |
X-Request-ID |
ULID (unique par requête, inclus dans les réponses d’erreur et les journaux) |
La documentation publiée définit en plus :
| En-tête | Valeur |
|---|---|
Cache-Control |
public, max-age=300 |
ETag |
Hash du contenu de la page rendue |
Limitation de débit
| Catégorie d’endpoints | Community Edition |
|---|---|
| Opérations de lecture | 100 / minute par utilisateur |
| Opérations d’écriture | 20 / minute par utilisateur |
| Recherche | 30 / minute par utilisateur |
| Authentification (connexion, inscription, réinitialisation) | 5 / minute par IP |
| Webhooks git | 10 / minute par espace de travail |
| Documentation publiée (publique) | 1 000 / minute par IP |
Les réponses de limitation de débit incluent les en-têtes Retry-After (secondes) et X-RateLimit-Reset (horodatage Unix).