Signaturios

Signaturios API

Signaturios is a developer-first REST API for legally-binding electronic signatures. POST a base64-encoded PDF with signer details; get back a hosted signing URL in one call. When the signer completes the flow, you receive a PAdES-signed PDF with a cryptographic audit trail — no SDK, no enterprise sales call, no per-envelope pricing.

Two signature tiers are supported out of the box: SES (email-verified consent, US ESIGN Act) and AES-eID (government-issued national eID, eIDAS Advanced Electronic Signature) with seven European identity methods.

Base URL
https://esign-api-gt01.onrender.com

Authentication

All developer endpoints require an API key passed as a Bearer token. Keys are prefixed esign_live_ and remain valid until revoked.

Get a key

Create a free account at /signup — your first API key is generated on the spot and shown once. Additional keys and revocation are available in the dashboard.

Send the key

Include it on every request to a developer endpoint:

HTTP header
Authorization: Bearer esign_live_...
request.sh
$ curl https://esign-api-gt01.onrender.com/health \
    -H "Authorization: Bearer esign_live_Ue0GGV8..."
response 200 OK
{ "status": "ok", "service": "esign-api", "version": "0.1.0" }
Never commit your API key to source control. Use environment variables or a secrets manager. If a key is compromised, revoke it from the dashboard and generate a new one.

Quickstart

From zero to a legally-binding signed PDF in four steps.

1. Create a signature request

POST the document (base64-encoded PDF), signer info, and the signature tier. The response contains a signing_url — a hosted page where the signer reviews and signs the document. Email it to them; you don't build any frontend.

$ curl -X POST https://esign-api-gt01.onrender.com/v1/signature-requests \
    -H "Authorization: Bearer esign_live_..." \
    -H "Content-Type: application/json" \
    -d '{
          "document_base64": "'"$(base64 -i contract.pdf)"'",
          "document_title":  "Service Agreement",
          "signer": { "name": "Jane Doe", "email": "jane@example.com" },
          "tier": "ses"
        }'
import base64, requests

API_KEY = "esign_live_..."
BASE    = "https://esign-api-gt01.onrender.com"

with open("contract.pdf", "rb") as f:
    doc_b64 = base64.b64encode(f.read()).decode()

resp = requests.post(
    f"{BASE}/v1/signature-requests",
    headers={"Authorization": f"Bearer {API_KEY}"},
    json={
        "document_base64": doc_b64,
        "document_title":  "Service Agreement",
        "signer": {"name": "Jane Doe", "email": "jane@example.com"},
        "tier": "ses",
    },
)
data = resp.json()
print("Signing URL:", data["signing_url"])
response 201 Created
{
  "request_id":  "req_01HKx9mP3Qrt7wbA",
  "signing_url": "https://esign-api-gt01.onrender.com/sign/req_01HKx9mP3...",
  "status":      "pending",
  "tier":        "ses"
}

2. Choose the signature tier

Set "tier" on the request body:

tier valueNameIdentity verificationLegal standard
ses (default) Simple Electronic Signature Email link + consent checkbox + IP capture US ESIGN Act · eIDAS SES
aes_eid Advanced Electronic Signature Government-issued national eID via OIDC eIDAS AES

For aes_eid, also set "eid_method" to pre-select the signer's national eID. If omitted, the signer is shown a selector page to choose their own.

eID methods (aes_eid tier)

Seven European national identity methods are supported. Pass the key as "eid_method":

eid_methodeID providerCountry
se_bankid Swedish BankID Sweden
dk_mitid Danish MitID Denmark
no_bankid Norwegian BankID Norway
fi_ftn Finnish Trust Network Finland
nl_idin Dutch iDIN Netherlands
de_personalausweisGerman PersonalausweisGermany
se_freja Swedish Freja eID Sweden
se_bankid example
{
  "document_base64": "JVBERi0x...",
  "signer": { "name": "Erik Svensson", "email": "erik@example.se" },
  "tier":       "aes_eid",
  "eid_method": "se_bankid"
}

3. Send the signing URL to your signer

Email signing_url to the signer. Signaturios serves the entire hosted signing experience — you build nothing. For the SES tier, the signer reviews the document inline and checks a consent box. For AES-eID, the signer authenticates through their national eID provider before the signature is applied. The signed PDF is produced automatically once the flow completes.

4. Poll for completion and retrieve the signed document

$ curl https://esign-api-gt01.onrender.com/v1/signature-requests/req_01HKx9mP3Qrt7wbA \
    -H "Authorization: Bearer esign_live_..."
import time, base64

request_id = data["request_id"]

while True:
    r = requests.get(
        f"{BASE}/v1/signature-requests/{request_id}",
        headers={"Authorization": f"Bearer {API_KEY}"},
    ).json()

    if r["status"] == "signed":
        pdf = base64.b64decode(r["signed_document_base64"])
        with open("signed.pdf", "wb") as f:
            f.write(pdf)
        print("Done. Audit trail:", r["audit_trail"])
        break

    time.sleep(5)  # poll every 5 s
response when signed 200 OK
{
  "request_id":             "req_01HKx9mP3Qrt7wbA",
  "status":                  "signed",
  "tier":                    "ses",
  "created_at":             "2026-06-18T09:00:00Z",
  "signer": { "name": "Jane Doe", "email": "jane@example.com" },
  "signed_at":              "2026-06-18T09:04:32Z",
  "captured_ip":            "203.0.113.42",
  "signed_document_base64": "JVBERi0x...",
  "audit_trail": {
    "signer": {
      "name":                "Jane Doe",
      "identifier":          "jane@example.com",
      "verification_method": "email_link",
      "tier":               "ESIGN (US) / eIDAS SES",
      "ip_address":          "203.0.113.42"
    },
    "intent_to_sign":        true,
    "consent_to_electronic": true,
    "timestamp_utc":         "2026-06-18T09:04:32Z",
    "document": {
      "sha256_before_signing": "a3b4c5d6e7f8a9b0...",
      "sha256_after_signing":  "c1d2e3f4a5b6c7d8..."
    },
    "signature_standard": "PAdES (ETSI EN 319 142) / ETSI.CAdES.detached"
  }
}
For AES-eID requests, the signer.verification_method will be the display name of the eID provider (e.g. "Swedish BankID"), and the signer's verified legal name is used in place of the developer-supplied name. Two additional fields — eid_method and country — are added to the signer object.

API Reference

Create a signature request

POST /v1/signature-requests Auth required

Creates a pending signing request and returns a one-time signing_url to deliver to the signer. The document and all signer metadata are stored server-side; only the URL needs to be sent.

Request body

FieldTypeDescription
document_base64 string Required Base64-encoded PDF. Must begin with the PDF magic bytes (%PDF).
signer.name string Required Signer's full name. Shown on the signing page and embedded in the audit trail.
signer.email string Required Signer's email. Embedded in the audit trail for the SES tier.
document_title string Optional Human-readable name displayed on the signing page.
tier string Optional "ses" (default) or "aes_eid". Legacy value "aes_bankid" is accepted and silently remapped to aes_eid + se_bankid for backward compatibility.
eid_method string Optional Pre-selects the national eID for the aes_eid tier. One of: se_bankid, dk_mitid, no_bankid, fi_ftn, nl_idin, de_personalausweis, se_freja. If omitted the signer chooses on the signing page.
anchor object Optional Text-anchor placement. Embed anywhere in your PDF and the visible signature appearance will be placed at that spot. If the tag is not found the signature is applied invisibly at the default position and audit_trail.anchor_placement.found will be false. See Anchor placement for details.

Response — 201 Created

FieldDescription
request_idOpaque token — use this to poll status.
signing_urlHosted signing page URL to send to the signer. Single-use per signing session.
statusAlways "pending" at creation time.
tierResolved tier: "ses" or "aes_eid".
eid_methodPresent only when an eid_method was specified or inferred.

Get a signature request

GET /v1/signature-requests/{id} Auth required

Returns the current status of a signature request. When status is "signed", the response also includes the PAdES-signed PDF (base64) and the complete audit trail.

FieldPresentDescription
request_id Always The request identifier.
status Always "pending" or "signed".
tier Always Signature tier used.
created_at Always ISO 8601 UTC timestamp.
signer Always Object with name and email.
eid_method AES only National eID method key.
signed_at When signed ISO 8601 UTC timestamp of signing.
captured_ip When signed Signer's IP address at time of signing.
signed_document_base64 When signed Base64-encoded PAdES-signed PDF.
audit_trail When signed Full audit trail object (see Quickstart for complete schema).

Anchor placement

Drop the text tag anywhere in your PDF and pass an anchor object in your request. The visible signature stamp is placed at that location, covering the tag. The PAdES cryptographic signature is always applied to the whole document regardless.

Anchor placement works for both POST /v1/signature-requests (hosted signing) and POST /v1/signatures (direct), and for both SES and AES tiers.

Anchor object fields

FieldTypeDefaultDescription
text string "" Exact text to find in the PDF. Case-sensitive. Must appear as a continuous run on one line.
x_offset number 0 Horizontal offset in points from the anchor's left edge. Positive = right.
y_offset number 0 Vertical offset in points from the anchor's bottom edge. Positive = up.
width number 200 Width of the signature appearance box in points (72 pt = 1 inch).
height number 60 Height of the signature appearance box in points.

Behavior

  • Tag found: Stamp is placed at the first occurrence. audit_trail.anchor_placement.found is true. If multiple occurrences exist, occurrences shows the count and a note is added.
  • Tag not found: Signing proceeds with an invisible PAdES signature at the default field position. audit_trail.anchor_placement.found is false.
  • No anchor field: Existing invisible-signature behavior. No anchor_placement key in the audit trail. Fully backward-compatible.
$ curl -X POST https://esign-api-gt01.onrender.com/v1/signature-requests \
    -H "Authorization: Bearer esign_live_..." \
    -H "Content-Type: application/json" \
    -d '{
          "document_base64": "...",
          "signer": { "name": "Jane Doe", "email": "jane@example.com" },
          "anchor": { "text": "", "width": 220, "height": 70 }
        }'
resp = requests.post(
    f"{BASE}/v1/signature-requests",
    headers={"Authorization": f"Bearer {API_KEY}"},
    json={
        "document_base64": doc_b64,
        "signer": {"name": "Jane Doe", "email": "jane@example.com"},
        "anchor": {"text": "", "width": 220, "height": 70},
    },
)

Direct signing (server-to-server)

POST /v1/signatures Auth required

Signs a PDF immediately and returns the signed document synchronously — no hosted signing page, no signer URL, no email. Use this when the signer's identity is already established in your system (e.g., they authenticated via your own login flow) and you need a programmatic SES signature embedded server-side.

This endpoint is for automated server-side flows. For user-facing consent flows that capture explicit intent to sign, use POST /v1/signature-requests instead.

Request body

FieldTypeDescription
document_base64 string Required Base64-encoded PDF.
signer.name string Required Signer name — embedded in the PAdES audit trail.
signer.identifier string Required Email or unique identifier — embedded in the audit trail as identifier.
anchor object Optional Text-anchor placement. Same schema as for POST /v1/signature-requests. See Anchor placement.

Response — 200 OK

FieldDescription
status Always "signed".
signed_document_base64 Base64-encoded PAdES-signed PDF.
audit_trail Full audit trail object (same schema as above).
$ curl -X POST https://esign-api-gt01.onrender.com/v1/signatures \
    -H "Authorization: Bearer esign_live_..." \
    -H "Content-Type: application/json" \
    -d '{
          "document_base64": "'"$(base64 -i contract.pdf)"'",
          "signer": {
            "name":       "Jane Doe",
            "identifier": "jane@example.com"
          }
        }'
import base64, requests

with open("contract.pdf", "rb") as f:
    doc_b64 = base64.b64encode(f.read()).decode()

resp = requests.post(
    f"{BASE}/v1/signatures",
    headers={"Authorization": f"Bearer {API_KEY}"},
    json={
        "document_base64": doc_b64,
        "signer": {"name": "Jane Doe", "identifier": "jane@example.com"},
    },
)
data = resp.json()
signed_pdf = base64.b64decode(data["signed_document_base64"])

Embedded signing

Embedded signing lets you mount the signing UI directly inside a <div> on your own page, without redirecting the user to an external hosted URL. A lightweight iframe widget communicates with your app via postMessage events so you can react to completion without polling.

The hosted signing_url remains the default and is always returned. Embedded mode is opt-in — add "embedded": true to your request to also receive an embed_url.

Step 1 — Create an embedded session

Add "embedded": true and optionally "allowed_origins" to your POST /v1/signature-requests call. The response includes a short-lived embed_url (valid for 1 hour) alongside the usual signing_url.

FieldTypeDescription
embedded boolean Optional Set to true to receive an embed_url in the response.
allowed_origins string[] Optional Array of https:// origins allowed to embed the widget, e.g. ["https://app.yourdomain.com"]. Used to set Content-Security-Policy: frame-ancestors on the widget. Omit to allow embedding from any origin (fine for development; restrict in production to prevent clickjacking).

Response additions when embedded is true

FieldDescription
embed_urlShort-lived URL for the signing widget. Pass this to SignaturiosEmbed.init(). Valid for 1 hour.
embed_expires_atISO 8601 UTC expiry of the embed session.

Step 2 — Include the embed SDK

Drop a container element and include the SDK. Call SignaturiosEmbed.init() with your embed_url. The widget mounts as an auto-resizing iframe inside the container.

<!-- 1. A container for the signing widget -->
<div id="sign-widget" style="width:100%;max-width:720px;border:1px solid #e5e5ea;border-radius:8px;overflow:hidden"></div>

<!-- 2. Embed SDK (served from your API domain) -->
<script src="https://esign-api-gt01.onrender.com/static/js/esign-embed.js"></script>

<!-- 3. Mount the widget -->
<script>
  var signer = SignaturiosEmbed.init({
    container: '#sign-widget',
    embedUrl:  '',  // from your server-side API response

    onLoaded: function(e) {
      console.log('Widget ready');
    },
    onSigned: function(e) {
      console.log('Signed!', e.request_id, e.signed_at);
      signer.unmount();          // remove the widget
      showSuccessMessage();      // your own UI
    },
    onError: function(e) {
      console.error('Error:', e.message);
    },
    onCancelled: function() {
      console.log('Cancelled');
    },
  });
</script>

postMessage events

The SDK abstracts the raw events into callbacks, but you can also listen directly on window if needed. All events have a type field prefixed with esign:.

Event typePayload fieldsDescription
esign:loaded Widget iframe has initialised and is ready.
esign:signed request_id, signed_at Document was signed. signed_at is ISO 8601 UTC.
esign:error message A signing error occurred. The widget may still be usable for retry.
esign:cancelled User explicitly cancelled the signing session.
esign:resize height (number, px) Widget content height changed. The SDK resizes the iframe automatically.
esign:eid-popup-required url Internal: SDK opens eID auth in a popup. Handled automatically by the SDK.

Refreshing an expired session

Embed sessions expire after 1 hour. If the signer hasn't completed by then, regenerate the session server-side and reinitialise the widget with the new URL.

POST /v1/signature-requests/{id}/embed-session
AuthAPI key required
Body (optional){ "allowed_origins": ["https://…"] } to update the allow-list
Returnsembed_url, embed_expires_at

eID (AES) tier in embedded mode

eID providers cannot run inside an iframe — BankID, MitID, and others actively refuse to load in framed contexts and use full-page redirects.

The widget handles this automatically: when the user clicks an eID button, the SDK opens the authentication in a pop-up window (not inside the iframe). After the user completes eID verification in the pop-up, the window closes and fires esign:signed directly to your page — no polling needed.

You must allow pop-ups from your domain. Tell your users to permit pop-ups when prompted. For best UX, trigger the pop-up from a button click so browsers don't block it as an unsolicited pop-up.

The widget shows a "waiting for authentication" spinner while the pop-up is open. If the user closes the pop-up without completing, the widget resets and allows retry.

Domain allow-listing

Setting allowed_origins restricts which pages can embed the widget using the browser's Content-Security-Policy: frame-ancestors directive. Browsers will block the iframe on any unlisted origin, preventing clickjacking attacks.

  • For development: omit allowed_origins — the widget can be embedded anywhere.
  • For production: set allowed_origins: ["https://app.yourdomain.com"].
  • Multiple origins are supported: ["https://app.yourdomain.com", "https://staging.yourdomain.com"].

Error responses

All errors use the same JSON envelope:

error response
{
  "error": {
    "message": "Invalid or inactive API key.",
    "status":  401
  }
}
StatusCause
400 Missing or invalid field — e.g. document_base64 is not valid base64, does not decode to a PDF, unknown tier or eid_method, or required fields (signer.name, signer.email) are absent.
401 Missing Authorization header, invalid key format, or revoked key. Response includes WWW-Authenticate: Bearer realm="esign-api".
402 Free allowance exhausted and no payment method on file. Add billing at /billing/setup. Test keys always bypass this check.
404 The request_id does not exist.
503 The aes_eid tier is requested but the server's eID provider credentials are not configured (IDURA_DOMAIN, IDURA_CLIENT_ID, IDURA_CLIENT_SECRET).

Webhooks

Instead of polling GET /v1/signature-requests/{id}, register a webhook endpoint and receive a signed HTTP POST the moment a document is signed. Every delivery is HMAC-SHA256 signed so you can verify it originated from Signaturios.

Webhooks respect the live/test split: test-key requests trigger test webhooks; live-key requests trigger live webhooks. Register separate endpoints per environment in the dashboard.


Register an endpoint

Go to your dashboard, scroll to Webhook Endpoints, enter your HTTPS URL, choose the environment, and click Register endpoint. The signing secret is shown once — copy it to your secrets manager before leaving the page.

The endpoint must be publicly reachable and return a 2xx response within 10 seconds. Signaturios will retry up to 5 times on failure (see Retries).

Managing endpoints via the dashboard

Each registered endpoint shows its URL, environment badge, and a Disable button. Disabled endpoints stop receiving events; re-register if you need to replace one.


Event types

EventFired when
signature_request.completed The signer has completed the flow and the PAdES signature has been applied to the document. The full audit trail is included in the payload.
Polling still works — webhooks are purely additive. GET /v1/signature-requests/{id} continues to return the current status and audit trail at any time.

Payload shape

Signaturios POSTs a JSON body to your endpoint. The top-level envelope wraps the full GET /v1/signature-requests/{id} object under data, including the structured audit trail.

POST body — signature_request.completed
{
  "event":   "signature_request.completed",
  "created": "2026-06-18T20:00:00Z",
  "livemode": true,
  "data": {
    "request_id": "Voh6hSbwxDUCIMc30vb...",
    "livemode":   true,
    "status":     "signed",
    "tier":       "ses",
    "created_at": "2026-06-18T19:58:00Z",
    "signed_at":  "2026-06-18T20:00:00Z",
    "captured_ip": "185.1.2.3",
    "signer": { "name": "Jane Doe", "email": "jane@example.com" },
    "audit_trail": {
      "document": { "sha256": "a3f...", "pages": 3 },
      "signer": {
        "name": "Jane Doe",
        "identifier": "jane@example.com",
        "verification_method": "email_link",
        "tier": "ESIGN (US) / eIDAS SES",
        "ip": "185.1.2.3"
      },
      "signed_at": "2026-06-18T20:00:00Z",
      "signature": { "algorithm": "RSA-SHA256", "subfilter": "ETSI.CAdES.detached" }
    },
    "signed_document_base64": "JVBERi0x..."
  }
}

Request headers

HeaderValue
Content-Type application/json
X-Signaturios-Signature sha256=<hex> — HMAC-SHA256 of the raw request body
X-Signaturios-Timestamp ISO 8601 UTC timestamp of this delivery attempt
User-Agent Signaturios-Webhooks/1.0

Verify the signature

Every delivery includes an X-Signaturios-Signature header of the form sha256=<hex>. The hex value is HMAC-SHA256 of the raw request body using your endpoint's signing secret as the key. Always verify this before processing the event.

Use constant-time comparison (hmac.compare_digest) to prevent timing attacks.

verify.py
import hmac, hashlib

def verify_esign_webhook(secret: str, body: bytes, header: str) -> bool:
    """Return True only if the delivery is authentic."""
    expected = "sha256=" + hmac.new(
        secret.encode(), body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, header)


# Flask handler example
@app.post("/webhooks/esign")
def handle_esign_webhook():
    sig = request.headers.get("X-Signaturios-Signature", "")
    if not verify_esign_webhook(WEBHOOK_SECRET, request.data, sig):
        abort(401)           # reject unknown origin

    event = request.get_json()
    if event["event"] == "signature_request.completed":
        data      = event["data"]
        audit     = data["audit_trail"]
        signed_pdf = base64.b64decode(data["signed_document_base64"])
        # ... store audit + PDF ...

    return "", 200  # acknowledge within 10 s
Never skip signature verification in production. Without it, anyone who discovers your endpoint URL can send forged events.

Retries & delivery guarantees

If your endpoint returns a non-2xx response or does not respond within 10 seconds, Signaturios retries up to 5 attempts with exponential backoff:

AttemptDelay before this attempt
1Immediately after the event
210 seconds
330 seconds
460 seconds
5120 seconds

Each attempt is logged (endpoint, event type, HTTP status, success/failure). After 5 failures the delivery is marked failed and no further retries occur. The HMAC signature is identical across all retries for the same event, so you can verify any attempt with the same code path.

To avoid double-processing, treat deliveries as at-least-once: use data.request_id as an idempotency key in your handler.

Delivery runs in a background thread so it never blocks the signer's response. If your server restarts mid-retry, the remaining attempts for that event are dropped — poll GET /v1/signature-requests/{id} to reconcile any gaps.

Pricing & billing

Usage-based per-completed-signature pricing. You are only ever charged for work that actually happened — a signer opened the link, consented, and the PDF was cryptographically signed. Failed attempts, expired links, and test-mode signatures are always free.

TierPrice per completed signatureTrigger
ses — Email consent (SES) €0.25 Signer checks consent box & submits
aes_eid — National eID (AES) €0.75 eID provider confirms identity & PDF is signed

Free allowance

Every developer account includes 10 completed live signatures free — enough to fully test your integration end-to-end with real documents before spending anything. The free allowance never expires.

Test mode is always free

Requests made with a esign_test_… key are never billed, never counted against the free allowance, and never require a payment method. Use test keys freely in CI, staging, and development.

What is never billed

  • Signatures made with a esign_test_… key
  • Pending or expired signing requests that were never completed
  • Signing attempts that returned an error (PDF rejected, eID auth failed, etc.)
  • API calls that returned 4xx / 5xx before signing began

How billing works

Add a payment method via your dashboard. Each completed live signature above the free allowance is posted as a Stripe invoice line-item immediately. Stripe collects all items into a monthly invoice and charges your card automatically. You can view past invoices and update your card via the Stripe Customer Portal (linked from the dashboard).

If a live signing request is created while billing is required but not set up, the API returns HTTP 402 Payment Required with the standard error envelope and a link to /billing/setup.

Rate limits

Limits are enforced per API key. Test keys and live keys have independent buckets. Signing pages (/sign/…) and the health endpoint are not rate-limited so real signers are never blocked.

EndpointPer minutePer day
POST /v1/signature-requests 10 500
POST /v1/signatures 10 500
GET /v1/signature-requests/{id} 120

429 response

When a limit is exceeded the API returns HTTP 429 with the standard error envelope and a Retry-After header (seconds until the current window resets).

429 rate limit exceeded
HTTP/1.1 429 Too Many Requests
Retry-After: 42
Content-Type: application/json

{
  "error": {
    "message": "Rate limit exceeded: 10 per 1 minute.",
    "status":  429
  }
}
The response also includes X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers on every response so you can track consumption proactively before hitting the limit. To switch to Redis-backed limiting in production, set the RATELIMIT_STORAGE_URI environment variable (e.g. redis://localhost:6379).

How It Works & Legal Validity

Understanding the underlying mechanism helps you explain the legal strength of these signatures to your customers — and why they're materially stronger than a JPEG stamped onto a PDF.

PAdES Cryptographic Signature

Every document is signed using PAdES — PDF Advanced Electronic Signatures (ETSI EN 319 142), the same standard used by European governments and courts. The signature is a cryptographic proof embedded in the PDF's binary structure using CAdES-detached encoding (SHA-256). Any modification to the document after signing is mathematically detectable by any PDF reader — Adobe Acrobat, Preview, or any ETSI-compliant validator — without calling any external service.

Tamper-Proof Audit Trail

Every signature embeds a structured audit trail directly in the signed PDF. It records: signer identity (name + email or masked eID reference), verification method, IP address, timestamp, explicit consent flags, and SHA-256 hashes of the document both before and after signing. The trail is part of the cryptographic proof — it cannot be modified without invalidating the signature. The document is self-contained and remains verifiable indefinitely.

US ESIGN Act — SES Tier

The Electronic Signatures in Global and National Commerce Act (15 U.S.C. § 7001) grants electronic signatures the same legal weight as ink when the signer demonstrates intent and consents to electronic execution. The SES tier satisfies this by capturing explicit consent via checkbox, intent via a click-to-sign action, the signer's email-verified identity, and a timestamp — all embedded in the PAdES audit trail as intent_to_sign: true and consent_to_electronic: true.

eIDAS AES — Government-Level Identity

eIDAS Regulation (EU 910/2014) defines Advanced Electronic Signatures as requiring uniquely-identified signatories through government-issued credentials. The AES-eID tier routes the signer through their national eID provider (BankID, MitID, iDIN, FTN and others), then embeds their verified legal name, masked national ID reference, and eID method in the PAdES audit trail. This creates an AES under eIDAS Article 26 — the highest level below QES, and the strongest electronic signature accepted across EU member states without a qualified certificate.

What this means for your customers — A signed PDF from Signaturios can be opened in Adobe Acrobat right now and the signature verified locally, with no external service call. The audit trail is court-admissible evidence under US and EU law. The document remains independently verifiable even if Signaturios ceases to exist.