Pour les développeurs
L’API Zentra.
Reliez votre site, votre boutique ou vos outils à Zentra. Lisez vos données, déposez des brouillons et soyez prévenu à chaque facture payée.
- Lecture en JSON
- Clients, articles, devis, commandes, factures et paiements, par une API REST simple.
- Rien sans votre accord
- Ce que vos outils envoient arrive en brouillon. Vous le validez dans l’app.
- Inclus
- Dans Solo, Équipe et Pro, sans supplément. 600 appels par heure et par clé.
Avant de commencer
- Dans l’app Zentra, ouvrez Paramètres › API, boutiques et courrier. Ou passez par votre compte en ligne.
- Créez une clé et donnez-lui un nom, par exemple « site internet ». Choisissez « Lecture », ou « Lecture et brouillons » si votre outil doit aussi déposer des demandes.
- Copiez la clé tout de suite : elle commence par zk_ et ne sera plus affichée. Gardez-la comme un mot de passe, côté serveur seulement.
- Ouvrez Zentra sur un poste de l’entreprise. L’app publie alors les données que l’API peut lire, puis les tient à jour pendant qu’elle est ouverte.
Seuls le titulaire et les administrateurs de l’entreprise créent et révoquent les clés. Une clé révoquée cesse de fonctionner immédiatement.
Vos données restent dans l’app : le serveur ne garde que la dernière version publiée de ces six types d’enregistrements, pour les entreprises qui ont créé une clé ou un webhook. Le champ publishedAt de chaque réponse dit quand l’app les a publiés.
Authentification
Chaque appel envoie la clé dans l’en-tête Authorization. Toutes les adresses commencent par :
https://zentraapp.ch/api/v1Exemplecurl https://zentraapp.ch/api/v1/invoices?status=issued&limit=20 \
-H "Authorization: Bearer zk_votre_cle"Les réponses sont en JSON, en UTF-8, et ne sont jamais mises en cache. N’appelez pas l’API depuis un navigateur : la clé serait visible.
Ressources
Six ressources en lecture seule. Chacune se lit en liste, ou un enregistrement à la fois par son identifiant.
/customers- Clients
id, number, name, company, contactPerson, email, phone, addressLine1, addressLine2, postalCode, city, country, archived /articles- Articles et prestations du catalogue
id, kind, sku, name, description, unit, unitPriceCents, vatBp, trackStock, stockQuantityMilli, archived /quotes- Devis
id, number, customerId, title, status, currency, issueDate, validUntil, subtotalCents, discountCents, vatCents, totalCents, lines /orders- Commandes clients
id, number, customerId, title, status, currency, orderDate, externalReference, totalCents, lines /invoices- Factures
id, number, customerId, title, status, type, currency, issueDate, dueDate, subtotalCents, discountCents, vatCents, totalCents, paidCents, openCents, lines /payments- Paiements reçus
id, invoiceId, date, amountCents, currency, method, reference
Les devis, commandes et factures contiennent leurs lignes, avec position, articleId, description, quantityMilli, unit, unitPriceCents, discountBp, vatBp et totalCents. Chaque enregistrement porte aussi updatedAt et changedAt.
Listes et pagination
Les listes sont triées par date de changement. Pour tout reprendre sans rien manquer, suivez nextCursor tant que hasMore vaut true.
limit- Nombre de résultats par page, de 1 à 100. 50 par défaut.
cursor- La valeur nextCursor de la page précédente.
updated_since- Seulement ce qui a changé depuis cette date (ISO 8601 ou horodatage Unix).
status- Filtre sur le statut, par exemple issued, paid ou draft.
customer_id- Seulement les documents d’un client.
include_deleted- true pour recevoir aussi les suppressions, sous la forme { id, deleted: true }.
{
"object": "list",
"entity": "invoices",
"data": [
{
"object": "invoice",
"id": "inv_8f2c",
"number": "2026-0142",
"customerId": "cus_51a0",
"status": "issued",
"currency": "CHF",
"issueDate": "2026-10-20",
"dueDate": "2026-11-19",
"totalCents": 129690,
"paidCents": 0,
"openCents": 129690,
"lines": [ … ],
"changedAt": "2026-10-20T09:14:03.000Z"
}
],
"hasMore": true,
"nextCursor": "c1_WzE3OTI…",
"publishedAt": "2026-10-26T07:55:12.000Z"
}Synchroniser les clients// Node 18 ou plus récent : reprendre tout ce qui a changé depuis la dernière fois
const headers = { Authorization: `Bearer ${process.env.ZENTRA_API_KEY}` };
let url = 'https://zentraapp.ch/api/v1/customers?limit=100&include_deleted=true&updated_since=2026-10-01';
while (url) {
const response = await fetch(url, { headers });
if (!response.ok) throw new Error((await response.json()).error.message);
const page = await response.json();
for (const customer of page.data) console.log(customer.id, customer.name ?? 'supprimé');
url = page.nextCursor ? `https://zentraapp.ch/api/v1/customers?limit=100&include_deleted=true&cursor=${page.nextCursor}` : null;
}Montants et quantités
Tous les chiffres sont des entiers calculés par l’app. Le serveur ne recalcule rien.
…Cents- Montants en centimes : 129690 = CHF 1’296.90.
…Milli- Quantités en millièmes : 1000 = une unité.
vatBp, discountBp- Taux en points de base : 100 = 1 %. Le taux normal de TVA s’écrit 810.
currency- CHF ou EUR.
openCents- Solde encore dû sur une facture.
Déposer un brouillon
Un formulaire de votre site peut créer une demande de devis, une commande ou un nouveau client. Le brouillon arrive dans l’app, à valider ou à refuser. Rien n’est enregistré ni envoyé sans vous.
POST /draftscurl -X POST https://zentraapp.ch/api/v1/drafts \
-H "Authorization: Bearer zk_votre_cle" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: site-demande-20261026-0042" \
-d '{
"kind": "quote",
"customer": { "name": "Marie Rochat", "email": "marie.rochat@example.ch" },
"reference": "Formulaire du site",
"note": "Pose prévue en novembre",
"lines": [
{ "sku": "RAD-600", "description": "Radiateur 600 W", "quantity": "3" },
{ "description": "Pose et mise en service", "quantity": "1.5" }
]
}'Réponse 202{ "object": "draft", "id": "in_3b9e…", "kind": "quote", "state": "pending", "duplicate": false }- Le champ kind vaut customer, quote ou order. Un devis ou une commande compte de 1 à 200 lignes.
- Indiquez un client existant par son id, ou ses coordonnées : name, company, email, phone, addressLine1, addressLine2, postalCode, city, country.
- Chaque ligne a une quantité, et un sku, un articleId ou une description. Les prix éventuels ne sont que des indications : l’app applique votre catalogue et calcule les totaux.
- Avec l’en-tête Idempotency-Key, un même envoi répété ne crée qu’un seul brouillon. La réponse indique alors duplicate: true.
- Pour suivre un brouillon, lisez /drafts/ suivi de son id : l’état vaut pending, imported ou rejected, avec la référence du document créé.
Webhooks
Zentra prévient votre adresse https à chaque événement choisi. Ajoutez le webhook au même endroit que les clés. Son secret de signature commence par whsec_ et n’est montré qu’une fois.
customer.created- Un client est créé.
quote.created- Un devis est émis.
quote.accepted- Un devis est accepté.
order.created- Une commande est confirmée.
invoice.created- Une facture est émise.
invoice.paid- Une facture est entièrement payée.
payment.created- Un paiement est enregistré.
draft.imported- Un brouillon déposé par l’API a été validé dans l’app.
draft.rejected- Un brouillon déposé par l’API a été refusé.
POST https://exemple.ch/zentra
Zentra-Event: invoice.paid
Zentra-Event-Id: evt_…
Zentra-Delivery-Id: dlv_…
Zentra-Signature: t=1793091600,v1=5d41c0…
{
"id": "evt_…",
"type": "invoice.paid",
"apiVersion": 1,
"createdAt": "2026-10-26T08:20:00.000Z",
"data": { "object": "invoice", "record": { "id": "inv_8f2c", "status": "paid", … } }
}La signature est un HMAC-SHA256 en hexadécimal, calculé avec votre secret sur l’horodatage t, un point, puis le corps reçu. Refusez un horodatage de plus de cinq minutes.
Vérifier la signature (Node)import { createHmac, timingSafeEqual } from 'node:crypto';
// rawBody : le corps reçu, tel quel, avant tout JSON.parse
export function isFromZentra(rawBody, signatureHeader, secret) {
const parts = Object.fromEntries(signatureHeader.split(',').map((part) => part.split('=')));
const timestamp = Number(parts.t);
if (!Number.isInteger(timestamp) || Math.abs(Date.now() / 1000 - timestamp) > 300) return false;
const expected = createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex');
return parts.v1?.length === expected.length && timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
}Abonnements par l’API
Zapier, Make, n8n ou votre propre outil peuvent créer et retirer leurs webhooks avec la clé d’API. GET /me vérifie la clé et donne le nom de l’entreprise ; GET /webhooks liste les abonnements.
S’abonner et se désabonnerPOST https://zentraapp.ch/api/v1/webhooks
Authorization: Bearer zk_votre_cle
Content-Type: application/json
{ "url": "https://hooks.zapier.com/hooks/catch/…", "events": ["invoice.paid"] }
201 { "object": "webhook", "id": "whe_…", "events": ["invoice.paid"], "secret": "whsec_…" }
DELETE https://zentraapp.ch/api/v1/webhooks/whe_…- Répondez par un code 2xx en moins de dix secondes. Les redirections ne sont pas suivies.
- En cas d’échec, l’envoi est repris jusqu’à huit fois : après 1 minute, 5 minutes, 30 minutes, puis 2, 6, 12 et 24 heures.
- Après vingt échecs de suite, le webhook se met en pause. Le bouton « Reprendre » le relance.
- Un même événement peut arriver deux fois : dédoublonnez avec Zentra-Event-Id.
- Le bouton « Envoyer un test » envoie un événement ping.
Assistants IA (MCP)
Claude, ChatGPT ou tout assistant compatible MCP peut lire vos données Zentra. Il répond à « Qui me doit encore de l’argent ? » à partir des montants calculés par l’app.
Dans Claude, sans clé
- Dans Claude, ouvrez Réglages, puis Connecteurs, et choisissez Ajouter un connecteur personnalisé. Sur une formule Team ou Enterprise, c’est un propriétaire de l’organisation Claude qui l’ajoute.
- Nommez-le Zentra et collez l’adresse du serveur. Laissez vides les réglages avancés.
- Cliquez sur Se connecter. Zentra s’ouvre : connectez-vous, choisissez l’entreprise et autorisez l’accès.
- Ouvrez une fois Zentra sur un poste de l’entreprise. L’app publie alors les données que l’assistant lit.
https://zentraapp.ch/api/mcpSeuls le titulaire et les administrateurs peuvent connecter un assistant, pour une entreprise à la fois. Les assistants connectés sont listés dans votre compte, sous Intégrations : un clic les déconnecte.
Dans Claude Code
Terminalclaude mcp add --transport http zentra https://zentraapp.ch/api/mcpLancez ensuite /mcp dans Claude Code et choisissez Zentra pour vous connecter.
Questions prêtes à l’emploi
Le serveur propose aussi des questions toutes faites. Dans Claude, elles apparaissent dans le menu « + » de la conversation.
qui_me_doit- Qui me doit de l’argent ?
ma_journee- Qu’est-ce qui m’attend aujourd’hui ?
relances_semaine- Préparer mes relances de la semaine
resume_mois- Résumer mon mois
creer_devis- Créer un devis
nouveau_client- Ajouter un client
Avec une clé d’API
Un outil qui ne propose pas la connexion au compte peut utiliser une clé d’API, envoyée dans l’en-tête Authorization.
Configuration{
"mcpServers": {
"zentra": {
"type": "http",
"url": "https://zentraapp.ch/api/mcp",
"headers": { "Authorization": "Bearer zk_votre_cle" }
}
}
}search_customers- Cherche des clients par nom, société ou e-mail.
get_customer- Un client et ses dernières factures.
list_invoices- Factures ouvertes, échues, payées ou en brouillon.
get_invoice- Une facture avec ses lignes et ses paiements.
list_quotes- Devis par statut et par client.
list_orders- Commandes clients.
search_articles- Articles du catalogue, avec prix et stock.
company_summary- Chiffres clés : factures ouvertes et échues, nombre d’enregistrements.
prepare_draft- Dépose à valider un client, un fournisseur, un article, un projet, une tâche de projet, un rendez-vous, un devis, une commande, une facture en brouillon ou une note. S’il manque un détail, répond par les questions à poser. Seulement si les brouillons sont autorisés.
draft_requirements- Les informations obligatoires de chaque brouillon, avec la question à poser.
search_projects- Projets et chantiers, pour y rattacher une tâche, un rendez-vous ou une note.
search_suppliers- Fournisseurs par nom ou contact.
list_autopilot_tasks- Les tâches en attente dans Autopilote, avec les actions que l’assistant peut demander.
act_on_autopilot_task- Confirme une tâche à votre demande : créer une fiche ou un brouillon reçu, ajouter un rendez-vous, reporter, ignorer, marquer une tâche faite. Seulement si vous avez coché « Confirmer des tâches Autopilote » à la connexion.
check_request- Dit si un brouillon ou une action demandée est déjà traité par l’app.
L’assistant ne peut rien envoyer, payer ni supprimer. Un brouillon qu’il prépare attend votre validation dans l’app, sauf si vous lui demandez de le confirmer.
Quand un détail manque, Zentra ne devine pas : un article sans prix, un client qui ressemble à plusieurs fiches ou un projet introuvable reviennent à l’assistant sous forme de questions, qu’il vous pose avant de créer.
Une action confirmée est exécutée par l’app Zentra dès qu’elle est ouverte sur un poste de l’entreprise, puis inscrite dans le journal d’Autopilote. Envoyer un mail ou une relance, enregistrer un paiement, émettre une facture et lancer une poursuite se font toujours dans l’app.
Erreurs et limites
Une erreur renvoie un code HTTP et un objet error avec un code stable et un message en français.
{ "error": { "code": "rate_limited", "message": "Trop d’appels pour cette clé. Réessayez dans une heure." } }- 400
invalid_query, invalid_draft, invalid_body - La demande est mal formée. Le message dit quel champ corriger.
- 401
unauthorized - Clé absente, mal copiée ou révoquée.
- 402
subscription_inactive - L’abonnement de l’entreprise n’est plus actif.
- 403
forbidden - La clé ne peut que lire. Créez une clé « Lecture et brouillons ».
- 404
not_found - Ressource ou identifiant inconnu.
- 405
method_not_allowed - Seul GET est accepté, sauf POST sur drafts.
- 429
rate_limited - Plus de 600 appels dans l’heure pour cette clé. L’en-tête Retry-After donne l’attente en secondes.
- 503
unavailable - Service momentanément indisponible. Réessayez plus tard.
Limites : 600 appels par heure et par clé, 100 résultats par page, 64 Ko par brouillon.
Relier Shopify, WooCommerce ou le courrier postalEssayez l’API sur vos données.
Quatorze jours gratuits, l’API et les webhooks compris.
Sans carte bancaire. Aucun paiement automatique.
