YAOKAPartenairesyaoka.fr
v1

Partners API

Démarrer

  • C'est quoi cette API ?
  • Prérequis
  • Vocabulaire
  • Quickstart 10 min

Concepts

  • Authentification
  • Sandbox vs Live
  • Idempotency
  • Erreurs
  • Rate limits

Endpoints

  • Provisioning
  • Clients
  • Devis
  • Factures
  • Factures fournisseurs
  • Notes de frais

Webhooks

  • C'est quoi un webhook ?
  • Configurer chez vous
  • Vérifier la signature
  • Liste des événements

Outils

  • Serveur MCP (bonus IA)
  • Erreurs courantes
  • FAQ
Retour au dashboardPrésentation produitSupport

Guide d'intégration

YAOKA Partners API

Ce guide t'explique comment intégrer YAOKA dans Qualibox, étape par étape, sans supposer que tu connais déjà notre stack. Si tu sais faire un fetch() en JS, tu peux suivre.

Migration OAuth en cours : le modèle d'auth a changé. Les clés sk_qbx_master_* et le provisioning par master key ne fonctionnent plus. Les OFs s'inscrivent eux-mêmes sur YAOKA et autorisent Qualibox via OAuth 2.0. Voir /partners/qualibox section technique pour le nouveau flow. Les sections de ce guide sont en cours de réécriture — les endpoints REST (/api/v1/orgs/[id]/x) restent identiques, seul l'header Authorization change (access_token OAuth ou clé org-owned sk_org_*).

Base URL

app.yaoka.fr/api/v1

Format

JSON · UTF-8

Auth

OAuth 2.0 + API keys

Mise à jour

2026-05-25

#C'est quoi cette API ?

L'API YAOKA Partners est un service web qui s'occupe de toute la facturation française pour vos clients OFs : générer les factures, les rendre conformes à la loi 2026, les pousser dans Pennylane, suivre les paiements.

Votre code Qualibox envoie des messages chez nous (ex: "crée une facture pour le client X, montant 1200 €"), et on s'occupe du reste. C'est une API REST classique — pas de truc exotique.

Métaphore

Pensez à Stripe pour les paiements. Vous n'avez pas implémenté le protocole carte bancaire — vous appelez l'API Stripe. YAOKA, c'est pareil mais pour la facturation française conforme.

#Prérequis avant de commencer

Avant de coder, assurez-vous d'avoir :

  • Une master API key YAOKA — on vous la donne après le call de démo. Ressemble à sk_qbx_test_xxxxxxxxxxxx. La test dedans = mode sandbox (gratuit, sans effets réels).
  • Un client HTTP — n'importe lequel : fetch en JS, requests en Python, le client HTTP de Go, ou juste cURL pour tester.
  • Capacité à recevoir des webhooks — une URL HTTPS publique côté Qualibox qui peut recevoir nos messages POST. Pour tester en local : ngrok ou cloudflare tunnel.
  • Une base de données chez vous pour stocker, pour chaque OF que vous provisionnez : son orgId YAOKA + son apiKey per-org.

#Vocabulaire

Tous les termes qu'on utilise dans la doc, définis.

TermeDéfinition
EndpointUne URL précise dans notre API. Ex: POST /api/v1/orgs crée un OF, GET /api/v1/orgs/{id} récupère son status.
Master API keyVotre clé Qualibox principale (sk_qbx_*). Permet de créer des OFs. Ne donne PAS accès aux données métier d'un OF spécifique.
Per-org API keyUne clé par OF (sk_org_*), retournée à la création d'un OF. Sert à émettre ses factures, devis, etc. Scopée — ne peut accéder qu'à CET OF.
Idempotency-KeyUn identifiant unique que vous envoyez en header sur un POST. Si vous le rejouez (ex: timeout réseau, vous re-essayez), on ne fait pas l'action 2 fois. Sécurité essentielle pour éviter les doubles factures.
WebhookMessage HTTP qu'on push à une URL chez vous quand un événement important survient (ex: facture payée). Vous configurez l'URL une fois dans votre dashboard admin.
HMAC SHA-256Signature cryptographique des webhooks. À chaque message, on calcule une signature avec un secret partagé. Vous la vérifiez pour être sûr que ça vient de nous.
SandboxMode test. Mêmes URLs, mêmes endpoints, mais aucun effet réel (pas d'envoi Pennylane, pas de facturation chez nous). Activé par le préfixe de la master key (sk_qbx_test_*).
Org (Organization)Dans notre vocabulaire, un OF = une org. orgId = identifiant unique YAOKA de cet OF.

#Quickstart en 10 minutes

Du zéro à une vraie facture émise. Copiez-collez, ça marche.

Étape 0 — Stocker votre master key

Mettez votre clé dans une variable d'environnement. JAMAIS dans le code en clair, JAMAIS dans le browser.

bash

# Dans votre fichier .env du backend Qualibox
YAOKA_MASTER_KEY=sk_qbx_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Étape 1 — Provisionner un OF

Premier appel. On crée l'OF dans notre DB et un compte Pennylane à son nom (en mode test, le compte Pennylane est mocké).

cURL

curl -X POST https://app.yaoka.fr/api/v1/orgs \
  -H "Authorization: Bearer $YAOKA_MASTER_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "name": "FormaPro Excellence",
    "siret": "12345678901234",
    "email": "contact@formapro.fr",
    "firstName": "Marie",
    "lastName": "Dupont",
    "qualiboxRefId": "qbx-of-4567"
  }'

Ce que vous recevez :

JSON

{
  "orgId": "org_abc123",
  "apiKey": "sk_org_test_xyz789",
  "slug": "formapro-excellence",
  "isSandbox": true,
  "pennylane": {
    "companyId": 5184271,
    "simulated": true
  }
}

Important

L'apiKey ne sera plus jamais affichée. Stockez-la dans votre DB Qualibox tout de suite, indexée par qualiboxRefId. Sans elle, vous ne pourrez plus rien faire pour cet OF (ni émettre des factures, ni rien).

Étape 2 — Créer un client

Avec la per-org API key, créez un client (le client de l'OF — c'est lui qui va recevoir la facture).

cURL

curl -X POST https://app.yaoka.fr/api/v1/orgs/org_abc123/customers \
  -H "Authorization: Bearer sk_org_test_xyz789" \
  -H "Content-Type: application/json" \
  -d '{
    "externalRef": "qbx-cust-9912",
    "type": "company",
    "name": "ACME Industries",
    "siret": "98765432109876",
    "emails": ["compta@acme.fr"]
  }'

Retour : { "customerId": "cust_qrs", "created": true }

Étape 3 — Émettre la facture (avec PDF Factur-X)

Le moment magique. On crée la facture, on génère le PDF Factur-X conforme, on push dans Pennylane. Le finalize: true dit "c'est pas un brouillon, attribue un numéro légal et fais tout le tralala".

cURL

curl -X POST https://app.yaoka.fr/api/v1/orgs/org_abc123/invoices \
  -H "Authorization: Bearer sk_org_test_xyz789" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "customerId": "cust_qrs",
    "lines": [{
      "label": "Formation Excel niveau 2 — 14h",
      "quantity": 1,
      "unitPrice": 1200,
      "vatRate": 0
    }],
    "dueInDays": 30,
    "finalize": true
  }'

Retour :

JSON

{
  "invoiceId": "inv_klm",
  "number": "FAC-2026-0042",
  "status": "finalized",
  "totalTtc": 1200,
  "pdfUrl": "https://utfs.io/f/xxxxx.pdf",
  "pennylane": {
    "synced": true,
    "pennylaneImportId": "..."
  }
}

Le pdfUrl pointe vers le PDF Factur-X — c'est ce que vous attachez/linkez dans l'email que Qualibox envoie au client OF.

Bravo

Vous venez d'émettre votre première facture YAOKA en sandbox. En mode live (clé sk_qbx_live_*), la même séquence fonctionne identiquement mais avec des vrais effets : vrai compte Pennylane, vrais documents légaux.

#Authentification

Comment prouver à YAOKA que c'est bien vous qui appelez.

Header HTTP

Toutes les requêtes utilisent un header standard Authorization: Bearer <votre-clé>. Comme partout en REST.

HTTP

Authorization: Bearer sk_qbx_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
Idempotency-Key: <uuid>   # recommandé sur tous les POST

Deux types de clés

Selon l'endpoint, vous utilisez l'une ou l'autre :

  • Master key (sk_qbx_*) — UNE seule pour tout Qualibox. Permet de créer des OFs. Ne donne PAS accès aux opérations métier d'un OF. À stocker en env var côté backend.
  • Per-org key (sk_org_*) — UNE par OF, retournée à la création de l'OF. Permet TOUT pour CET OF (clients, devis, factures…). À stocker dans votre DB Qualibox, indexée par votre référence interne.

Sécurité

Les clés sont stockées chez nous sous forme de hash SHA-256 uniquement. Si vous perdez une clé, on ne peut PAS vous la redonner — il faudra la régénérer (l'ancienne devient invalide instantanément).

#Sandbox vs Live

Le mode de votre master key détermine si c'est pour de vrai ou pour tester.

AspectSandbox (sk_qbx_test_*)Live (sk_qbx_live_*)
URLapp.yaoka.fr/api/v1app.yaoka.fr/api/v1(même)
Org créée en DB ?Oui (taggée sandbox)Oui
Compte Pennylane réel ?Non (mocké)Oui (Reseller API)
Email Pennylane PA envoyé ?NonOui (à l'OF)
PDF Factur-X généré ?Oui (réel)Oui (réel)
Push Pennylane réel ?Non (mocké)Oui
Facturé chez YAOKA ?Non (gratuit)Oui (20€/OF/mois)
Visibilité admin ?Badge SANDBOX jauneStandard

Recommandation

Faites toute votre intégration en sandbox. Quand tout marche, on vous donne une live key et vous basculez juste la valeur de la variable d'environnement. Aucun changement de code.

#Idempotency (anti-double-facture)

Comment éviter qu'un retry réseau crée 2 factures.

Imaginez : vous envoyez POST /invoices, votre dev timeout au bout de 30s sans réponse, votre code retry. Sans précaution, on crée 2 factures.

La solution : envoyez un header Idempotency-Key: <uuid-unique>. Si on voit le même key dans les 24h, on ne fait pas l'action 2 fois — on renvoie la réponse du 1er appel.

bash

# 1er appel
curl -X POST .../invoices \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -d '...'
# → { "invoiceId": "inv_aaa", "number": "FAC-2026-0042" }

# 2e appel (même Idempotency-Key, même body)
curl -X POST .../invoices \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -d '...'
# → { "invoiceId": "inv_aaa", "number": "FAC-2026-0042" } (idem)

Bonne pratique

Générez un UUID v4 dans votre code (lib standard de JS / Python / Go) AVANT le 1er appel. Stockez-le. Si vous retry, réutilisez le MÊME UUID.

#Format des erreurs

Toutes les erreurs ont le même format JSON.

JSON

{
  "error": {
    "code": "validation_error",
    "message": "Le champ 'siret' doit contenir 14 chiffres",
    "field": "siret"
  }
}

Le champ code est stable — vous pouvez switcher dessus dans votre code pour réagir différemment selon le type d'erreur. Le message est lisible humain et peut changer entre versions.

HTTPCodeQuand
400validation_errorBody JSON invalide (champ manquant, mauvais type, etc.)
401auth_requiredHeader Authorization manquant
403auth_invalidClé API révoquée ou invalide
403auth_wrong_scopeVous utilisez une master key sur un endpoint per-org (ou inverse)
404not_foundL'OF/client/facture n'existe pas
409conflictIdempotency-Key réutilisé avec un body différent
422business_rule_violationEx: facture sans ligne, transition de status invalide
429rate_limitedTrop de requêtes (voir headers X-RateLimit-*)
500internal_errorBug chez nous, on est notifiés en temps réel
503pennylane_unreachablePennylane down, retry après 30s

#Rate limits

Combien d'appels par minute on accepte.

  • Master key : 100 requêtes / minute
  • Per-org key : 60 requêtes / minute
  • Burst : 20 req/s pendant 5s autorisé (token bucket)

Chaque réponse contient les headers X-RateLimit-Remaining et X-RateLimit-Reset. En cas de dépassement, vous recevez un 429 — backoff exponentiel recommandé.

#Provisioning — créer un OF

Master key requise.

POST/api/v1/orgs

Crée un OF dans YAOKA. En mode live, crée aussi le compte Pennylane via notre Reseller API. Retourne une per-org API key (montrée 1 fois).

ChampTypeDescription
name*stringRaison sociale de l'OF
siretoptionnelstring(14)SIRET sans espaces
email*stringEmail de contact principal de l'OF
firstName*stringPrénom du contact (requis par Pennylane)
lastName*stringNom du contact
phoneoptionnelstringTéléphone format +33...
ibanoptionnelstringIBAN affiché sur les factures
logoUrloptionnelurlURL HTTPS du logo de l'OF (PDF Factur-X)
addressoptionnelobject{ street, city, zip, country }
qualiboxRefIdoptionnelstringVotre référence interne (pour mapper avec votre DB)

Response

JSON

{
  "orgId": "org_abc123",
  "apiKey": "sk_org_test_xyz789",
  "slug": "formapro-excellence",
  "createdAt": "2026-05-23T14:30:00Z",
  "externalRefId": "qbx-of-4567",
  "isSandbox": true,
  "mode": "test",
  "pennylane": {
    "companyId": 5184271,
    "simulated": true,
    "error": null
  }
}
GET/api/v1/orgs/{orgId}

Status d'un OF (provisioning, Pennylane, stats 30j). Accepte master key OU per-org key.

OAuth Pennylane (post-provisioning)

Pour que YAOKA puisse pousser les factures dans le Pennylane de l'OF, l'OF doit autoriser l'accès via OAuth. Lancé depuis Qualibox. En sandbox, le flow est mocké (call /complete avec n'importe quel code suffit).

POST/api/v1/orgs/{orgId}/pennylane/oauth/start

Body : { "redirectUri": "https://qualibox.fr/yaoka-callback" }. Renvoie { authUrl, state, sandbox }. Qualibox redirige l'OF vers authUrl.

POST/api/v1/orgs/{orgId}/pennylane/oauth/complete

Après que Pennylane ait redirigé l'OF avec un ?code=&state=, Qualibox POST le code ici. Body : { "code": "...", "state": "..." }. Réponse : { connected: true, pennylaneCompanyId }.

#Clients (de l'OF)

Per-org key requise. Les clients sont les entreprises/personnes que l'OF facture.

MethodPathDescription
POST/orgs/:id/customersCréer/upsert un client (idempotent sur externalRef)
GET/orgs/:id/customersLister avec ?search=&type=company|individual
GET/orgs/:id/customers/:idRécupérer un client
PATCH/orgs/:id/customers/:idModifier
DELETE/orgs/:id/customers/:idSupprimer (refusé s'il a des factures)

Exemple POST

JSON

{
  "externalRef": "qbx-cust-9912",
  "type": "company",
  "name": "ACME Industries",
  "siret": "98765432109876",
  "emails": ["compta@acme.fr"],
  "phone": "+33102030405",
  "address": {
    "street": "1 rue Test",
    "city": "Paris",
    "postalCode": "75001",
    "country": "FR"
  }
}

#Devis

Per-org key requise. Numérotation DEV-YYYY-NNNN automatique.

MethodPathDescription
POST/orgs/:id/quotesCréer un devis (draft)
GET/orgs/:id/quotesLister avec filtres ?status=&customerId=
GET/orgs/:id/quotes/:idRécupérer un devis
PATCH/orgs/:id/quotes/:idModifier (lignes, statut, etc.). Refusé si déjà invoiced.
DELETE/orgs/:id/quotes/:idSupprimer (draft seulement)
POST/orgs/:id/quotes/:id/convertConvertir en facture (full, deposit ou balance)

Exemple POST

JSON

{
  "customerId": "cust_qrs",
  "lines": [
    {
      "label": "Formation Excel niveau 2 — 14h",
      "quantity": 1,
      "unitPrice": 1200,
      "vatRate": 0
    }
  ],
  "validUntilDays": 30,
  "paymentConditions": "30 jours fin de mois"
}

Le status passe par : draft → finalized → sent → accepted → invoiced (ou declined / expired). Utilisez PATCH pour faire évoluer le status.

#Factures clients

Per-org key requise. Numérotation FAC-YYYY-NNNN continue (jamais réinitialisée — art. 242 nonies A CGI).

MethodPathDescription
POST/orgs/:id/invoicesCréer (draft) ou finalize:true pour générer PDF + push Pennylane immédiatement
GET/orgs/:id/invoicesLister avec ?status=&customerId=&fromDate=&toDate=
GET/orgs/:id/invoices/:idRécupérer une facture
PATCH/orgs/:id/invoices/:idStatus transitions (finalize, mark-paid, cancel) + éditions limitées
DELETE/orgs/:id/invoices/:idSupprimer (draft seulement — pour les autres, utiliser PATCH status=cancelled ou créer un avoir)
POST/orgs/:id/invoices/:id/credit-noteÉmettre un avoir (full ou partiel via amountTtc). L'original reste immuable.

Créer + finaliser en 1 call

JSON

{
  "customerId": "cust_qrs",
  "lines": [
    {
      "label": "Formation Excel niveau 2 — 14h",
      "quantity": 1,
      "unitPrice": 1200,
      "vatRate": 0
    }
  ],
  "dueInDays": 30,
  "paymentMethods": ["transfer"],
  "qualiopiRefId": "qbx-session-9988",
  "finalize": true
}

Marquer payée

JSON

PATCH /orgs/{id}/invoices/{invoiceId}
{
  "status": "paid",
  "paidAt": "2026-06-15T10:30:00Z",
  "paymentMethods": ["transfer"]
}

Marquer une facture payée déclenche le webhook invoice.paid chez vous.

Immutabilité

Une fois finalized, une facture ne peut PLUS être modifiée (lignes, montants). C'est la loi française. Pour corriger une erreur, créez un avoir (credit note) ou annulez (status: cancelled) puis réémettez.

#Factures fournisseurs

Per-org key requise. Upload de PDFs émis par les fournisseurs de l'OF.

MethodPathDescription
POST/orgs/:id/supplier-invoicesUploader (fileUrl ou fileBase64)
GET/orgs/:id/supplier-invoicesLister
GET/orgs/:id/supplier-invoices/:idRécupérer
PATCH/orgs/:id/supplier-invoices/:idModifier (status, payment, etc.)
DELETE/orgs/:id/supplier-invoices/:idSupprimer (draft seulement)

Exemple POST

JSON

{
  "supplierName": "Imprimerie Martin",
  "supplierSiren": "111222333",
  "invoiceNumber": "F-2026-118",
  "amountHt": 350,
  "amountTax": 70,
  "amountTtc": 420,
  "date": "2026-05-18T00:00:00Z",
  "dueDate": "2026-06-18T00:00:00Z",
  "fileUrl": "https://your-storage.com/invoice.pdf",
  "fileName": "facture-imprimerie-2026-05.pdf",
  "createdByUserId": "qbx-user-123"
}

#Notes de frais

Per-org key requise. Notes de frais des employés de l'OF.

MethodPathDescription
POST/orgs/:id/expense-reportsCréer (status=submitted)
GET/orgs/:id/expense-reportsLister avec ?status=&submittedById=
GET/orgs/:id/expense-reports/:idRécupérer
PATCH/orgs/:id/expense-reports/:idStatus workflow (submitted → approved → sent_to_pennylane)
DELETE/orgs/:id/expense-reports/:idSupprimer (sauf sent_to_pennylane)

Exemple POST

JSON

{
  "submittedById": "qbx-user-456",
  "title": "Déplacement client ACME — mai 2026",
  "amountHt": 23.75,
  "amountTax": 4.75,
  "amount": 28.50,
  "date": "2026-05-18T00:00:00Z",
  "category": "repas",
  "fileUrl": "https://your-storage.com/recu.jpg",
  "fileName": "recu-restaurant.jpg"
}

#C'est quoi un webhook ?

Un webhook est un message qu'on vous envoie (vs. l'API où c'est VOUS qui nous appelez). Quand un événement se passe chez nous (ex: facture payée), on POST un message JSON sur une URL HTTPS que vous nous donnez.

Cela évite que vous ayez à poller ("dis YAOKA, est-ce que la facture est payée ?" toutes les 5 min) — on vous prévient instantanément.

Métaphore

Vous donnez votre numéro de téléphone (URL webhook). Quand quelqu'un essaye de vous joindre (événement), on appelle directement votre numéro. Pas besoin d'appeler vous-même toutes les minutes pour demander.

#Configurer les webhooks chez Qualibox

Étape 1 — Créer un endpoint chez vous

Côté Qualibox, créez une route HTTPS publique qui accepte des POST JSON. Exemple en Next.js :

TypeScript

// app/api/yaoka/webhooks/route.ts (côté Qualibox)
export async function POST(req: Request) {
  const rawBody = await req.text();
  const signature = req.headers.get("x-yaoka-signature");

  // 1. Vérifier la signature (voir section suivante)
  if (!verifyYaokaSignature(rawBody, signature, process.env.YAOKA_WEBHOOK_SECRET)) {
    return new Response("Invalid signature", { status: 401 });
  }

  // 2. Parser
  const event = JSON.parse(rawBody);

  // 3. Réagir selon le type
  switch (event.event) {
    case "invoice.paid":
      await markInvoicePaidInQualibox(event.data.invoiceId);
      break;
    case "org.provisioned":
      await markOFProvisioned(event.data.orgId);
      break;
    // ...
  }

  // 4. Répondre 200 RAPIDEMENT (sinon YAOKA va retry)
  return new Response("OK", { status: 200 });
}

Étape 2 — Nous donner l'URL

Dans votre dashboard admin YAOKA (ou par email pendant la phase sandbox), donnez-nous :

  • L'URL : ex https://api.qualibox.fr/yaoka/webhooks
  • Un secret partagé (qu'on génère ensemble) — sert à signer chaque message

#Vérifier la signature HMAC

Critique pour la sécurité — sans ça, n'importe qui peut vous envoyer de faux messages.

À chaque webhook, on inclut un header X-Yaoka-Signature: t=<timestamp>,v1=<hmac_hex>. Le HMAC est calculé sur `${timestamp}.${rawBody}` avec votre secret partagé.

Vérification en Node.js

TypeScript

import { createHmac, timingSafeEqual } from "node:crypto";

function verifyYaokaSignature(
  rawBody: string,
  header: string | null,
  secret: string
): boolean {
  if (!header) return false;

  const [tsPart, sigPart] = header.split(",");
  const ts = tsPart.split("=")[1];
  const sig = sigPart.split("=")[1];

  // 1. Recalculer la signature attendue
  const signedPayload = `${ts}.${rawBody}`;
  const expected = createHmac("sha256", secret)
    .update(signedPayload)
    .digest("hex");

  // 2. Comparer en timing-safe (évite les attaques par mesure de temps)
  return timingSafeEqual(
    Buffer.from(sig),
    Buffer.from(expected)
  );
}

Important

Utilisez rawBody (le string brut), pas JSON.stringify(parsedBody) qui pourrait re-sérialiser différemment (ordre des clés, espaces) et casser la signature.

#Liste des événements

15 types d'événements envoyés en webhook.

Format du payload

HTTP

POST https://api.qualibox.fr/yaoka/webhooks
Content-Type: application/json
X-Yaoka-Signature: t=1748901234,v1=<hmac_hex>
X-Yaoka-Event-Id: evt_abc123

{
  "event": "invoice.paid",
  "eventId": "evt_abc123",
  "timestamp": 1748901234,
  "data": {
    "orgId": "org_abc",
    "invoiceId": "inv_klm",
    "invoiceNumber": "FAC-2026-0042",
    "totalTtc": 1200,
    "paidAt": "2026-05-23T15:30:00Z"
  }
}

Tous les événements

EventDescription
org.provisionedUn OF a été provisionné (org créée + Pennylane company)
pennylane.pa_designatedL'OF a cliqué sur l'email Pennylane et désigné Pennylane comme PA
pennylane.connectedOAuth Pennylane terminé — YAOKA peut push/lire
pennylane.sync_failedPush Pennylane échoué (retry recommandé après OAuth ou support)
quote.acceptedDevis marqué accepté
quote.convertedDevis converti en facture (totale ou acompte)
invoice.createdFacture créée (en draft)
invoice.finalizedFacture finalisée (PDF Factur-X généré + push Pennylane)
invoice.paidFacture marquée payée
invoice.overdueFacture en retard de paiement (J+1 après dueDate)
supplier_invoice.approvedFacture fournisseur approuvée par l'OF
supplier_invoice.paidFacture fournisseur payée
expense_report.submittedNote de frais soumise par un employé
expense_report.approvedNote de frais approuvée
expense_report.reimbursedNote de frais remboursée + push Pennylane

Idempotency côté Qualibox

Stockez les eventId reçus pendant 7 jours et ignorez les doublons. En cas de timeout réseau, on peut renvoyer le même event — votre code doit être idempotent.

Retry policy

Si vous renvoyez 5xx, on retry avec backoff exponentiel (60s, 5 min, 30 min, 2h, 6h, 12h) — 6 tentatives sur 24h max. Si 4xx, on abandonne (votre code a rejeté). Répondez 200 rapidement (avant 10s) pour acquittér.

#Serveur MCP — bonus pour vos devs IA

Si vos devs utilisent Cursor, Claude Code, Windsurf ou tout agent IA, on a un package npm qui transforme notre API en tools utilisables directement par l'IA.

MCP = Model Context Protocol. Un standard ouvert (créé par Anthropic) qui permet à n'importe quel agent IA d'utiliser des outils externes. Notre serveur MCP expose 9 tools (créer un OF, émettre une facture, marquer payée, etc.).

Installation

bash

# Pas d'installation locale — on lance directement via npx
npx -y @yaoka/partners-mcp

# Ou via pnpm dlx, bunx, peu importe

Configuration Claude Desktop

Édite ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) ou %APPDATA%\Claude\claude_desktop_config.json (Windows) :

JSON

{
  "mcpServers": {
    "yaoka": {
      "command": "npx",
      "args": ["-y", "@yaoka/partners-mcp"],
      "env": {
        "YAOKA_API_KEY": "sk_qbx_test_xxxxxxxxxxxx"
      }
    }
  }
}

Configuration Cursor

Édite ~/.cursor/mcp.json (ou .cursor/mcp.json à la racine de ton projet pour un scope projet) :

JSON

{
  "mcpServers": {
    "yaoka": {
      "command": "npx",
      "args": ["-y", "@yaoka/partners-mcp"],
      "env": {
        "YAOKA_API_KEY": "sk_qbx_test_xxxxxxxxxxxx"
      }
    }
  }
}

Configuration Windsurf / Cline / autres

La même config JSON fonctionne partout — c'est le standard MCP. Pour Windsurf : ~/.codeium/windsurf/mcp_config.json. Pour Cline (VS Code) : dans les settings de l'extension.

Redémarrez le client après config. Vous verrez les 9 tools YAOKA dans la liste disponible. Demandez par exemple : "Crée un OF FormaPro avec SIRET 12345678901234, email contact@formapro.fr" — l'IA appellera yaoka_provision_org toute seule.

Les 9 tools exposés

ToolDescriptionClé
yaoka_provision_orgCrée un OF + sa company Pennylane en un appel. Master key requise.Master
yaoka_get_org_statusRécupère provisioning, OAuth Pennylane, stats 30j d'un OF.Master / Org
yaoka_create_quoteCrée un devis (DEV-YYYY-NNNN), PDF + JSON structuré.Master / Org
yaoka_create_invoiceÉmet une facture Factur-X v1.0.6 conforme. PDF/A-3 + XML CII, push Pennylane auto.Master / Org
yaoka_mark_invoice_paidMarque payée avec date, montant, mode, référence. Déclenche le webhook invoice.paid.Master / Org
yaoka_list_invoicesListe les factures d'un OF avec filtres (status, période).Master / Org
yaoka_upload_supplier_invoiceUploade une facture fournisseur (PDF base64 ou URL). Lecture automatique optionnelle pour extraire les données.Master / Org
yaoka_create_expense_reportCrée l'entête d'une note de frais (user + période). Lignes ajoutées ensuite.Master / Org
yaoka_add_expense_itemAjoute une ligne de dépense (repas, essence, hôtel, péage…). Barème km pour fuel.Master / Org

Astuce

Les noms sont préfixés yaoka_ pour éviter les conflits si vous chargez plusieurs serveurs MCP en parallèle. Toutes les descriptions sont en français et incluent des hints sur quand les utiliser — l'IA les choisit beaucoup mieux comme ça.

Usage depuis votre backend Next.js

TypeScript

import { experimental_createMCPClient } from "ai";
import { Experimental_StdioMCPTransport } from "ai/mcp-stdio";

const yaoka = await experimental_createMCPClient({
  transport: new Experimental_StdioMCPTransport({
    command: "npx",
    args: ["-y", "@yaoka/partners-mcp"],
    env: { YAOKA_API_KEY: process.env.YAOKA_API_KEY! },
  }),
});

const tools = await yaoka.tools();
// Passer à n'importe quel LLM (Claude, Perplexity, GPT, Gemini)

Forker / patcher le serveur en local

Si vous voulez ajouter des tools custom, modifier le comportement, ou juste comprendre comment ça marche, le code source est ouvert sur npm et facile à lancer en local.

bash

# 1. Clone le package depuis npm
npm pack @yaoka/partners-mcp
tar -xzf yaoka-partners-mcp-*.tgz && cd package

# 2. Installer les deps
pnpm install   # ou npm install

# 3. Build TypeScript
pnpm build     # → dist/

# 4. Lancer en mode dev (rebuild auto)
pnpm dev

# 5. Tester avec MCP Inspector (UI graphique pour debug)
npx @modelcontextprotocol/inspector node ./dist/index.js

MCP Inspector lance une UI sur http://localhost:5173 où vous voyez les tools, lancez des appels, et inspectez les requêtes/réponses. Pratique pour debug avant de wire dans Claude Desktop ou Cursor.

Pour pointer votre fork local sur la prod YAOKA : YAOKA_API_KEY=sk_qbx_test_… dans .env.local. Pour pointer sur votre dev local YAOKA (port 3000) : YAOKA_BASE_URL=http://localhost:3000.

Pourquoi c'est bien

Vos devs peuvent prototyper / debug / scripter avec l'IA sans écrire un seul fetch. Et en prod, vos agents IA Qualibox peuvent utiliser YAOKA naturellement (ex: chatbot OF qui peut créer des factures à la demande).

#Erreurs courantes et solutions

401 auth_required

Cause : Header Authorization manquant ou mal formé

Fix : Vérifiez que vous envoyez `Authorization: Bearer sk_xxx_xxx_xxx`

403 auth_invalid

Cause : Clé révoquée, expirée, ou n'existe pas

Fix : Régénérez la clé depuis votre dashboard admin. Vérifiez que vous utilisez bien la live key en prod et test en sandbox.

403 auth_wrong_scope

Cause : Vous utilisez une master key sur un endpoint qui demande une per-org key (ou inverse)

Fix : Master key = /orgs (provisioning). Per-org key = /orgs/{id}/* (opérations métier).

422 business_rule_violation 'invoice without lines'

Cause : Vous avez envoyé un POST /invoices avec lines:[]

Fix : Une facture doit avoir au moins 1 ligne

422 business_rule_violation 'Invalid status transition'

Cause : Vous essayez de passer un statut illégal (ex: paid → draft)

Fix : Voir le workflow des status dans la section Invoices

404 not_found 'Customer'

Cause : Vous référencez un customerId qui n'existe pas dans CET OF

Fix : Vérifiez l'orgId et le customerId. Multi-tenant strict : un client d'un OF n'est pas visible par un autre OF.

409 conflict 'Idempotency-Key already used'

Cause : Vous avez réutilisé un UUID avec un body différent

Fix : Soit utilisez un UUID frais, soit envoyez exactement le même body que la 1ère fois

Webhook ne reçoit rien

Cause : URL non configurée, ou réponse non-200 → on abandonne après 6 retries

Fix : Vérifiez l'URL dans le dashboard admin. Vérifiez que vous répondez 200 sous 10s. Logs détaillés dans /admin/partners/{id}.

Webhook signature ne matche pas

Cause : Vous calculez le HMAC sur JSON.stringify(parsed) au lieu du rawBody

Fix : Stockez le rawBody (req.text() avant tout parsing), calculez le HMAC dessus

#FAQ

Je peux tester en local sur localhost ?

Oui — pointez la BASE URL sur localhost:3000/api/v1 si vous testez contre une instance YAOKA locale. En production, c'est app.yaoka.fr/api/v1. Pour les webhooks en local, utilisez ngrok ou cloudflare tunnel.

Combien de temps prend l'intégration ?

Un dev moyen, 1-2 semaines. 1ère semaine : auth + provisioning + customers + invoices basiques. 2ème : webhooks + edge cases + tests d'intégration.

Vous avez une lib TypeScript / SDK ?

Pas encore — l'API est volontairement simple, fetch suffit. Si vous voulez les types Zod, importez les schemas via npm @yaoka/partners-types (à venir).

Comment je teste les webhooks sans déployer ?

Installez ngrok (https://ngrok.com), lancez `ngrok http 3000`, donnez-nous l'URL https publique générée. On y push les webhooks comme en prod.

Mon OF a déjà un compte Pennylane. Qu'est-ce qui se passe ?

À la création, on vérifie d'abord par SIRET si une company Pennylane existe déjà. Si oui, on link. Sinon, on crée. Donc safe.

Le push Pennylane est-il garanti ?

Best-effort en V1. Si Pennylane est down ou si l'OF n'a pas encore fait OAuth, on stocke pennylane.synced: false et on log. Vous pouvez retry plus tard. Webhook pennylane.sync_failed vous prévient si problème.

Comment je supprime un OF de test ?

Sandbox = auto-purgée tous les 30 jours. Pour suppression manuelle, contact@yaoka.fr — on a un endpoint admin pour ça (pas exposé en API publique).

Versioning : comment je sais qu'il y a une nouvelle version ?

On annonce 60 jours à l'avance par email + sur cette page (changelog). /v1/ ne reçoit JAMAIS de breaking change — toute évolution incompatible passe par /v2/, et /v1/ reste maintenu 12 mois après v2 stable.

Support — qui je contacte ?

contact@yaoka.fr en V1. Slack partagé Qualibox ↔ YAOKA à partir du go-live. Status page : status.yaoka.fr (à venir).

Une question pas couverte ?

Envoyez-la à contact@yaoka.fr — on l'ajoute à cette FAQ si elle est utile à d'autres.

© YAOKA — Tous droits réservés
ProgrammeQualiboxDéveloppeurs