Intégration API

SendGrid : la facture PDF est encodée deux fois avant l’envoi

Jérémy Chomel Dawap
  • Date éditoriale : 16 septembre 2025
  • Temps de lecture : 16 minutes
  1. Pour qui : une facture correcte devient illisible dans le connecteur
  2. Ce que le SDK Python conserve réellement dans la pièce jointe
  3. Atelier : reproduire le double encodage sans envoyer de facture
  4. Erreurs fréquentes : deviner la représentation et multiplier les décodages
  5. Valider le document attendu, pas seulement un PDF ouvrable
  6. Constituer une recette exploitable par l’équipe de facturation
  7. Plan d’action : corriger la frontière sans renvoyer aveuglément
  8. Conclusion : une conversion explicite et une identité vérifiable
Portrait de Jérémy Chomel

Une facture s’ouvre correctement dans l’ERP. Le connecteur la joint à un message SendGrid, le destinataire télécharge un fichier nommé facture.pdf, puis son lecteur refuse de l’ouvrir. L’erreur peut précéder tout envoi : un adaptateur a encodé en Base64 un contenu que le logiciel de facturation fournissait déjà sous cette forme. Le nom, le type MIME et l’extension restent plausibles ; les octets transportés ont changé.

Le risque est de confondre une chaîne sérialisée avec le document attendu. Pour diagnostiquer cette rupture, le contrat d’intégration API doit préciser la représentation du document à chaque frontière. Un champ nommé « contenu » ne dit pas s’il contient les octets du PDF, leur représentation Base64 ou une réponse HTML d’authentification. Ajouter systématiquement un encodage masque cette différence au lieu de la résoudre.

L’atelier ci-dessous emploie réellement les modèles du SDK Python SendGrid et un lecteur PDF natif. Deux factures entièrement fictives permettent d’observer un double encodage, une pièce lisible appartenant au mauvais dossier et un fichier tronqué. Aucun appel Mail Send ni e-mail n’est effectué. Les contrôles portent sur la préparation locale du message ; ils ne prouvent ni son acceptation par SendGrid ni sa remise en boîte de réception.

Le problème concerne les éditeurs, responsables de facturation et équipes SI qui veulent relier SendGrid aux documents de leurs applications métier. Les contrats et les bibliothèques présentés sont ceux vérifiés en octobre 2026. Le périmètre du témoin Python est indiqué pour pouvoir reprendre le diagnostic sur la version réellement utilisée par votre connecteur.

Pour qui : une facture correcte devient illisible dans le connecteur

Reconstituer les transformations avant de modifier l’envoi

Commencez par le fichier produit par le logiciel de facturation, puis identifiez la sortie de son interface : téléchargement binaire, chaîne Base64 dans une réponse JSON ou lien temporaire. Relevez ensuite le mapping appliqué par le middleware et la valeur placée dans la pièce jointe du message. La même chaîne peut traverser plusieurs services sans exception technique, alors que sa signification a été perdue dès le premier adaptateur.

Le point de comparaison utile est le document effectivement attendu pour le dossier, pas seulement un nom de fichier. Conservez une référence de document, une version et une empreinte du binaire à un emplacement contrôlé. Un CRM qui affiche « facture générée » ne prouve pas que le payload préparé contient ce document. Cette distinction évite de chercher d’abord un problème de délivrabilité lorsque l’artefact local est déjà inutilisable.

Séparer les responsabilités du producteur et de l’adaptateur

Le producteur doit annoncer ce qu’il retourne ; l’adaptateur doit convertir cette représentation vers le contrat d’envoi. Si le premier fournit des octets, le second les encode une fois. Si le premier fournit déjà la chaîne Base64 attendue, le second la transmet après les vérifications convenues. Un changement d’interface du producteur impose donc une revue du mapping, même lorsque le champ conserve le même nom.

L’équipe qui exploite le flux doit pouvoir retrouver cette décision sans consulter le contenu d’une facture client. Des journaux contenant l’identifiant du document, la représentation déclarée, la version de l’adaptateur et le résultat du contrôle suffisent à localiser la branche suivie. Écrire la totalité du PDF encodé dans les logs n’aide pas à définir cette responsabilité et expose inutilement le contenu documentaire.

Ce que le SDK Python conserve réellement dans la pièce jointe

FileContent ne devine pas la représentation source

La référence officielle Mail Send décrit le contenu encodé en Base64 dans attachments[].content. Dans le SDK Python sendgrid 6.12.5 inspecté, FileContent conserve la valeur fournie et la restitue ; Attachment puis Mail.get() l’incluent dans la structure préparée. Ces helpers n’identifient pas automatiquement un PDF déjà encodé. Ce constat est limité à cette bibliothèque et à cette version.

L’exemple officiel de pièce jointe Python montre une lecture binaire suivie d’un encodage. Reprendre cette étape dans un connecteur dont l’entrée est déjà une chaîne Base64 produit une couche supplémentaire. Le SDK peut conserver cette chaîne sans erreur locale : l’absence d’exception de sérialisation n’établit pas la validité du document récupéré après décodage.

Un type MIME correct ne répare pas les octets

Le nom facture.pdf et le type application/pdf décrivent la pièce ; ils ne transforment pas son contenu en PDF. Le protocole JSON transporte une chaîne, pas les octets bruts du fichier. Dans l’exercice, transmettre directement les octets au helper ne les encode pas non plus : Mail.get() les conserve et la sérialisation JSON standard échoue avec TypeError avant tout appel réseau.

La disposition attachment appartient au profil de facture choisi pour cet atelier. Une disposition inline peut être légitime dans un autre message ; un e-mail sans pièce jointe peut aussi être parfaitement volontaire. Le contrôle local doit exprimer le besoin métier de ce flux, sans transformer ses règles en restrictions générales de SendGrid.

Atelier : reproduire le double encodage sans envoyer de facture

Exécuter les vrais helpers et lire le résultat natif

Dans le scénario fictif, le programme Python complet construit deux PDF fictifs en mémoire avec ReportLab, prépare des messages avec les modèles SendGrid et lit les documents décodés avec pypdf. Son exécution vérifiée utilise Python 3.14.8, sendgrid 6.12.5, pypdf 6.21.0 et reportlab 5.0.1. Les paquets originaux ont été comparés aux distributions officielles ; aucun helper fournisseur ni moteur PDF n’a été modifié pour obtenir les résultats.

Par exemple, le témoin naïf prend la représentation Base64 de la facture A et l’encode une seconde fois. Le SDK conserve effectivement le contenu fourni. Après un seul décodage, le préfixe obtenu est JVBER, du texte Base64, au lieu du préfixe PDF attendu. Le vrai lecteur pypdf lève PdfReadError. Voici la sortie de cette observation, extraite de l’exécution :

{
  "sdkPreservedSuppliedContent": true,
  "decision": "pdf_reader_review",
  "decodedOncePrefix": "JVBER",
  "pdfErrorType": "PdfReadError"
}

Comparer deux réparations et un contre-exemple métier

Deux chemins préparent correctement le même document témoin : encoder une fois ses octets bruts, ou transmettre sa représentation Base64 déjà produite. Dans les deux cas, le décodage unique restitue exactement l’empreinte attendue de la facture A et le lecteur ouvre une page. Le programme ne choisit pas le chemin en tentant des transformations successives : il exige une représentation source déclarée.

Paradoxalement, la facture B passe le contrôle de lecture tout en étant écartée du dossier A. Ce vrai PDF lisible est correctement encodé, mais porte une autre référence fictive et son empreinte diffère de celle attendue pour A. Elle est donc orientée vers une revue d’identité. Un contrôle de format réussi ne suffit pas à autoriser le rattachement au dossier.

Pièce préparéeObservation locale
Base64 encodé deux foisTexte JVBER après décodage ; erreur du lecteur PDF.
Base64 transmis sans réencodagePDF A lisible et empreinte attendue retrouvée.
Octets encodés une foisMême PDF A, même empreinte après décodage.
Autre PDF lisibleLecture réussie ; identité documentaire différente.
HTML renommé en PDFDécodage possible ; erreur du lecteur PDF.
PDF tronquéPréfixe PDF présent ; lecture native en échec.

Installer les dépendances et interpréter les compteurs

Téléchargez aussi le fichier des onze dépendances figées, puis lancez le programme dans un environnement dédié. L’exécution vérifiée a été isolée dans un espace réseau séparé avec une racine en lecture seule ; elle n’utilise aucun client d’envoi. Installer les paquets nécessite en revanche d’accéder à leur distribution, sauf si vous disposez déjà d’un dépôt local.

Les 32 assertions couvrent 17 cas. Quinze préparations appellent réellement Mail.get(). Six tentatives passent au lecteur PDF : trois réussissent, dont celle du mauvais document, et trois provoquent les erreurs natives attendues. Les deux PDF sont construits en mémoire ; le programme ne les écrit pas sur disque. Ces nombres décrivent cette expérience locale, pas une campagne de tests de l’API distante.

python3 -m venv .venv
.venv/bin/python -m pip install -r requirements.txt
.venv/bin/python sendgrid_pdf_base64.py

# Compteurs de l’exécution vérifiée
checksPassed: 32
cases: 17
nativeMailSerializations: 15
nativePdfReaderAttempts: 6
nativePdfReaderSuccesses: 3
expectedNativePdfReaderFailures: 3
mailSendApiCalls: 0
emailsSent: 0

Erreurs fréquentes : deviner la représentation et multiplier les décodages

Distinguer octets, chaîne Base64 et data URI

Une représentation binaire et sa chaîne Base64 décrivent le même fichier, mais exigent des traitements différents. Une data URI ajoute encore une enveloppe, avec un préfixe indiquant le média et l’encodage. Le champ de pièce jointe de cet atelier attend uniquement la chaîne Base64 convenue. Il ne faut pas y copier indistinctement une URL de prévisualisation, un préfixe data: ou le résultat textuel d’un téléchargement qui a échoué.

Le validateur local utilise le décodage strict de la bibliothèque standard Python. Les caractères invalides, la data URI et une chaîne avec retour à la ligne sont orientés vers une revue de syntaxe dans ce profil. Cela ne démontre pas que SendGrid rejetterait universellement toutes les variantes de retours à la ligne ; aucun de ces cas n’a été envoyé au fournisseur.

Ne pas confondre normalisation et récupération hasardeuse

Décoder plusieurs fois jusqu’à obtenir un fichier ouvrable crée une convention implicite difficile à maintenir. Le prochain producteur peut retourner un autre format ou une enveloppe JSON différente. La correction doit porter sur la frontière identifiée : entrée déclarée, transformation unique, sortie attendue et propriétaire du contrat. Une représentation inconnue part en revue avant la préparation du message. Le seuil d’acceptation du profil local est explicite : une seule pièce, lisible et identique au document témoin attendu.

Le programme refuse également une valeur dont le type contredit sa déclaration, sans appeler les helpers SendGrid. Cette protection conserve l’origine du défaut. Elle évite qu’un convertisseur « tolérant » fasse disparaître le symptôme tout en laissant deux applications incompatibles échanger des documents dont personne ne maîtrise la transformation.

Valider le document attendu, pas seulement un PDF ouvrable

Expliquer le résultat contre-intuitif du mauvais PDF

Une chaîne Base64 valide peut produire un document inutilisable ; un PDF parfaitement lisible peut aussi être la mauvaise facture. L’atelier rend ces deux écarts indépendants. Le préfixe %PDF- n’est pas une preuve suffisante : le témoin tronqué le possède et échoue au lecteur. À l’inverse, le témoin B s’ouvre mais ne correspond pas à la pièce sélectionnée pour le dossier A.

L’empreinte sert ici à vérifier l’égalité exacte avec un binaire témoin connu. Elle ne prouve pas à elle seule l’authenticité fiscale d’une facture ni la fiabilité de la source qui a fourni l’empreinte attendue. Dans un flux réel, sa production doit être rattachée à la sélection du document, à sa version et au bon dossier, puis conservée séparément du simple nom transmis à l’e-mail.

Observer les pièces fictives sans utiliser de données client

Les deux pièces, facture fictive A et facture fictive B, exportées pour inspection correspondent exactement aux empreintes du programme. Elles portent une mention explicite de test et ne constituent pas des factures réelles. Chaque page a été rendue et examinée, avec son texte, sa référence et sa police embarquée.

La lecture de ces deux fichiers par pypdf et leur contrôle visuel sont des vérifications distinctes de l’exécution principale. Un lecteur PDF ne certifie pas à lui seul toutes les propriétés du format, sa sécurité ou le contenu métier de la pièce. La recette d’intégration doit donc combiner l’identité documentaire, la lisibilité nécessaire à l’usage et les vérifications effectivement demandées par le service de facturation.

Constituer une recette exploitable par l’équipe de facturation

Comparer le message préparé plutôt que le seul appel au helper

La préparation de l’API REST doit être contrôlée à la sortie de la chaîne de sérialisation. La priorité est de vérifier le contenu final, après toutes les transformations et avant son accès au transport. Un test qui vérifie uniquement la valeur passée à FileContent peut manquer un deuxième traitement ajouté après Mail.get(). À l’inverse, une conversion correcte dans le dernier adaptateur ne répare pas une erreur de sélection faite en amont. Sur un témoin fictif, enregistrez le contrat d’entrée et inspectez la structure réellement destinée au transport : nombre de pièces, contenu, nom, média et disposition.

Le programme inclut un message sans pièce et un message avec deux pièces. Ils passent par les vrais helpers mais divergent du profil local qui attend exactement une facture. Trois autres cas modifient séparément le média, la disposition ou le nom. Les décisions obtenues ne prédisent pas une réponse HTTP de SendGrid : elles indiquent quel engagement du flux documentaire n’est plus respecté. L’exploitant peut ainsi lire une cause précise au lieu d’un unique état « envoi impossible » qui confond préparation, transport et document.

Figer la version documentaire utilisée pour la reprise

Lorsqu’une facture est régénérée entre l’échec et sa reprise, le fichier peut porter le même nom tout en contenant d’autres octets. La recette doit déterminer si la tentative concerne la version initiale ou la nouvelle pièce choisie par le service de facturation. Recopier une empreinte issue de la tentative précédente sur le nouveau document produirait une fausse alerte ; remplacer silencieusement la pièce détruirait la trace du choix. Cette décision relève du contrat du flux et doit rester visible pour son responsable.

Pour qualifier un correctif, repartez donc du même témoin et de la même représentation source que lors du défaut, puis ajoutez une seconde version explicitement identifiée. Vérifiez que la première retrouve son empreinte après la transformation corrigée et que la seconde n’est pas acceptée sous la référence de la première. Conservez les résultats avec la version du connecteur et les dépendances réellement exécutées. Une mise à jour du SDK, du générateur PDF ou du producteur devient alors un changement examinable, avec une régression documentaire reproductible plutôt qu’une simple affirmation de compatibilité.

Plan d’action : corriger la frontière sans renvoyer aveuglément

Rendre la préparation observable et révisable

À l’entrée, le responsable du connecteur définit le contrat de représentation avec l’éditeur du logiciel source. Le middleware vérifie ce contrat, sélectionne la conversion et produit une sortie traçable. Le mapping conserve la référence de pièce et la version de transformation ; le monitoring distingue défaut de syntaxe, erreur de lecture et mauvais rattachement. Cette observabilité permet d’assigner la correction à la bonne équipe.

Avant de reprendre la synchronisation, l’exploitant vérifie les journaux, le statut connu de la tentative et la correspondance document-destinataire. Le runbook indique qui autorise une reprise, quelle pièce constitue l’entrée de référence et quelle sortie est attendue. Un timeout d’envoi ne justifie pas à lui seul un retry : l’atelier ne fournit aucune preuve sur l’état distant d’un message réellement soumis.

  • Qualifier la représentation retournée par chaque producteur documentaire et son versioning.
  • Figer un témoin fictif, son empreinte et le dossier auquel il appartient.
  • Contrôler le message préparé après sérialisation, avant de lui donner accès au transport.
  • Faire valider la reprise selon les traces réelles de la tentative et les droits de l’opérateur.

Étendre la recette aux accès entre processus et aux rôles documentaires

Une correction de Base64 ne résout pas un fichier absent du processus chargé de l’envoi. Le diagnostic de la pièce jointe accessible au worker n8n traite cette autre frontière : l’accès effectif au binaire par le processus qui prépare le message. Les deux diagnostics se complètent sans imposer la même cause à tous les fichiers illisibles.

Le même raisonnement d’identité vaut lorsqu’une réponse contient plusieurs documents valides. L’atelier DHL sur la facture PDF confondue avec l’étiquette examine le rôle attendu du document. Pour SendGrid, l’objectif reste de préparer la pièce choisie pour le bon destinataire, puis de vérifier séparément le résultat réel du transport.

Conclusion : une conversion explicite et une identité vérifiable

Le double encodage survit facilement à une préparation de message : il produit encore une chaîne, avec un nom et un type MIME convaincants. Le témoin natif montre précisément où cette apparence cesse de suffire. Un décodage unique doit restituer le document attendu ; une lecture réussie doit ensuite être rapprochée du bon dossier.

La correction durable consiste à déclarer la représentation d’entrée et à confier une transformation déterminée au bon adaptateur. Les cas d’erreur doivent conserver leur sens : syntaxe, lecture, métadonnées, identité ou état d’envoi inconnu. Ils ne doivent pas déclencher automatiquement la même tentative de récupération ou de réexpédition.

Notre accompagnement en intégration API permet d’examiner cette chaîne entre logiciel de facturation, CRM, middleware et SendGrid, de formaliser les contrats puis de mettre en place la préparation et les contrôles adaptés. Le point de départ est un document fictif représentatif et les transformations réellement appliquées ; la décision de remise en service repose ensuite sur la recette convenue et les observations du système concerné.

Portrait de Jérémy Chomel

Fiabiliser vos factures préparées pour SendGrid

Dawap examine les représentations documentaires, les mappings et les contrôles entre votre logiciel de facturation, votre middleware et SendGrid.

Articles recommandés

Illustration du routage des documents PDF MyDHL entre facture commerciale et étiquette transport dans un WMS Intégration API DHL : la facture part à la place de l’étiquette Lire l'article
  • 12 septembre 2025
  • Lecture ~18 minutes

Le WMS ouvre bien un PDF, mais le poste reçoit une facture au lieu du document transport. Sur MyDHL REST, distinguez documents, typeCode, imageFormat et les options qui regroupent les pages. Un exercice pypdf reproduit une sélection par position et contrôle des fichiers fictifs avant toute impression réelle.

Illustration du trajet d’une pièce jointe entre réception n8n, stockage, worker et application de gestion Intégration API n8n : pièce jointe absente du worker Lire l'article
  • 13 août 2025
  • Lecture ~13 minutes

Le document est visible dans l’éditeur, mais son transfert ERP échoue selon le worker. Le diagnostic distingue référence, octets et preuve de rattachement. Une expérience locale de 28 contrôles montre les écarts de lecture, sans cluster n8n, appel distant ou écriture dans l’application de gestion destinataire.

Illustration des références documentaires et des transferts de fichiers entre applications métier Intégration API Google Drive : PDF en double Lire l'article
  • 3 septembre 2025
  • Lecture ~16 minutes

Le bon de livraison existe deux fois après un timeout du connecteur. Distinguez l’identifiant de fichier réservé de la session qui reçoit ses octets. Un exercice de 63 contrôles utilise le SDK Google avec 24 échanges préparés, observe les reprises à l’octet confirmé et conserve le conflit 409 comme une exception, sans connexion à Drive.

Illustration d’un inventaire documentaire Dropbox incomplet après la reprise du connecteur métier Intégration API Dropbox : des pièces disparaissent Lire l'article
  • 4 septembre 2025
  • Lecture ~16 minutes

Le plan reste dans Dropbox, mais le portail ne le propose plus après la relance du connecteur. Retrouvez la variante du refus et la page qui manque avant de retirer une pièce de l’inventaire actif. Notre expérience de 45 contrôles suit neuf échanges préparés du SDK, garde les références métier et refuse une reconstruction incomplète.

Fiabiliser vos factures préparées pour SendGrid

Dawap examine les représentations documentaires, les mappings et les contrôles entre votre logiciel de facturation, votre middleware et SendGrid.