API Captiv · v1

Connectez Captiv à votre stack

Une API REST pour lire vos leads, posts, ressources et analytics — et écrire dans votre pipeline depuis Make, n8n, Zapier, votre CRM ou votre propre code.

RESTBearer cptv_live_…RFC 7807Pagination curseurOpenAPI 3.1
GET /v1/me
curl https://captiv-ai.com/api/v1/me \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE"
{
  "data": {
    "object": "me",
    "workspace": { "name": "Mon workspace", "plan": "pro" },
    "api_key": { "scopes": ["read:leads", "read:posts"] }
  },
  "meta": { "request_id": "req_7ad84bf44a494163" }
}

01Démarrage

Opérationnel en 3 minutes

1

Créez une clé

Dans Réglages → API & Développeurs (plans Pro et Agence). Choisissez ses scopes, copiez-la immédiatement : elle n'est affichée qu'une seule fois.
2

Vérifiez l'authentification

bash
curl https://captiv-ai.com/api/v1/me \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE"
3

Lisez vos leads qualifiés

bash
curl "https://captiv-ai.com/api/v1/leads?min_score=70&limit=25" \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE"

02Authentification

Clés scopées, workspace cloisonné

Chaque requête porte votre clé dans le header Authorization: Bearer cptv_live_…. Une clé appartient à un workspace: tout ce que l'API lit ou écrit est strictement limité à ce workspace.

  • La clé n'est jamais stockée en clairchez Captiv (empreinte SHA-256) et n'est affichée qu'une fois, à la création.
  • Une clé compromise se révoque instantanément depuis les Réglages — la révocation est définitive.
  • L'API est incluse dans les plans Pro (1 000 req/h) et Agence(10 000 req/h).

Scopes disponibles

read:leadsLire les leads du workspace (liste, détail, interactions)
write:leadsQualifier un lead, ajouter des notes
read:postsLire les posts surveillés et leurs métriques
write:postsAttacher un visuel à un post Studio (sans coût IA)
read:resourcesLire les ressources (lead magnets) et leurs stats
write:resourcesÉditer les blocs d'une ressource (contenu, ordre, ajout, suppression)
read:analyticsLire les métriques agrégées du workspace
write:webhooksGérer les webhooks sortants du workspace
generate:postsGénérer des posts et leurs questions de cadrage — consomme le budget IA du workspace
generate:resourcesGénérer des ressources (lead magnets) — consomme le budget IA du workspace
read:followupsLire la liste des follow-ups en attente
write:followupsAjouter un lead à la liste des follow-ups (l'agent relancera plus tard, sous ses caps)
read:agentLire l'activité de l'agent : ce qu'il a prévu, ce qu'il a fait, la santé du compte LinkedIn et les quotas anti-ban restants (lecture seule)

03Conventions

Un contrat, quatre règles

Enveloppe de succès

Toute réponse 2xx est enveloppée dans { data, meta }. meta.request_id identifie la requête — donnez-le au support en cas de problème (il est aussi dans le header X-Request-Id).

Pagination par curseur

Les listes sont paginées par un curseur opaque — jamais d'offset. Tant que meta.pagination.has_more est vrai, rappelez le même endpoint avec ?cursor=<next_cursor>. Aucune ligne n'est sautée ni dupliquée, même si des écritures arrivent entre deux pages.

bash · boucle de pagination
cursor=""
while : ; do
  page=$(curl -s "https://captiv-ai.com/api/v1/leads?limit=100&cursor=$cursor" \
    -H "Authorization: Bearer cptv_live_VOTRE_CLE")
  echo "$page" | jq '.data[]'
  [ "$(echo "$page" | jq -r '.meta.pagination.has_more')" = "true" ] || break
  cursor=$(echo "$page" | jq -r '.meta.pagination.next_cursor')
done

Trois scores, trois sens

Un lead porte trois scores distincts — ne les confondez pas : lead_score (0-100, la source de vérité, celle du dashboard), engagement_score (0-100, pondéré par le comportement réel du lead sur vos ressources) et icp_fit_score (0-100, adéquation à votre ICP).

Isolation stricte

Un id qui n'appartient pas à votre workspace répond 404, jamais 403 : l'API ne confirme jamais l'existence d'une ressource chez un autre client.

04Référence

Les endpoints

Base : https://captiv-ai.com/api/v1 — la spécification machine-readable est sur /openapi/v1.yaml.

GET/v1/me#authentification seule

Introspection de la clé

Retourne le workspace, le plan et les scopes de la clé utilisée. C'est l'endpoint de smoke test : si cet appel répond 200, votre intégration est correctement authentifiée.

bash
curl https://captiv-ai.com/api/v1/me \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE"
réponse · application/json
{
  "data": {
    "object": "me",
    "workspace": { "id": "…", "name": "Mon workspace", "plan": "pro" },
    "api_key": {
      "id": "…", "name": "Sync CRM production",
      "environment": "live",
      "scopes": ["read:leads", "read:posts"],
      "created_at": "2026-08-06T18:00:00Z"
    }
  },
  "meta": { "request_id": "req_…" }
}
GET/v1/leads#scope : read:leads

Lister les leads

Liste paginée des leads capturés par vos posts, du plus récent au plus ancien. Chaque lead porte son profil, ses contacts, ses scores et l'état de son pipeline LinkedIn. Attention au sens des scores : lead_score mesure l'ADÉQUATION AU PROFIL recherché (un « VIP » est à 85+), pas l'engagement — un lead à 95 peut n'avoir jamais rien fait. L'engagement se mesure sur les interactions.

Paramètres de requête

post_iduuidNe retourner que les leads d'un post donné.
min_scoreentier 0-100lead_score minimal = adéquation au profil (ICP), pas engagement. VIP = 85+.
min_interactionsentierNombre minimal d'interactions du lead — un vrai signal de comportement.
resource_openedbooléenNe garder que ceux qui ont ouvert la ressource (signal fort).
searchtexteRecherche sur le prénom, le nom et la société. Le terme est assaini (lettres, chiffres, espace, apostrophe, trait d'union, esperluette) ; deux caractères utiles minimum, sinon le filtre est ignoré.
connection_statusconnected | pending | invitation_ignored | not_requested | not_connected | unreachable_locked | unreachable_no_urnOù en est la relation LinkedIn. not_requested = jamais invité ; invitation_ignored = restée sans suite.
limitentier 1-100Taille de page. Défaut : 50.
cursorchaîne opaqueCurseur de la page suivante, tel que renvoyé dans meta.pagination.next_cursor. Ne pas le construire soi-même.
ordercreated_desc | created_ascOrdre chronologique. Défaut : created_desc (plus récents d'abord).
sincedatetime ISO 8601Ne retourner que les objets créés après cette date.
untildatetime ISO 8601Ne retourner que les objets créés avant cette date.
bash
curl "https://captiv-ai.com/api/v1/leads?min_score=70&limit=25" \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE"
réponse · application/json
{
  "data": [
    {
      "id": "…", "object": "lead", "post_id": "…",
      "created_at": "2026-08-05T09:12:00Z", "source": "comment",
      "profile": { "first_name": "Marie", "last_name": "…", "job_title": "Head of Sales",
                   "company": "…", "linkedin_url": "…", "photo_url": "…",
                   "location_city": "Paris", "location_country": "France" },
      "contact": { "email": "…", "personal_email": null, "phone_e164": null },
      "scoring": { "lead_score": 86, "label": "vip", "engagement_score": 91,
                   "icp_fit_score": 86, "strengths": ["…"], "red_flags": [],
                   "reasoning": "…" },
      "pipeline": { "connection_status": "accepted", "resource_sent": true,
                    "resource_opened": true, "meeting_booked": false, "…": "…" },
      "interaction_count": 3
    }
  ],
  "meta": {
    "request_id": "req_…",
    "pagination": { "next_cursor": "eyJ2IjoxLCJjYSI6…", "has_more": true }
  }
}
GET/v1/leads/{id}#scope : read:leads

Détail d'un lead

Retourne un lead par son id. Un id inexistant — ou appartenant à un autre workspace — répond 404 : l'API ne confirme jamais l'existence d'une ressource hors de votre workspace.

bash
curl https://captiv-ai.com/api/v1/leads/LEAD_ID \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE"
réponse · application/json
{ "data": { "id": "…", "object": "lead", "…": "…" }, "meta": { "request_id": "req_…" } }
GET/v1/leads/{id}/notes#scope : read:leads

Notes d'un lead

Les 100 dernières notes du lead, plus récentes d'abord.

bash
curl https://captiv-ai.com/api/v1/leads/LEAD_ID/notes \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE"
réponse · application/json
{
  "data": [
    { "id": "…", "object": "lead_note", "lead_id": "…",
      "content": "Rappel prévu jeudi 10h", "source": "api",
      "created_at": "2026-08-06T10:00:00Z", "updated_at": null }
  ],
  "meta": { "request_id": "req_…" }
}
POST/v1/leads/{id}/notes#scope : write:leads

Ajouter une note à un lead

Cas d'usage type : votre CRM synchronise ses comptes-rendus d'appel vers Captiv. La note est attribuée au créateur de la clé et marquée source « api » pour l'audit. Répond 201.

Corps (JSON)

contentchaîne 1-2000Contenu de la note (texte brut).
bash
curl -X POST https://captiv-ai.com/api/v1/leads/LEAD_ID/notes \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{"content": "Appel du 6/08 : intéressée, rappel jeudi 10h"}'
réponse · application/json
{
  "data": { "id": "…", "object": "lead_note", "lead_id": "…",
            "content": "Appel du 6/08 : intéressée, rappel jeudi 10h",
            "source": "api", "created_at": "…", "updated_at": null },
  "meta": { "request_id": "req_…" }
}
GET/v1/lead-interactions#scope : read:leads

Lister les interactions

Chaque interaction est un événement (lead × post) : commentaire, réaction, livraison de ressource, ouverture, formulaire… C'est la matière première pour reconstruire une timeline — et c'est ICI que se mesure l'engagement : chaque interaction porte son propre engagement_score et le texte du commentaire.

Paramètres de requête

lead_iduuidNe retourner que les interactions d'un lead.
post_iduuidNe retourner que les interactions d'un post.
min_engagemententier 0-100engagement_score minimal. LE filtre d'engagement de l'API.
has_commentbooléenNe garder que les interactions où le lead a écrit un commentaire.
limitentier 1-100Taille de page. Défaut : 50.
cursorchaîne opaqueCurseur de la page suivante, tel que renvoyé dans meta.pagination.next_cursor. Ne pas le construire soi-même.
ordercreated_desc | created_ascOrdre chronologique. Défaut : created_desc (plus récents d'abord).
sincedatetime ISO 8601Ne retourner que les objets créés après cette date.
untildatetime ISO 8601Ne retourner que les objets créés avant cette date.
bash
curl "https://captiv-ai.com/api/v1/lead-interactions?min_engagement=40&has_comment=true" \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE"
réponse · application/json
{
  "data": [
    { "id": "…", "object": "lead_interaction", "lead_id": "…", "post_id": "…",
      "created_at": "…", "kind": "comment", "comment_text": "Intéressé !",
      "keyword_matched": true, "engagement_score": 74, "pipeline_status": "…",
      "resource": { "sent": true, "sent_at": "…", "opened": true, "opened_at": "…",
                    "clicked_at": "…", "click_count": 2 },
      "connection": { "requested_at": "…", "accepted_at": "…" },
      "form_submitted": false, "email_shared": true }
  ],
  "meta": { "request_id": "req_…", "pagination": { "next_cursor": null, "has_more": false } }
}
GET/v1/posts#scope : read:posts

Lister les posts

Liste paginée des posts LinkedIn surveillés par Captiv, avec leurs compteurs de capture.

Paramètres de requête

statusdraft | published | monitoring | completed | scheduled | publishing | failedFiltrer par statut.
limitentier 1-100Taille de page. Défaut : 50.
cursorchaîne opaqueCurseur de la page suivante, tel que renvoyé dans meta.pagination.next_cursor. Ne pas le construire soi-même.
ordercreated_desc | created_ascOrdre chronologique. Défaut : created_desc (plus récents d'abord).
sincedatetime ISO 8601Ne retourner que les objets créés après cette date.
untildatetime ISO 8601Ne retourner que les objets créés avant cette date.
bash
curl "https://captiv-ai.com/api/v1/posts?status=monitoring" \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE"
réponse · application/json
{
  "data": [
    { "id": "…", "object": "post", "subject": "…", "status": "monitoring",
      "linkedin_url": "https://www.linkedin.com/posts/…", "published_at": "…",
      "is_lead_magnet": true,
      "monitoring": { "active": true, "started_at": "…", "expires_at": "…" },
      "metrics": { "leads_count": 132, "captured_leads_count": 118, "comment_count": 214 },
      "resource_id": "…", "created_at": "…", "updated_at": "…" }
  ],
  "meta": { "request_id": "req_…", "pagination": { "next_cursor": null, "has_more": false } }
}
GET/v1/posts/{id}#scope : read:posts

Détail d'un post

Le post + ses statistiques LinkedIn natives (impressions, réactions, reposts…) quand elles ont été relevées.

bash
curl https://captiv-ai.com/api/v1/posts/POST_ID \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE"
réponse · application/json
{
  "data": {
    "id": "…", "object": "post", "…": "…",
    "linkedin_stats": { "impressions": 28400, "reactions": 312, "reposts": 14,
                        "followers_gained": 57, "profile_viewers": 190,
                        "fetched_at": "2026-08-06T07:00:00Z" }
  },
  "meta": { "request_id": "req_…" }
}
POST/v1/posts/{id}/visual#scope : write:posts

Transmettre un visuel

Attachez une image à un post Studio — en base64 (le cas « j'ai fait mon visuel dans Claude ») ou par URL https, re-téléchargée et hébergée chez Captiv. Le format réel est sniffé (le MIME déclaré n'est jamais cru), l'image est ré-encodée. Images OU vidéo, jamais les deux (règle LinkedIn) ; 9 visuels max ; un post publié ne change plus.

Corps (JSON)

image_base64chaîne base64 ≤ 8 MoL'image encodée (exclusif avec url).
urlURL httpsImage publique à re-héberger (exclusif avec image_base64).
modeappend | replace_coverAjouter au carrousel (défaut) ou remplacer la couverture.
bash
curl -X POST https://captiv-ai.com/api/v1/posts/POST_ID/visual \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://exemple.com/mon-visuel.png"}'
réponse · application/json
{
  "data": { "object": "post_visual", "post_id": "…",
            "visual_url": "https://…supabase.co/…/artefacts/…/api-1754….jpg",
            "cover_url": "…", "visual_urls": ["…"], "mode": "append" },
  "meta": { "request_id": "req_…" }
}
GET/v1/resources#scope : read:resources

Lister les ressources

Les lead magnets du workspace. public_url n'est renseignée que si la page publique peut réellement servir la ressource — une URL présente est toujours une page vivante, vous pouvez la relayer sans vérification. Le contenu complet des blocs n'est pas exposé par l'API.

Paramètres de requête

post_iduuidNe retourner que les ressources attachées à un post.
is_publishedtrue | falseFiltrer sur l'état de publication.
limitentier 1-100Taille de page. Défaut : 50.
cursorchaîne opaqueCurseur de la page suivante, tel que renvoyé dans meta.pagination.next_cursor. Ne pas le construire soi-même.
ordercreated_desc | created_ascOrdre chronologique. Défaut : created_desc (plus récents d'abord).
sincedatetime ISO 8601Ne retourner que les objets créés après cette date.
untildatetime ISO 8601Ne retourner que les objets créés avant cette date.
bash
curl "https://captiv-ai.com/api/v1/resources?is_published=true" \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE"
réponse · application/json
{
  "data": [
    { "id": "…", "object": "resource", "title": "Le guide complet …",
      "subtitle": "…", "slug": "guide-complet", "type": "guide",
      "language": "fr", "is_published": true, "published_at": "…",
      "gated": true, "view_count": 1240, "format_version": 2,
      "public_url": "https://captiv-ai.com/r/guide-complet",
      "post_id": "…", "created_at": "…", "updated_at": "…" }
  ],
  "meta": { "request_id": "req_…", "pagination": { "next_cursor": null, "has_more": false } }
}
GET/v1/resources/{id}#scope : read:resources

Détail d'une ressource

La ressource + blocks_count et updated_at — l'echo du verrou optimiste, à renvoyer tel quel en expected_updated_at sur toute écriture de blocs.

bash
curl https://captiv-ai.com/api/v1/resources/RESOURCE_ID \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE"
réponse · application/json
{ "data": { "id": "…", "object": "resource", "…": "…", "blocks_count": 27 },
  "meta": { "request_id": "req_…" } }
GET/v1/resources/{id}/blocks#scope : read:resources

Lire les blocs

Le tableau complet des blocs (id, type, content, metadata) + updated_at. C'est la lecture qui précède toute édition : les ids servent à cibler, updated_at sert de verrou.

bash
curl https://captiv-ai.com/api/v1/resources/RESOURCE_ID/blocks \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE"
réponse · application/json
{
  "data": {
    "resource_id": "…", "format_version": 2,
    "updated_at": "2026-08-07T10:00:00Z",
    "blocks": [
      { "id": "b3f2a1c9", "type": "heading", "content": "Le guide", "metadata": null },
      { "id": "b7e4d2b0", "type": "paragraph", "content": "…", "metadata": null }
    ]
  },
  "meta": { "request_id": "req_…" }
}
PATCH/v1/resources/{id}/blocks/{blockId}#scope : write:resources

Éditer un bloc

Édition DIRECTE du contenu par votre client — aucune IA côté Captiv. Le type est immuable ; les clés metadata absentes sont préservées. La sanitisation éditoriale s'applique (transformations listées dans sanitize_flags). 409 si la ressource a bougé depuis votre lecture.

Corps (JSON)

contentchaîne ≤ 50000Nouveau contenu (optionnel).
metadataobjetClés à fusionner en surface (optionnel).
expected_updated_atchaîneL'updated_at lu — fortement recommandé (verrou optimiste).
bash
curl -X PATCH https://captiv-ai.com/api/v1/resources/RESOURCE_ID/blocks/b7e4d2b0 \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{"content": "Le paragraphe corrigé.", "expected_updated_at": "2026-08-07T10:00:00Z"}'
réponse · application/json
{
  "data": { "block": { "id": "b7e4d2b0", "type": "paragraph", "content": "Le paragraphe corrigé.", "metadata": null },
            "sanitize_flags": [], "updated_at": "2026-08-07T10:02:11Z" },
  "meta": { "request_id": "req_…" }
}
POST/v1/resources/{id}/blocks#scope : write:resources

Insérer un bloc

Type dans l'allowlist partagée avec le générateur ; content JSON validé pour les types qui en stockent. position optionnelle (défaut : fin). Répond 201 avec l'id généré.

Corps (JSON)

typechaîneType de bloc (heading, paragraph, callout…).
contentchaîne ≤ 50000Contenu (défaut vide).
positionentier ≥ 0Index d'insertion (optionnel).
expected_updated_atchaîneVerrou optimiste (recommandé).
bash
curl -X POST https://captiv-ai.com/api/v1/resources/RESOURCE_ID/blocks \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{"type": "callout", "content": "À retenir : …", "position": 3}'
réponse · application/json
{ "data": { "block": { "id": "b9a1f4e2", "…": "…" }, "position": 3,
            "sanitize_flags": [], "updated_at": "…" }, "meta": { "request_id": "req_…" } }
DELETE/v1/resources/{id}/blocks/{blockId}#scope : write:resources

Supprimer un bloc

expected_updated_at passe en query string. 409 si la ressource a bougé.

bash
curl -X DELETE "https://captiv-ai.com/api/v1/resources/RESOURCE_ID/blocks/b9a1f4e2?expected_updated_at=2026-08-07T10:02:11Z" \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE"
réponse · application/json
{ "data": { "deleted": true, "block_id": "b9a1f4e2", "blocks_count": 26, "updated_at": "…" },
  "meta": { "request_id": "req_…" } }
PUT/v1/resources/{id}/blocks/order#scope : write:resources

Réordonner les blocs

block_ids doit être une permutation STRICTE des ids existants — ni manquant, ni inconnu, ni doublon : réordonner n'est jamais l'occasion de perdre un bloc.

Corps (JSON)

block_idstableau de chaînesTous les ids, dans le nouvel ordre.
expected_updated_atchaîneVerrou optimiste (recommandé).
bash
curl -X PUT https://captiv-ai.com/api/v1/resources/RESOURCE_ID/blocks/order \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{"block_ids": ["b3f2a1c9", "b9a1f4e2", "b7e4d2b0"]}'
réponse · application/json
{ "data": { "reordered": true, "blocks_count": 3, "updated_at": "…" }, "meta": { "request_id": "req_…" } }
GET/v1/resources/{id}/lint#scope : read:resources

Auto-vérification avant publication

Le même lint que le produit : chiffres sans source, blocs vides, placeholders… Le lint constate, il ne bloque jamais l'édition — c'est votre relecture automatique avant de publier.

bash
curl https://captiv-ai.com/api/v1/resources/RESOURCE_ID/lint \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE"
réponse · application/json
{ "data": { "resource_id": "…", "blocks_count": 27, "clean": false,
            "findings": [{ "…": "…" }] }, "meta": { "request_id": "req_…" } }
POST/v1/resources/{id}/preview#scope : read:resources

Lien de préversion (fidélité 100 %)

Génère un lien signé (45 min) vers la VRAIE page publique de la ressource — même renderer, même charte que ce que verra le lead, brouillons compris. Aucune vue comptée, jamais indexé. Ce n'est PAS un lien à envoyer à un prospect : il expire.

bash
curl -X POST https://captiv-ai.com/api/v1/resources/RESOURCE_ID/preview \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE"
réponse · application/json
{
  "data": { "object": "resource_preview", "resource_id": "…",
            "preview_url": "https://captiv-ai.com/r/guide-complet?preview=eyJ…",
            "expires_at": "2026-08-07T11:15:00Z" },
  "meta": { "request_id": "req_…" }
}
POST/v1/resources#scope : generate:resources

Créer une ressource (202 + suivi)

Le même moteur que l'écran : voix du créateur, charte, mouvement structurel. La ressource naît TOUJOURS en brouillon — la publier reste un geste explicite dans Captiv, jamais un effet de bord d'un agent — et n'est jamais attachée à un post à la naissance. Réponse 202 immédiate, génération en fond (25-96 s mesurés) à suivre via /v1/resources/{id}/status. Envoyez un Idempotency-Key : rejouer la même clé rend la même réponse, jamais deux ressources. Lot actuel : matière texte (titre, brief, liens).

Corps (JSON)

titlechaîne 2-200Le sujet de la ressource (le titre final est dérivé du contenu généré).
subtitlechaîne ≤ 800Sous-titre — dérivé automatiquement si absent.
extra_detailschaîne ≤ 6000Le brief : chiffres, scènes, verbatims — la matière première de la qualité.
languagefr | enDéfaut : fr.
resource_typechaîne ≤ 40Défaut : guide.
external_linkstableau ≤ 5{url, title} — sources à citer dans la ressource.
bash
curl -X POST https://captiv-ai.com/api/v1/resources \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "title": "Le guide de préqualification des leads",
    "extra_details": "40 relances/semaine automatisées, 3 h gagnées, verbatims clients"
  }'
réponse · application/json
{
  "data": {
    "object": "resource_generation", "status": "running",
    "resource": { "id": "…", "object": "resource", "title": "…",
                  "is_published": false, "…": "…" },
    "status_url": "/api/v1/resources/…/status",
    "typical_duration_seconds": [25, 96]
  },
  "meta": { "request_id": "req_…" }
}
GET/v1/resources/{id}/status#scope : read:resources

Suivre une génération de ressource

La génération d'une ressource tourne en fond (25-96 s mesurés). Cette route rend les faits du lifecycle — state (idle, running, completed, failed), horodatages, blocs réellement écrits — sans marteler la ressource entière. Fonctionne aussi pour une génération lancée depuis l'écran Studio.

bash
curl https://captiv-ai.com/api/v1/resources/RESOURCE_ID/status \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE"
réponse · application/json
{
  "data": { "object": "resource_generation_status", "resource_id": "…",
            "state": "completed", "started_at": "…", "finished_at": "…",
            "blocks_count": 37, "is_published": false, "updated_at": "…" },
  "meta": { "request_id": "req_…" }
}
POST/v1/studio/questions#scope : generate:posts

Questions de cadrage personnalisées

Les 3 questions d'interview écrites POUR votre idée (mots exacts repris, matière visée : chiffre, scène, verbatim). Toujours 3 questions : si le sur-mesure échoue, le serveur replie sur la banque déterministe — la provenance est dite dans source.

Corps (JSON)

ideachaîne 20-1200L'idée brute du post.
is_lead_magnetbooléenL'idée vise-t-elle un lead magnet ? Défaut : false.
bash
curl -X POST https://captiv-ai.com/api/v1/studio/questions \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{"idea": "j'\''ai remplacé mes relances client manuelles par un script Make, gain de temps énorme"}'
réponse · application/json
{
  "data": {
    "object": "post_questions", "source": "sur_mesure",
    "questions": [
      { "id": "sur-mesure-1",
        "texte": "\"Gain de temps énorme\" : tu l'as mesuré sur combien de relances par semaine ?",
        "justification": "Le chiffre vérifiable qui ancre le post" },
      { "id": "sur-mesure-2", "texte": "…", "justification": "…" },
      { "id": "sur-mesure-3", "texte": "…", "justification": "…" }
    ]
  },
  "meta": { "request_id": "req_…" }
}
POST/v1/studio/posts#scope : generate:posts

Générer un post LinkedIn complet

LE MÊME moteur que l'écran Studio : voix apprise du propriétaire du workspace, cadrage, correction anti-tics, mot à commenter extrait pour un lead magnet. Le post créé est un brouillon Studio normal, visible dans « Mes posts ». Envoyez un en-tête Idempotency-Key (UUID par tentative) : rejouer la même clé rend la même réponse, jamais deux posts. Idée trop mince ? 200 avec status needs_more_input et un feedback actionnable — enrichissez et rappelez. Chaque appel compte dans le pool IA quotidien du workspace et le quota posts/jour du plan.

Corps (JSON)

ideachaîne 10-4000L'idée brute — plus elle a de vécu, meilleur est le post.
languagefr | enLangue du post. Défaut : fr.
resource_iduuidRessource PUBLIÉE du workspace à attacher (flux lead magnet).
is_lead_magnetbooléenIntention lead magnet sans ressource encore créée.
cadragetableau ≤ 3Paires {question, reponse} issues de /v1/studio/questions.
styleobjet{forme, ton} pour imposer un style au lieu de celui du profil.
bash
curl -X POST https://captiv-ai.com/api/v1/studio/posts \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "idea": "j'\''ai remplacé mes relances client manuelles par un script Make",
    "cadrage": [
      { "question": "Tu l'\''as mesuré sur combien de relances ?",
        "reponse": "Environ 40 par semaine, je gagnais 3 h" }
    ]
  }'
réponse · application/json
{
  "data": {
    "object": "post_generation", "status": "generated",
    "post": { "id": "…", "object": "post", "subject": "…",
              "text": "Le post LinkedIn complet, dans votre voix…",
              "trigger_keyword": null, "status": "draft", "…": "…" },
    "feedback": null,
    "usage": { "input_tokens": 1240, "output_tokens": 812,
               "web_searches": 0, "cost_usd": 0.04 }
  },
  "meta": { "request_id": "req_…" }
}
GET/v1/analytics/summary#scope : read:analytics

KPI agrégés du workspace

Les mêmes compteurs que le dashboard Captiv (même moteur de calcul), agrégés sur la période demandée : capture, qualification, outreach et ouvertures de ressources. Si un compteur n'a pas pu être lu, il vaut null et partial passe à true — jamais 0 : un zéro se lit « il ne s'est rien passé », et c'est une phrase sur laquelle on décide.

Paramètres de requête

period7d | 30d | 90dFenêtre d'agrégation. Défaut : 30d.
bash
curl "https://captiv-ai.com/api/v1/analytics/summary?period=30d" \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE"
réponse · application/json
{
  "data": {
    "object": "analytics_summary", "period": "30d",
    "from": "2026-07-07T…", "to": "2026-08-06T…",
    "totals": {
      "leads_captured": 412, "leads_qualified": 96, "posts_published": 9,
      "invitations_sent": 240, "invitations_attempted": 251,
      "messages_sent": 187, "resources_delivered": 203,
      "resource_opens": 164, "unique_resource_openers": 149, "relation_checks": 96
    },
    "rates": { "invitation_success_rate": 96, "resource_open_rate": 73 },
    "partial": false
  },
  "meta": { "request_id": "req_…" }
}
GET/v1/resources/{id}/render-context#scope : read:resources

De quoi reproduire fidèlement la page

Les blocs disent CE QUI est écrit ; ceci dit à quoi la page RESSEMBLE — thème résolu (variables CSS à poser telles quelles), carte de l'auteur, identité de la ressource. À appeler avant de fabriquer un aperçu, sans quoi il sera structurellement juste et visuellement faux. theme.frozen = true signifie que la charte a été figée à la publication : c'est cette apparence qu'ont reçue les leads, pas la charte actuelle du créateur. Le brief privé du créateur ne sort jamais, et les variables CSS sont filtrées (préfixe --resource-, longueur bornée, pas de caractère qui referme une déclaration).

bash
curl "https://captiv-ai.com/api/v1/resources/RESOURCE_ID/render-context" \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE"
réponse · application/json
{
  "data": {
    "object": "resource_render_context", "resource_id": "…",
    "updated_at": "2026-08-10T09:00:00Z", "format_version": 2,
    "resource": { "title": "…", "subtitle": "…", "language": "fr", "is_published": true },
    "theme": {
      "css_variables": { "--resource-accent": "#0A66C2", "--resource-bg": "#FAFAF8" },
      "frozen": true
    },
    "author": { "name": "…", "headline": "…", "photo_url": "…", "show_intro": true }
  },
  "meta": { "request_id": "req_…" }
}
DELETE/v1/posts/{id}/visual#scope : write:posts

Retirer tous les visuels d'un post

Vide l'attache visuelle (couverture comprise) pour repartir proprement. Aucun coût IA, le texte du post n'est pas touché. Le cas d'usage principal est le carrousel : neuf images à 8 Mo ne tiennent pas dans une requête, l'envoi est donc séquentiel — et un envoi qui échoue en cours de route laissait jusqu'ici un carrousel amputé sans moyen de le reprendre. Refusé sur un post publié ou suivi depuis LinkedIn.

bash
curl -X DELETE "https://captiv-ai.com/api/v1/posts/POST_ID/visual" \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE"
réponse · application/json
{
  "data": {
    "object": "post_visual", "post_id": "…",
    "removed": 4, "visual_urls": [], "cover_url": null
  },
  "meta": { "request_id": "req_…" }
}
PATCH/v1/posts/{id}#scope : write:posts

Enregistrer un texte retravaillé

L'équivalent de taper dans l'éditeur du Studio : le texte fourni est rangé tel quel. N'APPELLE AUCUN MODÈLE et ne consomme AUCUN crédit IA — la génération (POST /v1/studio/posts) coûte, l'édition non. Fournissez expected_updated_at (lu au GET) : sans lui, une modification faite au même moment dans Captiv serait écrasée en silence. Refusé sur un post déjà publié, en cours de publication, ou suivi depuis LinkedIn plutôt qu'écrit dans le Studio.

bash
curl -X PATCH "https://captiv-ai.com/api/v1/posts/POST_ID" \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{"text":"Le post retravaille, complet.","expected_updated_at":"2026-08-10T09:00:00Z"}'
réponse · application/json
{
  "data": {
    "id": "…", "object": "post",
    "subject": "…", "status": "draft",
    "text": "Le post retravaille, complet.",
    "updated_at": "2026-08-10T09:04:12Z",
    "is_studio_authored": true,
    "metrics": { "leads_count": 0, "captured_leads_count": 0, "comment_count": 0 }
  },
  "meta": { "request_id": "req_…" }
}
GET/v1/agent/actions#scope : read:agent

Ce que l'agent a prévu et ce qu'il a fait

Les invitations, messages, commentaires et vérifications planifiés par l'agent, avec leur statut et la raison d'un éventuel saut. Filtrez sur la date PRÉVUE (scheduled_since / scheduled_until) pour répondre à « qu'est-ce qui part aujourd'hui ». Lecture seule : aucun endpoint ne permet de créer, reprogrammer, déclencher ni annuler une action — les garde-fous anti-ban restent chez l'agent. Le texte des messages n'est pas exposé.

Paramètres de requête

lead_iduuidLes actions visant ce lead.
post_iduuidLes actions liées à ce post.
action_typeinvitation | message | comment | check | followup_notifLe type d'action.
statustextepending, done, skipped…
scheduled_sinceISO 8601Date PRÉVUE ≥ — c'est ce filtre qui répond à « aujourd'hui ».
scheduled_untilISO 8601Date prévue ≤.
limitentier 1-100Taille de page. Défaut : 50.
cursorchaîne opaqueCurseur de la page suivante, tel que renvoyé dans meta.pagination.next_cursor. Ne pas le construire soi-même.
ordercreated_desc | created_ascOrdre chronologique. Défaut : created_desc (plus récents d'abord).
sincedatetime ISO 8601Ne retourner que les objets créés après cette date.
untildatetime ISO 8601Ne retourner que les objets créés avant cette date.
bash
curl "https://captiv-ai.com/api/v1/agent/actions?status=pending&action_type=invitation" \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE"
réponse · application/json
{
  "data": [
    {
      "id": "…", "object": "agent_action",
      "action_type": "invitation", "status": "pending",
      "scheduled_at": "2026-08-10T09:30:00Z", "executed_at": null,
      "lead_id": "…", "post_id": "…", "priority_score": 87,
      "reason": "Score eleve, commentaire recent", "skip_reason": null,
      "error_code": null, "created_at": "2026-08-09T18:00:00Z"
    }
  ],
  "meta": { "request_id": "req_…", "pagination": { "has_more": false, "next_cursor": null } }
}
GET/v1/agent/status#scope : read:agent

Santé du compte et plafonds anti-ban

L'état du compte LinkedIn qui agit pour ce workspace : connecté ou non, score de santé, jour de chauffe, et surtout les plafonds quotidiens et hebdomadaires. C'est la réponse à « est-ce que je peux encore inviter aujourd'hui ». Aucun identifiant de connexion ni nom de prestataire n'est exposé.

bash
curl "https://captiv-ai.com/api/v1/agent/status" \
  -H "Authorization: Bearer cptv_live_VOTRE_CLE"
réponse · application/json
{
  "data": {
    "object": "agent_status",
    "account": {
      "object": "agent_account", "connected": true,
      "name": "…", "status": "OK", "health_score": 92,
      "connected_at": "2026-05-02T…",
      "warmup": { "day": 41, "started_at": "2026-05-02T…" },
      "caps": {
        "daily_connect": 18, "weekly_connect": 100, "monthly_connect_soft": 300,
        "daily_message": 60, "daily_comment": 40
      }
    },
    "queue": { "pending": 5 },
    "readonly": true
  },
  "meta": { "request_id": "req_…" }
}

05Erreurs

RFC 7807, registre fermé

Toute erreur sort en application/problem+json avec un type stable, un detail lisible et le request_id :

réponse · 403
{
  "type": "https://captiv-ai.com/docs/api/errors#insufficient_scope",
  "title": "Insufficient scope",
  "status": 403,
  "detail": "This endpoint requires scope \"read:leads\". This key has: read:posts.",
  "instance": "/api/v1/leads",
  "request_id": "req_7ad84bf44a49416391264075"
}

Les erreurs de validation (400) ajoutent un tableau errors[] avec path et message par champ. Registre complet :

StatutCodeTitre
401missing_api_keyMissing API key
401invalid_api_keyInvalid API key
401expired_api_keyExpired API key
402plan_requiredPlan upgrade required
403insufficient_scopeInsufficient scope
404not_foundResource not found
404workspace_goneWorkspace no longer exists
400validation_failedValidation failed
429rate_limit_exceededRate limit exceeded
429too_many_auth_failuresToo many authentication failures
503api_disabledAPI temporarily disabled
409conflictConflict
429daily_budget_exceededDaily AI budget exceeded
429generation_quota_exceededDaily generation quota exceeded
429concurrency_limit_exceededToo many generations in flight
409idempotency_key_in_flightIdempotency key in flight
422idempotency_key_reuseIdempotency key reused with a different payload
500internal_errorInternal server error

06Limites

Rate limits, idempotence & versioning

  • Rate limits — deux niveaux. Global, par clé : 1 000 req/h (Pro), 10 000 req/h (Agence), avec X-RateLimit-Limit, X-RateLimit-Remaining et X-RateLimit-Reset (epoch secondes). Par classe, et cette fois par workspace (créer dix clés ne multiplie donc rien) : écritures 300/h (1 500 Agence), téléversements de visuels 60/h (200), générations par type — posts 10/h (30), ressources 3/h (8). Les en-têtes X-RateLimit-Class-*reflètent le compteur qui mordra en premier sur l'endpoint appelé ; un 429 nomme la classe dans rate_limit.class et porte Retry-After.
  • Réponses compactes ?compact=true sur les listes (leads, posts, ressources, interactions) renvoie la projection réduite : uniquement les champs qui servent à décider. Conçu pour les agents, qui paient chaque champ en jetons ; le détail complet reste accessible par le GETpar id. Le connecteur MCP l'active par défaut.
  • Texte écrit par des tiers — noms, titres et commentaires de leads sont du contenu arbitraire. En sortie, Captiv retire ce qui est invisible (largeurs nulles, surcharges bidirectionnelles, bloc de balises, caractères de contrôle) et borne la longueur. Emojis, accents et alphabets non latins passent intacts. Ce qui est écrit en clair reste en clair : si vous branchez un agent, traitez ces champs comme des données, jamais comme des instructions — et donnez-lui une clé lecture seule distincte de vos clés de génération.
  • Versioning — la v1 est stable : les changements cassants n'arriveront que dans une future /v2, la v1 restant maintenue au moins 12 mois après son remplacement. Des champs ou endpoints peuvent s'ajouter à la v1 sans préavis — codez vos parsers en conséquence (ignorez les champs inconnus).
  • Idempotence — le header Idempotency-Keyest honoré sur les endpoints de génération : un retry réseau rejoue la réponse d'origine à l'identique (header Idempotency-Replayed: true) au lieu de payer deux fois. Une clé par requête distincte ; réutiliser une clé avec un autre payload répond 422.
  • Budget IA — les générations consomment le budget quotidien du workspace (partagé avec l'application). Un refus répond 429 daily_budget_exceeded avec le détail chiffré (budget.cap_usd, budget.spent_usd, budget.resets_at — minuit UTC).
  • Ce que l'API n'expose pas — le déclenchement direct d'actions LinkedIn (invitations, DM). Ces actions restent pilotées par l'agent Captiv et ses garde-fous anti-ban ; l'API les expose en lecture (interactions, analytics).

Une question, un besoin d'endpoint ? Écrivez-nous : support@captiv-ai.com — mentionnez votre request_id pour toute erreur.

07Claude

Brancher Claude sur Captiv (MCP)

Captiv expose un serveur MCP (Model Context Protocol, transport Streamable HTTP) sur https://captiv-ai.com/api/mcp. Connectez-le à Claude et parlez à votre workspace en langage naturel : « montre-moi mes leads les plus chauds », « écris-moi un post sur… », « édite le bloc 3 de ma ressource et montre-moi l'aperçu ».

L'authentification est la même clé API que le reste de cette page, envoyée en header Authorization: Bearer. Chaque outil MCP passe par l'API v1 publique : scopes, quotas, budget IA, idempotence et audit s'appliquent à l'identique — un appel MCP est un appel API.

Claude Code (CLI)

terminal
claude mcp add captiv --transport http https://captiv-ai.com/api/mcp \
  --header "Authorization: Bearer cptv_live_VOTRE_CLE"

Claude.ai (connecteur personnalisé)

Paramètres → Connecteurs → « Ajouter un connecteur personnalisé » → URL https://captiv-ai.com/api/mcp. Aucune clé à coller : Claude vous renvoie sur Captiv, vous vous connectez, vous choisissez ce que le connecteur a le droit de faire, et c'est tout. La connexion se fait en OAuth 2.1(PKCE S256).

Par défaut, un connecteur n'obtient que la lecture — leads, posts, ressources, analytics. Les droits d'écriture et de génération se cochent un par un sur l'écran d'autorisation. Vous coupez l'accès quand vous voulez depuis Réglages → API & intégrations ; la coupure est immédiate, elle vaut aussi pour les sessions déjà ouvertes.

API Anthropic (MCP connector)

json
{
  "mcp_servers": [{
    "type": "url",
    "url": "https://captiv-ai.com/api/mcp",
    "name": "captiv",
    "authorization_token": "cptv_live_VOTRE_CLE"
  }]
}
  • Outils exposés — leads (liste, détail, notes, interactions), posts (liste, détail), génération Studio (questions de cadrage, post complet, visuel), ressources (blocs, lint, aperçu signé), follow-ups (mise en file, liste, annulation), analytics. Commencez par whoami.
  • Follow-ups queue_follow_up n'envoie rien. Il inscrit un lead dans la file de relance ; c'est l'agent Captiv qui écrira plus tard, étalé sur plusieurs jours, sous ses quotas quotidiens, ses horaires ouvrés et ses garde-fous anti-ban. Ajoutez-en quinze d'un coup si vous voulez : ils seront dispersés. Un lead déjà en file est refusé — personne ne reçoit deux fois la même relance.
  • Sécurité — les textes provenant de leads sont balisés comme données non fiables dans les sorties d'outils (anti-injection). Aucun outil n'écrit sur LinkedIn : pas d'invitation, pas de message envoyé, pas de commentaire. La seule mécanique qui débouche un jour sur un message est la file de follow-ups ci-dessus, et elle reste entre les mains de l'agent et de ses caps.
  • Scopes — donnez à la clé du connecteur uniquement les scopes dont vous vous servez ; un outil sans scope répond une erreur claire avec le scope manquant.

Prêt à construire

Construisez sur Captiv

Une clé, trois minutes, et vos leads arrivent dans votre stack.

← captiv-ai.comSpec OpenAPI 3.1Gérer mes clésAPI v1 · dernière mise à jour : 7 août 2026