MENU navbar-image

Introduction

API REST publique d'eBurnieSend — envoi de notifications WhatsApp / SMS / Email pour le marché ouest-africain.

This documentation aims to provide all the information you need to work with our API.

<aside>As you scroll, you'll see code examples for working with the API in different programming languages in the dark area to the right (or as part of the content on mobile).
You can switch the language used with the tabs at the top right (or from the nav menu at the top left on mobile).</aside>

Authenticating requests

To authenticate requests, include an Authorization header with the value "Bearer nh_live_xxx".

All authenticated endpoints are marked with a requires authentication badge in the documentation below.

Génère ta clé API depuis le dashboard ou via tinker (cf. README).

Contacts

Lister les contacts

requires authentication

Retourne le carnet d'adresses du tenant. Pagination cursor (max 200/page). Filtres facultatifs sur téléphone E.164, email, external_id (votre identifiant client) ou recherche libre.

Example request:
curl --request GET \
    --get "https://eburniesend.com/api/v1/contacts?per_page=50&phone_e164=%2B22505XXXXXXXX&email=client%40example.com&external_id=CUST-1234&q=moustapha" \
    --header "Authorization: Bearer nh_live_xxx" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://eburniesend.com/api/v1/contacts"
);

const params = {
    "per_page": "50",
    "phone_e164": "+22505XXXXXXXX",
    "email": "[email protected]",
    "external_id": "CUST-1234",
    "q": "moustapha",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer nh_live_xxx",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200, success):


{
    "data": [],
    "meta": {
        "current_page": 1,
        "per_page": 50,
        "total": 0
    }
}
 

Example response (401, unauthorized):


{
    "error": {
        "code": "unauthorized",
        "message": "Invalid or missing API key",
        "details": null
    }
}
 

Example response (429, rate_limited):


{
    "error": {
        "code": "rate_limit_exceeded",
        "message": "Too many requests",
        "details": {
            "retry_after": 60
        }
    }
}
 

Request      

GET api/v1/contacts

Headers

Authorization        

Example: Bearer nh_live_xxx

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

per_page   integer  optional    

Nombre d'éléments par page (1-200). Default 50. Example: 50

phone_e164   string  optional    

Filtre exact sur le téléphone E.164. Example: +22505XXXXXXXX

email   string  optional    

Filtre exact sur l'email. Example: [email protected]

external_id   string  optional    

Filtre exact sur l'identifiant client externe. Example: CUST-1234

q   string  optional    

Recherche libre (prénom, nom, téléphone, email). Example: moustapha

Créer un contact

requires authentication

Crée un nouveau contact dans le carnet d'adresses du tenant. Au moins un identifiant (phone_e164 OU email) est requis. Les opt-ins par canal sont initialisés à false par défaut.

Example request:
curl --request POST \
    "https://eburniesend.com/api/v1/contacts" \
    --header "Authorization: Bearer nh_live_xxx" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"phone_e164\": \"+22505XXXXXXXX\",
    \"email\": \"[email protected]\",
    \"first_name\": \"Jean\",
    \"last_name\": \"Diallo\",
    \"external_id\": \"CUST-1234\",
    \"language\": \"fr\",
    \"country_code\": \"CI\",
    \"opt_in_whatsapp\": true,
    \"opt_in_sms\": true,
    \"opt_in_email\": true,
    \"tags\": [
        \"vip\",
        \"newsletter\"
    ],
    \"custom_attributes\": {
        \"age\": 42,
        \"city\": \"Abidjan\"
    }
}"
const url = new URL(
    "https://eburniesend.com/api/v1/contacts"
);

const headers = {
    "Authorization": "Bearer nh_live_xxx",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "phone_e164": "+22505XXXXXXXX",
    "email": "[email protected]",
    "first_name": "Jean",
    "last_name": "Diallo",
    "external_id": "CUST-1234",
    "language": "fr",
    "country_code": "CI",
    "opt_in_whatsapp": true,
    "opt_in_sms": true,
    "opt_in_email": true,
    "tags": [
        "vip",
        "newsletter"
    ],
    "custom_attributes": {
        "age": 42,
        "city": "Abidjan"
    }
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201, created):


{
    "data": {
        "id": "7c8f9d10-1234-4567-89ab-cdef01234567",
        "phone_e164": "+22505XXXXXXXX",
        "email": "[email protected]"
    }
}
 

Example response (401, unauthorized):


{
    "error": {
        "code": "unauthorized",
        "message": "Invalid or missing API key",
        "details": null
    }
}
 

Example response (422, validation_failed):


{
    "error": {
        "code": "validation_failed",
        "message": "The given data was invalid.",
        "details": {
            "fields": {
                "phone_e164": [
                    "The phone_e164 field is required when email is not present."
                ]
            }
        }
    }
}
 

Request      

POST api/v1/contacts

Headers

Authorization        

Example: Bearer nh_live_xxx

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

phone_e164   string  optional    

required_without:email Téléphone E.164 (+225...). Example: +22505XXXXXXXX

email   string  optional    

required_without:phone_e164 Email RFC valide. Example: [email protected]

first_name   string  optional    

Prénom (max 100). Example: Jean

last_name   string  optional    

Nom (max 100). Example: Diallo

external_id   string  optional    

Votre identifiant client interne (max 100). Example: CUST-1234

language   string  optional    

Code langue ISO 639-1 (max 10). Example: fr

country_code   string  optional    

Code pays ISO 3166-1 alpha-2 (2 chars). Example: CI

opt_in_whatsapp   boolean  optional    

Opt-in WhatsApp. Default false. Example: true

opt_in_sms   boolean  optional    

Opt-in SMS. Default false. Example: true

opt_in_email   boolean  optional    

Opt-in Email. Default false. Example: true

tags   string[]  optional    

Tags libres (array de strings).

custom_attributes   object  optional    

Attributs libres clé/valeur.

Détails d'un contact

requires authentication

Example request:
curl --request GET \
    --get "https://eburniesend.com/api/v1/contacts/7c8f9d10-1234-4567-89ab-cdef01234567" \
    --header "Authorization: Bearer nh_live_xxx" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://eburniesend.com/api/v1/contacts/7c8f9d10-1234-4567-89ab-cdef01234567"
);

const headers = {
    "Authorization": "Bearer nh_live_xxx",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200, success):


{
    "data": {
        "id": "7c8f9d10-1234-4567-89ab-cdef01234567",
        "phone_e164": "+22505XXXXXXXX",
        "email": "[email protected]",
        "first_name": "Jean"
    }
}
 

Example response (401, unauthorized):


{
    "error": {
        "code": "unauthorized",
        "message": "Invalid or missing API key",
        "details": null
    }
}
 

Example response (404, not_found):


{
    "error": {
        "code": "not_found",
        "message": "Resource not found",
        "details": null
    }
}
 

Request      

GET api/v1/contacts/{uuid}

Headers

Authorization        

Example: Bearer nh_live_xxx

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

uuid   string     

UUID du contact. Example: 7c8f9d10-1234-4567-89ab-cdef01234567

Mettre à jour un contact

requires authentication

Update partiel (PATCH). Tous les champs sont nullable. Les opt-ins peuvent être basculés par cette API.

Example request:
curl --request PATCH \
    "https://eburniesend.com/api/v1/contacts/7c8f9d10-1234-4567-89ab-cdef01234567" \
    --header "Authorization: Bearer nh_live_xxx" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"phone_e164\": \"+22505XXXXXXXX\",
    \"email\": \"[email protected]\",
    \"first_name\": \"Jean\",
    \"last_name\": \"Diallo\",
    \"external_id\": \"CUST-1234\",
    \"language\": \"fr\",
    \"country_code\": \"CI\",
    \"opt_in_whatsapp\": false,
    \"opt_in_sms\": true,
    \"opt_in_email\": true,
    \"tags\": [
        \"vip\"
    ],
    \"custom_attributes\": {
        \"age\": 42
    }
}"
const url = new URL(
    "https://eburniesend.com/api/v1/contacts/7c8f9d10-1234-4567-89ab-cdef01234567"
);

const headers = {
    "Authorization": "Bearer nh_live_xxx",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "phone_e164": "+22505XXXXXXXX",
    "email": "[email protected]",
    "first_name": "Jean",
    "last_name": "Diallo",
    "external_id": "CUST-1234",
    "language": "fr",
    "country_code": "CI",
    "opt_in_whatsapp": false,
    "opt_in_sms": true,
    "opt_in_email": true,
    "tags": [
        "vip"
    ],
    "custom_attributes": {
        "age": 42
    }
};

fetch(url, {
    method: "PATCH",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200, updated):


{
    "data": {
        "id": "7c8f9d10-...",
        "phone_e164": "+22505XXXXXXXX",
        "first_name": "Jean"
    }
}
 

Example response (401, unauthorized):


{
    "error": {
        "code": "unauthorized",
        "message": "Invalid or missing API key",
        "details": null
    }
}
 

Example response (404, not_found):


{
    "error": {
        "code": "not_found",
        "message": "Resource not found",
        "details": null
    }
}
 

Example response (422, validation_failed):


{
    "error": {
        "code": "validation_failed",
        "message": "The given data was invalid.",
        "details": {
            "fields": {
                "email": [
                    "The email must be a valid email address."
                ]
            }
        }
    }
}
 

Request      

PATCH api/v1/contacts/{uuid}

Headers

Authorization        

Example: Bearer nh_live_xxx

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

uuid   string     

UUID du contact. Example: 7c8f9d10-1234-4567-89ab-cdef01234567

Body Parameters

phone_e164   string  optional    

Téléphone E.164. Example: +22505XXXXXXXX

email   string  optional    

Email RFC valide. Example: [email protected]

first_name   string  optional    

Prénom. Example: Jean

last_name   string  optional    

Nom. Example: Diallo

external_id   string  optional    

Identifiant client externe. Example: CUST-1234

language   string  optional    

Code langue ISO 639-1. Example: fr

country_code   string  optional    

Code pays ISO 3166-1 alpha-2. Example: CI

opt_in_whatsapp   boolean  optional    

Opt-in WhatsApp. Example: false

opt_in_sms   boolean  optional    

Opt-in SMS. Example: true

opt_in_email   boolean  optional    

Opt-in Email. Example: true

tags   string[]  optional    

Tags.

custom_attributes   object  optional    

Attributs libres.

Supprimer un contact

requires authentication

Suppression définitive (hard delete). Les messages déjà envoyés à ce contact restent dans l'historique avec leur snapshot.

Example request:
curl --request DELETE \
    "https://eburniesend.com/api/v1/contacts/7c8f9d10-1234-4567-89ab-cdef01234567" \
    --header "Authorization: Bearer nh_live_xxx" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://eburniesend.com/api/v1/contacts/7c8f9d10-1234-4567-89ab-cdef01234567"
);

const headers = {
    "Authorization": "Bearer nh_live_xxx",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Example response (204, deleted):

Empty response
 

Example response (401, unauthorized):


{
    "error": {
        "code": "unauthorized",
        "message": "Invalid or missing API key",
        "details": null
    }
}
 

Example response (404, not_found):


{
    "error": {
        "code": "not_found",
        "message": "Resource not found",
        "details": null
    }
}
 

Request      

DELETE api/v1/contacts/{uuid}

Headers

Authorization        

Example: Bearer nh_live_xxx

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

uuid   string     

UUID du contact. Example: 7c8f9d10-1234-4567-89ab-cdef01234567

Messages

Lister les messages

requires authentication

Retourne les messages du tenant courant, triés par date de création décroissante. Pagination cursor (max 200/page).

Example request:
curl --request GET \
    --get "https://eburniesend.com/api/v1/messages?per_page=50&status=delivered&channel=sms&recipient=%2B2250555" \
    --header "Authorization: Bearer nh_live_xxx" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://eburniesend.com/api/v1/messages"
);

const params = {
    "per_page": "50",
    "status": "delivered",
    "channel": "sms",
    "recipient": "+2250555",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer nh_live_xxx",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200, success):


{
    "data": [],
    "links": {
        "first": "...",
        "last": "...",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 1,
        "per_page": 50,
        "to": 0,
        "total": 0
    }
}
 

Example response (401, unauthorized):


{
    "error": {
        "code": "unauthorized",
        "message": "Invalid or missing API key",
        "details": null
    }
}
 

Example response (429, rate_limited):


{
    "error": {
        "code": "rate_limit_exceeded",
        "message": "Too many requests",
        "details": {
            "retry_after": 60
        }
    }
}
 

Request      

GET api/v1/messages

Headers

Authorization        

Example: Bearer nh_live_xxx

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

per_page   integer  optional    

Nombre d'éléments par page (1-200). Default 50. Example: 50

status   string  optional    

Filtre par statut (queued/sent/delivered/read/failed/expired/rejected). Example: delivered

channel   string  optional    

Filtre par code canal (whatsapp/sms/email). Example: sms

recipient   string  optional    

Filtre LIKE sur le destinataire (E.164 partiel ou email). Example: +2250555

Envoyer un message

requires authentication

Crée et dispatch un message sur le canal demandé. Le pricing v2 débite automatiquement les CreditLots FIFO (mode pack) ou incrémente le compteur Postpaid (mode postpaid). Le tenant doit avoir un mode d'accès actif sur le canal — sinon HTTP 403.

Idempotency via header HTTP Idempotency-Key (recommandé) ou champ idempotency_key du body. Une clé déjà consommée renvoie le Message existant avec son statut courant.

Email — signature tenant : si le tenant a configuré une signature email dans /app/settings/email-sender, elle est appendée automatiquement au corps HTML de tous les emails envoyés via cette API (séparateur <hr> injecté entre le corps et la signature). Aucun champ à fournir côté client.

Email — pièces jointes : utiliser le champ attachments (parité UI). Chaque path doit provenir d'un POST /api/v1/uploads préalable (scope strict tenant ; un path d'un autre tenant est silencieusement ignoré côté serveur).

Example request:
curl --request POST \
    "https://eburniesend.com/api/v1/messages" \
    --header "Authorization: Bearer nh_live_xxx" \
    --header "Idempotency-Key: UUID/string 8-128 caractères pour idempotence. Recommandé pour tous les POST. Example: 8f3a2b91-7c4d-4e8f-a1b2-3c4d5e6f7a8b" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"channel\": \"sms\",
    \"operator\": \"mtn_ci\",
    \"to\": \"+22505XXXXXXXX\",
    \"subject\": \"Confirmation commande #1234\",
    \"content\": \"Votre code de validation est 1234.\",
    \"template\": \"bulletin_de_note\",
    \"language\": \"fr\",
    \"variables\": [
        \"Jean\",
        \"EduCab\"
    ],
    \"message_type\": \"transactional\",
    \"priority\": 5,
    \"idempotency_key\": \"order-1234-confirm\",
    \"client_reference\": \"ORDER-1234\",
    \"metadata\": {
        \"template_header_data\": {
            \"link\": \"https:\\/\\/example.com\\/file.pdf\",
            \"filename\": \"bulletin.pdf\"
        }
    },
    \"attachments\": [
        {
            \"path\": \"tenants\\/7\\/email_attachments\\/abc.pdf\",
            \"name\": \"facture.pdf\",
            \"mime\": \"application\\/pdf\"
        }
    ]
}"
const url = new URL(
    "https://eburniesend.com/api/v1/messages"
);

const headers = {
    "Authorization": "Bearer nh_live_xxx",
    "Idempotency-Key": "UUID/string 8-128 caractères pour idempotence. Recommandé pour tous les POST. Example: 8f3a2b91-7c4d-4e8f-a1b2-3c4d5e6f7a8b",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "channel": "sms",
    "operator": "mtn_ci",
    "to": "+22505XXXXXXXX",
    "subject": "Confirmation commande #1234",
    "content": "Votre code de validation est 1234.",
    "template": "bulletin_de_note",
    "language": "fr",
    "variables": [
        "Jean",
        "EduCab"
    ],
    "message_type": "transactional",
    "priority": 5,
    "idempotency_key": "order-1234-confirm",
    "client_reference": "ORDER-1234",
    "metadata": {
        "template_header_data": {
            "link": "https:\/\/example.com\/file.pdf",
            "filename": "bulletin.pdf"
        }
    },
    "attachments": [
        {
            "path": "tenants\/7\/email_attachments\/abc.pdf",
            "name": "facture.pdf",
            "mime": "application\/pdf"
        }
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201, created):


{
    "data": {
        "id": "a1c9fe84-ec82-4f87-b257-acd90ae9f998",
        "status": "queued",
        "channel": "sms",
        "cost": 14,
        "currency": "XOF",
        "metadata": {
            "pricing_mode": "pack"
        }
    }
}
 

Example response (401, unauthorized):


{
    "error": {
        "code": "unauthorized",
        "message": "Invalid or missing API key",
        "details": null
    }
}
 

Example response (402, insufficient_credits):


{
    "error": {
        "code": "insufficient_credits",
        "message": "Tenant 7 : crédits insuffisants sur sms (besoin 14 XOF, dispo 0 XOF).",
        "details": {
            "tenant_id": 7,
            "channel_code": "sms",
            "amount_required_xof": 14,
            "amount_available_xof": 0
        }
    }
}
 

Example response (402, postpaid_volume_cap_exceeded):


{
    "error": {
        "code": "postpaid_volume_cap_exceeded",
        "message": "Tenant 12 : plafond mensuel Postpaid dépassé sur sms (contrat #5 : 50000/50000, +1 demandés).",
        "details": {
            "tenant_id": 12,
            "channel_code": "sms",
            "contract_id": 5,
            "current_volume": 50000,
            "requested_units": 1,
            "monthly_cap": 50000
        }
    }
}
 

Example response (402, insufficient_funds):


{
    "error": {
        "code": "insufficient_funds",
        "message": "Tenant v1 : solde insuffisant sur sms.",
        "details": {
            "tenant_id": 3,
            "channel_code": "sms",
            "amount_required": 14,
            "balance_available": 0
        }
    }
}
 

Example response (403, channel_blocked):


{
    "error": {
        "code": "channel_blocked",
        "message": "Aucun mode d'accès actif sur le canal sms.",
        "details": {
            "tenant_id": 7,
            "channel_code": "sms",
            "reason": "no_active_mode"
        }
    }
}
 

Example response (403, sender_id_not_authorized):


{
    "error": {
        "code": "sender_id_not_authorized",
        "message": "No authorized sender ID for tenant 7 on gateway (http_sms_gateway/mtn).",
        "details": {
            "tenant_id": 7,
            "gateway_id": 1,
            "gateway_type": "http_sms_gateway",
            "operator": "mtn"
        }
    }
}
 

Example response (404, template_not_found):


{
    "error": {
        "code": "not_found",
        "message": "Resource not found",
        "details": null
    }
}
 

Example response (422, validation_failed):


{
    "error": {
        "code": "validation_failed",
        "message": "The given data was invalid.",
        "details": {
            "fields": {
                "to": [
                    "The to field is required."
                ]
            }
        }
    }
}
 

Example response (422, no_active_contract):


{
    "error": {
        "code": "no_active_contract",
        "message": "Tenant 12 : aucun contrat Postpaid actif sur le canal sms.",
        "details": {
            "tenant_id": 12,
            "channel_code": "sms"
        }
    }
}
 

Example response (422, price_not_found):


{
    "error": {
        "code": "price_not_found",
        "message": "Prix introuvable.",
        "details": {
            "tenant_id": 7,
            "channel_id": 2,
            "operator_id": 1,
            "message_type_id": 1
        }
    }
}
 

Example response (429, rate_limited):


{
    "error": {
        "code": "rate_limit_exceeded",
        "message": "Too many requests",
        "details": {
            "retry_after": 60
        }
    }
}
 

Example response (502, no_gateway_available):


{
    "error": {
        "code": "no_gateway_available",
        "message": "Aucun gateway SMS disponible pour le tenant 7 (operator=orange) — preferred + fallback DOWN simultanément. tentés : [1, 2]",
        "details": {
            "tenant_id": 7,
            "recipient_operator": "orange",
            "attempted_gateway_ids": [
                1,
                2
            ]
        }
    }
}
 

Request      

POST api/v1/messages

Headers

Authorization        

Example: Bearer nh_live_xxx

Idempotency-Key        

Example: UUID/string 8-128 caractères pour idempotence. Recommandé pour tous les POST. Example: 8f3a2b91-7c4d-4e8f-a1b2-3c4d5e6f7a8b

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

channel   string     

Code canal (whatsapp/sms/email). Example: sms

operator   string  optional    

Code opérateur (mtn_ci/orange_ci/...). Auto-détecté si absent pour sms/whatsapp. Example: mtn_ci

to   string     

Destinataire (E.164 pour sms/whatsapp, email RFC pour email). Example: +22505XXXXXXXX

subject   string  optional    

Sujet (email uniquement, max 500 chars). Example: Confirmation commande #1234

content   string  optional    

required_without:template Contenu du message. Limites : sms/whatsapp max 4096 chars, email max 50000 chars (HTML riche supporté pour email). Pour whatsapp template, laisser vide. Example: Votre code de validation est 1234.

template   string  optional    

required_without:content Nom du template WhatsApp (templates plateforme is_shareable accessibles cross-tenant). Example: bulletin_de_note

language   string  optional    

required_with:template Langue du template (ISO 639-1, ex fr). Example: fr

variables   string[]  optional    

Variables du template (positionnelles : ["valeur1","valeur2"] mappées sur {{1}}, {{2}}).

message_type   string  optional    

Type métier (transactional/marketing/auth/service). Default transactional. Example: transactional

priority   integer  optional    

Priorité 1-9 (1 = max, 5 = default). Example: 5

idempotency_key   string  optional    

Alternative au header Idempotency-Key. Max 100 chars. Example: order-1234-confirm

client_reference   string  optional    

Référence opaque côté client, retournée tel quel sur tous les events. Example: ORDER-1234

metadata   object  optional    

Métadonnées libres (max 16 KiB). Pour WhatsApp HEADER DOCUMENT, utiliser metadata.template_header_data = {link, filename}.

attachments   string[]  optional    

Pièces jointes email (parité UI). Chaque item est un objet {path, name, mime}. Le path doit être obtenu via POST /api/v1/uploads (scope strict tenant — un path d'un autre tenant est silencieusement ignoré côté serveur, anti-exfiltration). Le name original sert au libellé reçu par le destinataire ; le mime est inféré côté upload si omis.

path   string  optional    

This field is required when attachments is present. Example: architecto

name   string  optional    

Must not be greater than 255 characters. Example: n

mime   string  optional    

Must not be greater than 120 characters. Example: g

Envoi en lot (batch)

requires authentication

Envoie jusqu'à 100 messages en une seule requête. Chaque message est traité indépendamment : un échec sur l'un n'arrête pas le traitement des autres. Le response retourne un tableau results[] où chaque entrée est soit {message: {...}} (succès, HTTP 201 implicite par item) soit {error: {...}} (erreur item).

Idempotency : chaque item peut porter son propre idempotency_key. Le header Idempotency-Key couvre toute la requête batch (utile pour retry HTTP) — pas chaque item.

Example request:
curl --request POST \
    "https://eburniesend.com/api/v1/messages/batch" \
    --header "Authorization: Bearer nh_live_xxx" \
    --header "Idempotency-Key: string Idempotence de la requête batch entière (8-128 chars). Example: batch-2026-05-17-001" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"messages\": [
        {
            \"channel\": \"sms\",
            \"to\": \"+2250555\",
            \"content\": \"Hello\"
        }
    ]
}"
const url = new URL(
    "https://eburniesend.com/api/v1/messages/batch"
);

const headers = {
    "Authorization": "Bearer nh_live_xxx",
    "Idempotency-Key": "string Idempotence de la requête batch entière (8-128 chars). Example: batch-2026-05-17-001",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "messages": [
        {
            "channel": "sms",
            "to": "+2250555",
            "content": "Hello"
        }
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200, mixed):


{
    "data": {
        "results": [
            {
                "message": {
                    "id": "...",
                    "status": "queued"
                }
            },
            {
                "error": {
                    "code": "validation_failed",
                    "message": "..."
                }
            }
        ]
    }
}
 

Example response (401, unauthorized):


{
    "error": {
        "code": "unauthorized",
        "message": "Invalid or missing API key",
        "details": null
    }
}
 

Example response (422, validation_failed):


{
    "error": {
        "code": "validation_failed",
        "message": "The given data was invalid.",
        "details": {
            "fields": {
                "messages": [
                    "The messages field is required."
                ]
            }
        }
    }
}
 

Example response (429, rate_limited):


{
    "error": {
        "code": "rate_limit_exceeded",
        "message": "Too many requests",
        "details": {
            "retry_after": 60
        }
    }
}
 

Request      

POST api/v1/messages/batch

Headers

Authorization        

Example: Bearer nh_live_xxx

Idempotency-Key        

Example: string Idempotence de la requête batch entière (8-128 chars). Example: batch-2026-05-17-001

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

messages   string[]     

Tableau de 1 à 100 items (mêmes champs que POST /messages).

Détails d'un message

requires authentication

Retourne le statut courant et l'historique complet d'un message identifié par son UUID.

Example request:
curl --request GET \
    --get "https://eburniesend.com/api/v1/messages/a1c9fe84-ec82-4f87-b257-acd90ae9f998" \
    --header "Authorization: Bearer nh_live_xxx" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://eburniesend.com/api/v1/messages/a1c9fe84-ec82-4f87-b257-acd90ae9f998"
);

const headers = {
    "Authorization": "Bearer nh_live_xxx",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200, success):


{
    "data": {
        "id": "a1c9fe84-ec82-4f87-b257-acd90ae9f998",
        "status": "delivered",
        "channel": "sms",
        "recipient": "+22505XXXXXXXX"
    }
}
 

Example response (401, unauthorized):


{
    "error": {
        "code": "unauthorized",
        "message": "Invalid or missing API key",
        "details": null
    }
}
 

Example response (404, not_found):


{
    "error": {
        "code": "not_found",
        "message": "Resource not found",
        "details": null
    }
}
 

Request      

GET api/v1/messages/{uuid}

Headers

Authorization        

Example: Bearer nh_live_xxx

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

uuid   string     

UUID du message. Example: a1c9fe84-ec82-4f87-b257-acd90ae9f998

Notifications

Envoyer une notification métier multi-canal

requires authentication

Déclenche l'envoi de N messages selon les préférences canaux configurées par le tenant pour cet event_type. Contrairement à POST /api/v1/messages qui est low-level (1 message = 1 channel explicite), cet endpoint route automatiquement vers les canaux activés via tenant_notification_channel_preferences.

Configuration des préférences : voir Filament app /app/settings/notifications (tenant) ou /admin/tenants/{id} onglet préférences (Ebernate admin).

Idempotency-Key : si fournie, le dispatcher suffix par canal ({key}-sms, {key}-email...) côté MessageDispatcher pour éviter les doublons en cas de retry consumer. Le replay d'un appel avec la même clé renvoie les mêmes Messages (mêmes UUIDs).

Example request:
curl --request POST \
    "https://eburniesend.com/api/v1/notifications" \
    --header "Authorization: Bearer nh_live_xxx" \
    --header "Idempotency-Key: string Clé d\&#039;idempotence (UUID v4 ou alphanumérique 8-128 chars). Example: 01970b6d-2c40-7000-91a0-aaaaaaaaaaaa" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"event_type\": \"business.order_confirmed\",
    \"recipient\": {
        \"email\": \"[email protected]\",
        \"phone_e164\": \"+22505XXXXXXXX\",
        \"whatsapp_e164\": \"+22505XXXXXXXX\"
    },
    \"template_data\": {
        \"order_id\": \"12345\",
        \"amount_xof\": 50000
    },
    \"client_reference\": \"ORDER-12345\"
}"
const url = new URL(
    "https://eburniesend.com/api/v1/notifications"
);

const headers = {
    "Authorization": "Bearer nh_live_xxx",
    "Idempotency-Key": "string Clé d&#039;idempotence (UUID v4 ou alphanumérique 8-128 chars). Example: 01970b6d-2c40-7000-91a0-aaaaaaaaaaaa",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "event_type": "business.order_confirmed",
    "recipient": {
        "email": "[email protected]",
        "phone_e164": "+22505XXXXXXXX",
        "whatsapp_e164": "+22505XXXXXXXX"
    },
    "template_data": {
        "order_id": "12345",
        "amount_xof": 50000
    },
    "client_reference": "ORDER-12345"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201, created):


{
    "data": {
        "notification_id": "01970b6d-2c40-7000-91a0-aaaaaaaaaaaa",
        "messages": [
            {
                "id": "01970b6d-2c40-7000-91a0-bbbbbbbbbbbb",
                "channel": "sms",
                "status": "queued"
            },
            {
                "id": "01970b6d-2c40-7000-91a0-cccccccccccc",
                "channel": "email",
                "status": "queued"
            }
        ]
    },
    "meta": {
        "channels_configured": 3,
        "channels_dispatched": 2,
        "channels_skipped": [
            {
                "channel": "whatsapp",
                "reason": "recipient_missing"
            }
        ]
    }
}
 

Example response (401, unauthorized):


{
    "error": {
        "code": "unauthorized",
        "message": "Invalid or missing API key",
        "details": null
    }
}
 

Example response (422, validation_failed):


{
    "error": {
        "code": "validation_failed",
        "message": "The given data was invalid.",
        "details": {
            "fields": {
                "event_type": [
                    "The event type format is invalid. Use lowercase letters, digits, dots and underscores starting with a letter (e.g. business.order_confirmed)."
                ]
            }
        }
    }
}
 

Example response (422, invalid_event_type):


{
    "error": {
        "code": "invalid_event_type",
        "message": "Invalid event type 'badformat': format_invalid",
        "details": {
            "event_type": "badformat",
            "reason": "format_invalid"
        }
    }
}
 

Example response (422, no_channels_configured):


{
    "error": {
        "code": "no_channels_configured",
        "message": "No channels configured for tenant 7 on event 'business.order_confirmed'.",
        "details": {
            "tenant_id": 7,
            "event_type": "business.order_confirmed"
        }
    }
}
 

Example response (422, all_channels_skipped):


{
    "error": {
        "code": "all_channels_skipped",
        "message": "All 2 configured channels skipped for tenant 7 on event 'business.order_confirmed'.",
        "details": {
            "tenant_id": 7,
            "event_type": "business.order_confirmed",
            "skipped": [
                {
                    "channel": "sms",
                    "reason": "recipient_missing"
                },
                {
                    "channel": "email",
                    "reason": "recipient_missing"
                }
            ]
        }
    }
}
 

Example response (429, rate_limited):


{
    "error": {
        "code": "rate_limit_exceeded",
        "message": "Too many requests",
        "details": {
            "retry_after": 60
        }
    }
}
 

Example response (502, no_gateway_available):


{
    "error": {
        "code": "no_gateway_available",
        "message": "Aucun gateway SMS disponible pour le tenant 7 (operator=orange) — preferred + fallback DOWN simultanément. tentés : [1, 2]",
        "details": {
            "tenant_id": 7,
            "recipient_operator": "orange",
            "attempted_gateway_ids": [
                1,
                2
            ]
        }
    }
}
 

Request      

POST api/v1/notifications

Headers

Authorization        

Example: Bearer nh_live_xxx

Idempotency-Key        

Example: string Clé d'idempotence (UUID v4 ou alphanumérique 8-128 chars). Example: 01970b6d-2c40-7000-91a0-aaaaaaaaaaaa

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

event_type   string     

Identifiant événement métier au format [a-z][a-z0-9_.]+ (3-100 chars). Convention namespace business.<name>. Example: business.order_confirmed

recipient   object     

Destinataire (au moins UNE clé parmi email, phone_e164, whatsapp_e164).

email   string  optional    

Email valide. Example: [email protected]

phone_e164   string  optional    

Téléphone format E.164. Example: +22505XXXXXXXX

whatsapp_e164   string  optional    

Téléphone WhatsApp format E.164. Example: +22505XXXXXXXX

template_data   object  optional    

Variables d'interpolation pour les payload templates SMS/Email.

client_reference   string  optional    

optional Référence libre propagée à tous les messages créés (max 255 chars). Example: ORDER-12345

Templates WhatsApp

Lister les templates WhatsApp

requires authentication

Retourne les templates WhatsApp du tenant (et les templates plateforme is_shareable=true accessibles cross-tenant). Triés par date de création décroissante.

Example request:
curl --request GET \
    --get "https://eburniesend.com/api/v1/templates?per_page=50&status=approved&language=fr&category=UTILITY" \
    --header "Authorization: Bearer nh_live_xxx" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://eburniesend.com/api/v1/templates"
);

const params = {
    "per_page": "50",
    "status": "approved",
    "language": "fr",
    "category": "UTILITY",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer nh_live_xxx",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200, success):


{
    "data": [],
    "meta": {
        "current_page": 1,
        "per_page": 50,
        "total": 0
    }
}
 

Example response (401, unauthorized):


{
    "error": {
        "code": "unauthorized",
        "message": "Invalid or missing API key",
        "details": null
    }
}
 

Request      

GET api/v1/templates

Headers

Authorization        

Example: Bearer nh_live_xxx

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

per_page   integer  optional    

Nombre d'éléments par page (1-200). Default 50. Example: 50

status   string  optional    

Filtre par statut Meta (draft/pending/approved/rejected/disabled). Example: approved

language   string  optional    

Filtre par langue ISO 639-1. Example: fr

category   string  optional    

Filtre par catégorie Meta (AUTHENTICATION/MARKETING/UTILITY). Example: UTILITY

Créer un template WhatsApp

requires authentication

Crée un template en statut draft. La soumission à Meta pour approbation se fait dans un second temps via l'admin Filament (un endpoint dédié sera disponible prochainement). Le tenant doit avoir au moins un WhatsappNumber configuré (soit explicitement via whatsapp_number_id, soit le default plateforme).

Example request:
curl --request POST \
    "https://eburniesend.com/api/v1/templates" \
    --header "Authorization: Bearer nh_live_xxx" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"whatsapp_number_id\": \"aabbccdd-1122-3344-5566-7788991122ff\",
    \"name\": \"order_confirmation\",
    \"language\": \"fr\",
    \"category\": \"UTILITY\",
    \"components\": [
        {
            \"type\": \"BODY\",
            \"text\": \"Hello {{1}}, your order #{{2}} is confirmed.\"
        }
    ]
}"
const url = new URL(
    "https://eburniesend.com/api/v1/templates"
);

const headers = {
    "Authorization": "Bearer nh_live_xxx",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "whatsapp_number_id": "aabbccdd-1122-3344-5566-7788991122ff",
    "name": "order_confirmation",
    "language": "fr",
    "category": "UTILITY",
    "components": [
        {
            "type": "BODY",
            "text": "Hello {{1}}, your order #{{2}} is confirmed."
        }
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201, created):


{
    "data": {
        "id": "11223344-...",
        "name": "order_confirmation",
        "status": "draft",
        "category": "UTILITY"
    }
}
 

Example response (401, unauthorized):


{
    "error": {
        "code": "unauthorized",
        "message": "Invalid or missing API key",
        "details": null
    }
}
 

Example response (422, no_whatsapp_number):


{
    "error": {
        "code": "no_whatsapp_number",
        "message": "Aucun numéro WhatsApp configuré pour ce tenant",
        "details": null
    }
}
 

Example response (422, validation_failed):


{
    "error": {
        "code": "validation_failed",
        "message": "The given data was invalid.",
        "details": {
            "fields": {
                "name": [
                    "The name format is invalid."
                ]
            }
        }
    }
}
 

Request      

POST api/v1/templates

Headers

Authorization        

Example: Bearer nh_live_xxx

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

whatsapp_number_id   string  optional    

UUID du WhatsappNumber cible. Si absent, utilise le default tenant ou plateforme. Example: aabbccdd-1122-3344-5566-7788991122ff

name   string     

Nom du template (lowercase, underscores, 1-100 chars). Regex ^[a-z0-9_]+$. Example: order_confirmation

language   string     

Langue ISO 639-1 (max 10). Example: fr

category   string     

Catégorie Meta : AUTHENTICATION, MARKETING ou UTILITY. Example: UTILITY

components   string[]     

Composants Meta (HEADER/BODY/FOOTER/BUTTONS). Voir spec WhatsApp Cloud API.

Détails d'un template WhatsApp

requires authentication

Example request:
curl --request GET \
    --get "https://eburniesend.com/api/v1/templates/11223344-aabb-ccdd-eeff-001122334455" \
    --header "Authorization: Bearer nh_live_xxx" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://eburniesend.com/api/v1/templates/11223344-aabb-ccdd-eeff-001122334455"
);

const headers = {
    "Authorization": "Bearer nh_live_xxx",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200, success):


{
    "data": {
        "id": "11223344-...",
        "name": "bulletin_de_note",
        "language": "fr",
        "status": "approved",
        "category": "UTILITY",
        "components": []
    }
}
 

Example response (401, unauthorized):


{
    "error": {
        "code": "unauthorized",
        "message": "Invalid or missing API key",
        "details": null
    }
}
 

Example response (404, not_found):


{
    "error": {
        "code": "not_found",
        "message": "Resource not found",
        "details": null
    }
}
 

Request      

GET api/v1/templates/{uuid}

Headers

Authorization        

Example: Bearer nh_live_xxx

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

uuid   string     

UUID du template. Example: 11223344-aabb-ccdd-eeff-001122334455

Uploads

Upload d'une pièce jointe pour email.

requires authentication

Example request:
curl --request POST \
    "https://eburniesend.com/api/v1/uploads" \
    --header "Authorization: Bearer nh_live_xxx" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "file=@C:\Users\EDUCAB02\AppData\Local\Temp\phpF05E.tmp" 
const url = new URL(
    "https://eburniesend.com/api/v1/uploads"
);

const headers = {
    "Authorization": "Bearer nh_live_xxx",
    "Content-Type": "multipart/form-data",
    "Accept": "application/json",
};

const body = new FormData();
body.append('file', document.querySelector('input[name="file"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());

Example response (201, created):


{
    "data": {
        "path": "tenants/7/email_attachments/a1c79606-b6a6-47cc-956b-ea058f8ecbe3.pdf",
        "name": "facture-2026.pdf",
        "mime": "application/pdf",
        "size": 12345
    }
}
 

Example response (401, unauthorized):


{
    "error": {
        "code": "unauthorized",
        "message": "Invalid or missing API key",
        "details": null
    }
}
 

Example response (422, validation_failed):


{
    "error": {
        "code": "validation_failed",
        "message": "The given data was invalid.",
        "details": {
            "fields": {
                "file": [
                    "The file must be a file of type: pdf, png, jpg, jpeg, gif, doc, docx, xls, xlsx."
                ]
            }
        }
    }
}
 

Request      

POST api/v1/uploads

Headers

Authorization        

Example: Bearer nh_live_xxx

Content-Type        

Example: multipart/form-data

Accept        

Example: application/json

Body Parameters

file   file     

Fichier à uploader (multipart/form-data). Max 10 Mo. MIME autorisés : pdf, png, jpg, jpeg, gif, doc, docx, xls, xlsx. Example: C:\Users\EDUCAB02\AppData\Local\Temp\phpF05E.tmp

Webhooks sortants

Lister les endpoints webhook

requires authentication

Retourne les endpoints webhook configurés par le tenant. Ces endpoints reçoivent les notifications d'événements (sent / delivered / failed / etc.) via le WebhookDispatcher.

Example request:
curl --request GET \
    --get "https://eburniesend.com/api/v1/webhooks" \
    --header "Authorization: Bearer nh_live_xxx" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://eburniesend.com/api/v1/webhooks"
);

const headers = {
    "Authorization": "Bearer nh_live_xxx",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200, success):


{
    "data": [],
    "meta": {
        "current_page": 1,
        "per_page": 50,
        "total": 0
    }
}
 

Example response (401, unauthorized):


{
    "error": {
        "code": "unauthorized",
        "message": "Invalid or missing API key",
        "details": null
    }
}
 

Request      

GET api/v1/webhooks

Headers

Authorization        

Example: Bearer nh_live_xxx

Content-Type        

Example: application/json

Accept        

Example: application/json

Créer un endpoint webhook

requires authentication

Enregistre une URL HTTPS qui recevra les notifications d'événements eBurnieSend. Chaque payload est signé HMAC-SHA256 avec le secret, transmis dans le header X-eBurnieSend-Signature: sha256=<hex>. Voir la doc de vérification de signature côté consumer pour l'algo exact.

Sécurité du secret :

Example request:
curl --request POST \
    "https://eburniesend.com/api/v1/webhooks" \
    --header "Authorization: Bearer nh_live_xxx" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Production webhook\",
    \"url\": \"https:\\/\\/api.example.com\\/webhooks\\/ebs\",
    \"subscribed_events\": [
        \"message.delivered\",
        \"message.failed\"
    ],
    \"secret\": \"whsec_my_custom_secret_at_least_32_chars_long_here\",
    \"timeout_seconds\": 10,
    \"max_retries\": 5
}"
const url = new URL(
    "https://eburniesend.com/api/v1/webhooks"
);

const headers = {
    "Authorization": "Bearer nh_live_xxx",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Production webhook",
    "url": "https:\/\/api.example.com\/webhooks\/ebs",
    "subscribed_events": [
        "message.delivered",
        "message.failed"
    ],
    "secret": "whsec_my_custom_secret_at_least_32_chars_long_here",
    "timeout_seconds": 10,
    "max_retries": 5
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201, created):


{
    "data": {
        "id": "ff112233-...",
        "name": "Production webhook",
        "url": "https://api.example.com/webhooks/ebs",
        "subscribed_events": [
            "message.delivered"
        ],
        "is_active": true
    },
    "meta": {
        "secret_plaintext": "whsec_abcdef0123456789abcdef0123456789abcdef0123",
        "warning": "This secret will not be shown again. Store it securely now. Use it to verify the X-eBurnieSend-Signature header on incoming webhook requests."
    }
}
 

Example response (401, unauthorized):


{
    "error": {
        "code": "unauthorized",
        "message": "Invalid or missing API key",
        "details": null
    }
}
 

Example response (422, validation_failed):


{
    "error": {
        "code": "validation_failed",
        "message": "The given data was invalid.",
        "details": {
            "fields": {
                "url": [
                    "The url must be a valid HTTPS URL."
                ],
                "secret": [
                    "The secret must be at least 32 characters."
                ]
            }
        }
    }
}
 

Request      

POST api/v1/webhooks

Headers

Authorization        

Example: Bearer nh_live_xxx

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

name   string     

Libellé interne (max 150). Example: Production webhook

url   string     

URL HTTPS publique (max 500). Example: https://api.example.com/webhooks/ebs

subscribed_events   string[]  optional    

Événements souscrits. Si absent, reçoit TOUS les événements. Valeurs valides : message.queued, message.sent, message.delivered, message.read, message.failed, message.expired.

secret   string  optional    

Secret HMAC custom (32-255 chars). Si absent, généré automatiquement (whsec_<42 chars> = 48 chars total). Example: whsec_my_custom_secret_at_least_32_chars_long_here

timeout_seconds   integer  optional    

Timeout HTTP côté eBurnieSend (1-60). Default 10. Example: 10

max_retries   integer  optional    

Nombre max de retries en cas d'échec (0-10). Default 5. Example: 5

Détails d'un endpoint webhook

requires authentication

Example request:
curl --request GET \
    --get "https://eburniesend.com/api/v1/webhooks/ff112233-4455-6677-8899-aabbccddeeff" \
    --header "Authorization: Bearer nh_live_xxx" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://eburniesend.com/api/v1/webhooks/ff112233-4455-6677-8899-aabbccddeeff"
);

const headers = {
    "Authorization": "Bearer nh_live_xxx",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200, success):


{
    "data": {
        "id": "ff112233-...",
        "name": "Production webhook",
        "url": "https://api.example.com/webhooks/ebs",
        "subscribed_events": [
            "message.delivered",
            "message.failed"
        ]
    }
}
 

Example response (401, unauthorized):


{
    "error": {
        "code": "unauthorized",
        "message": "Invalid or missing API key",
        "details": null
    }
}
 

Example response (404, not_found):


{
    "error": {
        "code": "not_found",
        "message": "Resource not found",
        "details": null
    }
}
 

Request      

GET api/v1/webhooks/{uuid}

Headers

Authorization        

Example: Bearer nh_live_xxx

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

uuid   string     

UUID de l'endpoint. Example: ff112233-4455-6677-8899-aabbccddeeff

Mettre à jour un endpoint webhook

requires authentication

Update partiel (PATCH). Le secret n'est PAS modifiable via cette API — pour rotation, supprimer + recréer l'endpoint.

Example request:
curl --request PATCH \
    "https://eburniesend.com/api/v1/webhooks/ff112233-4455-6677-8899-aabbccddeeff" \
    --header "Authorization: Bearer nh_live_xxx" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Production webhook v2\",
    \"url\": \"https:\\/\\/api.example.com\\/webhooks\\/ebs-v2\",
    \"subscribed_events\": [
        \"message.delivered\",
        \"message.failed\"
    ],
    \"is_active\": true,
    \"timeout_seconds\": 15,
    \"max_retries\": 3
}"
const url = new URL(
    "https://eburniesend.com/api/v1/webhooks/ff112233-4455-6677-8899-aabbccddeeff"
);

const headers = {
    "Authorization": "Bearer nh_live_xxx",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Production webhook v2",
    "url": "https:\/\/api.example.com\/webhooks\/ebs-v2",
    "subscribed_events": [
        "message.delivered",
        "message.failed"
    ],
    "is_active": true,
    "timeout_seconds": 15,
    "max_retries": 3
};

fetch(url, {
    method: "PATCH",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200, updated):


{
    "data": {
        "id": "ff112233-...",
        "name": "Production webhook v2",
        "is_active": true
    }
}
 

Example response (401, unauthorized):


{
    "error": {
        "code": "unauthorized",
        "message": "Invalid or missing API key",
        "details": null
    }
}
 

Example response (404, not_found):


{
    "error": {
        "code": "not_found",
        "message": "Resource not found",
        "details": null
    }
}
 

Example response (422, validation_failed):


{
    "error": {
        "code": "validation_failed",
        "message": "The given data was invalid.",
        "details": {
            "fields": {
                "url": [
                    "The url must be a valid HTTPS URL."
                ]
            }
        }
    }
}
 

Request      

PATCH api/v1/webhooks/{uuid}

Headers

Authorization        

Example: Bearer nh_live_xxx

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

uuid   string     

UUID de l'endpoint. Example: ff112233-4455-6677-8899-aabbccddeeff

Body Parameters

name   string  optional    

Libellé interne. Example: Production webhook v2

url   string  optional    

URL HTTPS publique. Example: https://api.example.com/webhooks/ebs-v2

subscribed_events   string[]  optional    

Événements souscrits.

is_active   boolean  optional    

Activer/désactiver l'endpoint. Example: true

timeout_seconds   integer  optional    

Timeout HTTP (1-60). Example: 15

max_retries   integer  optional    

Max retries (0-10). Example: 3

Supprimer un endpoint webhook

requires authentication

Suppression définitive. L'historique webhook_deliveries reste intact pour audit.

Example request:
curl --request DELETE \
    "https://eburniesend.com/api/v1/webhooks/ff112233-4455-6677-8899-aabbccddeeff" \
    --header "Authorization: Bearer nh_live_xxx" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://eburniesend.com/api/v1/webhooks/ff112233-4455-6677-8899-aabbccddeeff"
);

const headers = {
    "Authorization": "Bearer nh_live_xxx",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Example response (204, deleted):

Empty response
 

Example response (401, unauthorized):


{
    "error": {
        "code": "unauthorized",
        "message": "Invalid or missing API key",
        "details": null
    }
}
 

Example response (404, not_found):


{
    "error": {
        "code": "not_found",
        "message": "Resource not found",
        "details": null
    }
}
 

Request      

DELETE api/v1/webhooks/{uuid}

Headers

Authorization        

Example: Bearer nh_live_xxx

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

uuid   string     

UUID de l'endpoint. Example: ff112233-4455-6677-8899-aabbccddeeff