Flowli-Email
Accueil

Guide d'intégration du SDK

De l'installation à une gestion d'erreurs prête pour la production, y compris le cas attachment_url_expired (HTTP 410) et sa reprise automatique.

1. Installation

index.html
<!-- 1. Charger le SDK (v1.6.0) -->
<script src="https://votre-app.lovable.app/sdk/flowli-email.js"></script>
<script>
  flowli.init("pk_votre_cle_publique");
</script>

<!-- Alternative bundler -->
<!-- npm i --save-dev rien à installer : copiez le fichier ou importez-le en <script type="module"> -->

2. Premiers envois

send / sendSMS / pièces jointes
// Envoi simple
const res = await flowli.send("service_smtp", "tpl_welcome", {
  to: "client@exemple.com",
  first_name: "Ada",
  message: "Bienvenue !"
});
console.log(res.status, res.text); // 200 "OK"

// SMS / WhatsApp
await flowli.sendSMS("service_twilio", "tpl_code", { to: "+15145550123", code: "4821" });

// Pièces jointes : { id } | { content_base64 } | { url } signée
await flowli.send("service_smtp", "tpl_facture", { message: "Votre facture" }, null, {
  attachments: [
    { id: "att_9f3c21" },
    { filename: "cgv.pdf", content_base64: "JVBERi0x..." },
    { url: "https://votre-app.lovable.app/api/public/v1/att/att_77?exp=...&sig=..." }
  ]
});

3. Gestion complète des erreurs

Copiez ce helper tel quel : il couvre les erreurs de pièces jointes, les refus de validation, les quotas 429 et les pannes temporaires, avec reprise après expiration d'URL signée.

flowli-send.js
/**
 * Gestion d'erreurs complète et réutilisable.
 * Toute erreur rejetée par le SDK est une FlowliError :
 *   { name, message, status, code, details, retryable, isAttachmentError, isExpired, attachmentId, expiredAt }
 */
async function envoyerAvecGestionErreurs(payload) {
  const MAX_RETRIES = 3;

  for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
    try {
      return await flowli.send(
        payload.serviceId,
        payload.templateId,
        payload.params,
        null,
        { attachments: payload.attachments }
      );
    } catch (err) {
      // 1) Cas prioritaire : URL signée de pièce jointe expirée (HTTP 410)
      if (flowli.isExpiredAttachmentError(err)) {
        console.warn("URL expirée", err.attachmentId, err.expiredAt, err.message);

        // Le SDK tente déjà un refresh automatique une fois. Si on arrive ici,
        // on régénère explicitement puis on rejoue une dernière fois.
        const refreshed = await flowli.refreshExpiredAttachments(payload.attachments);
        if (refreshed.changed) {
          payload.attachments = refreshed.attachments;
          continue; // rejoue la boucle avec les nouvelles URLs
        }

        // Le fichier a été purgé du stockage : on ne peut pas rejouer tel quel.
        throw new Error(
          "Pièce jointe indisponible (" + err.attachmentId + ") — ré-uploadez le fichier."
        );
      }

      // 2) Autres erreurs de pièces jointes : définitives, inutile de réessayer
      if (flowli.isAttachmentError(err)) {
        console.error("Pièce jointe refusée:", err.code, err.message, err.details);
        throw err; // ex. attachment_too_large, attachment_type_blocked, attachment_not_found
      }

      // 3) Validation / autorisation : afficher à l'utilisateur, ne pas réessayer
      if (err.status === 401 || err.status === 403 || err.status === 422) {
        console.error("Requête refusée:", err.status, err.code, err.message);
        throw err; // clé invalide, origine CORS non autorisée, destinataire invalide
      }

      // 4) Quotas et pannes temporaires : backoff exponentiel
      if (err.retryable && attempt < MAX_RETRIES) {
        const waitMs = err.status === 429
          ? (Number(err.details.retry_after) || 2 ** attempt) * 1000
          : 2 ** attempt * 500;
        console.warn("Nouvel essai dans", waitMs, "ms —", err.code || err.status);
        await new Promise((r) => setTimeout(r, waitMs));
        continue;
      }

      throw err;
    }
  }
}

4. Régénérer une URL signée

POST /api/public/v1/att/refresh via le SDK
// Régénération manuelle d'URLs signées expirées
const { attachments, changed, failed } = await flowli.refreshExpiredAttachments(myAttachments);
if (failed.length) console.warn("Non régénérables (purgés) :", failed);

// Ou par identifiants
const out = await flowli.refreshAttachmentUrls(["att_9f3c21", "att_77"], { ttl: 3600 });
console.log(out.attachments[0].url);

5. Valider avant d'envoyer (dry-run)

Validation sans envoi ni facturation
// Valider clés, template, destinataires et pièces jointes SANS envoyer
const report = await flowli.send("service_smtp", "tpl_facture", params, null, {
  attachments,
  dryRun: true
});

if (!report.ok) {
  report.errors.forEach((e) => console.error(e.code, e.message));
} else {
  console.log(report.preview.subject, report.attachments, report.estimate);
}

// Pré-validation 100% locale (aucun appel réseau)
const check = flowli.validateAttachments(attachments, { channel: "email" });
if (!check.ok) alert(check.message); // ex. "Fichier trop volumineux (max 8 Mo)"

6. Référence des codes d'erreur

CodeHTTPAction recommandée
attachment_url_expired410URL signée périmée avant l'envoi → régénérer via refreshExpiredAttachments() puis rejouer.
attachment_expired410Le fichier stocké a dépassé son TTL → ré-uploader.
attachment_not_found404Identifiant inconnu pour ce compte → vérifier l'id.
attachment_url_invalid400Signature altérée ou URL non https → ne pas rejouer.
attachment_too_large413Dépasse la taille max du canal (8 Mo email, 5 Mo MMS).
attachments_too_many422Trop de fichiers pour la politique du compte.
attachment_type_blocked415Extension/MIME interdit (exécutables, archives selon politique).
rate_limited429Quota canal dépassé → attendre details.retry_after puis réessayer.
invalid_public_key401Clé publique inconnue ou révoquée.
origin_not_allowed403Domaine appelant absent de la whitelist CORS du service.

Bonnes pratiques

  • Ne jamais exposer une clé sk_ dans le navigateur : uniquement pk_.
  • Préférer { id } à { url } pour les pièces jointes stockées : aucun risque d'expiration de signature.
  • Écouter le webhook attachment.url_expired pour être alerté côté serveur.
  • Utiliser dryRun en CI pour valider vos templates avant déploiement.