API v1 · OpenAPI 3.1 · stable
Référence API
Envoyez du courrier postal depuis votre CRM, ERP ou backoffice. Une seule URL de base, authentification par clé Bearer, facturation depuis votre wallet — aucune carte bancaire par appel.
Vue d'ensemble
Une API REST stateless, JSON in/out, versionnée. Toutes les routes sont préfixées par /v1.
- URL de base
https://bjet24.com/api - Version
v1 · OpenAPI 3.1 - Format
application/json - Auth
Bearer / X-Api-Key
Authentification
Toutes les requêtes doivent inclure une clé API valide soit dans l'en-tête Authorization au format Bearer, soit dans l'en-tête X-Api-Key. Les clés ont le format bjk_<id>_<secret>.
curl https://bjet24.com/api/v1/mails \
-H "Authorization: Bearer bjk_xxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"Scopes disponibles
read— lecture des envois, devis, listessend— création d'envois (débite le wallet)webhooks— création/suppression de webhooksaddresses— Gérer le carnet d'adresses (expéditeurs et destinataires).
Créez et révoquez vos clés depuis /account/api-keys.
Format des erreurs
Toutes les erreurs renvoient un code HTTP 4xx ou 5xx et un corps JSON avec un champ code (machine) et message (humain). Les erreurs de validation incluent un tableau issues décrivant chaque problème.
{
"code": "VALIDATION_ERROR",
"message": "Invalid request body",
"issues": [
{ "path": ["pages"], "message": "Number must be greater than or equal to 1" }
]
}VALIDATION_ERRORLe corps de la requête ne respecte pas le schéma. Voir issues.UNAUTHORIZEDClé API absente ou invalide.FORBIDDENLa clé n'a pas le scope requis pour cette route.NOT_FOUNDRessource introuvable ou n'appartenant pas à votre organisation.INSUFFICIENT_FUNDSSolde wallet insuffisant pour couvrir le coût de l'envoi.PRICING_UNAVAILABLEPricing non configuré pour la zone/service demandé. Contactez le support.
Idempotence
Pour éviter les doublons en cas de retry réseau, passez un champ externalId unique (typiquement l'id de votre CRM) sur POST /v1/mails. Les rejouages avec le même externalId renvoient l'envoi existant — aucun nouveau débit.
Comment ça marche
Limites de débit
Les limites sont appliquées par clé API. Les requêtes au-delà du quota reçoivent un 429 avec un en-tête Retry-After.
- Lecture (GET)
120 / min - Écriture (POST, DELETE)
60 / min - Burst max
30
Obtenir un devis
/v1/quoteScope requis : readCalcule le prix total pour un service / zone / nombre de pages. Aucun débit n'est effectué. Utile pour afficher le tarif côté client avant un envoi.
Corps de la requête
zonestringrequisNATIONALINTERNATIONALZone d'envoi. NATIONAL = Belgique, INTERNATIONAL = hors Belgique.
serviceTypestringrequisstandardpriorregisteredType de service postal. registered ajoute une preuve de dépôt et un numéro de suivi.
pagesintegerrequisNombre total de pages imprimées (1 à 2000).
colorbooleanoptionneldéfautfalseImpression couleur. Tarif majoré.
acknowledgmentReceiptbooleanoptionneldéfautfalseAccusé de réception (registered uniquement). Tarif majoré.
recipientCountintegeroptionneldéfaut1Nombre de destinataires distincts (1 à 100). Multiplie le prix unitaire.
Réponses
- 200Devis calculé. Le total inclut TVA si applicable.
- 400Corps de requête invalide. Voir issues.
- 401Clé API absente ou invalide.
curl -X POST https://bjet24.com/api/v1/quote \
-H "Authorization: Bearer $BJET24_KEY" \
-H "Content-Type: application/json" \
-d '{
"zone": "NATIONAL",
"serviceType": "registered",
"pages": 3,
"color": false,
"acknowledgmentReceipt": false,
"recipientCount": 1
}'Pré-signer une URL d'upload
/v1/uploadsScope requis : sendRenvoie une URL PUT S3 valide 10 minutes. Uploadez votre PDF ou image dessus, puis passez la clé retournée comme s3Key dans POST /v1/mails. Les uploads vont directement vers S3 — pas de gros payloads transitent par notre API.
Corps de la requête
filenamestringrequisNom de fichier original. Utilisé pour les logs et la facture.
mimeTypestringrequisapplication/pdfimage/jpegimage/pngimage/heicimage/heifType MIME du fichier. PDF recommandé pour les courriers ; images converties automatiquement.
sizeBytesintegerrequisTaille en octets. Max 20 Mo.
Réponses
- 200URL pré-signée renvoyée. Uploadez en PUT dans les 10 minutes.
- 400Corps de requête invalide. Voir issues.
- 401Clé API absente ou invalide.
curl -X POST https://bjet24.com/api/v1/uploads \
-H "Authorization: Bearer $BJET24_KEY" \
-H "Content-Type: application/json" \
-d '{
"filename": "contract.pdf",
"mimeType": "application/pdf",
"sizeBytes": 124532
}'Créer et envoyer un courrier
/v1/mailsScope requis : sendMet l'envoi en file et débite immédiatement votre wallet. Si dryRun=true, l'envoi est validé et tarifé sans débit ni création. Passez externalId pour l'idempotence et webhookUrl pour un callback par envoi.
Corps de la requête
zonestringrequisNATIONALINTERNATIONALZone d'envoi. NATIONAL = Belgique, INTERNATIONAL = hors Belgique.
serviceTypestringrequisstandardpriorregisteredType de service postal. registered ajoute une preuve de dépôt et un numéro de suivi.
pagesintegerrequisNombre total de pages imprimées (1 à 2000).
colorbooleanoptionneldéfautfalseImpression couleur. Tarif majoré.
acknowledgmentReceiptbooleanoptionneldéfautfalseAccusé de réception (registered uniquement). Tarif majoré.
scheduledDropAtstring (ISO 8601)optionnelDate/heure de dépôt programmée (ISO 8601). Si absent, dépôt à la prochaine cutoff.
recipientsRecipient[]requisListe des destinataires (1 à 100). Chacun reçoit un exemplaire imprimé.
senderSenderoptionnelAdresse expéditeur affichée sur l'enveloppe. Si absent, utilise l'adresse par défaut de votre compte.
filesFile[]requisFichiers à imprimer (1 à 20). Concaténés dans l'ordre. s3Key vient de POST /v1/uploads.
externalIdstringoptionnelIdentifiant côté appelant (ex. id de votre CRM). Garantit l'idempotence.
webhookUrlstring (uri)optionnelURL de webhook spécifique à cet envoi. Surcharge la config globale.
dryRunbooleanoptionneldéfautfalseSi true, valide et tarife sans débiter ni persister. Réponse identique mais mailJobId est null.
Réponses
- 201Envoi créé et mis en file. Le wallet est débité.
- 200Hit d'idempotence — l'envoi existait déjà avec le même externalId.
- 400Corps de requête invalide. Voir issues.
- 402Solde wallet insuffisant. Rechargez avant de réessayer.
- 503Pricing non configuré pour la zone/service.
curl -X POST https://bjet24.com/api/v1/mails \
-H "Authorization: Bearer $BJET24_KEY" \
-H "Content-Type: application/json" \
-d '{
"zone": "NATIONAL",
"serviceType": "registered",
"pages": 3,
"color": false,
"recipients": [{
"name": "Jean Dupont",
"line1": "Rue de la Loi 16",
"zip": "1000",
"city": "Brussels",
"country": "BE"
}],
"files": [{
"s3Key": "uploads/2026-05-19/8af9...c12.pdf",
"filename": "contract.pdf",
"mimeType": "application/pdf",
"sizeBytes": 124532
}],
"externalId": "crm-contract-7281"
}'Lister les envois
/v1/mailsScope requis : readRenvoie vos envois, du plus récent au plus ancien. Paginé par curseur.
Paramètres de requête
statusstringoptionnelFiltre par statut (queued, printed, posted, delivered, failed).
externalIdstringoptionnelFiltre par externalId exact.
limitintegeroptionneldéfaut20Nombre de résultats par page (1 à 100).
cursorstringoptionnelCurseur de pagination renvoyé par la réponse précédente.
Réponses
- 200Liste paginée d'envois.
curl "https://bjet24.com/api/v1/mails?status=queued&limit=20" \
-H "Authorization: Bearer $BJET24_KEY"Récupérer un envoi
/v1/mails/{id}Scope requis : readRenvoie le détail complet d'un envoi : statut, destinataires, fichiers, montants, timestamps.
Paramètres de chemin
idstringrequisIdentifiant de l'envoi (mj_…).
Réponses
- 200Détail de l'envoi.
- 404Ressource introuvable.
curl https://bjet24.com/api/v1/mails/mj_01HZX5K8... \
-H "Authorization: Bearer $BJET24_KEY"Simuler le cycle de vie (sandbox)
/v1/mails/{id}/simulateScope requis : sendMode test uniquement. Fait avancer un envoi sandbox dans son cycle de vie et déclenche le webhook « test » correspondant. Sans `to`, l'envoi avance d'une étape (queued → printed → posted → delivered) ; avec `to`, il saute directement à ce statut — par ex. {"to":"failed"} pour tester la gestion d'échec. Les envois sandbox progressent aussi automatiquement : cet endpoint sert au test déterministe et à la demande. Refusé (403) avec une clé live.
Paramètres de chemin
idstringrequisIdentifiant de l'envoi (mj_…).
Corps de la requête
tostringoptionnelprintedposteddeliveredfailedStatut cible. Omis : avance d'une étape. Valeurs : printed, posted, delivered, failed. Un saut direct ne déclenche que le webhook du statut atteint.
Réponses
- 200Transition appliquée — renvoie l'ancien et le nouveau statut.
- 400Corps de requête invalide. Voir issues.
- 403Identifiants live — la simulation est réservée au mode test.
- 404Ressource introuvable.
- 409Transition impossible (statut déjà terminal ou cible inatteignable).
# Test mode only — advance the sandbox mail one step
curl -X POST https://bjet24.com/api/v1/mails/mj_01HZX5K8.../simulate \
-H "Authorization: Bearer $BJET24_TEST_KEY"
# …or jump straight to a status (e.g. test failure handling)
curl -X POST https://bjet24.com/api/v1/mails/mj_01HZX5K8.../simulate \
-H "Authorization: Bearer $BJET24_TEST_KEY" \
-H "Content-Type: application/json" \
-d '{"to":"failed"}'Lister les adresses
/v1/addressesScope requis : addressesRenvoie les adresses enregistrées du carnet — expéditeurs et destinataires. Filtrable par type et par recherche texte.
Paramètres de requête
kindstringoptionnelSENDERRECIPIENTFiltre par type d'adresse.
qstringoptionnelRecherche texte sur le nom, la ville, le code postal ou le libellé.
limitintegeroptionneldéfaut100Nombre maximum d'adresses renvoyées (1–200).
Réponses
- 200La liste des adresses du carnet.
- 401Clé API absente ou invalide.
curl "https://bjet24.com/api/v1/addresses?kind=RECIPIENT&limit=50" \
-H "Authorization: Bearer $BJET24_KEY"Créer une adresse
/v1/addressesScope requis : addressesEnregistre une adresse dans le carnet — par exemple pour synchroniser les contacts de votre CRM.
Corps de la requête
kindstringrequisSENDERRECIPIENTType d'adresse : expéditeur (SENDER) ou destinataire (RECIPIENT).
namestringrequisNom du destinataire ou de l'expéditeur.
line1stringrequisPremière ligne d'adresse (rue et numéro).
line2stringoptionnelComplément d'adresse facultatif (boîte, étage…).
zipstringrequisCode postal.
citystringrequisVille.
countrystringrequisCode pays ISO 3166-1 alpha-2 (ex. BE).
labelstringoptionnelLibellé interne facultatif, ex. Siège ou Client X.
isDefaultbooleanoptionneldéfautfalseDéfinit cette adresse comme adresse par défaut pour son type.
Réponses
- 201L'adresse, dans sa forme publique.
- 400Corps de requête invalide. Voir issues.
curl -X POST https://bjet24.com/api/v1/addresses \
-H "Authorization: Bearer $BJET24_KEY" \
-H "Content-Type: application/json" \
-d '{
"kind": "RECIPIENT",
"name": "Jean Dupont",
"line1": "Rue de la Loi 16",
"zip": "1000",
"city": "Brussels",
"country": "BE",
"label": "Client 7281"
}'Récupérer une adresse
/v1/addresses/{id}Scope requis : addressesRenvoie une adresse précise du carnet.
Paramètres de chemin
idstringrequisIdentifiant de l'adresse.
Réponses
- 200L'adresse, dans sa forme publique.
- 404Ressource introuvable.
curl https://bjet24.com/api/v1/addresses/adr_01HZX5K8... \
-H "Authorization: Bearer $BJET24_KEY"Modifier une adresse
/v1/addresses/{id}Scope requis : addressesMet à jour les champs fournis d'une adresse existante. Les champs omis restent inchangés.
Paramètres de chemin
idstringrequisIdentifiant de l'adresse.
Corps de la requête
kindstringoptionnelSENDERRECIPIENTType d'adresse : expéditeur (SENDER) ou destinataire (RECIPIENT).
namestringoptionnelNom du destinataire ou de l'expéditeur.
line1stringoptionnelPremière ligne d'adresse (rue et numéro).
line2stringoptionnelComplément d'adresse facultatif (boîte, étage…).
zipstringoptionnelCode postal.
citystringoptionnelVille.
countrystringoptionnelCode pays ISO 3166-1 alpha-2 (ex. BE).
labelstringoptionnelLibellé interne facultatif, ex. Siège ou Client X.
isDefaultbooleanoptionnelDéfinit cette adresse comme adresse par défaut pour son type.
Réponses
- 200L'adresse, dans sa forme publique.
- 400Corps de requête invalide. Voir issues.
- 404Ressource introuvable.
curl -X PATCH https://bjet24.com/api/v1/addresses/adr_01HZX5K8... \
-H "Authorization: Bearer $BJET24_KEY" \
-H "Content-Type: application/json" \
-d '{ "city": "Bruxelles", "isDefault": true }'Supprimer une adresse
/v1/addresses/{id}Scope requis : addressesSupprime définitivement une adresse du carnet.
Paramètres de chemin
idstringrequisIdentifiant de l'adresse.
Réponses
- 200Adresse supprimée.
- 404Ressource introuvable.
curl -X DELETE https://bjet24.com/api/v1/addresses/adr_01HZX5K8... \
-H "Authorization: Bearer $BJET24_KEY"Lister vos webhooks
/v1/webhooksScope requis : readRenvoie tous les endpoints webhook actifs sur votre compte.
Réponses
- 200Liste des webhooks (sans le secret).
curl https://bjet24.com/api/v1/webhooks \
-H "Authorization: Bearer $BJET24_KEY"Enregistrer un webhook
/v1/webhooksScope requis : webhooksCrée un endpoint webhook. Le secret de signature n'est renvoyé qu'UNE seule fois — stockez-le immédiatement, on ne peut pas le récupérer.
Corps de la requête
urlstring (uri)requisURL HTTPS où les événements seront postés.
eventsstring[]requisMAIL_QUEUEDMAIL_PRINTEDMAIL_POSTEDMAIL_DELIVEREDMAIL_FAILEDListe d'événements à recevoir. Au moins un requis.
Réponses
- 201Webhook créé. Notez le secret renvoyé.
- 400Corps de requête invalide. Voir issues.
curl -X POST https://bjet24.com/api/v1/webhooks \
-H "Authorization: Bearer $BJET24_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.example.com/webhooks/bjet24",
"events": ["MAIL_QUEUED", "MAIL_POSTED", "MAIL_DELIVERED", "MAIL_FAILED"]
}'Supprimer un webhook
/v1/webhooks/{id}Scope requis : webhooksDésactive immédiatement l'endpoint. Les événements ne seront plus livrés.
Paramètres de chemin
idstringrequisIdentifiant du webhook (wh_…).
Réponses
- 200Webhook supprimé.
- 404Ressource introuvable.
curl -X DELETE https://bjet24.com/api/v1/webhooks/wh_01HZX5K8... \
-H "Authorization: Bearer $BJET24_KEY"Événements webhook
Cinq événements couvrent le cycle de vie d'un envoi. Chaque livraison inclut le payload JSON, l'en-tête X-Bjet24-Event et la signature.
MAIL_QUEUEDEnvoi mis en file après débit du wallet.MAIL_PRINTEDDocument imprimé à l'atelier.MAIL_POSTEDDéposé au bureau de poste.MAIL_DELIVEREDDistribué (recommandé uniquement).MAIL_FAILEDÉchec d'impression, de dépôt ou annulation.
Exemple de payload
{
"event": "MAIL_POSTED",
"mailJobId": "mj_01HZX5K8...",
"externalId": "crm-contract-7281",
"status": "posted",
"postedAt": "2026-05-19T16:42:11.000Z"
}Signature & vérification
Chaque livraison webhook est signée en HMAC-SHA256 avec le secret renvoyé à la création. Vérifiez la signature avant de traiter le payload.
En-tête de signature
X-Bjet24-Signature: t=<ts>,v1=<hex>
import crypto from 'node:crypto'
export function verifyBjet24(req, secret) {
const header = req.headers['x-bjet24-signature']
// header looks like: t=1714940000,v1=<hmac-sha256-hex>
const [tPart, sPart] = header.split(',')
const ts = tPart.split('=')[1]
const sig = sPart.split('=')[1]
const expected = crypto
.createHmac('sha256', secret)
.update(`${ts}.${req.rawBody}`)
.digest('hex')
if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
throw new Error('Bad signature')
}
if (Date.now() / 1000 - Number(ts) > 300) {
throw new Error('Replay')
}
}Protection contre le rejeu
Connecter une application tierce (OAuth2)
Pour qu'une application (un CRM, un outil métier) agisse au nom de plusieurs utilisateurs Bjet24 — chacun avec son propre portefeuille — utilisez OAuth2 plutôt qu'une clé API. L'utilisateur connecte son compte en un clic, sans copier-coller de clé.
Clé API ou OAuth2 ?
Enregistrer votre application
Le flux Authorization Code + PKCE
1. Rediriger l'utilisateur vers l'écran de consentement.
https://bjet24.com/fr/oauth/authorize
?response_type=code
&client_id=bjc_xxxxxxxxxxxxxxxxxxxxxxxx
&redirect_uri=https://your-app.example.com/callback
&scope=send%20read%20wallet
&state=<opaque-random>
&code_challenge=<base64url(sha256(code_verifier))>
&code_challenge_method=S2562. Échanger le code reçu contre une paire de jetons.
curl -X POST https://bjet24.com/api/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d grant_type=authorization_code \
-d client_id=bjc_xxxxxxxxxxxxxxxxxxxxxxxx \
-d client_secret=$BJET24_CLIENT_SECRET \
-d code=$AUTH_CODE \
-d redirect_uri=https://your-app.example.com/callback \
-d code_verifier=$PKCE_VERIFIER{
"access_token": "bja_...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "bjr_...",
"scope": "send read wallet"
}3. Appeler l'API avec l'access token en Bearer.
curl https://bjet24.com/api/v1/wallet \
-H "Authorization: Bearer bja_..."4. Renouveler l'access token expiré avec le refresh token.
curl -X POST https://bjet24.com/api/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d grant_type=refresh_token \
-d client_id=bjc_xxxxxxxxxxxxxxxxxxxxxxxx \
-d client_secret=$BJET24_CLIENT_SECRET \
-d refresh_token=$REFRESH_TOKENDurée de vie et révocation
Scopes OAuth
Les scopes demandés au moment de l'autorisation déterminent ce que votre application peut faire.
sendcréation d'envois (débite le wallet)readlecture des envois, devis, listeswebhookscréation/suppression de webhookswalletConsulter le solde du portefeuille de l'utilisateur.addressesGérer le carnet d'adresses (expéditeurs et destinataires).