API partenaire v1 · Version préliminaire, pas encore en service

API partenaire v1 : aperçu

Version préliminaire, pas encore en service. Cette page décrit le contrat v1 proposé pour les partenaires (version 1.0.0-rc.1). Aucun point d’accès décrit ici n’est déployé, et aucune clé d’API ne peut encore être émise.

La version 1 est la couche destinée aux partenaires, construite au-dessus du service de données de Crown Intelligence. Elle ajoute des clés API avec portées, la mesure de l’utilisation, la mise en correspondance par lots avec un statut clair pour chaque requête, des webhooks, la surveillance des entrées de listes publiques et des dossiers de preuves. Cette page explique les règles que suit chaque opération.

État de cette API

Le contrat v1 est une ébauche, version 1.0.0-rc.1. Il n’est ni déployé, ni publié, ni facturé. Les adresses ci-dessous sont proposées; elles ne pointent pas encore vers un service en fonctionnement.

Adresses de base proposées pour l’API partenaire v1
Production (proposée)https://api.crownintelligence.net
Bac à sable (proposé)https://sandbox.api.crownintelligence.net, mêmes registres publics, jamais facturé
Contrat lisible par machinecrown-intelligence-v1.yaml (OpenAPI 3.1.0, ébauche)

Clés API et portées

Envoyez la clé dans l’en-tête X-Api-Key à chaque requête, sauf pour GET /v1/status, qui n’exige aucune clé.

  • Les clés de production commencent par ci_live_. Les clés de bac à sable commencent par ci_test_. Une clé de bac à sable n’atteint jamais une route de production et ne crée jamais de commande facturable.
  • Le secret n’est affiché qu’une seule fois, à la création de la clé. Crown Intelligence ne conserve qu’un hachage et le préfixe visible.
  • Chaque clé comporte des portées explicites. Une clé qui n’a pas la portée requise par une route reçoit l’erreur 403 forbidden_scope.
  • La révocation est immédiate et définitive. Une clé révoquée renvoie 401 key_revoked. Les clés sont retirées, jamais supprimées.
  • Une clé de production doit indiquer la commande d’abonnement à laquelle elle est facturée (order_id).
Portées de l’API et ce que chacune couvre
PortéeCouvre
match:readPOST /v1/match et rapprochement
search:readRecherche, fiches, entités et collections
sources:readSources, programmes et statistiques des collections
exports:readTéléchargements de registres, lorsque la licence le permet
webhooks:managePoints d’accès des webhooks et leurs journaux de livraison
monitors:manageSurveillance des entrées de listes publiques
cases:read, cases:writeDossiers, décisions et dossiers de preuves
usage:readUtilisation en unités
keys:manageCréation et révocation des clés

Essayez : une requête avec clé

curl :

curl https://api.crownintelligence.net/v1/sources \
  -H "X-Api-Key: $CI_API_KEY"

Node (20 ou version ultérieure) :

const res = await fetch("https://api.crownintelligence.net/v1/sources", {
  headers: { "X-Api-Key": process.env.CI_API_KEY }
});
if (!res.ok) throw new Error("HTTP " + res.status);
const body = await res.json();

Vérification de l’état sans clé :

curl https://api.crownintelligence.net/v1/status

Référence de facturation : appels internes seulement

Chaque appel entre services comporte une référence de facturation, envoyée dans l’en-tête X-Billing-Ref sous forme de JSON ASCII compact d’au plus 1 024 octets. Elle indique pour qui l’appel est effectué, afin que l’utilisation puisse être facturée et que le coût soit consigné.

{"source_service":"partner-api","source_account":"acct_demo_bank",
 "order_id":"ord_2026_1041","type":"ORDER","parent_call":null,"invoice_line_id":null}
  • Les appels des partenaires peuvent omettre l’en-tête. La passerelle renseigne source_service et source_account à partir de la clé authentifiée, et déduit la commande de la valeur order_id de la clé. Un partenaire ne peut pas les définir lui-même. Si un en-tête envoyé contredit sa clé, l’appel est refusé avec l’erreur 400 billing_ref_invalid.
  • Les appels internes doivent l’envoyer. Un service Crown qui appelle Crown Intelligence sans l’en-tête est refusé avec l’erreur 400 billing_ref_missing. L’objet est transmis tel quel tout au long de la chaîne, et chaque maillon définit parent_call comme l’appel qui l’a déclenché.
  • type vaut ORDER lorsqu’un client paie l’appel. Il vaut OVERHEAD pour le trafic système, sans facture, le coût étant tout de même consigné. Un ORDER doit comporter order_id; un OVERHEAD comporte order_id: null.
  • Aucun autre champ n’est autorisé. L’API n’affiche jamais de montants : l’utilisation est exprimée en unités seulement, et les prix figurent sur la facture.
// Internal Crown service calling match: pass the caller's billing_ref unchanged in X-Billing-Ref.
const billingRef = {
  source_service: "your-registered-service-id",
  source_account: null,
  order_id: null,
  type: "OVERHEAD",
  parent_call: incomingBillingRefId,   // id of the call that triggered this one, or null at the top
  invoice_line_id: null
};
const res = await fetch("https://api.crownintelligence.net/v1/match", {
  method: "POST",
  headers: {
    "X-Api-Key": process.env.CI_INTERNAL_KEY,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
    "X-Billing-Ref": JSON.stringify(billingRef)
  },
  body: JSON.stringify({ queries: [{ query_ref: "svc-1", entity_type: "Company", name: "Northwind Trading LLC" }] })
});

Points en suspens : l’identifiant de source partner-api n’est pas encore enregistré dans le catalogue de services, et le propriétaire n’a pas encore décidé si les lignes d’utilisation comportent une référence de prix. Voir le contrat, sections 4 et 16.

Statuts de correspondance : jamais « clair » par défaut

Chaque requête d’une réponse de correspondance a l’un des quatre statuts. La règle est simple : une requête n’est déclarée claire que si tous les registres qu’elle devait couvrir ont été consultés et que rien ne correspond. Le mot clear n’est une valeur nulle part dans l’API.

Les quatre statuts de correspondance
StatutSignificationPeut-on le traiter comme une absence de correspondance?
hitAu moins une correspondance à min_band ou au-dessusNon. Examinez les correspondances.
no_hit_searchedTous les registres énumérés ont été consultés. Aucune correspondance. La réponse énumère chaque registre avec loaded_at.Oui. C’est le seul résultat négatif.
not_searchedRien n’a été vérifié, par exemple un identifiant qu’aucun registre chargé ne couvre. La raison est indiquée.Jamais. Non vérifié ne signifie pas clair.
errorLa vérification a échoué. Réessayez.Jamais. Une vérification échouée ne signifie pas clair.

Un statut not_searched ou error ne doit jamais être affiché comme clair, enregistré comme clair ni transmis comme clair. Concevez votre logique de décision de façon que seul no_hit_searched puisse produire un résultat négatif.

Niveaux de correspondance

Chaque correspondance comporte un niveau plutôt qu’un seuil brut. Les niveaux découlent du score (de 0 à 1) et des identifiants concordants.

Niveaux de correspondance et leurs règles
NiveauRègleRenvoyé lorsque
STRONGScore de 0,90 ou plus, ou IMO, MMSI ou LEI exactToujours, si à min_band ou au-dessus
POSSIBLEScore de 0,75 à 0,89Valeur par défaut de min_band
WEAKScore de 0,60 à 0,74Seulement lorsque min_band est WEAK

Rien en dessous de 0,60 n’est renvoyé. Les seuils des niveaux sont proposés et n’ont pas encore été calibrés sur un ensemble de paires étiquetées; ne fondez donc aucune règle fixe sur eux avant la publication de la calibration. Les versions du moteur de correspondance seront indiquées dans chaque réponse.

Limites de débit et quotas

Ces valeurs par défaut sont proposées. Les limites des forfaits sont provisoires jusqu’à ce que le propriétaire les établisse.

Limites de débit proposées
LimiteValeur par défaut proposée
Requêtes par minute, par clé120
Unités de requête par minute, par clé600
Requêtes par lot100
Requêtes d’exportation par minute, par clé10
Unités mensuellesDéfini selon le forfait. Tout dépassement renvoie 429 quota_exceeded.

Chaque réponse comporte RateLimit-Limit, RateLimit-Remaining et RateLimit-Reset. Les appels mesurés comportent également X-Usage-Units. Une réponse 429 comporte Retry-After.

Idempotence et versions

Chaque requête POST accepte un en-tête Idempotency-Key d’au plus 255 caractères. Répéter la même clé avec le même corps dans un délai de 24 heures renvoie la réponse enregistrée, sans second débit. Réutiliser la clé avec un corps différent renvoie 409 idempotency_mismatch.

La version figure dans le chemin (/v1). Les changements au sein de v1 sont additifs. Un changement incompatible passe à /v2, avec un préavis d’au moins six mois avant toute dépréciation.

Erreurs

Chaque erreur est de type application/problem+json (RFC 9457). Chaque erreur comporte un code stable, un request_id à indiquer au soutien technique, ainsi que son message en anglais (messages.en) et en français (messages.fr).

{"type":"https://api.crownintelligence.net/problems/invalid_request","title":"Invalid request",
 "status":400,"code":"invalid_request","detail":"queries[3].name must be at least 2 characters",
 "messages":{"en":"queries[3].name must be at least 2 characters",
   "fr":"queries[3].name doit comporter au moins 2 caractères"},
 "request_id":"req_01J9Z3K4M6","errors":[{"pointer":"/queries/3/name","code":"too_short",
   "message":{"en":"Use at least 2 characters.","fr":"Utilisez au moins 2 caractères."}}]}
Codes d’erreur courants
HTTPCodeQuand
400invalid_requestLe corps ou un paramètre ne respecte pas le schéma
400billing_ref_missing, billing_ref_invalidUn appel interne n’a pas de référence de facturation, ou ses champs ne concordent pas avec la clé
400personal_data_not_acceptedLe corps d’une surveillance contient un champ de données personnelles
401unauthenticated, key_revokedLa clé est absente, inconnue ou révoquée
403forbidden_scope, forbidden_environmentLa portée est absente, ou une clé de bac à sable appelle une route en production
404not_foundÉgalement utilisé lorsqu’une fiche n’est pas visible pour le compte
409conflict, idempotency_mismatchUn doublon, ou une Idempotency-Key réutilisée avec un corps différent
413payload_too_large, batch_too_largePlus de 1 Mo, ou plus de 100 requêtes
429rate_limited, quota_exceededVoir les limites de débit ci-dessus. Attendez Retry-After.
503register_unavailable, unavailableUn registre ou le service est indisponible. Réessayez avec un délai d’attente progressif.
500internal_errorSignalez-la avec le request_id

Dans un lot, une requête individuelle peut échouer avec un statut error. Le lot lui-même renvoie tout de même 200, et cette requête n’est jamais signalée comme sans correspondance.

const res = await fetch(url, { headers: { "X-Api-Key": process.env.CI_API_KEY } });
if (!res.ok) {
  const problem = await res.json();              // application/problem+json
  console.error(problem.code, problem.request_id, problem.messages.en);
  if (res.status === 429 || res.status === 503) {
    const wait = Number(res.headers.get("Retry-After") ?? 5);   // back off, then retry the same request
  }
}

Pages de cette section