Qorami.AI

Référence API

Qorami est un point de contrôle entre vos agents IA et l'envoi réel d'emails. Avant chaque envoi, l'agent appelle Qorami, qui répond : envoyer, faire valider par un humain, ou ne pas envoyer.
English version →

Authentification

Toutes les requêtes utilisent votre clé API workspace dans l'en-tête x-qorami-api-key. Récupérez-la dans votre espace client (panneau « Démarrer »).

x-qorami-api-key: VOTRE_CLE

Vérifier un email POST /api/verify-email

L'appel principal : l'agent soumet l'email avant de l'envoyer.

Requête

ChampTypeDescription
recipientstringDestinataire de l'email.
subjectstringObjet.
bodystringCorps de l'email.
policyProfilestringgeneral, sales, support ou legal-finance.
idempotencyKeystringOptionnel : rejoue le même résultat sans re-facturer.

Réponse — le contrat agent

Obéissez à nextAction.type :

nextAction.typeCe que fait l'agent
sendEnvoyer l'email.
request_human_confirmationMettre en pause : un humain doit valider (notifié par email + espace client).
do_not_sendNe pas envoyer en l'état.

Exemple — curl

curl -X POST https://qorami.fr/api/verify-email \
  -H "x-qorami-api-key: VOTRE_CLE" \
  -H "content-type: application/json" \
  -d '{
    "recipient": "client@example.com",
    "subject": "Notre offre",
    "body": "Bonjour, voici notre proposition...",
    "policyProfile": "sales"
  }'

Exemple — JavaScript

const res = await fetch("https://qorami.fr/api/verify-email", {
  method: "POST",
  headers: {
    "x-qorami-api-key": process.env.QORAMI_API_KEY,
    "content-type": "application/json",
  },
  body: JSON.stringify({
    recipient: "client@example.com",
    subject: "Notre offre",
    body: emailBody,
    policyProfile: "sales",
  }),
});
const { nextAction, action } = await res.json();

if (nextAction.type === "send") {
  await sendEmail();                 // autorisé
} else if (nextAction.type === "request_human_confirmation") {
  // mettez en file, puis interrogez le statut (voir ci-dessous)
} // sinon do_not_send : on n'envoie pas

Exemple — Python

import os, requests

res = requests.post(
    "https://qorami.fr/api/verify-email",
    headers={"x-qorami-api-key": os.environ["QORAMI_API_KEY"]},
    json={
        "recipient": "client@example.com",
        "subject": "Notre offre",
        "body": email_body,
        "policyProfile": "sales",
    },
)
data = res.json()
if data["nextAction"]["type"] == "send":
    send_email()  # autorisé

Remédiation (que corriger)

En plus de reasons (pourquoi), la réponse renvoie verification.suggestions : des correctifs concrets pour rendre l'email envoyable. L'agent peut corriger et re-vérifier.

"verification": { "decision": "Bloque", "concerns": ["secret_leak"],
  "suggestions": ["Retirez la clé API du corps", "Envoyez la clé via un coffre à secrets"] }

Mode test (dry-run)

Ajoutez "dryRun": true pour obtenir la décision sans consommer de crédit ni enregistrer d'action — idéal pour tester votre politique/intégration.

curl -X POST https://qorami.fr/api/verify-email \
  -H "x-qorami-api-key: VOTRE_CLE" -H "content-type: application/json" \
  -d '{"recipient":"a@b.com","subject":"Test","body":"...","dryRun":true}'
# -> { "dryRun": true, "verification": {...}, "nextAction": {...} }  (0 crédit)

Vérification par lot POST /api/verify-email/batch

Vérifiez jusqu'à 20 emails en un appel et obtenez une décision par item (aperçu, sans débit ni persistance).

curl -X POST https://qorami.fr/api/verify-email/batch \
  -H "x-qorami-api-key: VOTRE_CLE" -H "content-type: application/json" \
  -d '{"items":[{"recipient":"a@b.com","subject":"Hi","body":"..."}, ...]}'
# -> { "count": N, "summary": {...}, "results": [{ "decision": "...", "nextAction": {...} }, ...] }

Découvrir les règles GET /api/policy

L'agent peut lire les règles actives de son workspace pour s'adapter avant d'envoyer (profils + seuils, termes bloqués/à valider, contrat de décision, coût en crédits).

curl https://qorami.fr/api/policy -H "x-qorami-api-key: VOTRE_CLE"
# -> { "profiles": [{ "id":"sales", "reviewThreshold":30, "blockThreshold":70 }, ...],
#      "policy": { "contentRules": { "blockedTerms": [...], "reviewTerms": [...] } },
#      "decisionContract": { "send": "...", "request_human_confirmation": "...", "do_not_send": "..." },
#      "credits": { "costPerVerification": 1, "dryRunCost": 0 } }

Suivre une validation humaine GET /api/email-actions/:id

Quand nextAction.type vaut request_human_confirmation, interrogez cet endpoint pour savoir si un humain a validé. Le champ nextAction renvoie send une fois approuvé, do_not_send si bloqué, ou reste en attente.

curl https://qorami.fr/api/email-actions/ACTION_ID \
  -H "x-qorami-api-key: VOTRE_CLE"

# -> { "action": { "status": "Authorized", ... },
#      "nextAction": { "type": "send", ... } }

Gestion des erreurs et intégration robuste

Pour un fonctionnement autonome et résilient de votre flotte d'agents, gérez les cas d'erreur de facturation, de limites de taux (rate limit), et de validation en suivant ces recommandations :

HTTPCode d'erreurDescription et traitement recommandé
402 insufficient_credits Le compte n'a plus de crédits. L'agent doit suspendre l'envoi et alerter l'administrateur (pensez à activer le rechargement automatique dans votre espace client).
429 rate_limited Limite d'appels atteinte. L'agent doit lire l'en-tête Retry-After et retenter après le délai spécifié.
403 insufficient_scope La clé API n'a pas le droit d'effectuer cette action (par ex. une clé verify qui tente d'approuver ou de bloquer).
422 validation_error Le payload est mal formé ou invalide. Ne pas ré-essayer sans correction préalable.

Exemple de boucle d'intégration complète (JavaScript)

// 1. Appel de vérification initial
async function guardEmail(email) {
  try {
    const res = await fetch("https://qorami.fr/api/verify-email", {
      method: "POST",
      headers: {
        "x-qorami-api-key": process.env.QORAMI_API_KEY,
        "content-type": "application/json",
      },
      body: JSON.stringify(email)
    });

    if (res.status === 402) {
      console.error("Crédits insuffisants. Envoi suspendu.");
      return { status: "insufficient_credits" };
    }

    if (res.status === 429) {
      const retryAfter = parseInt(res.headers.get("retry-after") || "60", 10);
      console.warn(`Limite de taux atteinte. Attente de ${retryAfter}s...`);
      await new Promise(r => setTimeout(r, retryAfter * 1000));
      return guardEmail(email); // Ré-essai après délai
    }

    if (!res.ok) {
      const error = await res.json();
      console.error(`Erreur d'intégration (${res.status}):`, error);
      return { status: "error", error };
    }

    const { nextAction, action } = await res.json();

    if (nextAction.type === "send") {
      return { status: "send" }; // Autorisé : envoyer l'email
    }

    if (nextAction.type === "request_human_confirmation") {
      // 2. Validation humaine requise (polling)
      console.log(`Action ${action.id} en attente. Début de l'interrogation...`);
      return pollActionStatus(action.id);
    }

    return { status: "blocked" }; // do_not_send
  } catch (err) {
    console.error("Erreur réseau ou appel API :", err);
    return { status: "error", err };
  }
}

// 3. Boucle d'interrogation du statut (Polling)
async function pollActionStatus(actionId) {
  const maxAttempts = 10;
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    await new Promise(r => setTimeout(r, 30000)); // Attendre 30 secondes
    
    try {
      const res = await fetch(`https://qorami.fr/api/email-actions/${actionId}`, {
        headers: { "x-qorami-api-key": process.env.QORAMI_API_KEY }
      });
      
      if (res.ok) {
        const { nextAction } = await res.json();
        if (nextAction.type === "send") return { status: "send" };
        if (nextAction.type === "do_not_send") return { status: "blocked" };
      }
    } catch (e) {
      console.warn("Échec de l'appel d'interrogation de statut...", e);
    }
  }
  return { status: "timeout" }; // Expiration sans validation humaine
}

Webhooks (push) POST /api/workspace/webhook

Plutôt que d'interroger le statut en boucle, recevez une notification dès qu'un humain valide ou bloque. Configurez une URL (authentifié, propriétaire) :

curl -X POST https://qorami.fr/api/workspace/webhook \
  -H "x-qorami-api-key: VOTRE_CLE" \
  -H "content-type: application/json" \
  -d '{"url":"https://votre-app.com/qorami-webhook"}'
# -> { "configured": true, "url": "...", "secret": "qwh_..." }  (secret affiché une seule fois)

Qorami envoie alors un POST signé à chaque résolution. type vaut action.confirmed (l'agent peut envoyer) ou action.blocked. L'URL doit être en https et publique.

// corps reçu
{ "type": "action.confirmed", "workspaceId": "...", "sentAt": "...",
  "action": { "id": "act_...", "status": "Authorized", "nextAction": { "type": "send" } } }

// en-tête de signature : x-qorami-signature: t=<ts>,v1=<hmac>
// vérifier : HMAC_SHA256(secret, `${t}.${rawBody}`) === v1

SDK & intégrations

Des clients sans dépendance (JavaScript & Python) et un tool LangChain branchent Qorami en quelques lignes. Code : github.com/…/qorami-sdk.

JavaScript

// npm i qorami
import { QoramiClient } from "qorami";
const qorami = new QoramiClient({ apiKey: process.env.QORAMI_API_KEY });

await qorami.guard(
  { recipient, subject, body, policyProfile: "sales" },
  {
    send: () => mailer.send(),                       // autorisé
    requestHumanConfirmation: (r) => queue(r.action.id), // humain notifié
    doNotSend: () => {},                             // bloqué
  },
);

Python

from qorami import QoramiClient
qorami = QoramiClient(api_key=os.environ["QORAMI_API_KEY"])

result = qorami.verify(recipient="client@example.com", subject="Offre",
                       body=email_body, policy_profile="sales")
if result.next_action_type == "send":
    send_email()

LangChain

from langchain_tool import build_qorami_tool
tool = build_qorami_tool()   # outil 'qorami_check_email' pour votre agent

Pour une flotte d'agents

Plusieurs agents dans un même workspace ? De quoi rendre chacun observable et contrôlable indépendamment.

Autres endpoints

Format d'erreur

Toutes les erreurs suivent la même enveloppe, avec un requestId traçable :

{
  "error": {
    "code": "insufficient_credits",
    "message": "Not enough Qorami credits for this workspace.",
    "requestId": "req_..."
  }
}

Chaque réponse porte aussi l'en-tête x-qorami-request-id.

Crédits

1 action tracée = 1 crédit (un blocage est gratuit). Achetez des packs dans votre espace client. Nouveaux comptes : crédits offerts pour tester.