ClientZen

Développeurs

API REST ClientZen v1.0

Branchez ClientZen sur votre site, votre boutique ou vos outils internes : clients, catalogue, devis, factures (création et émission), encaissements, rendez-vous et webhooks signés. L'API applique exactement les règles de l'interface — validation, quotas de l'offre, numérotation figée des factures.

URL de base https://clientzen.fr/api/v1
  • OpenAPI 3.1
  • 41 opérations
  • Erreurs RFC 9457
  • Idempotence des créations
  • Webhooks signés HMAC-SHA256
  • SDK PHP · JavaScript · Python

Démarrage en trois étapes

  1. Activez l'API. Dans votre espace : Paramètres › Modules → « API REST » (offres PME, Tout inclus et Enterprise).
  2. Créez un jeton. Paramètres › API et webhooks → « Nouveau jeton », choisissez les portées. Le secret n'est affiché qu'une fois : rangez-le dans une variable d'environnement.
  3. Vérifiez l'accès. GET /me renvoie votre espace et les portées du jeton. L'espace est déduit du jeton : aucun identifiant d'espace à transmettre.
curl "https://clientzen.fr/api/v1/me" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
<?php
require 'ClientZenClient.php';

$cz = new ClientZenClient(getenv('CLIENTZEN_TOKEN'));
$me = $cz->get('/me');
echo $me['data']['tenant']['name'];
// Node 18+ (fetch natif)
import { ClientZen } from './clientzen.mjs';

const cz = new ClientZen(process.env.CLIENTZEN_TOKEN);
const { data } = await cz.get('/me');
console.log(data.tenant.name, data.token.scopes);
import os
from clientzen import ClientZen

cz = ClientZen(os.environ["CLIENTZEN_TOKEN"])
me = cz.get("/me")
print(me["data"]["tenant"]["name"])

Authentification et portées

Chaque requête porte l'en-tête Authorization: Bearer czk_…. Le jeton est haché chez nous, révocable et expirable depuis votre espace ; créez un jeton par intégration et nommez-le. Une portée manquante renvoie 403 avec le champ required_scope.

Portées disponibles
PortéeAutorise
customers:readLire les clients
customers:writeCréer et modifier des clients
products:readLire le catalogue
quotes:readLire les devis
invoices:readLire les factures (dont PDF)
invoices:writeCréer des brouillons de facture et les émettre
payments:readLire les encaissements
appointments:readLire les services, disponibilités et rendez-vous
appointments:writeCréer et annuler des rendez-vous
knowledge:readLire les articles publiés de la base de connaissance
chat:readLire les conversations du tchat
chat:writeRépondre aux visiteurs du tchat et clore une conversation
webhooks:manageAbonner Zapier, Make ou n8n aux événements (webhooks gérés par ce jeton)
Jamais de jeton dans un navigateur ni dans une application mobile. Il y serait lisible par n'importe qui. Appelez l'API depuis votre serveur. CORS est ouvert sur /api/* pour les outils de développement, pas pour exposer un jeton.

Conventions

Montants

Entiers en centimes (total_incl_cents: 12000 = 120,00 €), avec le code devise. Aucun nombre à virgule flottante.

Dates

ISO 8601. Horodatages en UTC (2026-09-28T10:00:00+00:00), dates métier sans heure (2026-09-28).

Identifiants

ULID de 26 caractères, non prévisibles. Une ressource d'un autre espace répond 404, jamais 403.

Pagination

page et per_page (≤ 100). Réponse { data, meta: { page, per_page, total, last_page }, links: { next, prev } }.

Idempotence

Sur les créations, envoyez Idempotency-Key (≤ 128 caractères). Une réémission renvoie la première réponse avec Idempotent-Replayed: true : aucun doublon, même après une coupure réseau.

Débit et traçabilité

120 requêtes par minute et par jeton (en-têtes X-RateLimit-Limit et X-RateLimit-Remaining ; au-delà, 429 et Retry-After). X-Request-Id est renvoyé dans chaque réponse : citez-le au support.

Erreurs

Toutes les erreurs sont en application/problem+json (RFC 9457).

Codes d'erreur
CodeSignification
401Jeton absent, invalide, révoqué ou expiré.
403Portée manquante, ou module API non inclus dans l'offre de l'espace.
404Ressource inexistante — ou appartenant à un autre espace.
409Règle métier : quota atteint, facture déjà émise, créneau déjà pris, chronologie de numérotation.
422Données invalides ; le détail par champ est dans errors.
429Débit dépassé ; attendez la durée de Retry-After.
500Erreur de notre côté, sans trace technique ; citez request_id.
Exemple — 422
{
    "type": "https://clientzen.fr/developpeurs#erreurs",
    "title": "Données invalides",
    "status": 422,
    "detail": "Le champ name est obligatoire.",
    "request_id": "7f3c2a9e-5b1d-4c8e-9a2f-0e1d2c3b4a59",
    "errors": {
        "name": [
            "Le champ name est obligatoire."
        ]
    },
    "required_scope": "customers:write",
    "required_permission": "chat.reply"
}

Espace

Vérifier le jeton et l'espace auquel il donne accès.

GET /openapi.yaml #

Cette spécification

public, sans jeton

Réponses

  • 200 Spécification OpenAPI 3.1 (YAML)
Requête
curl "https://clientzen.fr/api/v1/openapi.yaml" \
  -H "Accept: application/json"
GET /me #

Espace et jeton courants

Le moyen le plus simple de vérifier un jeton : renvoie l'espace et les portées accordées.

Réponses

  • 200 OK
  • 401 Jeton absent
Requête
curl "https://clientzen.fr/api/v1/me" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
Réponse
{
    "data": {
        "tenant": {
            "id": "01k2m7q9x4v8b3n6c1d5f0g2h7",
            "name": "Atelier Martin",
            "slug": "atelier-martin"
        },
        "token": {
            "id": "01k2m8a1b2c3d4e5f6g7h8j9k0",
            "name": "Site vitrine",
            "scopes": [
                "customers:read",
                "customers:write"
            ],
            "expires_at": null
        }
    }
}

Clients

Clients et prospects de l'espace : lecture, recherche, création, modification.

GET /customers #

Lister les clients

portée customers:read

Recherche par nom, e-mail ou SIREN (`q`) ; `updated_since` pour une synchronisation incrémentale.

Paramètres

NomOùTypeDescription
pagerequêteintegerNuméro de page (1 par défaut)
per_pagerequêteintegerÉléments par page (25 par défaut, 100 au plus)
qrequêtestringRecherche (nom, e-mail, SIREN)
updated_sincerequêtestring (date-time)Modifiés depuis (ISO 8601)

Réponses

  • 200 OK
  • 401 Jeton absent
  • 403 Portée insuffisante ou module indisponible
Requête
curl "https://clientzen.fr/api/v1/customers" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
Réponse
{
    "data": [
        {
            "id": "01k2m9c3d4e5f6g7h8j9k0m1n2",
            "type": "company",
            "name": "Boulangerie Petit",
            "siren": "552100554",
            "vat_number": "FR40552100554",
            "vat_status": "taxable",
            "email": "contact@boulangerie-petit.fr",
            "phone": "+33 3 88 00 00 00",
            "website": "https://boulangerie-petit.fr",
            "is_prospect": false,
            "is_supplier": false,
            "tags": [
                "site-web"
            ],
            "custom_fields": [],
            "addresses": [
                {
                    "kind": "billing",
                    "line1": "12 rue des Fours",
                    "line2": null,
                    "postal_code": "67000",
                    "city": "Strasbourg",
                    "country": "FR",
                    "is_default": true
                }
            ],
            "created_at": "2026-09-28T10:00:00+00:00",
            "updated_at": "2026-09-28T10:00:00+00:00"
        }
    ],
    "meta": {
        "page": 1,
        "per_page": 25,
        "total": 42,
        "last_page": 2
    },
    "links": {
        "next": "https://clientzen.fr/api/v1/customers?page=2",
        "prev": null
    }
}
POST /customers #

Créer un client

portée customers:write Idempotency-Key

Idempotent avec `Idempotency-Key`. Le quota de clients de l'offre s'applique (409 s'il est atteint).

Paramètres

NomOùTypeDescription
Idempotency-Keyen-têtestringClé propre à votre opération : la première réponse est rejouée à l'identique

Corps JSON

ChampTypeDescription
type*stringValeurs : company, individual
name*string ex. Boulangerie Petit
sirenstring9 chiffres, clé de Luhn vérifiée ex. 552100554
vat_numberstring ex. FR40552100554
vat_statusstringValeurs : taxable, non_taxable, public
emailstring (email) ex. contact@boulangerie-petit.fr
phonestring ex. +33 3 88 00 00 00
websitestring (uri) ex. https://boulangerie-petit.fr
is_prospectboolean ex. true
is_supplierboolean ex. false
tagsarray<string>
notesstring ex. Demande reçue par le formulaire du site.
addressobject
address.line1string ex. 12 rue des Fours
address.line2string
address.postal_codestring ex. 67000
address.citystring ex. Strasbourg
address.countrystring ex. FR

Réponses

  • 201 Créé
  • 409 Règle métier (quota
  • 422 Données invalides (champ `errors`)
Requête
curl -X POST "https://clientzen.fr/api/v1/customers" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"type":"company","name":"Boulangerie Petit","siren":"552100554","vat_number":"FR40552100554","email":"contact@boulangerie-petit.fr","phone":"+33 3 88 00 00 00","website":"https://boulangerie-petit.fr","is_prospect":true,"is_supplier":false,"tags":["site-web"],"notes":"Demande reçue par le formulaire du site.","address":{"line1":"12 rue des Fours","postal_code":"67000","city":"Strasbourg","country":"FR"}}'
Réponse
{
    "data": {
        "id": "01k2m9c3d4e5f6g7h8j9k0m1n2",
        "type": "company",
        "name": "Boulangerie Petit",
        "siren": "552100554",
        "vat_number": "FR40552100554",
        "vat_status": "taxable",
        "email": "contact@boulangerie-petit.fr",
        "phone": "+33 3 88 00 00 00",
        "website": "https://boulangerie-petit.fr",
        "is_prospect": false,
        "is_supplier": false,
        "tags": [
            "site-web"
        ],
        "custom_fields": [],
        "addresses": [
            {
                "kind": "billing",
                "line1": "12 rue des Fours",
                "line2": null,
                "postal_code": "67000",
                "city": "Strasbourg",
                "country": "FR",
                "is_default": true
            }
        ],
        "created_at": "2026-09-28T10:00:00+00:00",
        "updated_at": "2026-09-28T10:00:00+00:00"
    }
}
GET /customers/{id} #

Détail d'un client

portée customers:read

Paramètres

NomOùTypeDescription
id*cheminstringIdentifiant (ULID)

Réponses

  • 200 OK
  • 404 Ressource introuvable dans cet espace
Requête
curl "https://clientzen.fr/api/v1/customers/01k2md6h7j8k9m0n1p2q3r4s5t" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
Réponse
{
    "data": {
        "id": "01k2m9c3d4e5f6g7h8j9k0m1n2",
        "type": "company",
        "name": "Boulangerie Petit",
        "siren": "552100554",
        "vat_number": "FR40552100554",
        "vat_status": "taxable",
        "email": "contact@boulangerie-petit.fr",
        "phone": "+33 3 88 00 00 00",
        "website": "https://boulangerie-petit.fr",
        "is_prospect": false,
        "is_supplier": false,
        "tags": [
            "site-web"
        ],
        "custom_fields": [],
        "addresses": [
            {
                "kind": "billing",
                "line1": "12 rue des Fours",
                "line2": null,
                "postal_code": "67000",
                "city": "Strasbourg",
                "country": "FR",
                "is_default": true
            }
        ],
        "created_at": "2026-09-28T10:00:00+00:00",
        "updated_at": "2026-09-28T10:00:00+00:00"
    }
}
PATCH /customers/{id} #

Modifier un client

portée customers:write

Seuls les champs envoyés sont modifiés.

Paramètres

NomOùTypeDescription
id*cheminstringIdentifiant (ULID)

Corps JSON

ChampTypeDescription
type*stringValeurs : company, individual
name*string ex. Boulangerie Petit
sirenstring9 chiffres, clé de Luhn vérifiée ex. 552100554
vat_numberstring ex. FR40552100554
vat_statusstringValeurs : taxable, non_taxable, public
emailstring (email) ex. contact@boulangerie-petit.fr
phonestring ex. +33 3 88 00 00 00
websitestring (uri) ex. https://boulangerie-petit.fr
is_prospectboolean ex. true
is_supplierboolean ex. false
tagsarray<string>
notesstring ex. Demande reçue par le formulaire du site.
addressobject
address.line1string ex. 12 rue des Fours
address.line2string
address.postal_codestring ex. 67000
address.citystring ex. Strasbourg
address.countrystring ex. FR

Réponses

  • 200 OK
  • 422 Données invalides (champ `errors`)
Requête
curl -X PATCH "https://clientzen.fr/api/v1/customers/01k2md6h7j8k9m0n1p2q3r4s5t" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"type":"company","name":"Boulangerie Petit","siren":"552100554","vat_number":"FR40552100554","email":"contact@boulangerie-petit.fr","phone":"+33 3 88 00 00 00","website":"https://boulangerie-petit.fr","is_prospect":true,"is_supplier":false,"tags":["site-web"],"notes":"Demande reçue par le formulaire du site.","address":{"line1":"12 rue des Fours","postal_code":"67000","city":"Strasbourg","country":"FR"}}'
Réponse
{
    "data": {
        "id": "01k2m9c3d4e5f6g7h8j9k0m1n2",
        "type": "company",
        "name": "Boulangerie Petit",
        "siren": "552100554",
        "vat_number": "FR40552100554",
        "vat_status": "taxable",
        "email": "contact@boulangerie-petit.fr",
        "phone": "+33 3 88 00 00 00",
        "website": "https://boulangerie-petit.fr",
        "is_prospect": false,
        "is_supplier": false,
        "tags": [
            "site-web"
        ],
        "custom_fields": [],
        "addresses": [
            {
                "kind": "billing",
                "line1": "12 rue des Fours",
                "line2": null,
                "postal_code": "67000",
                "city": "Strasbourg",
                "country": "FR",
                "is_default": true
            }
        ],
        "created_at": "2026-09-28T10:00:00+00:00",
        "updated_at": "2026-09-28T10:00:00+00:00"
    }
}

Catalogue

Produits et prestations, avec leur prix hors taxe et leur code de TVA.

GET /products #

Lister le catalogue

portée products:read

Paramètres

NomOùTypeDescription
pagerequêteintegerNuméro de page (1 par défaut)
per_pagerequêteintegerÉléments par page (25 par défaut, 100 au plus)

Réponses

  • 200 OK
Requête
curl "https://clientzen.fr/api/v1/products" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
Réponse
{
    "data": [
        {
            "id": "01k2mb4f5g6h7j8k9m0n1p2q3r",
            "sku": "POSE-01",
            "name": "Pose de parquet",
            "description": "Pose flottante, fournitures non comprises.",
            "type": "goods",
            "unit_code": "m2",
            "unit_price": "45.000000",
            "currency": "EUR",
            "tax_code": "FR_STANDARD",
            "updated_at": "2026-09-28T10:00:00+00:00"
        }
    ],
    "meta": {
        "page": 1,
        "per_page": 25,
        "total": 42,
        "last_page": 2
    },
    "links": {
        "next": "https://clientzen.fr/api/v1/customers?page=2",
        "prev": null
    }
}
GET /products/{id} #

Détail d'un produit

portée products:read

Paramètres

NomOùTypeDescription
id*cheminstringIdentifiant (ULID)

Réponses

  • 200 OK
  • 404 Ressource introuvable dans cet espace
Requête
curl "https://clientzen.fr/api/v1/products/01k2md6h7j8k9m0n1p2q3r4s5t" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
Réponse
{
    "data": {
        "id": "01k2mb4f5g6h7j8k9m0n1p2q3r",
        "sku": "POSE-01",
        "name": "Pose de parquet",
        "description": "Pose flottante, fournitures non comprises.",
        "type": "goods",
        "unit_code": "m2",
        "unit_price": "45.000000",
        "currency": "EUR",
        "tax_code": "FR_STANDARD",
        "updated_at": "2026-09-28T10:00:00+00:00"
    }
}
GET /customers/{id}/prices #

Prix d'un article pour un client

portée products:read

Tarif négocié applicable (groupe du client, règle propre au client, palier de quantité, à la date demandée), ou prix du catalogue s'il n'y en a pas. Le prix catalogue est conservé ; la remise est donnée en % (`discount_percent_e2`) ou en montant de ligne (`discount_amount_cents`), exactement comme sur une ligne de facture.

Paramètres

NomOùTypeDescription
id*cheminstringIdentifiant (ULID)
product_id*requêtestring
quantityrequêtestring
daterequêtestring (date)

Réponses

  • 200 OK
  • 404 Ressource introuvable dans cet espace
  • 422 Données invalides (champ `errors`)
Requête
curl "https://clientzen.fr/api/v1/customers/01k2md6h7j8k9m0n1p2q3r4s5t/prices?product_id=VALEUR" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
Réponse
{
    "data": {
        "customer_id": "texte",
        "product_id": "texte",
        "quantity": "10",
        "catalog_unit_price": "49",
        "unit_price": "49",
        "net_unit_price": "41.65",
        "discount_percent_e2": 1500,
        "discount_amount_cents": 0,
        "rule_id": "texte",
        "label": "Tarif Revendeurs : −15 %"
    }
}

Devis

Devis de l'espace, en lecture.

GET /quotes #

Lister les devis

portée quotes:read

Paramètres

NomOùTypeDescription
pagerequêteintegerNuméro de page (1 par défaut)
per_pagerequêteintegerÉléments par page (25 par défaut, 100 au plus)
statusrequêtestring
customer_idrequêtestring

Réponses

  • 200 OK
Requête
curl "https://clientzen.fr/api/v1/quotes" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
Réponse
{
    "data": [
        {
            "id": "01k2mc5g6h7j8k9m0n1p2q3r4s",
            "number": "DEV-2026-0042",
            "status": "draft",
            "customer_id": "texte",
            "customer_name": "Boulangerie Petit",
            "title": "Rénovation de la boutique",
            "total_excl_cents": 250000,
            "total_tax_cents": 50000,
            "total_incl_cents": 300000,
            "currency": "EUR",
            "valid_until": "2026-10-28",
            "created_at": "2026-09-28T10:00:00+00:00"
        }
    ],
    "meta": {
        "page": 1,
        "per_page": 25,
        "total": 42,
        "last_page": 2
    },
    "links": {
        "next": "https://clientzen.fr/api/v1/customers?page=2",
        "prev": null
    }
}
GET /quotes/{id} #

Détail d'un devis

portée quotes:read

Paramètres

NomOùTypeDescription
id*cheminstringIdentifiant (ULID)

Réponses

  • 200 OK
  • 404 Ressource introuvable dans cet espace
Requête
curl "https://clientzen.fr/api/v1/quotes/01k2md6h7j8k9m0n1p2q3r4s5t" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
Réponse
{
    "data": {
        "id": "01k2mc5g6h7j8k9m0n1p2q3r4s",
        "number": "DEV-2026-0042",
        "status": "draft",
        "customer_id": "texte",
        "customer_name": "Boulangerie Petit",
        "title": "Rénovation de la boutique",
        "total_excl_cents": 250000,
        "total_tax_cents": 50000,
        "total_incl_cents": 300000,
        "currency": "EUR",
        "valid_until": "2026-10-28",
        "created_at": "2026-09-28T10:00:00+00:00"
    }
}

Factures

Brouillons, émission (numéro définitif, irréversible), PDF. Mêmes règles que l'interface : validation, quotas, numérotation.

GET /companies #

Lister les sociétés émettrices

portée invoices:read

Avec le code de TVA à utiliser par défaut pour chacune (`default_tax_code`) : FRANCHISE_293B pour une société en franchise en base (art. 293 B du CGI), FR_STANDARD sinon.

Réponses

  • 200 OK
Requête
curl "https://clientzen.fr/api/v1/companies" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
Réponse
{
    "data": [
        {
            "id": "texte",
            "name": "Atelier Martin",
            "siren": "texte",
            "vat_number": "texte",
            "vat_regime": "normal",
            "is_default": false,
            "default_tax_code": "FRANCHISE_293B"
        }
    ]
}
GET /tax-codes #

Lister les codes de TVA en vigueur

portée invoices:read

Les valeurs admises dans `tax_code`, avec leur taux du jour.

Réponses

  • 200 OK
Requête
curl "https://clientzen.fr/api/v1/tax-codes" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
Réponse
{
    "data": [
        {
            "code": "FR_STANDARD",
            "label": "TVA 20 %",
            "rate": "20.00",
            "category": "texte",
            "valid_from": "2026-09-28"
        }
    ]
}
GET /invoices #

Lister les factures

portée invoices:read

Paramètres

NomOùTypeDescription
pagerequêteintegerNuméro de page (1 par défaut)
per_pagerequêteintegerÉléments par page (25 par défaut, 100 au plus)
statusrequêtestring
payment_statusrequêtestring
customer_idrequêtestring
issued_fromrequêtestring (date)Émises à partir de (AAAA-MM-JJ)

Réponses

  • 200 OK
Requête
curl "https://clientzen.fr/api/v1/invoices" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
Réponse
{
    "data": [
        {
            "id": "01k2md6h7j8k9m0n1p2q3r4s5t",
            "number": "F-2026-00128",
            "type": "invoice",
            "status": "draft",
            "payment_status": "unpaid",
            "transmission_status": "none",
            "customer_id": "texte",
            "customer_name": "Boulangerie Petit",
            "legal_entity_id": "texte",
            "title": "Rénovation de la boutique — solde",
            "issue_date": "2026-09-28",
            "due_date": "2026-10-28",
            "currency": "EUR",
            "total_excl_cents": 250000,
            "total_tax_cents": 50000,
            "total_incl_cents": 300000,
            "amount_due_cents": 300000,
            "amount_paid_cents": 0,
            "remaining_cents": 300000,
            "vat_treatment": "domestic",
            "pdf_sha256": "9f2c…",
            "payment_url": "https://clientzen.fr/portail/01k2…/factures/Xy9…",
            "created_at": "2026-09-28T10:00:00+00:00",
            "snapshot": {}
        }
    ],
    "meta": {
        "page": 1,
        "per_page": 25,
        "total": 42,
        "last_page": 2
    },
    "links": {
        "next": "https://clientzen.fr/api/v1/customers?page=2",
        "prev": null
    }
}
POST /invoices #

Créer un brouillon de facture, éventuellement l'émettre

portée invoices:write Idempotency-Key

Le brouillon n'a pas de numéro. Avec `issue: true`, la facture est émise dans la foulée : numéro définitif attribué, contenu figé, irréversible (une correction passe par un avoir).

Paramètres

NomOùTypeDescription
Idempotency-Keyen-têtestringClé propre à votre opération : la première réponse est rejouée à l'identique

Corps JSON

ChampTypeDescription
legal_entity_idstringSociété émettrice (GET /companies). Absent : la société par défaut de l'espace. ex. 01k2m7r0y5w9c4p7d2f6g1h3j8
customer_id*string ex. 01k2m9c3d4e5f6g7h8j9k0m1n2
titlestring ex. Rénovation de la boutique
operation_category*stringValeurs : goods, services, mixed ex. services
vat_treatmentstringDoit être admissible pour le client (proposition serveur sinon) ex. domestic
service_date_fromstring (date)
service_date_tostring (date)
due_datestring (date) ex. 2026-10-28
payment_termsstring ex. Paiement à 30 jours.
notesstring
issuebooleantrue = émission immédiate (numéro définitif, irréversible) ex. false
sendbooleanAvec `issue: true` : envoyer aussitôt la facture au client (PDF et lien de paiement). Absent : le réglage de l'espace « Envoyer au client chaque facture émise par l'API ». Un échec d'envoi n'annule pas l'émission (voir `meta.email`). ex. true
apply_customer_pricesbooleantrue = les lignes portant un `product_id` reçoivent le tarif négocié du client (prix catalogue + remise), à la place du prix et de la remise envoyés ex. false
lines*array<object>

Réponses

  • 201 Créée
  • 409 Règle métier (quota
  • 422 Données invalides (champ `errors`)
Requête
curl -X POST "https://clientzen.fr/api/v1/invoices" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"legal_entity_id":"01k2m7r0y5w9c4p7d2f6g1h3j8","customer_id":"01k2m9c3d4e5f6g7h8j9k0m1n2","title":"Rénovation de la boutique","operation_category":"services","vat_treatment":"domestic","due_date":"2026-10-28","payment_terms":"Paiement à 30 jours.","issue":false,"send":true,"apply_customer_prices":false,"lines":[{"kind":"line","description":"Pose de parquet","quantity":"2.5","unit_code":"m2","unit_price":"45.00","discount_percent_e2":0,"tax_code":"FR_STANDARD"}]}'
Réponse
{
    "data": {
        "id": "01k2md6h7j8k9m0n1p2q3r4s5t",
        "number": "F-2026-00128",
        "type": "invoice",
        "status": "draft",
        "payment_status": "unpaid",
        "transmission_status": "none",
        "customer_id": "texte",
        "customer_name": "Boulangerie Petit",
        "legal_entity_id": "texte",
        "title": "Rénovation de la boutique — solde",
        "issue_date": "2026-09-28",
        "due_date": "2026-10-28",
        "currency": "EUR",
        "total_excl_cents": 250000,
        "total_tax_cents": 50000,
        "total_incl_cents": 300000,
        "amount_due_cents": 300000,
        "amount_paid_cents": 0,
        "remaining_cents": 300000,
        "vat_treatment": "domestic",
        "pdf_sha256": "9f2c…",
        "payment_url": "https://clientzen.fr/portail/01k2…/factures/Xy9…",
        "created_at": "2026-09-28T10:00:00+00:00",
        "snapshot": {}
    },
    "meta": {
        "email": {
            "sent": false,
            "to": "compta@client.fr",
            "error": "Adresse e-mail du destinataire absente ou invalide."
        }
    }
}
GET /invoices/{id} #

Détail d'une facture (avec instantané figé)

portée invoices:read

Paramètres

NomOùTypeDescription
id*cheminstringIdentifiant (ULID)

Réponses

  • 200 OK
  • 404 Ressource introuvable dans cet espace
Requête
curl "https://clientzen.fr/api/v1/invoices/01k2md6h7j8k9m0n1p2q3r4s5t" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
Réponse
{
    "data": {
        "id": "01k2md6h7j8k9m0n1p2q3r4s5t",
        "number": "F-2026-00128",
        "type": "invoice",
        "status": "draft",
        "payment_status": "unpaid",
        "transmission_status": "none",
        "customer_id": "texte",
        "customer_name": "Boulangerie Petit",
        "legal_entity_id": "texte",
        "title": "Rénovation de la boutique — solde",
        "issue_date": "2026-09-28",
        "due_date": "2026-10-28",
        "currency": "EUR",
        "total_excl_cents": 250000,
        "total_tax_cents": 50000,
        "total_incl_cents": 300000,
        "amount_due_cents": 300000,
        "amount_paid_cents": 0,
        "remaining_cents": 300000,
        "vat_treatment": "domestic",
        "pdf_sha256": "9f2c…",
        "payment_url": "https://clientzen.fr/portail/01k2…/factures/Xy9…",
        "created_at": "2026-09-28T10:00:00+00:00",
        "snapshot": {}
    }
}
GET /invoices/{id}/pdf #

PDF de la facture émise

portée invoices:read

Le PDF archivé à l'émission (son empreinte est `pdf_sha256`).

Paramètres

NomOùTypeDescription
id*cheminstringIdentifiant (ULID)

Réponses

  • 200 PDF
  • 404 Ressource introuvable dans cet espace
Requête
curl "https://clientzen.fr/api/v1/invoices/01k2md6h7j8k9m0n1p2q3r4s5t/pdf" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
POST /invoices/{id}/issue #

Émettre un brouillon

portée invoices:write Idempotency-Key

Numéro définitif, contenu figé, irréversible. Idempotent. `send: true` dans le corps envoie aussitôt la facture au client (voir `meta.email`).

Paramètres

NomOùTypeDescription
id*cheminstringIdentifiant (ULID)
Idempotency-Keyen-têtestringClé propre à votre opération : la première réponse est rejouée à l'identique

Corps JSON

ChampTypeDescription
issue_datestring (date)
sendboolean

Réponses

  • 200 Émise
  • 409 Règle métier (quota
Requête
curl -X POST "https://clientzen.fr/api/v1/invoices/01k2md6h7j8k9m0n1p2q3r4s5t/issue" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
Réponse
{
    "data": {
        "id": "01k2md6h7j8k9m0n1p2q3r4s5t",
        "number": "F-2026-00128",
        "type": "invoice",
        "status": "draft",
        "payment_status": "unpaid",
        "transmission_status": "none",
        "customer_id": "texte",
        "customer_name": "Boulangerie Petit",
        "legal_entity_id": "texte",
        "title": "Rénovation de la boutique — solde",
        "issue_date": "2026-09-28",
        "due_date": "2026-10-28",
        "currency": "EUR",
        "total_excl_cents": 250000,
        "total_tax_cents": 50000,
        "total_incl_cents": 300000,
        "amount_due_cents": 300000,
        "amount_paid_cents": 0,
        "remaining_cents": 300000,
        "vat_treatment": "domestic",
        "pdf_sha256": "9f2c…",
        "payment_url": "https://clientzen.fr/portail/01k2…/factures/Xy9…",
        "created_at": "2026-09-28T10:00:00+00:00",
        "snapshot": {}
    },
    "meta": {
        "email": {
            "sent": false,
            "to": "compta@client.fr",
            "error": "Adresse e-mail du destinataire absente ou invalide."
        }
    }
}
POST /invoices/{id}/send #

Envoyer la facture au client

portée invoices:write Idempotency-Key

E-mail avec le PDF et le lien de paiement en ligne (`payment_url`), comme le bouton « Envoyer au client ». Rejouer avec la même `Idempotency-Key` n'envoie pas un second e-mail. Sans `to`, l'adresse de la fiche client. 409 si la facture n'est pas émise ou si aucune adresse n'est connue.

Paramètres

NomOùTypeDescription
id*cheminstringIdentifiant (ULID)
Idempotency-Keyen-têtestringClé propre à votre opération : la première réponse est rejouée à l'identique

Corps JSON

ChampTypeDescription
tostring (email) ex. compta@client.fr
messagestring ex. Merci pour votre commande.

Réponses

  • 200 Envoyée
  • 409 Règle métier (quota
  • 422 Données invalides (champ `errors`)
Requête
curl -X POST "https://clientzen.fr/api/v1/invoices/01k2md6h7j8k9m0n1p2q3r4s5t/send" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"to":"compta@client.fr","message":"Merci pour votre commande."}'
Réponse
{
    "data": {
        "id": "01k2md6h7j8k9m0n1p2q3r4s5t",
        "number": "F-2026-00128",
        "type": "invoice",
        "status": "draft",
        "payment_status": "unpaid",
        "transmission_status": "none",
        "customer_id": "texte",
        "customer_name": "Boulangerie Petit",
        "legal_entity_id": "texte",
        "title": "Rénovation de la boutique — solde",
        "issue_date": "2026-09-28",
        "due_date": "2026-10-28",
        "currency": "EUR",
        "total_excl_cents": 250000,
        "total_tax_cents": 50000,
        "total_incl_cents": 300000,
        "amount_due_cents": 300000,
        "amount_paid_cents": 0,
        "remaining_cents": 300000,
        "vat_treatment": "domestic",
        "pdf_sha256": "9f2c…",
        "payment_url": "https://clientzen.fr/portail/01k2…/factures/Xy9…",
        "created_at": "2026-09-28T10:00:00+00:00",
        "snapshot": {}
    },
    "meta": {
        "email": {
            "sent": false,
            "to": "compta@client.fr",
            "error": "Adresse e-mail du destinataire absente ou invalide."
        }
    }
}

Encaissements

Paiements reçus et leur affectation aux factures.

GET /payments #

Lister les encaissements

portée payments:read

Paramètres

NomOùTypeDescription
pagerequêteintegerNuméro de page (1 par défaut)
per_pagerequêteintegerÉléments par page (25 par défaut, 100 au plus)
fromrequêtestring (date)
torequêtestring (date)

Réponses

  • 200 OK
Requête
curl "https://clientzen.fr/api/v1/payments" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
Réponse
{
    "data": [
        {
            "id": "01k2mf7j8k9m0n1p2q3r4s5t6v",
            "customer_id": "texte",
            "method": "transfer",
            "amount_cents": 300000,
            "currency": "EUR",
            "received_at": "2026-10-02",
            "reference": "VIR F-2026-00128",
            "status": "recorded",
            "unallocated_cents": 0,
            "allocations": [
                {
                    "invoice_id": "texte",
                    "amount_cents": 300000
                }
            ]
        }
    ],
    "meta": {
        "page": 1,
        "per_page": 25,
        "total": 42,
        "last_page": 2
    },
    "links": {
        "next": "https://clientzen.fr/api/v1/customers?page=2",
        "prev": null
    }
}

Rendez-vous

Services réservables, disponibilités calculées par le serveur, réservations et annulations (module Rendez-vous).

GET /booking/services #

Lister les services réservables

portée appointments:read

Avec leurs ressources (intervenants, salles). Module Rendez-vous requis.

Réponses

  • 200 OK
  • 403 Portée insuffisante ou module indisponible
Requête
curl "https://clientzen.fr/api/v1/booking/services" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
Réponse
{
    "data": [
        {
            "id": "texte",
            "slug": "consultation",
            "name": "Consultation (1 h)",
            "category": "Conseil",
            "description": "texte",
            "duration_min": 60,
            "buffer_before_min": 0,
            "buffer_after_min": 15,
            "capacity": 1,
            "price_excl_cents": 8000,
            "tax_code": "FR_STANDARD",
            "deposit_excl_cents": 4000,
            "location_mode": "onsite",
            "is_public": true,
            "resources": [
                {
                    "id": "texte",
                    "name": "Salle 1",
                    "kind": "room"
                }
            ]
        }
    ]
}
GET /booking/availability #

Créneaux disponibles d'un service pour un jour

portée appointments:read

Calculés par le serveur (plages, indisponibilités, capacité, délais), dans le fuseau de l'espace.

Paramètres

NomOùTypeDescription
service_id*requêtestring
day*requêtestring (date)
resource_idrequêtestring
seatsrequêteinteger

Réponses

  • 200 OK
Requête
curl "https://clientzen.fr/api/v1/booking/availability?service_id=VALEUR&day=2026-10-05" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
Réponse
{
    "data": [
        {
            "starts_at": "2026-10-05T09:00:00+02:00",
            "ends_at": "2026-10-05T10:00:00+02:00",
            "resource_id": "texte",
            "resource_name": "Salle 1",
            "remaining": 1
        }
    ],
    "meta": {
        "timezone": "Europe/Paris",
        "day": "2026-10-05"
    }
}
GET /appointments #

Lister les rendez-vous

portée appointments:read

Paramètres

NomOùTypeDescription
pagerequêteintegerNuméro de page (1 par défaut)
per_pagerequêteintegerÉléments par page (25 par défaut, 100 au plus)
fromrequêtestring (date-time)
torequêtestring (date-time)
statusrequêtestring
resource_idrequêtestring
customer_idrequêtestring

Réponses

  • 200 OK
Requête
curl "https://clientzen.fr/api/v1/appointments" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
Réponse
{
    "data": [
        {
            "id": "texte",
            "reference": "RDV-A1B2C3D4",
            "status": "pending_payment",
            "source": "api",
            "service": {
                "id": "texte",
                "name": "Consultation (1 h)"
            },
            "resource": {
                "id": "texte",
                "name": "Salle 1"
            },
            "customer": {
                "id": "texte",
                "name": "Marie Durand",
                "email": "marie.durand@exemple.fr",
                "phone": "texte"
            },
            "starts_at": "2026-09-28T10:00:00+00:00",
            "ends_at": "2026-09-28T10:00:00+00:00",
            "timezone": "Europe/Paris",
            "seats": 1,
            "message": "texte",
            "deposit_invoice_id": "texte",
            "final_invoice_id": "texte",
            "hold_expires_at": "2026-09-28T10:00:00+00:00",
            "cancel_reason": "texte",
            "created_at": "2026-09-28T10:00:00+00:00",
            "manage_url": "texte",
            "payment_url": "texte"
        }
    ],
    "meta": {
        "page": 1,
        "per_page": 25,
        "total": 42,
        "last_page": 2
    },
    "links": {
        "next": "https://clientzen.fr/api/v1/customers?page=2",
        "prev": null
    }
}
POST /appointments #

Réserver un créneau

portée appointments:write Idempotency-Key

Mêmes règles que le mini-site (délai, horizon, plages, capacité). Service avec acompte : facture d'acompte créée et `payment_url` renvoyé.

Paramètres

NomOùTypeDescription
Idempotency-Keyen-têtestringClé propre à votre opération : la première réponse est rejouée à l'identique

Corps JSON

ChampTypeDescription
service_id*string
resource_idstring | nullAbsent = première ressource disponible
starts_at*string (date-time) ex. 2026-10-05T09:00:00+02:00
seatsinteger ex. 1
customer*object
customer.idstring
customer.namestring ex. Marie Durand
customer.emailstring ex. marie.durand@exemple.fr
customer.phonestring
customer.addressobjectRequise si un acompte est facturé
messagestring
reminders_consentboolean ex. true
sourcestringValeurs : api, widget, site

Réponses

  • 201 Rendez-vous créé
  • 409 Règle métier (quota
  • 422 Données invalides (champ `errors`)
Requête
curl -X POST "https://clientzen.fr/api/v1/appointments" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"service_id":"texte","starts_at":"2026-10-05T09:00:00+02:00","seats":1,"customer":{"name":"Marie Durand","email":"marie.durand@exemple.fr","address":{}},"reminders_consent":true}'
Réponse
{
    "data": {
        "id": "texte",
        "reference": "RDV-A1B2C3D4",
        "status": "pending_payment",
        "source": "api",
        "service": {
            "id": "texte",
            "name": "Consultation (1 h)"
        },
        "resource": {
            "id": "texte",
            "name": "Salle 1"
        },
        "customer": {
            "id": "texte",
            "name": "Marie Durand",
            "email": "marie.durand@exemple.fr",
            "phone": "texte"
        },
        "starts_at": "2026-09-28T10:00:00+00:00",
        "ends_at": "2026-09-28T10:00:00+00:00",
        "timezone": "Europe/Paris",
        "seats": 1,
        "message": "texte",
        "deposit_invoice_id": "texte",
        "final_invoice_id": "texte",
        "hold_expires_at": "2026-09-28T10:00:00+00:00",
        "cancel_reason": "texte",
        "created_at": "2026-09-28T10:00:00+00:00",
        "manage_url": "texte",
        "payment_url": "texte"
    }
}
GET /appointments/{id} #

Détail d'un rendez-vous

portée appointments:read

Paramètres

NomOùTypeDescription
id*cheminstringIdentifiant (ULID)

Réponses

  • 200 OK
  • 404 Ressource introuvable dans cet espace
Requête
curl "https://clientzen.fr/api/v1/appointments/01k2md6h7j8k9m0n1p2q3r4s5t" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
Réponse
{
    "data": {
        "id": "texte",
        "reference": "RDV-A1B2C3D4",
        "status": "pending_payment",
        "source": "api",
        "service": {
            "id": "texte",
            "name": "Consultation (1 h)"
        },
        "resource": {
            "id": "texte",
            "name": "Salle 1"
        },
        "customer": {
            "id": "texte",
            "name": "Marie Durand",
            "email": "marie.durand@exemple.fr",
            "phone": "texte"
        },
        "starts_at": "2026-09-28T10:00:00+00:00",
        "ends_at": "2026-09-28T10:00:00+00:00",
        "timezone": "Europe/Paris",
        "seats": 1,
        "message": "texte",
        "deposit_invoice_id": "texte",
        "final_invoice_id": "texte",
        "hold_expires_at": "2026-09-28T10:00:00+00:00",
        "cancel_reason": "texte",
        "created_at": "2026-09-28T10:00:00+00:00",
        "manage_url": "texte",
        "payment_url": "texte"
    }
}
POST /appointments/{id}/cancel #

Annuler un rendez-vous

portée appointments:write

`by_customer: true` applique le délai d'annulation libre du client.

Paramètres

NomOùTypeDescription
id*cheminstringIdentifiant (ULID)

Corps JSON

ChampTypeDescription
reasonstring ex. Empêchement
by_customerboolean ex. true

Réponses

  • 200 OK
  • 409 Règle métier (quota
Requête
curl -X POST "https://clientzen.fr/api/v1/appointments/01k2md6h7j8k9m0n1p2q3r4s5t/cancel" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"reason":"Empêchement","by_customer":true}'
Réponse
{
    "data": {
        "id": "texte",
        "reference": "RDV-A1B2C3D4",
        "status": "pending_payment",
        "source": "api",
        "service": {
            "id": "texte",
            "name": "Consultation (1 h)"
        },
        "resource": {
            "id": "texte",
            "name": "Salle 1"
        },
        "customer": {
            "id": "texte",
            "name": "Marie Durand",
            "email": "marie.durand@exemple.fr",
            "phone": "texte"
        },
        "starts_at": "2026-09-28T10:00:00+00:00",
        "ends_at": "2026-09-28T10:00:00+00:00",
        "timezone": "Europe/Paris",
        "seats": 1,
        "message": "texte",
        "deposit_invoice_id": "texte",
        "final_invoice_id": "texte",
        "hold_expires_at": "2026-09-28T10:00:00+00:00",
        "cancel_reason": "texte",
        "created_at": "2026-09-28T10:00:00+00:00",
        "manage_url": "texte",
        "payment_url": "texte"
    }
}

Tchat et base de connaissance

Articles publiés de la base de connaissance, conversations du tchat, réponse et clôture au nom du membre qui a créé le jeton (module Base de connaissance).

GET /knowledge/articles #

Lister les articles publiés

portée knowledge:read

Les brouillons ne sortent jamais. Module Base de connaissance requis.

Paramètres

NomOùTypeDescription
pagerequêteintegerNuméro de page (1 par défaut)
per_pagerequêteintegerÉléments par page (25 par défaut, 100 au plus)
qrequêtestringRecherche dans le titre
categoryrequêtestring
updated_sincerequêtestring (date-time)

Réponses

  • 200 OK
  • 403 Portée insuffisante ou module indisponible
Requête
curl "https://clientzen.fr/api/v1/knowledge/articles" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
Réponse
{
    "data": [
        {
            "id": "texte",
            "title": "Horaires d'ouverture",
            "slug": "horaires-d-ouverture",
            "category": "Pratique",
            "source_type": "article",
            "source_url": "texte",
            "version": 3,
            "published_at": "2026-09-28T10:00:00+00:00",
            "updated_at": "2026-09-28T10:00:00+00:00",
            "body": "texte"
        }
    ],
    "meta": {
        "page": 1,
        "per_page": 25,
        "total": 42,
        "last_page": 2
    },
    "links": {
        "next": "https://clientzen.fr/api/v1/customers?page=2",
        "prev": null
    }
}
GET /knowledge/articles/{id} #

Détail d'un article publié, avec son texte

portée knowledge:read

Paramètres

NomOùTypeDescription
id*cheminstringIdentifiant (ULID)

Réponses

  • 200 OK
  • 404 Ressource introuvable dans cet espace
Requête
curl "https://clientzen.fr/api/v1/knowledge/articles/01k2md6h7j8k9m0n1p2q3r4s5t" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
Réponse
{
    "data": {
        "id": "texte",
        "title": "Horaires d'ouverture",
        "slug": "horaires-d-ouverture",
        "category": "Pratique",
        "source_type": "article",
        "source_url": "texte",
        "version": 3,
        "published_at": "2026-09-28T10:00:00+00:00",
        "updated_at": "2026-09-28T10:00:00+00:00",
        "body": "texte"
    }
}
GET /chat/conversations #

Lister les conversations du tchat

portée chat:read

Les plus récentes d'abord. Filtrez sur `waiting` pour retrouver les visiteurs qui attendent l'équipe.

Paramètres

NomOùTypeDescription
pagerequêteintegerNuméro de page (1 par défaut)
per_pagerequêteintegerÉléments par page (25 par défaut, 100 au plus)
statusrequêtestring
widget_idrequêtestring
updated_sincerequêtestring (date-time)

Réponses

  • 200 OK
  • 403 Portée insuffisante ou module indisponible
Requête
curl "https://clientzen.fr/api/v1/chat/conversations" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
Réponse
{
    "data": [
        {
            "id": "texte",
            "status": "bot",
            "widget_id": "texte",
            "widget_name": "Site principal",
            "origin_host": "www.exemple.fr",
            "page_url": "texte",
            "visitor": {
                "name": "texte",
                "email": "texte",
                "phone": "texte",
                "consent_at": "2026-09-28T10:00:00+00:00"
            },
            "customer_id": "texte",
            "assigned_user_id": "texte",
            "rating": 0,
            "visitor_messages": 0,
            "bot_answers": 0,
            "abstentions": 0,
            "escalated_at": "2026-09-28T10:00:00+00:00",
            "first_agent_reply_at": "2026-09-28T10:00:00+00:00",
            "closed_at": "2026-09-28T10:00:00+00:00",
            "last_message_at": "2026-09-28T10:00:00+00:00",
            "created_at": "2026-09-28T10:00:00+00:00",
            "updated_at": "2026-09-28T10:00:00+00:00",
            "messages": [
                {
                    "id": "texte",
                    "author": "visitor",
                    "user_id": "texte",
                    "body": "Êtes-vous ouverts le samedi ?",
                    "abstained": false,
                    "sources": [
                        null
                    ],
                    "created_at": "2026-09-28T10:00:00+00:00"
                }
            ]
        }
    ],
    "meta": {
        "page": 1,
        "per_page": 25,
        "total": 42,
        "last_page": 2
    },
    "links": {
        "next": "https://clientzen.fr/api/v1/customers?page=2",
        "prev": null
    }
}
GET /chat/conversations/{id} #

Détail d'une conversation, avec tous ses messages

portée chat:read

Paramètres

NomOùTypeDescription
id*cheminstringIdentifiant (ULID)

Réponses

  • 200 OK
  • 404 Ressource introuvable dans cet espace
Requête
curl "https://clientzen.fr/api/v1/chat/conversations/01k2md6h7j8k9m0n1p2q3r4s5t" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
Réponse
{
    "data": {
        "id": "texte",
        "status": "bot",
        "widget_id": "texte",
        "widget_name": "Site principal",
        "origin_host": "www.exemple.fr",
        "page_url": "texte",
        "visitor": {
            "name": "texte",
            "email": "texte",
            "phone": "texte",
            "consent_at": "2026-09-28T10:00:00+00:00"
        },
        "customer_id": "texte",
        "assigned_user_id": "texte",
        "rating": 0,
        "visitor_messages": 0,
        "bot_answers": 0,
        "abstentions": 0,
        "escalated_at": "2026-09-28T10:00:00+00:00",
        "first_agent_reply_at": "2026-09-28T10:00:00+00:00",
        "closed_at": "2026-09-28T10:00:00+00:00",
        "last_message_at": "2026-09-28T10:00:00+00:00",
        "created_at": "2026-09-28T10:00:00+00:00",
        "updated_at": "2026-09-28T10:00:00+00:00",
        "messages": [
            {
                "id": "texte",
                "author": "visitor",
                "user_id": "texte",
                "body": "Êtes-vous ouverts le samedi ?",
                "abstained": false,
                "sources": [
                    {
                        "article_id": null,
                        "title": null
                    }
                ],
                "created_at": "2026-09-28T10:00:00+00:00"
            }
        ]
    }
}
POST /chat/conversations/{id}/reply #

Répondre au visiteur

portée chat:write Idempotency-Key

Au nom du membre qui a créé le jeton, s'il a toujours le droit de répondre au tchat (403 sinon). Le visiteur parti depuis plus de 90 s et ayant laissé son adresse avec son accord reçoit aussi la réponse par e-mail.

Paramètres

NomOùTypeDescription
id*cheminstringIdentifiant (ULID)
Idempotency-Keyen-têtestringClé propre à votre opération : la première réponse est rejouée à l'identique

Corps JSON

ChampTypeDescription
body*string ex. Oui, de 9 h à 12 h.

Réponses

  • 201 Réponse envoyée
  • 403 Portée insuffisante ou module indisponible
  • 409 Règle métier (quota
  • 422 Données invalides (champ `errors`)
Requête
curl -X POST "https://clientzen.fr/api/v1/chat/conversations/01k2md6h7j8k9m0n1p2q3r4s5t/reply" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"body":"Oui, de 9 h à 12 h."}'
Réponse
{
    "data": {
        "id": "texte",
        "status": "bot",
        "widget_id": "texte",
        "widget_name": "Site principal",
        "origin_host": "www.exemple.fr",
        "page_url": "texte",
        "visitor": {
            "name": "texte",
            "email": "texte",
            "phone": "texte",
            "consent_at": "2026-09-28T10:00:00+00:00"
        },
        "customer_id": "texte",
        "assigned_user_id": "texte",
        "rating": 0,
        "visitor_messages": 0,
        "bot_answers": 0,
        "abstentions": 0,
        "escalated_at": "2026-09-28T10:00:00+00:00",
        "first_agent_reply_at": "2026-09-28T10:00:00+00:00",
        "closed_at": "2026-09-28T10:00:00+00:00",
        "last_message_at": "2026-09-28T10:00:00+00:00",
        "created_at": "2026-09-28T10:00:00+00:00",
        "updated_at": "2026-09-28T10:00:00+00:00",
        "messages": [
            {
                "id": "texte",
                "author": "visitor",
                "user_id": "texte",
                "body": "Êtes-vous ouverts le samedi ?",
                "abstained": false,
                "sources": [
                    {
                        "article_id": null,
                        "title": null
                    }
                ],
                "created_at": "2026-09-28T10:00:00+00:00"
            }
        ]
    }
}
POST /chat/conversations/{id}/close #

Clore une conversation

portée chat:write

Sans effet sur une conversation déjà close.

Paramètres

NomOùTypeDescription
id*cheminstringIdentifiant (ULID)

Réponses

  • 200 OK
  • 403 Portée insuffisante ou module indisponible
Requête
curl -X POST "https://clientzen.fr/api/v1/chat/conversations/01k2md6h7j8k9m0n1p2q3r4s5t/close" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
Réponse
{
    "data": {
        "id": "texte",
        "status": "bot",
        "widget_id": "texte",
        "widget_name": "Site principal",
        "origin_host": "www.exemple.fr",
        "page_url": "texte",
        "visitor": {
            "name": "texte",
            "email": "texte",
            "phone": "texte",
            "consent_at": "2026-09-28T10:00:00+00:00"
        },
        "customer_id": "texte",
        "assigned_user_id": "texte",
        "rating": 0,
        "visitor_messages": 0,
        "bot_answers": 0,
        "abstentions": 0,
        "escalated_at": "2026-09-28T10:00:00+00:00",
        "first_agent_reply_at": "2026-09-28T10:00:00+00:00",
        "closed_at": "2026-09-28T10:00:00+00:00",
        "last_message_at": "2026-09-28T10:00:00+00:00",
        "created_at": "2026-09-28T10:00:00+00:00",
        "updated_at": "2026-09-28T10:00:00+00:00",
        "messages": [
            {
                "id": "texte",
                "author": "visitor",
                "user_id": "texte",
                "body": "Êtes-vous ouverts le samedi ?",
                "abstained": false,
                "sources": [
                    {
                        "article_id": null,
                        "title": null
                    }
                ],
                "created_at": "2026-09-28T10:00:00+00:00"
            }
        ]
    }
}

Abonnements aux événements

« REST hooks » pour Zapier, Make ou n8n : l'outil s'abonne lui-même aux événements quand un scénario est activé (`POST /webhooks`), et se désabonne quand il est coupé (`DELETE /webhooks/{id}`). Chaque jeton ne voit et ne supprime que ses abonnements ; révoquer le jeton les désactive. Répondre 410 à une livraison désactive l'abonnement.

GET /webhooks/events #

Lister les événements auxquels s'abonner

portée webhooks:manage

Réponses

  • 200 OK
Requête
curl "https://clientzen.fr/api/v1/webhooks/events" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
Réponse
{
    "data": [
        {
            "event": "invoice.paid",
            "label": "Facture réglée"
        }
    ]
}
GET /webhooks/sample/{event} #

Échantillon de la charge d'un événement

portée webhooks:manage

Même forme que la charge réellement envoyée (avec `sample: true`), pour proposer les champs avant le premier événement réel.

Paramètres

NomOùTypeDescription
event*cheminstring

Réponses

  • 200 OK
  • 404 Ressource introuvable dans cet espace
Requête
curl "https://clientzen.fr/api/v1/webhooks/sample/{event}" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
Réponse
{
    "data": [
        {
            "event": "texte",
            "resource_type": "Invoice",
            "resource_id": "texte",
            "occurred_at": "2026-09-28T10:00:00+00:00",
            "correlation_id": "texte",
            "sample": false
        }
    ]
}
GET /webhooks #

Lister les abonnements créés par ce jeton

portée webhooks:manage

Réponses

  • 200 OK
Requête
curl "https://clientzen.fr/api/v1/webhooks" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
Réponse
{
    "data": [
        {
            "id": "texte",
            "target_url": "texte",
            "events": [
                "texte"
            ],
            "label": "texte",
            "is_active": false,
            "created_at": "2026-09-28T10:00:00+00:00"
        }
    ]
}
POST /webhooks #

S'abonner à des événements

portée webhooks:manage Idempotency-Key

`target_url` (ou `targetUrl`) en https, adresse publique. `events` (liste) ou `event` (un seul, forme Zapier). Le `secret` de signature n'est rendu qu'ici, une seule fois. Idempotent avec `Idempotency-Key`.

Paramètres

NomOùTypeDescription
Idempotency-Keyen-têtestringClé propre à votre opération : la première réponse est rejouée à l'identique

Corps JSON

ChampTypeDescription
target_url*string (uri) ex. https://hooks.zapier.com/hooks/standard/123/abc
eventsarray<string>
eventstring ex. invoice.paid
labelstring ex. Zap : facture réglée → Google Sheets

Réponses

  • 201 Abonné
  • 409 Règle métier (quota
  • 422 Données invalides (champ `errors`)
Requête
curl -X POST "https://clientzen.fr/api/v1/webhooks" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"target_url":"https://hooks.zapier.com/hooks/standard/123/abc","events":["invoice.paid"],"event":"invoice.paid","label":"Zap : facture réglée → Google Sheets"}'
Réponse
{
    "data": {
        "id": "texte",
        "target_url": "texte",
        "events": [
            "texte"
        ],
        "label": "texte",
        "is_active": false,
        "created_at": "2026-09-28T10:00:00+00:00",
        "secret": "czs_…"
    }
}
DELETE /webhooks/{id} #

Se désabonner

portée webhooks:manage

Seulement un abonnement créé par ce jeton (404 sinon).

Paramètres

NomOùTypeDescription
id*cheminstringIdentifiant (ULID)

Réponses

  • 204 Désabonné
  • 404 Ressource introuvable dans cet espace
Requête
curl -X DELETE "https://clientzen.fr/api/v1/webhooks/01k2md6h7j8k9m0n1p2q3r4s5t" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"

Boutiques en ligne

Appelée par le module d'une boutique (WooCommerce, ou votre propre intégration) avec le jeton créé dans Ventes › Boutiques en ligne (portée `shop:sync`) : la boutique est déduite du jeton. Une commande payée devient une facture émise par ClientZen ; ce qui ne se facture pas juste (devise, hors de France, TVA inconnue, article absent, total différent) est mis à examiner.

GET /shop/handshake #

Vérifier la connexion de la boutique

portée shop:sync

Renvoie la boutique, la société qui facture et qui fait foi pour le catalogue et pour le stock (`clientzen` ou `shop`).

Réponses

  • 200 OK
  • 403 Portée insuffisante ou module indisponible
Requête
curl "https://clientzen.fr/api/v1/shop/handshake" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
Réponse
{
    "data": {
        "shop": {
            "id": "texte",
            "name": "Boutique Miel",
            "platform": "woocommerce",
            "store_url": "texte"
        },
        "company": {
            "name": "texte",
            "siren": "texte",
            "vat_regime": "texte"
        },
        "catalog_master": "clientzen",
        "stock_master": "clientzen",
        "auto_invoice": false,
        "currency": "EUR",
        "server_time": "2026-09-28T10:00:00+00:00"
    }
}
GET /shop/catalog #

Lire le catalogue et le stock disponible

portée shop:sync

Articles vendables ayant une référence (SKU), prix hors taxe et taux de TVA, stock disponible de la société pour les articles suivis, textes de vente et de référencement.

Paramètres

NomOùTypeDescription
updated_sincerequêtestring (date-time)

Réponses

  • 200 OK
Requête
curl "https://clientzen.fr/api/v1/shop/catalog" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json"
Réponse
{
    "data": [
        {
            "sku": "MIEL-500",
            "name": "texte",
            "description": "texte",
            "web_description": "texte",
            "short_description": "texte",
            "seo_title": "texte",
            "seo_description": "texte",
            "price_excl": "10.000000",
            "tax_rate": "5.50",
            "track_stock": false,
            "stock": "12",
            "active": false
        }
    ]
}
POST /shop/orders #

Envoyer une commande (création ou mise à jour)

portée shop:sync

Idempotent par `external_id`. Payée (`paid: true`) et pas encore facturée : la facture est émise et l'encaissement enregistré, une seule fois. Montants en décimal, taux de TVA lu dans la règle de la boutique.

Corps JSON

ChampTypeDescription
external_id*string ex. 1042
numberstring
currencystring ex. EUR
paidboolean
paid_atstring (date-time) | null
customerobject
customer.emailstring
customer.first_namestring
customer.last_namestring
customer.companystring
customer.phonestring
shipping_countrystring ex. FR
lines*array<object>
shippingarray<object>
total_incl*string ex. 51.10

Réponses

  • 200 OK
  • 422 Données invalides (champ `errors`)
Requête
curl -X POST "https://clientzen.fr/api/v1/shop/orders" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"external_id":"1042","currency":"EUR","customer":{},"shipping_country":"FR","lines":[{"quantity":"2","total_excl":"20.00","tax_rate":"5.50"}],"total_incl":"51.10"}'
Réponse
{
    "data": {
        "external_id": "texte",
        "number": "texte",
        "handling": "recorded",
        "review_reason": "texte",
        "payment_state": "texte",
        "invoice_number": "texte"
    }
}
POST /shop/orders/{external_id}/refunds #

Déclarer un remboursement

portée shop:sync

Idempotent par `external_id` du remboursement. Commande facturée : un avoir est émis et le trop-perçu remboursé.

Paramètres

NomOùTypeDescription
external_id*cheminstring

Corps JSON

ChampTypeDescription
external_id*string
amount*string ex. 51.10
reasonstring

Réponses

  • 200 OK
  • 422 Données invalides (champ `errors`)
Requête
curl -X POST "https://clientzen.fr/api/v1/shop/orders/{external_id}/refunds" \
  -H "Authorization: Bearer $CLIENTZEN_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"external_id":"texte","amount":"51.10"}'
Réponse
{
    "data": {
        "external_id": "texte",
        "handling": "recorded",
        "review_reason": "texte",
        "amount_cents": 0
    }
}

Webhooks

Déclarez une URL https dans Paramètres › API et webhooks et choisissez les événements. Nous envoyons un POST JSON une fois l'opération enregistrée. La charge utile ne contient aucune donnée personnelle : relisez la ressource par l'API avec son identifiant.

Événements
ÉvénementDéclenché quand
customer.createdClient créé
customer.updatedClient modifié
quote.sentDevis transmis
quote.acceptedDevis accepté
quote.refusedDevis refusé
invoice.issuedFacture émise
invoice.paidFacture réglée
payment.recordedEncaissement enregistré
pos.ticket_issuedTicket de caisse émis
appointment.createdRendez-vous créé
appointment.confirmedRendez-vous confirmé
appointment.rescheduledRendez-vous reprogrammé
appointment.cancelledRendez-vous annulé
chat.conversation_escalatedConversation du tchat transmise à l'équipe
chat.conversation_closedConversation du tchat close
Corps envoyé
{
    "event": "invoice.paid",
    "resource_type": "Invoice",
    "resource_id": "01k2md6h7j8k9m0n1p2q3r4s5t",
    "occurred_at": "2026-10-02T08:14:03+00:00",
    "correlation_id": "texte"
}
En-têtes
Content-Type: application/json
User-Agent: ClientZen-Webhooks/1
X-ClientZen-Event: invoice.paid
X-ClientZen-Delivery: 01k2md6h7j8k9m0n1p2q3r4s5t
X-ClientZen-Signature: t=1790589600,v1=5f2b…

Remise et nouvelles tentatives

  • Répondez 2xx en moins de 8 secondes ; traitez le reste en arrière-plan. Les redirections ne sont pas suivies.
  • En cas d'échec : 3 tentatives au total, la deuxième 30 secondes après la première, la troisième 5 minutes après.
  • Après 10 échecs consécutifs, le webhook est désactivé ; réactivez-le depuis votre espace, où chaque livraison est journalisée avec sa réponse.
  • Une même livraison peut arriver deux fois : dédoublonnez sur X-ClientZen-Delivery.

Vérifier la signature

v1 est le HMAC-SHA256, en hexadécimal, de t + "." + corps brut avec le secret du webhook. Calculez-le sur le corps tel que reçu (pas un JSON réencodé), comparez en temps constant et refusez un horodatage de plus de 5 minutes.

$raw = file_get_contents('php://input');
$secret = getenv('CLIENTZEN_WEBHOOK_SECRET');
if (! preg_match('/^t=(\d+),v1=([a-f0-9]{64})$/', $_SERVER['HTTP_X_CLIENTZEN_SIGNATURE'] ?? '', $m)
    || abs(time() - (int) $m[1]) > 300
    || ! hash_equals(hash_hmac('sha256', $m[1].'.'.$raw, $secret), $m[2])) {
    http_response_code(401);
    exit;
}
$event = json_decode($raw, true); // ex. invoice.paid → GET /invoices/{resource_id}
http_response_code(204);
import express from 'express';
import { createHmac, timingSafeEqual } from 'node:crypto';
import { verifyWebhookSignature } from './clientzen.mjs';

const app = express();
app.post('/webhooks/clientzen', express.raw({ type: 'application/json' }), (req, res) => {
  const ok = verifyWebhookSignature(req.body.toString('utf8'), req.get('X-ClientZen-Signature'),
    process.env.CLIENTZEN_WEBHOOK_SECRET, { createHmac, timingSafeEqual });
  if (!ok) return res.sendStatus(401);
  const event = JSON.parse(req.body);
  res.sendStatus(204);
});
import os
from flask import Flask, request, abort
from clientzen import verify_webhook_signature

app = Flask(__name__)

@app.post("/webhooks/clientzen")
def clientzen_webhook():
    if not verify_webhook_signature(request.get_data(), request.headers.get("X-ClientZen-Signature", ""),
                                    os.environ["CLIENTZEN_WEBHOOK_SECRET"]):
        abort(401)
    event = request.get_json()
    return "", 204

SDK

Un fichier, aucune dépendance. Chaque SDK gère le jeton, l'idempotence, la pagination (each), le téléchargement des PDF et convertit les erreurs RFC 9457 en exceptions.

PHP

PHP 8.1+, extension curl

ClientZenClient.php

JavaScript

Node 18+, Deno, Bun

clientzen.mjs

Python

Python 3.9+, bibliothèque standard

clientzen.py

Exemple : créer et émettre une facture (JavaScript)

const { data: invoice } = await cz.post('/invoices', {
  legal_entity_id: 'ID_SOCIETE', customer_id: 'ID_CLIENT', operation_category: 'services',
  title: 'Maintenance mensuelle', issue: true,
  lines: [{ kind: 'line', description: 'Maintenance', quantity: '1', unit_price: '90.00', tax_code: 'FR_STANDARD' }],
}, { idempotencyKey: 'commande-2026-0042' });

console.log(invoice.number, invoice.total_incl_cents / 100, '€ TTC');
const pdf = await cz.download(`/invoices/${invoice.id}/pdf`);

Schémas

Les champs marqués * sont toujours présents (réponses) ou obligatoires (requêtes).

WebhookSubscription 6 champs
ChampTypeDescription
idstring
target_urlstring (uri)
eventsarray<string>
labelstring | null
is_activeboolean
created_atstring (date-time) | null
WebhookSubscriptionCreated 7 champs
ChampTypeDescription
idstring
target_urlstring (uri)
eventsarray<string>
labelstring | null
is_activeboolean
created_atstring (date-time) | null
secretstring ex. czs_…
Problem 8 champs
ChampTypeDescription
typestring ex. https://clientzen.fr/developpeurs#erreurs
titlestring ex. Données invalides
statusinteger ex. 422
detailstring ex. Le champ name est obligatoire.
request_idstring ex. 7f3c2a9e-5b1d-4c8e-9a2f-0e1d2c3b4a59
errorsobjectChamps invalides (422 uniquement)
required_scopestringPortée manquante (403 d'un jeton sans la portée) ex. customers:write
required_permissionstringDroit que le membre auteur du jeton n'a pas (403) ex. chat.reply
Meta 4 champs
ChampTypeDescription
pageinteger ex. 1
per_pageinteger ex. 25
totalinteger ex. 42
last_pageinteger ex. 2
Me 9 champs
ChampTypeDescription
tenantobject
tenant.idstring ex. 01k2m7q9x4v8b3n6c1d5f0g2h7
tenant.namestring ex. Atelier Martin
tenant.slugstring ex. atelier-martin
tokenobject
token.idstring ex. 01k2m8a1b2c3d4e5f6g7h8j9k0
token.namestring ex. Site vitrine
token.scopesarray<string>
token.expires_atstring (date-time) | null
Customer 16 champs
ChampTypeDescription
idstring ex. 01k2m9c3d4e5f6g7h8j9k0m1n2
typestringValeurs : company, individual
namestring ex. Boulangerie Petit
sirenstring | null ex. 552100554
vat_numberstring | null ex. FR40552100554
vat_statusstring | nullValeurs : taxable, non_taxable, public, null
emailstring | null ex. contact@boulangerie-petit.fr
phonestring | null ex. +33 3 88 00 00 00
websitestring | null ex. https://boulangerie-petit.fr
is_prospectboolean ex. false
is_supplierboolean ex. false
tagsarray<string>
custom_fieldsobject
addressesarray<object> | null
created_atstring (date-time)
updated_atstring (date-time)
Address 7 champs
ChampTypeDescription
kindstring ex. billing
line1string ex. 12 rue des Fours
line2string | null
postal_codestring ex. 67000
citystring ex. Strasbourg
countrystring ex. FR
is_defaultboolean ex. true
CustomerInput 18 champs
ChampTypeDescription
type*stringValeurs : company, individual
name*string ex. Boulangerie Petit
sirenstring9 chiffres, clé de Luhn vérifiée ex. 552100554
vat_numberstring ex. FR40552100554
vat_statusstringValeurs : taxable, non_taxable, public
emailstring (email) ex. contact@boulangerie-petit.fr
phonestring ex. +33 3 88 00 00 00
websitestring (uri) ex. https://boulangerie-petit.fr
is_prospectboolean ex. true
is_supplierboolean ex. false
tagsarray<string>
notesstring ex. Demande reçue par le formulaire du site.
addressobject
address.line1string ex. 12 rue des Fours
address.line2string
address.postal_codestring ex. 67000
address.citystring ex. Strasbourg
address.countrystring ex. FR
Product 10 champs
ChampTypeDescription
idstring ex. 01k2mb4f5g6h7j8k9m0n1p2q3r
skustring | null ex. POSE-01
namestring ex. Pose de parquet
descriptionstring | null ex. Pose flottante, fournitures non comprises.
typestringValeurs : goods, service
unit_codestring ex. m2
unit_pricestringHT, 6 décimales max ex. 45.000000
currencystring ex. EUR
tax_codestring | null ex. FR_STANDARD
updated_atstring (date-time)
CustomerPrice 10 champs
ChampTypeDescription
customer_idstring
product_idstring
quantitystring ex. 10
catalog_unit_pricestringPrix HT du catalogue ex. 49
unit_pricestringPrix unitaire HT à porter sur la ligne (le prix du catalogue, sauf prix négocié supérieur) ex. 49
net_unit_pricestringPrix unitaire HT après remise ex. 41.65
discount_percent_e2integerRemise en centièmes de % ex. 1500
discount_amount_centsintegerRemise de ligne en centimes, quand le prix négocié ne tombe pas juste en % ex. 0
rule_idstring | null
labelstring | null ex. Tarif Revendeurs : −15 %
Quote 12 champs
ChampTypeDescription
idstring ex. 01k2mc5g6h7j8k9m0n1p2q3r4s
numberstring | null ex. DEV-2026-0042
statusstringValeurs : draft, pending_approval, sent, viewed, accepted, refused, expired, superseded
customer_idstring
customer_namestring | null ex. Boulangerie Petit
titlestring | null ex. Rénovation de la boutique
total_excl_centsinteger ex. 250000
total_tax_centsinteger ex. 50000
total_incl_centsinteger ex. 300000
currencystring ex. EUR
valid_untilstring (date) | null ex. 2026-10-28
created_atstring (date-time)
Invoice 24 champs
ChampTypeDescription
idstring ex. 01k2md6h7j8k9m0n1p2q3r4s5t
numberstring | nullnull tant que le brouillon n'est pas émis ex. F-2026-00128
typestringValeurs : invoice, deposit, balance, credit_note, proforma
statusstringValeurs : draft, issued
payment_statusstringValeurs : unpaid, partial, paid, overpaid
transmission_statusstring | null ex. none
customer_idstring
customer_namestring | null ex. Boulangerie Petit
legal_entity_idstring
titlestring | null ex. Rénovation de la boutique — solde
issue_datestring (date) | null ex. 2026-09-28
due_datestring (date) | null ex. 2026-10-28
currencystring ex. EUR
total_excl_centsinteger ex. 250000
total_tax_centsinteger ex. 50000
total_incl_centsinteger ex. 300000
amount_due_centsinteger ex. 300000
amount_paid_centsinteger ex. 0
remaining_centsinteger ex. 300000
vat_treatmentstring | null ex. domestic
pdf_sha256string | null ex. 9f2c…
payment_urlstring (uri) | nullFacture en ligne (consultation et paiement par vos moyens de paiement) : stable, non devinable, le même lien que dans les e-mails. Seulement sur le détail d'une facture émise (ni avoir ni pro forma), espace client actif. ex. https://clientzen.fr/portail/01k2…/factures/Xy9…
created_atstring (date-time)
snapshotobjectContenu figé à l'émission (détail uniquement)
EmailResult 3 champs
ChampTypeDescription
sentboolean
tostring | null ex. compta@client.fr
errorstring | null ex. Adresse e-mail du destinataire absente ou invalide.
InvoiceInput 14 champs
ChampTypeDescription
legal_entity_idstringSociété émettrice (GET /companies). Absent : la société par défaut de l'espace. ex. 01k2m7r0y5w9c4p7d2f6g1h3j8
customer_id*string ex. 01k2m9c3d4e5f6g7h8j9k0m1n2
titlestring ex. Rénovation de la boutique
operation_category*stringValeurs : goods, services, mixed ex. services
vat_treatmentstringDoit être admissible pour le client (proposition serveur sinon) ex. domestic
service_date_fromstring (date)
service_date_tostring (date)
due_datestring (date) ex. 2026-10-28
payment_termsstring ex. Paiement à 30 jours.
notesstring
issuebooleantrue = émission immédiate (numéro définitif, irréversible) ex. false
sendbooleanAvec `issue: true` : envoyer aussitôt la facture au client (PDF et lien de paiement). Absent : le réglage de l'espace « Envoyer au client chaque facture émise par l'API ». Un échec d'envoi n'annule pas l'émission (voir `meta.email`). ex. true
apply_customer_pricesbooleantrue = les lignes portant un `product_id` reçoivent le tarif négocié du client (prix catalogue + remise), à la place du prix et de la remise envoyés ex. false
lines*array<object>
Payment 10 champs
ChampTypeDescription
idstring ex. 01k2mf7j8k9m0n1p2q3r4s5t6v
customer_idstring | null
methodstring ex. transfer
amount_centsinteger ex. 300000
currencystring ex. EUR
received_atstring (date) ex. 2026-10-02
referencestring | null ex. VIR F-2026-00128
statusstringValeurs : recorded, pending, rejected, cancelled
unallocated_centsinteger ex. 0
allocationsarray<object>
Service 15 champs
ChampTypeDescription
idstring
slugstring ex. consultation
namestring ex. Consultation (1 h)
categorystring | null ex. Conseil
descriptionstring | null
duration_mininteger ex. 60
buffer_before_mininteger ex. 0
buffer_after_mininteger ex. 15
capacityinteger ex. 1
price_excl_centsinteger | null ex. 8000
tax_codestring | null ex. FR_STANDARD
deposit_excl_centsinteger ex. 4000
location_modestring ex. onsite
is_publicboolean ex. true
resourcesarray<object>
Slot 5 champs
ChampTypeDescription
starts_atstring (date-time) ex. 2026-10-05T09:00:00+02:00
ends_atstring (date-time) ex. 2026-10-05T10:00:00+02:00
resource_idstring
resource_namestring ex. Salle 1
remaininginteger ex. 1
Appointment 27 champs
ChampTypeDescription
idstring
referencestring ex. RDV-A1B2C3D4
statusstringValeurs : pending_payment, confirmed, cancelled_customer, cancelled_staff, no_show, done
sourcestring ex. api
serviceobject
service.idstring
service.namestring ex. Consultation (1 h)
resourceobject
resource.idstring
resource.namestring ex. Salle 1
customerobject
customer.idstring | null
customer.namestring ex. Marie Durand
customer.emailstring | null ex. marie.durand@exemple.fr
customer.phonestring | null
starts_atstring (date-time)
ends_atstring (date-time)
timezonestring ex. Europe/Paris
seatsinteger ex. 1
messagestring | null
deposit_invoice_idstring | null
final_invoice_idstring | null
hold_expires_atstring (date-time) | null
cancel_reasonstring | null
created_atstring (date-time)
manage_urlstringPage client (reprogrammer, annuler, ICS) — à la création seulement
payment_urlstring | nullLien de paiement de l'acompte — à la création seulement
AppointmentInput 13 champs
ChampTypeDescription
service_id*string
resource_idstring | nullAbsent = première ressource disponible
starts_at*string (date-time) ex. 2026-10-05T09:00:00+02:00
seatsinteger ex. 1
customer*object
customer.idstring
customer.namestring ex. Marie Durand
customer.emailstring ex. marie.durand@exemple.fr
customer.phonestring
customer.addressobjectRequise si un acompte est facturé
messagestring
reminders_consentboolean ex. true
sourcestringValeurs : api, widget, site
KbArticle 10 champs
ChampTypeDescription
idstring
titlestring ex. Horaires d'ouverture
slugstring ex. horaires-d-ouverture
categorystring | null ex. Pratique
source_typestringValeurs : article, faq, web, file
source_urlstring | null
versioninteger ex. 3
published_atstring (date-time) | null
updated_atstring (date-time)
bodystringTexte Markdown — sur le détail seulement
ChatMessage 7 champs
ChampTypeDescription
idstring
authorstringValeurs : visitor, bot, agent, system
user_idstring | nullMembre auteur (réponse de l'équipe)
bodystring ex. Êtes-vous ouverts le samedi ?
abstainedbooleanL'assistant n'a pas trouvé de réponse fondée
sourcesarray<object>
created_atstring (date-time)
ChatConversation 24 champs
ChampTypeDescription
idstring
statusstringbot : l'assistant répond ; waiting : le visiteur attend l'équipe ; human : l'équipe a répondu ; closed : close
widget_idstring
widget_namestring ex. Site principal
origin_hoststring | null ex. www.exemple.fr
page_urlstring | null
visitorobjectCoordonnées données par le visiteur avec son accord ; vides sinon
visitor.namestring | null
visitor.emailstring | null
visitor.phonestring | null
visitor.consent_atstring (date-time) | null
customer_idstring | null
assigned_user_idstring | null
ratinginteger | null
visitor_messagesinteger
bot_answersinteger
abstentionsinteger
escalated_atstring (date-time) | null
first_agent_reply_atstring (date-time) | null
closed_atstring (date-time) | null
last_message_atstring (date-time) | null
created_atstring (date-time)
updated_atstring (date-time)
messagesarray<object>Sur le détail seulement
WebhookEvent 5 champs
ChampTypeDescription
eventstring ex. invoice.paid
resource_typestringType de la ressource (nom de classe) ex. Invoice
resource_idstring ex. 01k2md6h7j8k9m0n1p2q3r4s5t
occurred_atstring (date-time) ex. 2026-10-02T08:14:03+00:00
correlation_idstring | null

Versions et limites

v1 : un champ existant ne change ni de sens ni de type ; des champs et des événements peuvent s'ajouter — ignorez ce que vous ne connaissez pas. Une rupture ferait l'objet d'une v2, annoncée sur cette page et dans les espaces.

Ce que l'API ne fait pas aujourd'hui. Devis, catalogue et encaissements sont en lecture seule : on ne crée ni devis ni encaissement par l'API. Pas de flux OAuth : l'accès se fait par jeton d'espace. La spécification est vérifiée par un test contre les réponses réelles de l'API à chaque livraison.