Limites
Garder la requête ouverte jusqu’à l’arrivée du courrier
Section intitulée « Garder la requête ouverte jusqu’à l’arrivée du courrier »Ajoutez wait à GET/messages, en millisecondes. La requête répond dès qu’un message correspond à vos critères :
curl --silent --show-error --fail-with-body --get \ -H "Authorization: Bearer $FACTEUR_API_KEY" \ --data-urlencode "inbox=j3k9x2mq" \ --data-urlencode "hasOtp=true" \ --data-urlencode "wait=20000" \ https://api.facteur.eu/messagesCe que fait cette requête :
- Elle regarde une fois avant d’attendre. Un message arrivé il y a une seconde revient tout de suite.
- Tant que rien ne correspond, elle regarde de nouveau toutes les 250 millisecondes, et répond dès qu’un message correspond.
- Si rien n’a correspondu à l’échéance, elle répond
200avec une liste vide. Votre test décide si c’est un échec. - Sans
wait, ou avec0, c’est une recherche unique avec une réponse unique.
Envoyez la recherche avant de déclencher l’action qui envoie l’e-mail. Si elle part après, elle peut encore trouver un message déjà arrivé, mais votre test n’attend plus en même temps le courrier et l’action.
Les plafonds
Section intitulée « Les plafonds »| Plafond | Valeur | Ce qui se passe au-delà |
|---|---|---|
| Durée d’une attente | 300000 ms, cinq minutes | Un wait plus long est ramené à cinq minutes. Il n’est pas refusé. |
| Attentes ouvertes en même temps, pour une organisation | 20 | La recherche suivante répond 429 avec le code TOO_MANY_WAITS et Retry-After: 1. |
| Requêtes d’une clé | 600 par minute | La requête répond 429 avec le code RATE_LIMITED et un en-tête Retry-After. |
| Taille d’une page de messages | 200 | Un limit plus grand est ramené à 200. La valeur par défaut est 50. |
Une attente ouverte garde une connexion jusqu’à sa réponse. C’est pourquoi le deuxième plafond compte toute l’organisation et non une clé : toutes vos clés puisent dans les mêmes vingt. Une place se libère quand une de vos propres attentes se termine, donc ce qui lève un TOO_MANY_WAITS, c’est d’attendre vos recherches ou de lancer moins de workers.
Une attente coûte une requête, quelle que soit sa durée. Une suite qui attend n’approche pas 600 requêtes par minute, alors qu’une boucle qui interroge le fait.
Voir où vous en êtes
Section intitulée « Voir où vous en êtes »Chaque réponse à une requête authentifiée dit ce qui reste de la minute :
curl --silent --show-error --include \ -H "Authorization: Bearer $FACTEUR_API_KEY" \ https://api.facteur.eu/inboxes| En-tête | Ce qu’il dit |
|---|---|
RateLimit-Policy |
Ce que le service autorise : "key";q=600;w=60, "waits";q=20;qu="concurrent-requests". 600 requêtes dans une fenêtre de 60 secondes, et 20 attentes simultanées. |
RateLimit |
Où en est cette clé : "key";r=<requêtes restantes>;t=<secondes avant la fin de la fenêtre>. |
X-RateLimit-Limit |
600. |
X-RateLimit-Remaining |
Requêtes restantes dans la minute, jamais en dessous de zéro. |
X-RateLimit-Reset |
Secondes avant la fin de la fenêtre. C’est un nombre de secondes et non une date. |
Les deux familles portent les mêmes nombres. RateLimit et RateLimit-Policy suivent le brouillon de l’IETF sur les en-têtes de limite de débit, et les noms en X- sont l’ancienne convention que beaucoup de bibliothèques clientes lisent déjà. Utilisez la famille que votre client HTTP comprend. Une requête sans clé valide reçoit seulement RateLimit-Policy et X-RateLimit-Limit, parce qu’il n’y a aucun budget à rapporter.
Parcourir plus de cinquante messages
Section intitulée « Parcourir plus de cinquante messages »Deux routes lisent les messages d’une inbox. Les deux paginent par curseur :
| Route | Page |
|---|---|
GET/inboxes/{inboxId}/messages |
limit et cursor, dans l’adresse. Elle liste les messages d’une inbox, quel que soit leur état. |
GET/messages |
limit et cursor, dans la requête. Elle ne renvoie que les messages parsed, sauf si vous demandez status=any. |
nextCursor vaut null sur la dernière page : bouclez tant qu’il a une valeur. Renvoyez-le exactement comme vous l’avez reçu. Un curseur que vous avez construit ou modifié reçoit 400 INVALID_CURSOR.
Ce qu’une recherche met dans son adresse
Section intitulée « Ce qu’une recherche met dans son adresse »GET/messages est une requête GET : chaque critère est donc un paramètre de son adresse, par exemple /messages?inbox=j3k9x2mq&subject=Welcome. Les mots que vous cherchez apparaissent donc dans cette adresse, et un proxy ou une passerelle entre votre machine et Facteur peut les écrire dans ses journaux. Facteur ne les écrit ni dans ses journaux d’accès, ni dans ses journaux d’application, ni dans ses traces. L’adresse de la page de l’application Facteur d’où une recherche est lancée n’y est pas non plus, parce que le navigateur n’envoie que le nom du site. Les journaux d’un composant qui n’est pas à Facteur sont hors de sa portée. Cherchez une adresse, un sujet ou un code, jamais un secret.
Chaque texte a une longueur maximale, comptée en caractères :
| Paramètre | Valeur la plus longue |
|---|---|
inbox |
36 |
workspace |
40 |
sentTo et sentFrom |
254 |
subject, body et attachmentName |
255 |
otp |
16 |
Une valeur plus longue est refusée avec 400 CRITERIA_INVALID, et la recherche ne s’exécute pas sur un texte coupé. Un cursor fait 64 caractères au plus, et un plus long répond 400 INVALID_CURSOR, comme tout curseur que Facteur n’a pas émis. Un paramètre vrai ou faux (hasOtp, hasAttachment) s’écrit true ou false et rien d’autre. limit et wait s’écrivent en chiffres. since est une date ISO 8601, comme 2026-11-03T10:00:00Z, et toute autre valeur répond 400 CRITERIA_INVALID.
Chercher avec un corps JSON
Section intitulée « Chercher avec un corps JSON »POST/messages/search prend les critères de GET/messages dans un corps JSON, comme {"inbox": "j3k9x2mq", "subject": "Welcome", "wait": 20000}. C’est la même recherche : les mêmes critères, les mêmes valeurs maximales, les mêmes limit, cursor et wait, la même page dans la réponse et les mêmes refus. Servez-vous-en quand vous ne voulez pas d’un critère dans une adresse, ou quand votre client envoie déjà la recherche en JSON. Dans le corps, hasOtp et hasAttachment sont des booléens JSON, limit et wait des nombres JSON, et un champ que la recherche ne connaît pas répond 400 CRITERIA_UNKNOWN, comme dans l’adresse. Un corps qui n’est pas du JSON répond 400 BAD_REQUEST. Quand vous donnez inbox et workspace ensemble, l’inbox l’emporte et workspace est ignoré, avec le GET comme avec le POST.
Ce que renvoie une recherche
Section intitulée « Ce que renvoie une recherche »Chaque message d’une recherche arrive avec ses corps, textBody et htmlBody, ses headers et ce que la connexion savait (connection), exactement comme GET/messages/{messageId} le rend. Un test qui attend un courrier lit son lien ou son code dans cette réponse, sans envoyer de seconde requête. Un message refusé (quota_exceeded) n’a aucun corps, et les clés sont absentes. La liste d’une inbox, GET/inboxes/{inboxId}/messages, reste sans les corps.
Une page de 200 messages avec leurs corps peut être volumineuse. Demandez moins avec limit quand vous lisez beaucoup de courriers d’un coup.
Lire status avant tout ce qui vient du corps
Section intitulée « Lire status avant tout ce qui vient du corps »Facteur accepte un message d’abord et le lit un instant plus tard. Pendant cet instant, il n’a ni sujet, ni code, ni liens.
status |
Sens | Ce que vous faites |
|---|---|---|
received |
Gardé, pas encore lu. | Regardez de nouveau dans un instant. |
parsed |
Chaque champ lu dans le corps est rempli. | Faites vos assertions sur subject, otp et links. |
quota_exceeded |
Refusé parce qu’une limite était atteinte. Rien n’a été lu. | Lisez errorCode, voir le guide des erreurs. |
Une recherche ne renvoie que les messages parsed par défaut : « le courrier n’est pas arrivé » et « le courrier est là mais pas encore lu » ne peuvent donc pas se confondre. Passez status=any pour voir les autres états.
Écrire des critères qui correspondent
Section intitulée « Écrire des critères qui correspondent »Les critères de texte sont sentTo, sentFrom, subject, body, otp et attachmentName. Chacun correspond à une sous-chaîne, sans tenir compte de la casse. % et _ sont des caractères ordinaires : chercher 100% trouve le texte 100%.
bodyest cherché dans la partie texte et dans la partie HTML du message.attachmentNamecorrespond au nom de fichier d’une vraie pièce jointe. Une image en ligne, comme un logo de signature, n’est pas une pièce jointe et ne correspond jamais.otpcorrespond au code que Facteur a extrait, et à rien d’autre.otp=882314veut dire « le code est 882314 ».body=882314trouverait aussi un message qui le cite seulement.
match décide comment les critères de texte se combinent : all (tous, par défaut) ou any (n’importe lequel). since, type, hasAttachment, hasOtp et status ne sont pas des critères de texte. Ils restreignent le résultat dans les deux modes.
Une recherche doit nommer un ensemble. inbox, sentTo, sentFrom, subject, body, otp, hasOtp, hasAttachment et since le font chacun. workspace, type et status ne font que restreindre un ensemble : seuls, ils répondent 400 CRITERIA_EMPTY. Un champ que la recherche ne connaît pas répond 400 CRITERIA_UNKNOWN et le nomme.
Combien de temps un message est gardé
Section intitulée « Combien de temps un message est gardé »Chaque inbox a un retentionDays, fixé d’après l’offre que l’organisation avait à l’ouverture de l’inbox. Un message porte expiresAt, le moment où la rétention le retire, ou null s’il n’expire pas.
Renvoyer une écriture sans la faire deux fois
Section intitulée « Renvoyer une écriture sans la faire deux fois »Ajoutez un en-tête Idempotency-Key à un POST, avec une valeur de votre choix, par exemple un UUID. Si votre script doit renvoyer la requête, parce que la connexion a coupé avant l’arrivée de la réponse, renvoyez-la avec la même clé : Facteur vous rend la réponse de la première requête et ne refait pas le travail une seconde fois.
curl --silent --show-error --fail-with-body \ -H "Authorization: Bearer $FACTEUR_API_KEY" \ -H "Idempotency-Key: 6f1c2b7e-3a49-4d8e-9d52-0b1f7c4a8e31" \ -H "Content-Type: application/json" \ -d '{"name": "signup-tests"}' \ https://api.facteur.eu/workspaces/$WORKSPACE/inboxesFacteur garde la réponse 24 heures. Une requête qui revient avec la même clé après ce délai est traitée comme une requête neuve et refait son travail : renvoyez donc vos tentatives dans la journée. La clé appartient à la clé d’API qui l’a envoyée : deux clés d’API peuvent employer la même valeur sans se croiser. Une réponse en échec n’est pas gardée, donc une requête refusée peut être renvoyée avec la même clé.