KAPACITY · API v1

Documentation de l'API

Vos contacts, vos tables, vos rendez-vous, vos formulaires, vos contrats et vos ventes, lisibles et modifiables par vos outils. 28 adresses, 11 événements de webhook, et une clé qui se crée en deux clics dans Intégrations.

Toute la documentation, à coller dans votre IA. Votre clé n'en fait jamais partie.

Commencer

Une clé porte sur UN espace. Elle se crée dans Kapacity, page Intégrations, onglet API, par le propriétaire ou un administrateur. Elle n'est lisible qu'une fois : copiez-la tout de suite.

GET https://kapacity.app/api/v1/w/{workspace_id}/me
Authorization: Bearer kap_live_…

Si cet appel répond 200, votre clé et votre identifiant d'espace sont bons : c'est le premier appel à faire quand on branche une intégration.

Deux portées, et rien d'autre

  • Lecture seule : toutes les lectures. Une écriture reçoit 403.
  • Lecture et écriture : les lectures et les écritures.

Donnez à chaque outil la portée dont il a besoin : un tableau de bord qui ne fait que lire n'a aucune raison de pouvoir supprimer un contact.

Quotas

Trois limites, comptées sur l'espace, toutes méthodes confondues : par mois (selon le plan), par seconde et par heure. Chaque réponse porte les en-têtes X-Kapacity-Quota-Limit, X-Kapacity-Quota-Remaining et X-Kapacity-Quota-Reset. Un dépassement répond 429, avec Retry-After en secondes : attendez ce délai, puis réessayez.

Lire un exemple de requête

Chaque adresse ci-dessous montre la requête à envoyer, puis la réponse que Kapacity rend. Par exemple, pour envoyer un contrat :

POST https://kapacity.app/api/v1/w/{workspace_id}/contracts
Authorization: Bearer kap_live_…
Content-Type: application/json
Idempotency-Key: contrat-jean-dupont-2026-09-23

{
  "modele_id": "3e5a7c9b-2d4f-4e6a-8b1c-9d0e2f4a6b8c",
  "contact_id": "7fc74cfb-c67a-43d2-868f-f4a8ab028475",
  "montant_cents": 450000
}
  • La PREMIÈRE ligne donne la méthode puis l'adresse complète. GET lit, POST crée ou envoie, PATCH modifie, DELETE supprime.
  • Les lignes suivantes sont les EN-TÊTES. « Authorization: Bearer <votre clé> » est toujours là ; « Content-Type: application/json » l'accompagne dès qu'il y a un body.
  • Après la ligne vide vient le BODY JSON : c'est là que se mettent les valeurs marquées « dans le body JSON » dans le tableau des paramètres. Une requête GET n'a pas de body : ses options vont après le « ? » de l'adresse.
  • Les identifiants des exemples (les longues suites de chiffres et de lettres) sont des EXEMPLES : remplacez-les par les vôtres, que les autres adresses vous donnent. « {workspace_id} » est l'identifiant de votre espace, affiché dans Intégrations, onglet API.
  • Les montants sont en CENTIMES : 450000 = 4 500 €.
  • Dans n8n : un nœud « HTTP Request », la méthode et l'adresse de la première ligne, l'en-tête Authorization (ou « Header Auth »), et pour un POST ou un PATCH, « Send Body » en JSON avec le body de l'exemple.

Pas à pas

Les parcours les plus demandés, appel par appel, dans l'ordre.

Envoyer un contrat à un contact

Un deal est gagné dans votre outil : le contrat part au client, au bon montant, et vous récupérez son lien de signature.

  1. 1Trouver le modèle de contratGET/contract-templates

    Listez vos modèles et repérez celui à envoyer. À faire une seule fois : un modèle garde son identifiant, vous pouvez le recopier dans votre scénario.

    GET https://kapacity.app/api/v1/w/{workspace_id}/contract-templates
    Authorization: Bearer kap_live_…

    À GARDER Le « id » du modèle : c'est votre « modele_id ».

  2. 2Trouver le contactGET/tables/leads/records

    Cherchez le contact par son e-mail dans Mon Pipe (« leads ») ou dans Setting (« leads_setting »). S'il n'existe pas encore, créez-le avec POST /tables/leads/records : sa réponse donne aussi son identifiant.

    GET https://kapacity.app/api/v1/w/{workspace_id}/tables/leads/records?filtre=Email:eq:jean@acme.fr
    Authorization: Bearer kap_live_…

    À GARDER Le « id » de la première ligne de « lignes » : c'est votre « contact_id ». Si « lignes » est vide, aucun contact n'a cet e-mail.

  3. 3Envoyer le contratPOST/contracts

    Envoyez le modèle au contact, avec le montant en CENTIMES (450000 = 4 500 €). Le contrat est rempli avec les informations du contact, et l'e-mail rattaché au modèle part avec le lien pour signer.

    POST https://kapacity.app/api/v1/w/{workspace_id}/contracts
    Authorization: Bearer kap_live_…
    Content-Type: application/json
    Idempotency-Key: contrat-jean-dupont-2026-09-23
    
    {
      "modele_id": "3e5a7c9b-2d4f-4e6a-8b1c-9d0e2f4a6b8c",
      "contact_id": "7fc74cfb-c67a-43d2-868f-f4a8ab028475",
      "montant_cents": 450000
    }

    À GARDER « contrat.lien_signature » (la page où le contact signe) et « email.envoye » (true si l'e-mail est parti ; sinon « email.raison » dit pourquoi).

Être prévenu quand un contrat est signé

Déclencher la suite (facture, accès à la formation, message à l'équipe) au moment où le client signe, sans interroger l'API en boucle.

  1. 1Donner à Kapacity l'adresse à appelerPOST/webhooks

    Abonnez une adresse à l'événement « contrat.signe ». Dans n8n, c'est l'adresse de production d'un nœud Webhook. À faire une seule fois.

    POST https://kapacity.app/api/v1/w/{workspace_id}/webhooks
    Authorization: Bearer kap_live_…
    Content-Type: application/json
    
    {
      "nom": "Relance après signature",
      "url": "https://n8n.exemple.fr/webhook/abc",
      "evenements": [
        "contrat.signe"
      ],
      "entete": {
        "nom": "X-Api-Key",
        "valeur": "une-valeur-que-vous-choisissez"
      },
      "ignorer_api": true
    }

    À GARDER Le « secret » : il n'est montré qu'une fois, et sert à vérifier que l'appel vient bien de Kapacity.

  2. 2Recevoir la signatureWEBHOOKcontrat.signe

    À chaque signature, Kapacity appelle votre adresse avec ce corps. « objet » est le contrat signé, « contact » est le contact qui l'a signé.

    {
      "evenement": "contrat.signe",
      "emis_le": "2026-09-22T14:20:00.000Z",
      "provenance": "visiteur",
      "table": "contracts",
      "objet": {
        "id": "c1…",
        "champs": {
          "lead_id": "l1…",
          "title": "Accompagnement 6 mois",
          "status": "signe",
          "value_cents": 500000,
          "signer_email": "jean@acme.test",
          "signed_at": "2026-09-22T14:20:00.000Z"
        }
      },
      "contact": {
        "id": "l1…",
        "champs": {
          "first_name": "Jean",
          "last_name": "Dupont",
          "email": "jean@acme.test",
          "stage": "signe"
        }
      }
    }

    À GARDER « objet.id » (le contrat), « objet.champs.value_cents » (le montant, en centimes) et « contact.champs.email ».

Les adresses

Toutes commencent par https://kapacity.app/api/v1/w/{workspace_id}. Cliquez sur une adresse pour voir ses paramètres, un exemple de requête et un exemple de réponse.

Votre espace

GET/meVotre espace, votre plan et l'état de votre quota.

Le premier appel à faire quand on branche une intégration : il confirme que la clé fonctionne, sur quel espace elle porte, et combien d'appels il reste ce mois-ci.

CLÉ : LECTURE SEULE SUFFIT

paramètreoù le mettreobligatoiredescription
workspace_iddans l'adresseouiL'identifiant de votre espace, affiché dans Intégrations, onglet API. (ex. b7f1c0de-0000-4000-8000-000000000000)
  • Aucun paramètre : si cet appel répond 200, votre clé et votre identifiant d'espace sont bons.
  • Les en-têtes X-Kapacity-Quota-* accompagnent CHAQUE réponse de l'API, pas seulement celle-ci.
REQUÊTE
GET https://kapacity.app/api/v1/w/{workspace_id}/me
Authorization: Bearer kap_live_…
RÉPONSE
{
  "espace": {
    "id": "b7f1c0de-0000-4000-8000-000000000000",
    "nom": "Top Closer Academy",
    "plan": "free"
  },
  "cle": {
    "nom": "n8n",
    "portee": "lecture_ecriture",
    "proprietaire": true
  },
  "quota": {
    "mois": 12,
    "limite_mois": 10000,
    "restant_mois": 9988,
    "reset_mois": "2026-10-01T00:00:00.000Z"
  }
}

CRM : tables et lignes

GET/tablesLes tables du CRM de cet espace, et la base de chacune.

Le deuxième appel d'une intégration, après /me : il donne les identifiants de table dont tout le reste a besoin.

CLÉ : LECTURE SEULE SUFFIT

  • DEUX SORTES DE TABLES. Les tables « native » sont vos pipelines : « leads » = Mon Pipe, « leads_setting » = Setting. Leur identifiant est ce mot, pas un UUID.
  • Les tables « personnalisee » sont celles que votre équipe a créées (Ventes, Setting, Ressources…), identifiées par un UUID.
  • Les deux s'utilisent EXACTEMENT de la même façon par la suite : mêmes adresses, mêmes réponses.
  • On ne crée ni table ni colonne par API : cette partie du CRM se lit, elle ne se façonne pas.
REQUÊTE
GET https://kapacity.app/api/v1/w/{workspace_id}/tables
Authorization: Bearer kap_live_…
RÉPONSE
{
  "tables": [
    {
      "id": "leads",
      "nom": "Leads",
      "type": "native",
      "base": "pipe",
      "base_libelle": "Mon Pipe",
      "icone": null,
      "lignes": 42
    },
    {
      "id": "leads_setting",
      "nom": "Leads Setting",
      "type": "native",
      "base": "setting",
      "base_libelle": "Setting",
      "icone": null,
      "lignes": 8
    },
    {
      "id": "6b0c…",
      "nom": "Ventes",
      "type": "personnalisee",
      "base": "ventes",
      "base_libelle": "Mes Ventes",
      "icone": "card",
      "creee_le": "2026-09-01T09:12:00.000Z"
    }
  ],
  "bases": [
    {
      "id": "pipe",
      "libelle": "Mon Pipe"
    }
  ]
}
GET/tables/{id}Les colonnes d'une table : identifiant, nom, type, choix possibles.

CLÉ : LECTURE SEULE SUFFIT

paramètreoù le mettreobligatoiredescription
iddans l'adresseouiL'identifiant de la table, rendu par GET /tables : un UUID, ou « leads » / « leads_setting » pour vos pipelines.
  • L'erreur numéro un d'une intégration : dans Kapacity, une colonne est identifiée par un UUID, pas par son nom.
  • On PEUT écrire par le nom (l'API le résout, sans tenir compte des accents, de la casse ni de la ponctuation), mais un outil sûr de lui relève d'abord ces identifiants.
  • Une colonne « lecture_seule » (formule, cumul, lookup) est refusée en écriture, et l'API dit pourquoi.
REQUÊTE
GET https://kapacity.app/api/v1/w/{workspace_id}/tables/leads
Authorization: Bearer kap_live_…
RÉPONSE
{
  "table": {
    "id": "6b0c…",
    "nom": "Mes Ventes",
    "base": "ventes",
    "icone": "card",
    "lignes": 128
  },
  "colonnes": [
    {
      "id": "f1a2…",
      "nom": "Client",
      "type": "texte",
      "lecture_seule": false
    },
    {
      "id": "f3b4…",
      "nom": "Montant",
      "type": "monetaire",
      "lecture_seule": false
    },
    {
      "id": "f5c6…",
      "nom": "Statut",
      "type": "selection",
      "lecture_seule": false,
      "options": [
        {
          "id": "o1",
          "libelle": "Signé"
        }
      ]
    }
  ]
}
GET/tables/{id}/recordsLes lignes d'une table, page par page.

CLÉ : LECTURE SEULE SUFFIT

paramètreoù le mettreobligatoiredescription
iddans l'adresseouiL'identifiant de la table (UUID, ou « leads » / « leads_setting »).
limiteaprès le « ? » de l'adressenonDe 1 à 200. 50 par défaut. (ex. 100)
curseuraprès le « ? » de l'adressenonLe « curseur_suivant » de la réponse précédente.
modifie_depuisaprès le « ? » de l'adressenonNe rend que les lignes modifiées depuis cette date (ISO 8601). (ex. 2026-09-01T00:00:00Z)
filtreaprès le « ? » de l'adressenonCherche une ligne : « colonne:operateur:valeur ». Répétable, les conditions se cumulent en ET. Opérateurs : eq, ne, contient, commence, gt, gte, lt, lte, vide, non_vide. L'opérateur est facultatif et vaut eq. (ex. Email:eq:jean@acme.fr)
rechercheaprès le « ? » de l'adressenonCherche ce texte dans toutes les colonnes de texte de la table (et, pour un contact, son nom, son e-mail, sa société et son téléphone). (ex. Dupont)
champaprès le « ? » de l'adressenonForme simple, équivalente à filtre=<champ>:eq:<vaut>. (ex. Statut)
vautaprès le « ? » de l'adressenonLa valeur exacte attendue par « champ ».
colonnesaprès le « ? » de l'adressenonAjoute le catalogue des colonnes à la réponse. Absent par défaut : il est long, et GET /tables/{id} le donne une fois pour toutes. (ex. 1)
formataprès le « ? » de l'adressenon« lisible » pour des valeurs prêtes à lire : un montant en EUROS au lieu de centimes, une sélection par son LIBELLÉ, un membre par son NOM. Vaut aussi en écriture (un montant s'écrit alors en euros) et dans un filtre (montant en euros, sélection par son libellé). Absent : les valeurs brutes, comme avant. (ex. lisible)
  • Les lignes sortent de la plus ANCIENNE à la plus récente : c'est ce qui rend une synchronisation reprenable.
  • Pagination par CURSEUR, jamais par numéro de page : une ligne créée entre deux appels ne fait sauter personne.
  • Tant que « curseur_suivant » n'est pas null, il reste des lignes.
  • Les valeurs sont BRUTES : un montant est en CENTIMES, une sélection est l'identifiant de son option. Avec « format=lisible » : euros, libellés et noms des membres.
  • CHERCHER UNE LIGNE : filtre=Email:eq:jean@acme.fr. Plusieurs « filtre » se cumulent en ET. Un montant se compare en CENTIMES (filtre=Montant:gt:100000).
  • Pour une SÉLECTION, filtrez par l'identifiant de l'option (rendu par GET /tables/{id}). Avec « format=lisible », par son libellé, et un montant en euros (filtre=Montant:gt:1000).
  • Pour chercher sans savoir dans quelle colonne : recherche=Dupont.
  • Le nom d'une colonne se donne comme on veut : « E-mail », « Email » et « email » désignent la même. Accents, casse et ponctuation sont ignorés.
REQUÊTE
GET https://kapacity.app/api/v1/w/{workspace_id}/tables/leads/records?filtre=Email:eq:jean@acme.fr
Authorization: Bearer kap_live_…
RÉPONSE
{
  "lignes": [
    {
      "id": "a1b2…",
      "cree_le": "2026-09-02T10:00:00.000Z",
      "modifie_le": "2026-09-20T14:31:00.000Z",
      "champs": {
        "f1a2…": "Acme",
        "f3b4…": 350000
      }
    }
  ],
  "curseur_suivant": "MjAyNi0wOS0wMlQxMDowMDowMC4wMDBafGExYjI"
}
POST/tables/{id}/recordsCrée une ligne dans une table.

CLÉ : LECTURE ET ÉCRITURE

paramètreoù le mettreobligatoiredescription
iddans l'adresseouiL'identifiant de la table (UUID, ou « leads » / « leads_setting »).
champsdans le body JSONouiLes valeurs, rangées par NOM ou par identifiant de colonne. (ex. { "Client": "Acme", "Montant": 350000 })
automatisationsaprès le « ? » de l'adressenonfalse pour écrire SANS déclencher les automatisations. Le garde-fou anti-boucle à utiliser pour un import en masse. (ex. false)
formataprès le « ? » de l'adressenon« lisible » pour des valeurs prêtes à lire : un montant en EUROS au lieu de centimes, une sélection par son LIBELLÉ, un membre par son NOM. Vaut aussi en écriture (un montant s'écrit alors en euros) et dans un filtre (montant en euros, sélection par son libellé). Absent : les valeurs brutes, comme avant. (ex. lisible)
  • Une valeur invalide est IGNORÉE et expliquée dans « ignores », jamais convertie en effacement.
  • Si AUCUNE valeur n'a pu être écrite, l'appel est refusé en 422 plutôt que de créer une ligne vide.
  • Une création déclenche les automatisations « Quand un enregistrement est créé », comme une saisie à la main.
  • Dans « leads » ou « leads_setting », un contact a besoin d'un « Prénom » ou d'un « Nom » : sans l'un ni l'autre, l'appel est refusé plutôt que de créer un contact « Sans nom ».
REQUÊTE
POST https://kapacity.app/api/v1/w/{workspace_id}/tables/leads/records
Authorization: Bearer kap_live_…
Content-Type: application/json

{
  "champs": {
    "Prénom": "Jean",
    "Nom": "Dupont",
    "Email": "jean@acme.fr"
  }
}
RÉPONSE
{
  "ligne": {
    "id": "a1b2…",
    "cree_le": "2026-09-22T10:00:00.000Z",
    "modifie_le": null,
    "champs": {
      "f1a2…": "Acme"
    }
  }
}
POST/tables/{id}/records/bulkCréer jusqu'à 100 lignes en un seul appel.

Sans lui, importer 500 contacts coûte 500 appels, soit 5 % du quota mensuel d'un espace gratuit pour un seul import. Avec lui, cinq appels.

CLÉ : LECTURE ET ÉCRITURE

paramètreoù le mettreobligatoiredescription
iddans l'adresseouiL'identifiant de la table, ou « leads » / « leads_setting ».
lignesdans le body JSONouiJusqu'à 100 objets { champs: { … } }. (ex. [{ "champs": { "Prénom": "Jean" } }])
automatisationsaprès le « ? » de l'adressenon1 pour déclencher les automatisations. COUPÉ par défaut ici.
formataprès le « ? » de l'adressenon« lisible » pour des valeurs prêtes à lire : un montant en EUROS au lieu de centimes, une sélection par son LIBELLÉ, un membre par son NOM. Vaut aussi en écriture (un montant s'écrit alors en euros) et dans un filtre (montant en euros, sélection par son libellé). Absent : les valeurs brutes, comme avant. (ex. lisible)
Idempotency-Keyen en-têtenonRejouer le même appel rend la réponse du premier au lieu de créer des doublons.
  • Une ligne refusée n'arrête PAS les autres : chaque entrée rend son sort dans « resultats », avec son rang. Un import de 500 lignes qui s'arrête à la 37e est un import à refaire à la main.
  • Les automatisations sont COUPÉES par défaut, contrairement à l'écriture à l'unité : 100 lignes importées, ce serait 100 séquences d'e-mails envoyées à des gens qui viennent d'être importés. « ?automatisations=1 » les rallume.
  • 201 dès qu'une ligne est passée, 422 si tout a été refusé : on peut aiguiller sur le code sans lire le détail.
  • Posez un « Idempotency-Key » : si le réseau coupe avant la réponse, le rejeu rend le résultat du premier appel. La réponse rejouée porte l'en-tête X-Kapacity-Idempotent-Replay.
REQUÊTE
POST https://kapacity.app/api/v1/w/{workspace_id}/tables/leads/records/bulk
Authorization: Bearer kap_live_…
Content-Type: application/json
Idempotency-Key: import-salon-2026-lot-1

{
  "lignes": [
    {
      "champs": {
        "Prénom": "Jean",
        "Nom": "Dupont",
        "Email": "jean@acme.fr"
      }
    },
    {
      "champs": {
        "Prénom": "Léa",
        "Nom": "Martin",
        "Email": "lea@acme.fr"
      }
    }
  ]
}
RÉPONSE
{
  "creees": 2,
  "refusees": 1,
  "automatisations": false,
  "resultats": [
    {
      "i": 0,
      "ok": true,
      "id": "l1…"
    },
    {
      "i": 1,
      "ok": true,
      "id": "l2…"
    },
    {
      "i": 2,
      "ok": false,
      "erreur": "Aucune valeur n'a pu être écrite."
    }
  ],
  "lignes": []
}
GET/records/{id}Une ligne et les colonnes de sa table.

CLÉ : LECTURE SEULE SUFFIT

paramètreoù le mettreobligatoiredescription
iddans l'adresseouiL'identifiant de la ligne.
formataprès le « ? » de l'adressenon« lisible » pour des valeurs prêtes à lire : un montant en EUROS au lieu de centimes, une sélection par son LIBELLÉ, un membre par son NOM. Vaut aussi en écriture (un montant s'écrit alors en euros) et dans un filtre (montant en euros, sélection par son libellé). Absent : les valeurs brutes, comme avant. (ex. lisible)
  • La ligne porte sa table : l'adresse n'a pas besoin de la répéter.
REQUÊTE
GET https://kapacity.app/api/v1/w/{workspace_id}/records/7fc74cfb-c67a-43d2-868f-f4a8ab028475
Authorization: Bearer kap_live_…
RÉPONSE
{
  "ligne": {
    "id": "a1b2…",
    "cree_le": "2026-09-02T10:00:00.000Z",
    "modifie_le": null,
    "champs": {
      "f1a2…": "Acme"
    }
  },
  "table": {
    "id": "6b0c…",
    "nom": "Mes Ventes"
  },
  "colonnes": [
    {
      "id": "f1a2…",
      "nom": "Client",
      "type": "texte",
      "lecture_seule": false
    }
  ]
}
PATCH/records/{id}Modifie les valeurs d'une ligne.

CLÉ : LECTURE ET ÉCRITURE

paramètreoù le mettreobligatoiredescription
iddans l'adresseouiL'identifiant de la ligne.
champsdans le body JSONouiSeules les colonnes citées changent, les autres restent.
automatisationsaprès le « ? » de l'adressenonfalse pour ne rien déclencher.
formataprès le « ? » de l'adressenon« lisible » pour des valeurs prêtes à lire : un montant en EUROS au lieu de centimes, une sélection par son LIBELLÉ, un membre par son NOM. Vaut aussi en écriture (un montant s'écrit alors en euros) et dans un filtre (montant en euros, sélection par son libellé). Absent : les valeurs brutes, comme avant. (ex. lisible)
  • Pour VIDER une colonne, envoyez null (ou une chaîne vide, ou une liste vide).
  • Une modification déclenche les automatisations « Quand un enregistrement est modifié ».
REQUÊTE
PATCH https://kapacity.app/api/v1/w/{workspace_id}/records/a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d
Authorization: Bearer kap_live_…
Content-Type: application/json

{
  "champs": {
    "Client": "Acme SAS",
    "Montant": 350000
  }
}
RÉPONSE
{
  "ligne": {
    "id": "a1b2…",
    "cree_le": "2026-09-02T10:00:00.000Z",
    "modifie_le": "2026-09-22T11:00:00.000Z",
    "champs": {
      "f1a2…": "Acme SAS"
    }
  }
}
DELETE/records/{id}Supprime une ligne. Définitif.

CLÉ : LECTURE ET ÉCRITURE

paramètreoù le mettreobligatoiredescription
iddans l'adresseouiL'identifiant de la ligne.
  • Aucune corbeille : la ligne est perdue. Le journal d'activité garde la trace de la suppression et de la clé qui l'a faite.
REQUÊTE
DELETE https://kapacity.app/api/v1/w/{workspace_id}/records/a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d
Authorization: Bearer kap_live_…
RÉPONSE
{
  "supprime": true,
  "id": "a1b2…"
}

Rendez-vous

GET/event-typesLes types de rendez-vous, avec leur lien public de réservation.

CLÉ : LECTURE SEULE SUFFIT

  • Envoyez le « lien_public » à votre prospect : il choisit son créneau, et le rendez-vous, le contact et l'invitation d'agenda se créent tout seuls.
  • Poser un rendez-vous directement par API n'existe pas : le round-robin, les disponibilités et l'anti-double-réservation se décident côté Kapacity.
  • « parts » donne la répartition voulue entre les assignés de même priorité, en pourcentage (somme 100 pour chaque niveau de priorité) : c'est le réglage du type, pas ce qui a été reçu.
  • « priorites » donne le niveau de chaque assigné, de 1 à 5 (5 la plus haute, 3 quand rien n'est réglé) : sur un créneau, les assignés libres du niveau le plus haut reçoivent tous les rendez-vous, et un niveau plus bas ne prend le relais que lorsque tous ceux du dessus sont pris. Vide quand le type tourne par équipe.
  • « repartition » dit comment l'équipe tourne : « tour » (chacun son tour), « charge » (le moins chargé, en suivant les parts) ou « hasard » (tirage au sort pondéré par les parts, « Par % de chance » à l'écran).
  • « attribution_par » vaut « equipes » quand le type tourne par équipe : « equipes » donne alors chaque équipe cochée, sa « part » en pourcentage (somme 100) et ses « membres » actuels, « assignes » tous ces membres, et « parts » (par membre) reste vide. Un rendez-vous part d'abord à une équipe selon sa part, puis à l'un de ses membres libres, à parts égales.
REQUÊTE
GET https://kapacity.app/api/v1/w/{workspace_id}/event-types
Authorization: Bearer kap_live_…
RÉPONSE
{
  "types_de_rendez_vous": [
    {
      "id": "e1f2…",
      "nom": "Appel de qualification",
      "duree_min": 30,
      "lieu": "Google Meet",
      "attribution": "round_robin",
      "attribution_par": "membres",
      "repartition": "charge",
      "repartition_libelle": "Le moins chargé",
      "assignes": [
        "u1…",
        "u2…",
        "u3…"
      ],
      "parts": {
        "u1…": 67,
        "u2…": 33,
        "u3…": 100
      },
      "priorites": {
        "u1…": 5,
        "u2…": 5,
        "u3…": 3
      },
      "equipes": [],
      "actif": true,
      "questions": 3,
      "lien_public": "https://kapacity.app/b/mon-espace/appel-de-qualification"
    }
  ]
}
GET/bookingsLes rendez-vous, du plus ancien au plus récent.

CLÉ : LECTURE SEULE SUFFIT

paramètreoù le mettreobligatoiredescription
depuisaprès le « ? » de l'adressenonRendez-vous commençant après cette date. (ex. 2026-09-22T00:00:00Z)
jusqu_aaprès le « ? » de l'adressenonRendez-vous commençant avant cette date.
statutaprès le « ? » de l'adressenonNe garde que ce statut : confirme, annule ou termine. (ex. confirme)
contactaprès le « ? » de l'adressenonNe garde que les rendez-vous de ce contact (son identifiant).
limiteaprès le « ? » de l'adressenonDe 1 à 200. 50 par défaut.
curseuraprès le « ? » de l'adressenonLe « curseur_suivant » de la réponse précédente.
  • Chaque identifiant vient avec son nom : un scénario n'a jamais à faire un second appel pour savoir qui est « u1… ».
REQUÊTE
GET https://kapacity.app/api/v1/w/{workspace_id}/bookings?depuis=2026-09-22T00:00:00Z&statut=confirme
Authorization: Bearer kap_live_…
RÉPONSE
{
  "rendez_vous": [
    {
      "id": "b1c2…",
      "debut": "2026-09-23T09:00:00.000Z",
      "fin": "2026-09-23T09:30:00.000Z",
      "statut": "confirme",
      "type": {
        "id": "e1f2…",
        "nom": "Appel de qualification"
      },
      "assigne": {
        "id": "u1…",
        "nom": "Camille Royer"
      },
      "contact": {
        "id": "l1…",
        "nom": "Jean Dupont"
      },
      "invite": {
        "nom": "Jean Dupont",
        "email": "jean@acme.fr",
        "telephone": "+33612345678"
      },
      "lien_visio": "https://meet.google.com/abc-defg-hij"
    }
  ],
  "curseur_suivant": null
}

Formulaires

GET/formsLes formulaires de qualification et leur lien public.

CLÉ : LECTURE SEULE SUFFIT

REQUÊTE
GET https://kapacity.app/api/v1/w/{workspace_id}/forms
Authorization: Bearer kap_live_…
RÉPONSE
{
  "formulaires": [
    {
      "id": "f1…",
      "titre": "Candidature",
      "statut": "publie",
      "questions": 12,
      "lien_public": "https://kapacity.app/f/candidature"
    }
  ]
}
GET/forms/{id}/responsesLes réponses d'un formulaire, avec leur score.

CLÉ : LECTURE SEULE SUFFIT

paramètreoù le mettreobligatoiredescription
iddans l'adresseouiL'identifiant du formulaire.
depuisaprès le « ? » de l'adressenonRéponses soumises après cette date.
qualifieaprès le « ? » de l'adressenontrue ou false pour ne garder que les qualifiés ou les non qualifiés. (ex. true)
limiteaprès le « ? » de l'adressenonDe 1 à 200.
curseuraprès le « ? » de l'adressenonLe curseur de la page suivante.
  • Les réponses sortent avec le LIBELLÉ de chaque question, pas son identifiant : elles se lisent sans relire le formulaire.
REQUÊTE
GET https://kapacity.app/api/v1/w/{workspace_id}/forms/f1e2d3c4-b5a6-4978-8695-a4b3c2d1e0f9/responses?qualifie=true
Authorization: Bearer kap_live_…
RÉPONSE
{
  "formulaire": {
    "id": "f1…",
    "titre": "Candidature"
  },
  "reponses": [
    {
      "id": "r1…",
      "soumise_le": "2026-09-21T18:02:00.000Z",
      "score": 86,
      "qualifie": true,
      "complete": true,
      "repondant": {
        "nom": "Jean Dupont",
        "email": "jean@acme.fr"
      },
      "contact": {
        "id": "l1…",
        "nom": "Jean Dupont"
      },
      "reponses": {
        "Votre budget": "Plus de 5 000 €",
        "Votre objectif": "Doubler mon chiffre"
      }
    }
  ],
  "curseur_suivant": null
}

Contrats

GET/contractsLes contrats, leur statut et leurs dates clés.

CLÉ : LECTURE SEULE SUFFIT

paramètreoù le mettreobligatoiredescription
depuisaprès le « ? » de l'adressenonContrats créés après cette date.
statutaprès le « ? » de l'adressenonbrouillon, envoye, vu ou signe. (ex. signe)
contactaprès le « ? » de l'adressenonNe garde que les contrats de ce contact.
limiteaprès le « ? » de l'adressenonDe 1 à 200.
curseuraprès le « ? » de l'adressenonLe curseur de la page suivante.
  • Le corps du contrat n'est pas rendu ici, il est long : GET /contracts/{id} le donne.
  • Les montants sont en CENTIMES.
REQUÊTE
GET https://kapacity.app/api/v1/w/{workspace_id}/contracts?statut=signe
Authorization: Bearer kap_live_…
RÉPONSE
{
  "contrats": [
    {
      "id": "c1…",
      "titre": "Accompagnement 6 mois",
      "statut": "signe",
      "montant_cents": 500000,
      "contact": {
        "id": "l1…",
        "nom": "Jean Dupont"
      },
      "signataire": {
        "nom": "Jean Dupont",
        "email": "jean@acme.fr"
      },
      "envoye_le": "2026-09-19T10:00:00.000Z",
      "signe_le": "2026-09-20T08:14:00.000Z"
    }
  ],
  "curseur_suivant": null
}
GET/contracts/{id}Un contrat, son corps et sa piste d'audit.

CLÉ : LECTURE SEULE SUFFIT

paramètreoù le mettreobligatoiredescription
iddans l'adresseouiL'identifiant du contrat.
  • La piste d'audit est ce qui donne sa valeur probante à la signature : horodatage et adresse IP de chaque étape.
  • Elle est en ajout seul en base : l'API la lit, personne ne la réécrit.
REQUÊTE
GET https://kapacity.app/api/v1/w/{workspace_id}/contracts/b423fdff-34f8-4a04-a66f-68034f0d77fe
Authorization: Bearer kap_live_…
RÉPONSE
{
  "contrat": {
    "id": "c1…",
    "titre": "Accompagnement 6 mois",
    "statut": "signe",
    "montant_cents": 500000,
    "empreinte": "9f2c…"
  },
  "piste_audit": [
    {
      "etape": "cree",
      "libelle": "Contrat créé",
      "le": "2026-09-19T09:58:00.000Z",
      "ip": null
    },
    {
      "etape": "signe",
      "libelle": "Signé",
      "le": "2026-09-20T08:14:00.000Z",
      "ip": "81.…"
    }
  ]
}
GET/contract-templatesLes modèles de contrat, pour savoir lequel envoyer.

C'est d'ici que vient le « modele_id » qu'attend POST /contracts. Chaque modèle dit s'il est utilisable maintenant et quel message e-mail partira avec lui.

CLÉ : LECTURE SEULE SUFFIT

  • Le « id » de chaque modèle est le « modele_id » à passer à POST /contracts.
  • « utilisable » vaut false pour un modèle désactivé ou suspendu par le plan gratuit, et « raison » dit lequel des deux : POST /contracts le refuserait.
  • « message_email » est l'e-mail qui partira au contact avec le lien de signature. S'il vaut null, aucun e-mail ne partira : le contrat sera quand même envoyé, et son lien rendu.
  • « montant_defaut_cents » est le montant réglé sur le modèle, à titre indicatif : POST /contracts vous demande toujours le montant du contrat. En CENTIMES.
REQUÊTE
GET https://kapacity.app/api/v1/w/{workspace_id}/contract-templates
Authorization: Bearer kap_live_…
RÉPONSE
{
  "modeles": [
    {
      "id": "3e5a7c9b-2d4f-4e6a-8b1c-9d0e2f4a6b8c",
      "titre": "Accompagnement 6 mois",
      "montant_defaut_cents": 500000,
      "utilisable": true,
      "raison": null,
      "message_email": {
        "id": "8a6c4e2f-0b9d-4f7b-a5c3-1e9f7d5b3a1c",
        "nom": "Votre contrat à signer"
      },
      "cree_le": "2026-09-10T09:00:00.000Z"
    }
  ]
}
POST/contractsEnvoie un contrat à un contact, au montant que vous indiquez, et rend son lien de signature.

Vous dites QUEL modèle de contrat, à QUEL contact, et pour QUEL montant : les trois vont dans le body JSON. Kapacity fait alors exactement ce que fait le bouton « Envoyer le contrat » de l'application : le contrat est rempli avec les informations du contact (nom, e-mail, société) et le montant, il part à la signature, le contact passe à « Contrat envoyé », et l'e-mail rattaché au modèle lui est envoyé avec le lien pour signer. La réponse vous rend ce lien (« lien_signature »), pour l'envoyer aussi par un autre canal si vous le voulez.

CLÉ : LECTURE ET ÉCRITURE

paramètreoù le mettreobligatoiredescription
modele_iddans le body JSONouiL'identifiant du MODÈLE de contrat à envoyer. Vous le trouvez avec GET /contract-templates (champ « id »).
contact_iddans le body JSONouiL'identifiant du contact à qui l'envoyer, de Mon Pipe ou de Setting. Vous le trouvez avec GET /tables/leads/records?filtre=Email:eq:<son e-mail> (champ « id »), ou dans le webhook « contact.cree ».
montant_centsdans le body JSONouiLe montant du contrat, en CENTIMES : 450000 pour 4 500 €. Il est écrit dans le contrat à la place de {{montant}}. Pour 0 €, écrivez 0. (ex. 450000)
titredans le body JSONnonLe titre du contrat. Absent : « <modèle> · <nom du contact> ».
envoyer_emaildans le body JSONnonfalse pour ne PAS envoyer l'e-mail : le contrat part à la signature, et vous transmettez vous-même le « lien_signature » (WhatsApp, SMS…). true par défaut. (ex. false)
message_email_iddans le body JSONnonUn autre modèle d'e-mail de contrat que celui rattaché au modèle.
automatisationsaprès le « ? » de l'adressenonfalse pour n'en déclencher aucune (ni le webhook « contrat.envoye »).
Idempotency-Keyen en-têtenonRejouer le même appel rend le premier envoi au lieu d'envoyer un second contrat.
  • Le LIEN DE SIGNATURE est dans « contrat.lien_signature ». Il est TOUJOURS rendu, que l'e-mail parte ou non : c'est la page où le contact lit et signe son contrat.
  • Le montant est OBLIGATOIRE et s'écrit en CENTIMES : 450000 = 4 500 €, 99900 = 999 €. Sans lui, l'appel est refusé (422) : un contrat ne part jamais à un prix que vous n'avez pas écrit.
  • « email » dit si l'e-mail est parti : { envoye: true, a: <adresse> }. Sinon { envoye: false, raison } explique pourquoi (pas de message rattaché au modèle, pas d'adresse chez le contact, pas de boîte d'envoi reliée, ou « envoyer_email »: false). Le contrat, lui, est envoyé dans tous les cas.
  • Posez un « Idempotency-Key » (un texte unique par envoi, par exemple l'identifiant du deal) : si le réseau coupe et que votre scénario rejoue l'appel, le contact recevrait sinon DEUX contrats. Avec lui, le second appel rend le premier envoi.
  • Le message part de la boîte du Sales du contact s'il en a relié une, sinon de la boîte de l'espace.
  • Le contrat est FIGÉ à l'envoi : modifier le contact ensuite ne change pas le contrat.
  • La piste d'audit du contrat dit « créé » puis « envoyé » par votre clé, sous son nom.
  • Au plan gratuit, 5 envois de contrat par mois : au-delà, 429 « limite_plan » avec un Retry-After jusqu'au mois suivant.
  • Un modèle désactivé ou suspendu par le plan est refusé en 422 : GET /contract-templates le dit à l'avance.
REQUÊTE
POST https://kapacity.app/api/v1/w/{workspace_id}/contracts
Authorization: Bearer kap_live_…
Content-Type: application/json
Idempotency-Key: contrat-jean-dupont-2026-09-23

{
  "modele_id": "3e5a7c9b-2d4f-4e6a-8b1c-9d0e2f4a6b8c",
  "contact_id": "7fc74cfb-c67a-43d2-868f-f4a8ab028475",
  "montant_cents": 450000
}
RÉPONSE
{
  "contrat": {
    "id": "b423fdff-34f8-4a04-a66f-68034f0d77fe",
    "titre": "Accompagnement 6 mois · Jean Dupont",
    "statut": "envoye",
    "montant_cents": 450000,
    "contact": {
      "id": "7fc74cfb-c67a-43d2-868f-f4a8ab028475",
      "nom": "Jean Dupont",
      "email": "jean@acme.fr"
    },
    "signataire": {
      "nom": "Jean Dupont",
      "email": "jean@acme.fr"
    },
    "lien_signature": "https://kapacity.app/c/ab29cbefa0874ba5b2cc1c0758aa9fcd6f276503a714411391a2e8017d780ca6",
    "cree_le": "2026-09-23T11:50:37.723277+00:00",
    "envoye_le": "2026-09-23T11:50:37.498Z"
  },
  "email": {
    "envoye": true,
    "a": "jean@acme.fr"
  },
  "automatisations": true
}

Ventes et paiements

GET/productsLe catalogue de produits, avec prix et échéanciers.

CLÉ : LECTURE SEULE SUFFIT

  • Prix, acompte et coût sont en CENTIMES.
REQUÊTE
GET https://kapacity.app/api/v1/w/{workspace_id}/products
Authorization: Bearer kap_live_…
RÉPONSE
{
  "produits": [
    {
      "id": "p1…",
      "nom": "Accompagnement 6 mois",
      "prix_cents": 500000,
      "mensualites": 3,
      "actif": true
    }
  ]
}
GET/paymentsLes paiements reçus, tels que le fournisseur les a constatés.

CLÉ : LECTURE SEULE SUFFIT

paramètreoù le mettreobligatoiredescription
depuisaprès le « ? » de l'adressenonPaiements reçus après cette date.
jusqu_aaprès le « ? » de l'adressenonPaiements reçus avant cette date.
statutaprès le « ? » de l'adressenonsucceeded ou failed.
contactaprès le « ? » de l'adressenonNe garde que les paiements de ce contact.
limiteaprès le « ? » de l'adressenonDe 1 à 200.
curseuraprès le « ? » de l'adressenonLe curseur de la page suivante.
  • Lecture seule, et volontairement : un paiement est un fait constaté chez le fournisseur, pas une donnée qu'on saisit.
  • Les montants sont en CENTIMES et HORS TAXES.
REQUÊTE
GET https://kapacity.app/api/v1/w/{workspace_id}/payments?depuis=2026-09-01T00:00:00Z
Authorization: Bearer kap_live_…
RÉPONSE
{
  "paiements": [
    {
      "id": "pay1…",
      "recu_le": "2026-09-20T08:20:00.000Z",
      "montant_cents": 180000,
      "devise": "eur",
      "statut": "succeeded",
      "fournisseur": "whop",
      "payeur": {
        "nom": "Jean Dupont",
        "email": "jean@acme.fr"
      },
      "contact": {
        "id": "l1…",
        "nom": "Jean Dupont"
      },
      "rattache_a_une_vente": true
    }
  ],
  "curseur_suivant": null
}

Équipe

GET/membersLes membres de l'espace et leur rôle.

CLÉ : LECTURE SEULE SUFFIT

  • Une colonne « membre » (Sales, Setter) s'écrit avec l'identifiant rendu ici, ou avec l'e-mail ou le nom exact.
  • « equipes » donne les identifiants des équipes du membre, ceux que « GET /event-types » rend dans « equipes ».
REQUÊTE
GET https://kapacity.app/api/v1/w/{workspace_id}/members
Authorization: Bearer kap_live_…
RÉPONSE
{
  "membres": [
    {
      "id": "u1…",
      "nom": "Camille Royer",
      "email": "camille@…",
      "roles": [
        "closer"
      ],
      "equipes": [
        "t1…"
      ],
      "proprietaire": false
    }
  ]
}

Journal d'activité

GET/activityLe journal d'activité de l'espace : qui a fait quoi, quand.

CLÉ : LECTURE SEULE SUFFIT · RÉSERVÉ AU PROPRIÉTAIRE

paramètreoù le mettreobligatoiredescription
depuisaprès le « ? » de l'adressenonAprès cette date.
domaineaprès le « ? » de l'adressenoncontacts, tables, contrats, booking… (ex. contacts)
quiaprès le « ? » de l'adressenonmember, system, automation, public ou api. (ex. api)
limiteaprès le « ? » de l'adressenonDe 1 à 200.
curseuraprès le « ? » de l'adressenonLe curseur de la page suivante.
  • RÉSERVÉ aux clés créées par le PROPRIÉTAIRE de l'espace : « Logs Workspace » lui est réservé dans l'application, l'API ne fait pas exception.
  • Le plus récent d'abord, contrairement aux autres listes : un journal se lit à l'envers.
  • Le genre « api » retrouve tout ce que vos intégrations ont écrit, avec le nom de la clé.
REQUÊTE
GET https://kapacity.app/api/v1/w/{workspace_id}/activity?qui=api
Authorization: Bearer kap_live_…
RÉPONSE
{
  "activite": [
    {
      "id": "a1…",
      "le": "2026-09-22T11:02:00.000Z",
      "action": "lead.create",
      "phrase": "a ajouté le contact « Jean Dupont »",
      "acteur": {
        "genre": "api",
        "id": null,
        "nom": "API · n8n"
      },
      "objet": {
        "nature": "lead",
        "id": "l1…",
        "libelle": "Jean Dupont"
      }
    }
  ],
  "curseur_suivant": null
}

Webhooks

GET/webhooksVos abonnements aux événements, et la liste des événements possibles.

Un abonnement dit « appelle cette adresse quand ceci se passe ». C'est ce qui remplace un scénario qui interroge l'API toutes les cinq minutes : il consommerait du quota pour rien et réagirait en retard.

CLÉ : LECTURE SEULE SUFFIT

  • Le secret de signature n'est JAMAIS rendu ici : il n'est lisible qu'une fois, à la création.
REQUÊTE
GET https://kapacity.app/api/v1/w/{workspace_id}/webhooks
Authorization: Bearer kap_live_…
RÉPONSE
{
  "webhooks": [
    {
      "id": "w1…",
      "url": "https://n8n.exemple.fr/webhook/abc",
      "evenements": [
        "contrat.signe"
      ],
      "actif": true,
      "ignorer_api": true,
      "description": "Scénario de closing",
      "cree_le": "2026-09-22T09:00:00.000Z",
      "dernier_envoi_le": "2026-09-22T11:30:00.000Z",
      "echecs_consecutifs": 0
    }
  ],
  "evenements_possibles": [
    {
      "id": "contrat.signe",
      "libelle": "Quand un contrat est signé",
      "description": "Un contrat revient signé."
    }
  ]
}
POST/webhooksS'abonner à un ou plusieurs événements.

CLÉ : LECTURE ET ÉCRITURE

paramètreoù le mettreobligatoiredescription
urldans le body JSONouiL'adresse appelée, en https. (ex. https://n8n.exemple.fr/webhook/abc)
evenementsdans le body JSONouiLes événements écoutés. (ex. ["contrat.signe", "rendez_vous.pris"])
nomdans le body JSONnonSon nom, pour savoir qui est quoi dans la liste d'Intégrations et dans le journal (80 caractères au plus). Sans nom, il s'affiche par son adresse. (ex. Relance après signature)
entetedans le body JSONnonUn en-tête ajouté à chaque envoi, { nom, valeur }, pour que votre outil refuse tout ce qui ne vient pas de Kapacity (dans n8n : « Header Auth »). La valeur n'est plus jamais relisible.
ignorer_apidans le body JSONnonNe pas vous renvoyer ce que votre propre clé a écrit. Le garde-fou anti-boucle.
descriptiondans le body JSONnonUne note libre, plus longue que le nom (200 caractères au plus).
  • Le « secret » n'est rendu QU'ICI, une seule fois : notez-le. Il sert à vérifier notre signature, sans lui n'importe qui pourrait se faire passer pour Kapacity auprès de votre serveur.
  • L'adresse doit être publique et en https. Une adresse privée est refusée, à la création comme à chaque envoi.
  • Si votre scénario ÉCRIT dans Kapacity et écoute les mêmes événements, mettez « ignorer_api » à true : sinon il se réveille lui-même, indéfiniment.
REQUÊTE
POST https://kapacity.app/api/v1/w/{workspace_id}/webhooks
Authorization: Bearer kap_live_…
Content-Type: application/json

{
  "nom": "Relance après signature",
  "url": "https://n8n.exemple.fr/webhook/abc",
  "evenements": [
    "contrat.signe"
  ],
  "entete": {
    "nom": "X-Api-Key",
    "valeur": "une-valeur-que-vous-choisissez"
  },
  "ignorer_api": true
}
RÉPONSE
{
  "webhook": {
    "id": "w1…",
    "nom": "Relance après signature",
    "url": "https://n8n.exemple.fr/webhook/abc",
    "evenements": [
      "contrat.signe"
    ],
    "actif": true,
    "entete_nom": "X-Api-Key"
  },
  "secret": "whsec_…"
}
PATCH/webhooks/{id}Renommer un abonnement, changer son adresse, ses événements ou son en-tête, ou le réactiver.

CLÉ : LECTURE ET ÉCRITURE

paramètreoù le mettreobligatoiredescription
iddans l'adresseouiL'identifiant de l'abonnement.
nomdans le body JSONnonLe nouveau nom. Une chaîne vide ou null le retire.
urldans le body JSONnonLa nouvelle adresse, en https.
evenementsdans le body JSONnonLa nouvelle liste d'événements. Elle REMPLACE l'ancienne.
entetedans le body JSONnonLe nouvel en-tête, { nom, valeur } : la valeur est à redonner, elle n'est jamais relisible. null le retire.
actifdans le body JSONnonSuspendre les envois, ou les reprendre.
ignorer_apidans le body JSONnonLe garde-fou anti-boucle.
descriptiondans le body JSONnonLa note libre.
  • Seuls les champs envoyés changent : les autres restent tels quels.
  • Le secret ne se change pas : pour en changer, supprimez l'abonnement et recréez-le. C'est ce qui garantit qu'il n'existe qu'à un seul endroit.
  • Réactiver remet le compteur d'échecs à zéro : un abonnement réparé repart avec toutes ses chances.
REQUÊTE
PATCH https://kapacity.app/api/v1/w/{workspace_id}/webhooks/5c7e9a1b-3d5f-4b7d-9f1a-2c4e6a8b0d2f
Authorization: Bearer kap_live_…
Content-Type: application/json

{
  "nom": "Relance après signature",
  "evenements": [
    "contrat.envoye",
    "contrat.signe"
  ],
  "entete": {
    "nom": "X-Api-Key",
    "valeur": "une-nouvelle-valeur"
  }
}
RÉPONSE
{
  "webhook": {
    "id": "w1…",
    "nom": "Relance après signature",
    "evenements": [
      "contrat.envoye",
      "contrat.signe"
    ],
    "actif": true,
    "echecs_consecutifs": 0,
    "entete_nom": "X-Api-Key"
  }
}
DELETE/webhooks/{id}Supprimer un abonnement, et son journal d'envois.

CLÉ : LECTURE ET ÉCRITURE

paramètreoù le mettreobligatoiredescription
iddans l'adresseouiL'identifiant de l'abonnement.
  • Définitif. Pour arrêter les envois sans perdre l'historique, passez « actif » à false.
REQUÊTE
DELETE https://kapacity.app/api/v1/w/{workspace_id}/webhooks/5c7e9a1b-3d5f-4b7d-9f1a-2c4e6a8b0d2f
Authorization: Bearer kap_live_…
RÉPONSE
{
  "supprime": true,
  "id": "w1…"
}
POST/webhooks/{id}/testEnvoyer un événement d'exemple, pour brancher sans attendre un vrai.

L'essai passe par la MÊME file que les vrais envois : même signature, mêmes en-têtes, mêmes reprises. Un essai qui aboutit prouve donc que le vrai aboutira.

CLÉ : LECTURE ET ÉCRITURE

paramètreoù le mettreobligatoiredescription
iddans l'adresseouiL'identifiant de l'abonnement.
evenementaprès le « ? » de l'adressenonLequel simuler. Par défaut, le premier de l'abonnement. (ex. contrat.signe)
  • Le corps porte « essai: true » : votre scénario peut s'arrêter là plutôt que de créer une vraie facture.
  • La réponse est un 202 : l'envoi part dans les secondes qui suivent. Son résultat se lit dans le journal des envois.
REQUÊTE
POST https://kapacity.app/api/v1/w/{workspace_id}/webhooks/5c7e9a1b-3d5f-4b7d-9f1a-2c4e6a8b0d2f/test?evenement=contrat.signe
Authorization: Bearer kap_live_…
RÉPONSE
{
  "envoi": {
    "id": "d1…",
    "event_id": "e1…",
    "evenement": "contrat.signe",
    "statut": "en_attente"
  }
}
GET/webhooks/{id}/deliveriesLe journal des envois : ce qui est parti, ce qui a été refusé, et pourquoi.

CLÉ : LECTURE SEULE SUFFIT

paramètreoù le mettreobligatoiredescription
iddans l'adresseouiL'identifiant de l'abonnement.
statutaprès le « ? » de l'adressenonen_attente, envoi, envoye, echec ou abandonne. (ex. echec)
limiteaprès le « ? » de l'adressenonDe 1 à 200.
curseuraprès le « ? » de l'adressenonLe curseur de la page suivante.
  • C'est ici que « je ne reçois rien » trouve sa réponse : le code HTTP et le début de la réponse de VOTRE serveur y sont écrits.
  • Le plus récent d'abord : on vient y chercher le dernier envoi, pas rejouer l'historique.
REQUÊTE
GET https://kapacity.app/api/v1/w/{workspace_id}/webhooks/5c7e9a1b-3d5f-4b7d-9f1a-2c4e6a8b0d2f/deliveries?statut=echec
Authorization: Bearer kap_live_…
RÉPONSE
{
  "envois": [
    {
      "id": "d1…",
      "event_id": "e1…",
      "evenement": "contrat.signe",
      "statut": "echec",
      "tentatives": 4,
      "code_http": 401,
      "reponse": "Unauthorized",
      "erreur": null,
      "duree_ms": 180,
      "prochaine_tentative_le": null,
      "envoye_le": null,
      "cree_le": "2026-09-22T11:30:00.000Z"
    }
  ],
  "curseur": null
}
POST/webhooks/{id}/deliveries/{envoi}/replayRenvoyer un événement déjà émis, par exemple après une panne de votre serveur.

CLÉ : LECTURE ET ÉCRITURE

paramètreoù le mettreobligatoiredescription
iddans l'adresseouiL'identifiant de l'abonnement.
envoidans l'adresseouiL'identifiant de l'envoi à rejouer.
  • Le corps rejoué est celui de l'ÉPOQUE, pas l'état d'aujourd'hui : c'est bien l'événement qui est rejoué.
  • Le « webhook-id » reste le même : un destinataire qui déduplique reconnaît un doublon au lieu de traiter deux fois.
  • Un envoi encore en cours de tentatives ne se rejoue pas : attendez son résultat.
REQUÊTE
POST https://kapacity.app/api/v1/w/{workspace_id}/webhooks/5c7e9a1b-3d5f-4b7d-9f1a-2c4e6a8b0d2f/deliveries/9d8c7b6a-5f4e-4d3c-8b2a-1f0e9d8c7b6a/replay
Authorization: Bearer kap_live_…
RÉPONSE
{
  "envoi": {
    "id": "d2…",
    "event_id": "e1…",
    "evenement": "contrat.signe",
    "statut": "en_attente"
  },
  "rejoue_depuis": "d1…"
}

Les refus

Toute erreur rend { error, code }. Le code est stable, le message peut changer : aiguillez sur le code, jamais sur le texte.

codeHTTPce que ça veut dire
cle_absente401Clé d'API absente. Ajoutez l'en-tête « Authorization: Bearer kap_live_… » à votre requête.
cle_invalide401Clé d'API inconnue. Vérifiez que vous utilisez bien la clé affichée à sa création.
cle_expiree403Cette clé d'API a expiré. Créez-en une nouvelle depuis Intégrations, onglet API.
cle_revoquee403Cette clé d'API a été révoquée. Créez-en une nouvelle depuis Intégrations, onglet API.
espace_incorrect403Cette clé n'appartient pas à l'espace demandé. Vérifiez l'identifiant d'espace dans l'adresse.
portee403Cette clé est en lecture seule : elle ne peut pas modifier de données. Créez une clé « Lecture et écriture » pour cette intégration.
proprietaire403Cette partie est réservée au propriétaire de l'espace : utilisez une clé créée par lui.
introuvable404Cet élément n'existe pas dans cet espace.
validation422La requête a été refusée : une valeur ne convient pas.
quota_mensuel429Le quota d'appels du mois est atteint pour cet espace. Il repart au premier jour du mois prochain, et le plan Pro en comprend davantage.
debit429Trop d'appels en une seconde. Espacez vos requêtes, puis réessayez.
debit_heure429Trop d'appels sur la dernière heure. Réessayez un peu plus tard.
disjoncteur429Écritures suspendues sur cet enregistrement : il a été modifié trop souvent en quelques secondes par cette clé. C'est le garde-fou anti-boucle.
limite_plan429Une limite du plan de cet espace est atteinte.
api_fermee403L'API n'est pas encore ouverte sur cet espace.

Webhooks

Ne faites pas interroger l'API toutes les cinq minutes par un scénario : cela brûle votre quota à ne rien trouver, et réagit en retard. Abonnez-vous, et Kapacity appelle votre adresse au moment où la chose se passe.

événementquand
contact.creeUn contact entre dans Mon Pipe ou dans Setting, par l'écran, un formulaire, une réservation, un import ou l'API.
contact.modifieUne valeur d'un contact change. Le corps porte l'état APRÈS la modification.
ligne.creeeUne ligne entre dans une table créée par l'équipe (Mes Ventes, Produits, une table à vous).
ligne.modifieeUne valeur d'une ligne change, quelle que soit la colonne et quelle que soit la main qui l'écrit.
rendez_vous.prisUn prospect réserve un créneau sur une page publique de réservation.
rendez_vous.reprogrammeLe créneau d'un rendez-vous change.
rendez_vous.annuleUn rendez-vous est annulé, par le contact ou par l'équipe.
formulaire.soumisQuelqu'un termine un formulaire de qualification. Le corps porte le score.
contrat.envoyeUn contrat part vers son signataire, depuis l'application ou par POST /contracts. « objet » est le CONTRAT, et « contact » le contact qui le signera.
contrat.signeUn contrat revient signé. C'est l'événement que la plupart des scénarios attendent. « objet » est le CONTRAT, et « contact » le contact qui l'a signé.
paiement.recuUn paiement est constaté chez le fournisseur et rattaché à l'espace.

Chaque envoi est signé (convention Standard Webhooks : webhook-id, webhook-timestamp, webhook-signature). La livraison est « au moins une fois » : dédupliquez sur webhook-id. La marche à suivre complète est dans la référence, bouton ci-dessus.

Ce que l'API ne fait pas

À lire avant d'écrire un appel qui n'existe pas :

  • Créer ou modifier une table, une colonne, un statut : Décision de Nicolas du 22 septembre 2026 : c'est du DDL logique. L'API lit la structure, elle ne la façonne pas. Une intégration qui aurait besoin d'une colonne la demande à un administrateur.
  • Visioconférence, enregistrements, transcriptions : Module masqué derrière FEATURES.visio : rien de ce qui n'est pas en service ne s'expose.
  • Pages opt-in, modèles d'e-mail, tutoriels : Surfaces d'édition, sans usage d'automatisation identifié. À rouvrir si un client le demande, jamais par anticipation.
  • Panel d'administration de la plateforme, page Associés : Écrans internes de Kapacity, hors de l'espace d'un client. Ils n'ont rien à faire dans une API de tenant.
  • Finances & Charges : les charges, leurs factures et leurs montants HT, TVA et TTC : Données financières réservées aux administrateurs de l'espace (lecture comprise). Aucun usage d'automatisation identifié à l'ajout des factures et des montants HT et TTC (2 octobre 2026) : à rouvrir si un client le demande.
  • Les factures de l'abonnement Kapacity et les informations de facturation de la société : Documents comptables émis par Kapacity à l'espace (et non données du client sur ses propres ventes), réservés aux administrateurs, édités au premier téléchargement dans les Paramètres (0182, 2 octobre 2026). Aucun usage d'automatisation identifié : à rouvrir si un client demande à récupérer ses factures par l'API.
  • Les inscrits gratuits versés dans l'espace de vente de Kapacity, et les commissions de notre équipe : Mécaniques INTERNES à Kapacity (22 septembre et 2 octobre 2026) : elles n'existent que dans notre propre espace de vente, jamais dans celui d'un client. Les leads qu'elles créent se lisent par l'API de cet espace comme les autres.
  • Créer, modifier ou supprimer une clé d'API : Une clé ne se fabrique pas par une clé : ce serait une escalade de droits silencieuse. La création reste dans l'application, réservée au propriétaire et aux administrateurs.
  • Inviter un membre ou lui créer son compte : Même raison qu'une clé : une clé qui fabrique des comptes fabrique des accès. Faire entrer quelqu'un reste dans la page Équipe, réservé aux administrateurs et aux rôles qui ont le droit « Inviter un membre » (23 septembre 2026), dans un rôle qui n'a pas plus de droits que le leur. L'API lit les membres, elle n'en ajoute pas.
  • Construire le parcours d'un formulaire : routes, tranches de score, cartes de l'onglet Parcours : Surface d'édition (24 septembre 2026), comme l'éditeur de formulaire tout entier : la logique s'écrit dans l'application, où elle se voit en cartes et se vérifie. L'API lit les réponses, dont le score est déjà calculé sur le chemin suivi.
  • Vérifier qu'un contact existe déjà avant de l'ajouter (fenêtre « Ce contact existe peut-être déjà ») : Une mise en garde faite pour quelqu'un devant l'écran, qui choisit « Annuler » ou « Créer quand même ». Un scénario, lui, cherche le contact par sa liste (filtre par e-mail) avant d'écrire, et l'écriture groupée de l'API est déjà idempotente (28 septembre 2026).
  • Acheter un pack de crédits IA : Un paiement engage l'entreprise et se fait sur la page Whop, par un administrateur connecté : une clé ne paie pas. Le solde des crédits se lit dans l'application (24 septembre 2026).
  • Droits et pages par rôle, colonne Admin comprise : Régler ce que chaque rôle peut faire, c'est régler les accès : même raison qu'une clé ou une invitation. Le tableau reste dans la page Équipe, aux administrateurs, et sa colonne Admin au seul propriétaire de l'espace (24 septembre 2026).
  • « Setup mon workspace avec l'IA » (nom, logo, formulaires et pages préparés à partir du site) : Un geste d'accueil fait UNE seule fois par espace, offert, par un administrateur connecté qui coche la déclaration de droits sur son site : rien à automatiser. Ce qu'il crée (formulaires, réponses, contacts) se lit ensuite par l'API comme le reste (1er octobre 2026).
  • Trier une grille du CRM en cliquant sur le nom d'une colonne : C'est un réglage d'AFFICHAGE, pas une donnée : il vit dans l'adresse de la page et n'écrit rien. L'API rend les lignes dans un ordre stable (de la plus ancienne à la plus récente) pour qu'une synchronisation reprenne où elle s'est arrêtée ; l'intégration trie de son côté (28 septembre 2026).
  • Consentement des visiteurs aux traceurs d'une page opt-in : Le choix du visiteur vit dans SON navigateur, jamais chez Kapacity : il n'y a rien à lire ni à écrire. Les identifiants du pixel Meta et de Google Analytics se règlent dans l'éditeur de la page (29 septembre 2026).
  • Disponibilités d'un membre : plages de la semaine et exceptions au jour le jour : Chacun règle SES disponibilités, connecté, dans Agenda & RDV : une clé d'espace n'est pas un membre. Et l'API ne pose pas de rendez-vous (les disponibilités se décident côté Kapacity) ; une intégration qui voudrait bloquer un agenda passe par Google Agenda, déjà relu à chaque réservation (29 septembre 2026, 0175).

Retour à Kapacity