Bureau API — Référence développeurs
L'API Bureau permet d'intégrer la production de contenu, la gestion de portail client et la facturation directement dans vos systèmes existants. Cette page documente l'ensemble des endpoints publics et les flows d'intégration recommandés.
Introduction
Bureau expose 12 endpoints serverless organisés en 4 catégories :
- Santé et monitoring :
/api/health - Portail public :
/api/portal,/api/waitlist,/api/track - Flux agence :
/api/generate,/api/deliver,/api/create-checkout,/api/stripe-portal - Webhooks entrants :
/api/stripe-webhook,/api/webhook,/api/auth-signup,/api/cron
Base URL
https://bureau-app.com/api
Tous les endpoints retournent du JSON (sauf /api/stripe-webhook qui attend du raw body). Les headers Content-Type: application/json sont attendus sur les requêtes POST.
Authentification
Les endpoints publics ne requièrent aucune authentification. Les endpoints agence seront progressivement protégés par un token Bearer une fois le client Supabase côté frontend activé.
Rate limits
Chaque endpoint a un rate limit individuel basé sur l'IP source, réinitialisé toutes les heures :
| Endpoint | Limite | Fenêtre |
|---|---|---|
| /api/generate | 50 | 1 heure |
| /api/auth-signup | 5 | 1 heure |
| /api/waitlist | 10 | 1 heure |
| /api/create-checkout | 20 | 1 heure |
| /api/portal (GET) | 200 | 1 heure |
| /api/track | 300 | 1 heure |
Une réponse 429 Too Many Requests inclut un header Retry-After en secondes.
Codes d'erreur
| Code | Signification |
|---|---|
| 200 | Succès |
| 400 | Validation d'input échouée (format, longueur, champs manquants) |
| 401 | Authentication manquante ou invalide |
| 404 | Ressource introuvable (ex : slug portail inexistant) |
| 429 | Rate limit atteint |
| 500 | Erreur serveur — à signaler |
| 503 | Service externe dégradé (Anthropic crédits épuisés, Stripe indisponible, etc.) — avec champ queued si applicable |
Endpoint : Health
GET/api/health
Exemple :
curl -s https://bureau-app.com/api/health | jq
Réponse :
{
"status": "ok",
"version": "1.4",
"ms": 873,
"timestamp": "2026-04-11T18:55:00.000Z",
"services": {
"anthropic": { "name": "anthropic", "ok": true, "ms": 120 },
"stripe": { "name": "stripe", "ok": true, "ms": 40 },
"brevo": { "name": "brevo", "ok": true, "ms": 372 },
"supabase": { "name": "supabase", "ok": true, "ms": 324 }
},
"env": {
"BREVO_API_KEY": true,
"STRIPE_SECRET_KEY": true,
"ANTHROPIC_API_KEY": true,
"SUPABASE_URL": true,
"EMAIL_FROM": "hello@bureau-app.com"
}
}
Codes : 200 (tout OK ou dégradé non critique), 503 (plusieurs services down).
Endpoint : Portal — lookup
GET/api/portal?slug={slug}
/p/:slug.| Paramètre | Type | Description |
|---|---|---|
| slugREQUIS | string | Slug de l'agence (100 chars max, lowercase, alphanumeric + tirets) |
Exemple :
curl -s "https://bureau-app.com/api/portal?slug=atelier-lumiere"
Réponse (200) :
{
"firm": {
"slug": "atelier-lumiere",
"name": "Atelier Lumière",
"description": "Production éditoriale premium",
"color": "#C8A96E",
"icon": "star",
"services": [...],
"tiers": [...],
"logo_url": null
}
}
Codes : 200 (trouvé), 400 (slug manquant), 404 (introuvable).
Endpoint : Portal — submit brief
POST/api/portal
| Body field | Type | Description |
|---|---|---|
| slugREQUIS | string | Slug de l'agence destinataire |
| clientEmailREQUIS | Email du client soumettant le brief | |
| clientNameREQUIS | string | Nom du client (120 chars max) |
| serviceREQUIS | string | Type de service demandé |
| briefREQUIS | string | Contenu du brief (20-5000 chars) |
| clientCompany | string | Nom de l'entreprise (optionnel) |
| tier | string | Tier de service (optionnel) |
Exemple :
curl -X POST https://bureau-app.com/api/portal \
-H "Content-Type: application/json" \
-d '{
"slug": "atelier-lumiere",
"clientEmail": "marie@exemple.com",
"clientName": "Marie Dupont",
"service": "Article de blog",
"brief": "Article sur les bonnes pratiques SEO 2026 pour e-commerce, 1500 mots, ton expert accessible"
}'
Réponse :
{ "ok": true, "ref": "REQ-MKQXL-7A3" }
Endpoint : Waitlist
POST/api/waitlist
| Body field | Type | Description |
|---|---|---|
| emailREQUIS | Email du prospect | |
| firstName | string | Prénom |
| agencyName | string | Nom de l'agence |
| agencyType | string | marketing, communication, conseil, coaching, juridique, comptabilité, immobilier, autre |
| monthlyVolume | string | <10, 10-50, 50-200, >200 |
| source | string | Source du lead (default: landing) |
curl -X POST https://bureau-app.com/api/waitlist \
-H "Content-Type: application/json" \
-d '{
"email": "marie@exemple.com",
"firstName": "Marie",
"agencyName": "Atelier Lumière",
"agencyType": "marketing",
"monthlyVolume": "10-50"
}'
Endpoint : Analytics tracking
POST/api/track
curl -X POST https://bureau-app.com/api/track \
-H "Content-Type: application/json" \
-d '{ "event": "landing_view", "page": "/landing" }'
Endpoint : Generate deliverable
POST/api/generate
| Body field | Type | Description |
|---|---|---|
| messagesREQUIS | array | Historique de messages au format Anthropic [{role, content}, ...] |
| system | string | System prompt (jusqu'à 50 000 chars incluant messages) |
| max_tokens | number | Max output tokens (default 4000, max 8192) |
| model | string | Model ID Anthropic (default claude-sonnet-4) |
| min_words | number | Nombre de mots minimum. Si le livrable est plus court, le serveur appelle Claude une seconde fois pour l'étendre. |
curl -X POST https://bureau-app.com/api/generate \
-H "Content-Type: application/json" \
-d '{
"system": "Tu es un rédacteur senior...",
"messages": [
{ "role": "user", "content": "Écris un article sur le SEO 2026" }
],
"max_tokens": 4000,
"min_words": 900
}'
503 et un body {"queued": true, "retryAfterMinutes": 15}. La requête est sauvegardée en Supabase et retentée automatiquement par /api/cron?task=retryQueued.Endpoint : Deliver
POST/api/deliver
emailLivrableDisponible.curl -X POST https://bureau-app.com/api/deliver \
-H "Content-Type: application/json" \
-d '{
"clientEmail": "marie@exemple.com",
"clientName": "Marie",
"serviceName": "Article blog SEO",
"agencyName": "Atelier Lumière",
"portalUrl": "https://bureau-app.com/p/atelier-lumiere"
}'
Endpoint : Checkout
POST/api/create-checkout
curl -X POST https://bureau-app.com/api/create-checkout \
-H "Content-Type: application/json" \
-d '{
"plan": "pro",
"userId": "user_123",
"userEmail": "pro@exemple.com"
}'
Réponse :
{
"url": "https://checkout.stripe.com/c/pay/cs_live_...",
"id": "cs_live_..."
}
Endpoint : Stripe Portal
POST/api/stripe-portal
curl -X POST https://bureau-app.com/api/stripe-portal \
-H "Content-Type: application/json" \
-d '{ "email": "user@exemple.com" }'
Réponse :
{ "url": "https://billing.stripe.com/session/..." }
Webhook : Stripe
POST/api/stripe-webhook
Events gérés :
checkout.session.completedcustomer.subscription.created→ email upgradecustomer.subscription.updatedcustomer.subscription.deletedinvoice.paid→ email paiement reçuinvoice.payment_failed→ email rappel paiement
stripe-signature doit être présent et STRIPE_WEBHOOK_SECRET doit être défini dans les env vars Vercel, sinon la signature n'est pas vérifiée et les events sont traités mais loggés comme warning.Webhook : Generic events
POST/api/webhook
signup.curl -X POST https://bureau-app.com/api/webhook \
-H "Content-Type: application/json" \
-d '{
"event": "signup",
"email": "new@exemple.com",
"name": "Nouvel utilisateur"
}'
Changelog
- v1.4 (2026-04-11) —
min_wordsvalidation côté serveur, health env vars check, security headers renforcés - v1.3 (2026-04-11) — 5 articles de blog + RSS feed + waitlist endpoint refactor
- v1.2 (2026-04-10) — Blog system, pages légales, status page, admin dashboard
- v1.1 (2026-04-09) — Stripe Portal, webhook raw body fix, confetti upgrade modal
- v1.0 (2026-04-08) — Initial release
Roadmap
- v1.5 — SSE streaming pour génération en temps réel
- v1.6 — API publique v1 avec Bearer token authentication
- v1.7 — Webhook sortants configurables par agence
- v2.0 — SDK JavaScript officiel, facturation intégrée, marque blanche complète