e-Signature.eu REST API — electronic registered mail
1. Overview
The e-Signature.eu REST API lets your application send electronic registered mail without using our website. You send the PDF, a verified sender address and the recipient; the document receives a qualified electronic timestamp, and the recipient signs an acknowledgement of receipt after verifying his identity. You get the result back through the API or a callback.
| Base URL | https://api.e-signature.eu/v3/regis/ |
| Postman | Postman documentation and collection |
The previous base URL https://www.e-signature.eu/wp-json/esig/v3/regis/ remains valid. We recommend using the api.e-signature.eu URL.
2. Authentication
Your credentials are available in My account > Signatures API once you are logged in on e-signature.eu:
- User ID: sent in the URL as the
useridparameter of every call; - API token: sent in the
x-api-keyHTTP header of every call.
curl -X GET "https://api.e-signature.eu/v3/regis/requestBalance?userid=123" \
-H "x-api-key: YOUR_API_TOKEN"
Keep your token secret: it gives full access to your account through the API. Calls must be made from your server, never from a browser or a mobile app.
3. Conventions
Requests and responses
- HTTPS only. Request bodies are JSON (
Content-Type: application/json). - Every response is JSON, in one of these two forms:
{ "success": true, "result": { ... } }
{ "success": false, "error": "Readable error message" }
Limits
| Limit | Value |
|---|---|
| Requests | 10 per minute per account and IP address (HTTP 403 beyond, retry after one minute) |
| Document | One PDF per request, 30 MB maximum, not encrypted / not password protected |
| Download of signed documents | 30 days after completion (see Retention) |
4. Credits
Requests are paid with your prepaid e-signature.eu credits. The price of a method can be retrieved with getMethods (it takes your specific prices into account). Your balance is returned by requestBalance and in every request object (credits_balance).
Automatic credit recharge
To avoid running out of credits, you can let your account recharge itself automatically. It is set up once in My account > Signatures API, not through the API:
- Choose a threshold (in credits) and the credit pack to order.
- Save a payment method with our payment provider Mollie (card, or SEPA Direct Debit through Bancontact, iDEAL…). A verification payment of €0.01 is requested once.
- Whenever credits are debited and your balance falls below the threshold, an order for the chosen pack is created and paid automatically with the saved payment method. The invoice is sent to you by email.
| Automatic recharge | |
|---|---|
| Trigger | A debit of credits (e.g. a new request) that brings the balance below the threshold |
| Credits added | As soon as the payment is confirmed: immediately by card, after a few working days by SEPA Direct Debit |
| Recharges in progress | One at a time |
| Safety limit | A maximum number of automatic recharges per 30 days |
| Payment failure | The order is marked as failed, automatic recharge is turned off and you are informed by email |
You do not have enough credits) and does not trigger a recharge: choose a threshold higher than the cost of your largest requests, and keep a margin when paying by SEPA Direct Debit. Your balance remains available at any time through requestBalance and credits_balance.5. How a registered mail works
- You send the PDF, a verified sender address and the recipient. The document receives a qualified electronic timestamp and the credits are debited.
- The recipient receives an email with two links: one to sign the acknowledgement of receipt and access the document, one to decline.
- To sign, the recipient verifies his identity with one of the accepted methods (itsme®, Evrotrust, OTP…).
- The registered mail becomes
finished(signed),declined, orexpiredif nothing happened after 14 days.
One sender, one recipient per registered mail. As on the website, an automatic reminder is sent every 4 days (not configurable); there is no manual reminder and no cancellation.
6. Sender address
As on the website, the sender address (sender_email) must be verified before it can be used:
- Call
verifySender: an email with a verification link is sent to the address (response202). If the address is already verified, the response is200with"verified": true. - The owner of the address clicks the link.
- The address can then be used in
request.
{ "email": "[email protected]", "lang": "en" }
{
"success": true,
"result": {
"email": "[email protected]",
"verified": false,
"message": "A verification email has been sent to [email protected]. The address can be used as sender_email once the link in this email has been clicked."
}
}
Lists the sender addresses of your account with their status.
{
"success": true,
"result": [
{
"email": "[email protected]",
"verified": true
},
{
"email": "[email protected]",
"verified": false
}
]
}
7. Send a registered mail
{
"title": "Formal notice rent October",
"lang": "en",
"sender_email": "[email protected]",
"recipient": {
"firstname": "Ann",
"lastname": "Peeters",
"email": "[email protected]",
"phone": "+32475111222"
},
"methods": [
"itsme-qes-signature",
"evrotrust-signature"
],
"send_email": true,
"note_html": "Please acknowledge receipt.",
"signature_redirect": "https://www.example.com/thank-you",
"external_reference": "INV-2026-0042",
"metadata": {
"customer_id": 311
},
"pdf_file_base64": "JVBERi0xLjcKJeLjz9MK..."
}
| Field | Required | Type | Description |
|---|---|---|---|
title |
yes | string | Name of the document. Letters, digits and dashes are kept; a suffix -1, -2… is added if the name already exists. |
lang |
yes | string | Language of the emails and pages: fr, nl, en, de, es. |
sender_email |
yes | string | Verified sender address, see Sender address. |
recipient.lastname, recipient.email |
yes | string | |
recipient.firstname |
no | string | |
recipient.phone |
no | string | International format (+32…). An invalid number is ignored. |
methods |
yes | array | Methods accepted to sign the acknowledgement of receipt, see getMethods. |
pdf_file_base64 |
yes | string | The PDF, base64 encoded. One file, 30 MB maximum, not encrypted. |
send_email |
no | boolean | Default true. With false, no email is sent to the recipient nor to the sender: send the signature_link and declined_link yourself. |
note_html |
no | string | Message added to the email sent to the recipient. |
signature_redirect |
no | string (URL) | Page shown to the recipient after signing. |
external_reference |
no | string | Your own identifier (invoice, file…), max 255 characters. |
metadata |
no | object | Free JSON object, max 4 KB, returned as is. |
callback_url |
no | string (URL) | Callback URL for this registered mail only (public HTTPS). Default: the URL of your account. |
Cost
Price of the most expensive method in methods, for one recipient. Debited when the registered mail is created.
Response 201 Created
{
"success": true,
"result": {
"type": "regis",
"id_sign": 123,
"doc_name": "Formal-notice-rent-October",
"lang": "en",
"sender_email": "[email protected]",
"status_id": "pending",
"date_added": "2026-10-20 11:50:00",
"date_status": "2026-10-20 11:50:00",
"date_expired": "2026-11-03 11:50:00",
"timezone": "Europe/Brussels",
"methods": [
"itsme-qes-signature",
"evrotrust-signature"
],
"reminder": 4,
"last_reminder": null,
"send_email": true,
"from_api": true,
"credits": 1,
"credits_balance": 41,
"recipient": {
"firstname": "Ann",
"lastname": "Peeters",
"email": "[email protected]",
"phone": "+32475111222",
"status": "pending",
"signing_time": "",
"signing_method": "",
"verified_firstname": "",
"verified_lastname": "",
"signature_link": "https://www.e-signature.eu/en/sign-regis/?key=…",
"declined_link": "https://www.e-signature.eu/en/sign-regis/?key=…"
},
"external_reference": "INV-2026-0042",
"metadata": {
"customer_id": 311
}
}
}
8. The registered mail object
Returned by request, getRequest, modifyRecipient, delete and sent in the callbacks. The field "type": "regis" distinguishes it from a signature request when both use the same callback URL.
| Field | Description |
|---|---|
id_sign |
Identifier of the registered mail |
status_id |
pending waiting for the recipient · finished acknowledgement signed · declined declined by the recipient · expired not signed after 14 days · deleted |
date_added, date_status, date_expired |
Creation, last status change, expiry (time zone of the registered mail) |
sender_email, lang, methods, reminder, send_email |
As defined at creation |
last_reminder |
Date of the last reminder, or null |
credits, credits_balance |
Credits of the registered mail, current balance of your account |
recipient |
firstname, lastname, email, phone, status; once signed signing_time, signing_method, verified_firstname, verified_lastname; while pending signature_link and declined_link |
pdf_file_base64 |
Timestamped document, once the registered mail is no longer pending |
receipt_file_base64 |
Signed acknowledgement of receipt, when finished |
external_reference, metadata |
As sent at creation |
9. Other endpoints
All endpoints take userid in the URL and x-api-key in the headers. id_sign is passed in the URL.
List the registered mails
status_id (optional): pending, finished, declined or expired. external_reference (optional): only the registered mails with this reference.
{
"success": true,
"result": [
{
"id_sign": 123,
"status_id": "pending",
"doc_name": "Formal-notice-rent-October",
"date_added": "2026-10-20 11:50:00",
"external_reference": "INV-2026-0042"
}
]
}
Get a registered mail
Modify the recipient
{ "email": "[email protected]", "phone": "+32475333444" }
Registered mail pending only. Send only the fields to change among firstname, lastname, email, phone. If the email changes, the previous recipient is informed that the registered mail is cancelled for him and the new recipient receives it.
Delete a registered mail
Status finished, declined or expired only. Deletes the files and the history; the response contains the registered mail one last time (status_id: deleted): download the documents before deleting.
Download the audit trail
Not available while pending. Returns the registered mail with audit_trail_base64: a timestamped PDF with the full history.
Get the credit balance
List the methods
Methods accepted in methods for registered mail, with the price for your account. The list can differ from the one of the signatures API.
{
"success": true,
"result": [
{
"id": "itsme-qes-signature",
"label": "itsme® | QES",
"credits": 1
},
{
"id": "otp-email-non-qualified",
"label": "OTP by Email/SMS | SES",
"credits": 0.4
}
]
}
10. Callbacks (webhooks)
Define your callback URL in My account > Signatures API (HTTPS, publicly reachable). You can also set a callback_url per request. The callback is a POST with a JSON body containing the full current state of the request (same format as getRequest), sent for requests created through the API.
Event fields
| Field | Description |
|---|---|
event |
Event type (see below) |
event_id |
Unique identifier of the event (evt_…). Identical on every retry of the same event: use it to ignore duplicates. |
event_at |
Date of the event (UTC, ISO 8601) |
changes |
Recipient status change, e.g. [{"signer_key": 1, "from": "pending", "to": "signed"}] (only when a signer changed) |
changes (signer.email_failed) |
Undelivered email, e.g. [{"signer_key": 2, "email": "[email protected]", "bounce_type": "permanent", "reason": "smtp; 550 5.1.1 user unknown"}] |
external_reference, metadata |
As sent when the request was created |
Event types
| Event | When |
|---|---|
signer.signed |
A signer signed, the request is still in progress |
signer.declined, signer.skipped, signer.expired |
A signer declined, was skipped or expired |
request.completed |
All signers signed: the signed PDF is included (pdf_file_base64) |
request.stopped |
The request was stopped: the partially signed PDF is included |
request.cancelled |
The request was cancelled |
request.deleted |
The request was deleted |
request.expired |
The request expired |
request.declined |
Registered mail declined by the recipient |
signer.email_failed |
An email sent to a signer could not be delivered, see Undelivered emails |
request.updated |
Any other change: reminder sent, signer modified, new signing links… |
The body always contains the full current state: your integration can rely on status_id and on the status of each signer rather than on the event type alone.
Undelivered emails
When an email sent by e-Signature.eu to a signer (or to the recipient of a registered mail) is returned by his mail server (unknown address, mailbox full, server refusing the message…), you receive the callback signer.email_failed. The status of the request and of the signer does not change.
Field of changes |
Description |
|---|---|
signer_key |
Key of the signer in signers (always 1 for a registered mail) |
email |
Address in error |
bounce_type |
permanent: the address does not exist or refuses messages · transient: temporary problem (mailbox full, server unavailable), delivery abandoned after several attempts · undetermined: unknown reason |
reason |
Technical message of the recipient’s mail server, when available |
Correct the address with modifySigner (signature request) or modifyRecipient (registered mail): the invitation is sent to the new address, and no cancellation email is sent to the address in error. The account owner is also informed by email, and the signer is marked Email not delivered in the online dashboard.
permanent: no more emails are sent to this address for this request (reminders included) until it is corrected. transient or undetermined: emails are not blocked; the next reminder is sent normally and, if it fails again, you receive a new signer.email_failed callback.
Only pending requests and signers who have not signed yet are concerned. With send_email: false, e-Signature.eu sends no email to the signers, so this event does not occur.
Headers
| Header | Example |
|---|---|
X-Esig-Api-Version |
3 |
X-Esig-Event |
signer.signed |
X-Esig-Event-Id |
evt_4f0c2b9e1d7a3c55e8b1a2f0 |
X-Esig-Delivery-Attempt |
1, 2, … |
X-Esig-Signature |
t=1792407600,v1=5d41402abc4b2a76b9719d911017c592… |
Verifying the signature
Your signing secret is shown in My account > Signatures API, where it can also be regenerated. v1 is the HMAC-SHA256 of "<t>.<raw request body>" with your secret, hex encoded. Use the raw body, before any JSON parsing, and reject callbacks older than 5 minutes.
PHP
$body = file_get_contents( 'php://input' );
$header = $_SERVER['HTTP_X_ESIG_SIGNATURE'] ?? '';
preg_match( '/t=(\d+),v1=([a-f0-9]{64})/', $header, $m );
$valid = $m
&& abs( time() - (int) $m[1] ) <= 300
&& hash_equals( hash_hmac( 'sha256', $m[1] . '.' . $body, $secret ), $m[2] );
Node.js
const crypto = require('crypto');
function verify(rawBody, header, secret) {
const m = /t=(\d+),v1=([a-f0-9]{64})/.exec(header || '');
if (!m || Math.abs(Date.now() / 1000 - Number(m[1])) > 300) return false;
const expected = crypto.createHmac('sha256', secret).update(`${m[1]}.${rawBody}`).digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(m[2]));
}
C# (.NET)
using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions;
static bool Verify(string rawBody, string header, string secret) {
var m = Regex.Match(header ?? "", @"t=(\d+),v1=([a-f0-9]{64})");
if (!m.Success || Math.Abs(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - long.Parse(m.Groups[1].Value)) > 300) return false;
using var h = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
var expected = Convert.ToHexString(h.ComputeHash(Encoding.UTF8.GetBytes(m.Groups[1].Value + "." + rawBody))).ToLowerInvariant();
return CryptographicOperations.FixedTimeEquals(Encoding.ASCII.GetBytes(expected), Encoding.ASCII.GetBytes(m.Groups[2].Value));
}
Responses and retries
- Reply with HTTP 2xx as soon as the callback is received, and process it asynchronously if needed.
- On network error, timeout (30 s), HTTP 429 or 5xx, the callback is sent again every hour with the same
event_id, up to 24 attempts. Other 4xx answers are considered final. If the request changes again before a successful delivery, the next callback carries a new event and the full current state. The current state remains available at any time throughgetRequest. - Redirects are followed (the
POSTmethod is kept).localhostand private addresses can not be used.
For registered mail, the callback is sent when the acknowledgement is signed, declined or expired, at each automatic reminder and when the recipient is modified (on the website or through the API). The recipient is signer_key 1 in changes.
11. Errors
Errors are returned with "success": false and a readable error message. Rely on the HTTP code for your logic: messages may be improved over time.
| HTTP code | Meaning |
|---|---|
| 200 / 201 / 202 | OK / registered mail created / verification email sent |
| 400 | Missing or invalid field (the message names the field) |
| 401 | Invalid userid or x-api-key |
| 403 | Not enough credits, or request limit reached (10 per minute) |
| 404 | Missing id_sign, or unknown for this account |
| 412 | Action not possible in the current state (sender address not verified, wrong status) |
12. Retention
The timestamped document and the signed acknowledgement of receipt can be downloaded for 30 days, through getRequest or the callback. The audit trail is kept for the period defined in our DPA. Archive the documents on your side within 30 days.