Skip to main content

Authentication and signing

Two-host architecture

Fatoora Cloud (business.fatoora.tn)FatooraSigner (127.0.0.1:38443)
Runs whereFatoora's serversEnd client's own machine (installed agent)
ProtocolHTTPSPlain HTTP — no TLS by default
You manageDomain whitelist, client pre-registrationNothing — it's the client's local install
EnforcesYour allowed_domains whitelist + pre-registered client listA separate, broader CORS policy (localhost, 127.0.0.1, *.fatoora.tn)
Known limitation — a third, undocumented CORS gate

A session-open call actually passes through three layers, not two:

  1. Browser → local agent: the agent's own CORS policy (above).
  2. Local agent → Fatoora Cloud (GET /api/auth/trust-signer-session): fatooraBusiness's global Express cors() middleware, driven by the server-side CORS_ORIGINS env var — completely separate from, and unaware of, your self-service allowed_domains whitelist.
  3. Cloud business logic: your allowed_domains check, which normally returns 403 ORIGIN_NOT_WHITELISTED.

Whitelisting your domain via POST /api/partner/signer/domains is necessary but not sufficient — Fatoora staff also need to add it to the server's CORS_ORIGINS configuration, a step you cannot self-service. If your session-open calls fail with a raw 500 mentioning "not allowed by CORS" rather than the expected 403 ORIGIN_NOT_WHITELISTED, this is why — contact Fatoora support to have your domain added server-side.

Session flow

Step A — End-client login (cloud)

POST https://business.fatoora.tn/api/auth/login
{ "email": "client@example.com", "password": "..." }
→ 200 { "accessToken": "eyJhbGc..." }

Step B — Open a local bridge session

POST http://127.0.0.1:38443/api/web-bridge/v1/session
Authorization: Bearer <accessToken>
X-Partner-App-ID: <your partnerAppId>

The agent forwards your JWT to GET /api/auth/trust-signer-session, which checks in order: partner app exists → compatible partner_type → active/trial subscription → whitelisted domain → pre-registered client tax ID.

Success response:

{ "sessionId": "sess_xxxxxxxx", "idleExpiresAt": "2026-07-11T15:00:00Z", "partnerMode": true }

Use sessionId as the X-Fatoora-Signer-Session header on every subsequent call. Default idle timeout: 1 hour.

Signing operations

All require X-Fatoora-Signer-Session and target the local agent.

EndpointRole
GET /api/web-bridge/v1/tokens/certificatesLists public certificates on the token — no PIN required
POST /api/web-bridge/v1/tokens/detect-with-certificatesDetects the token and reads certificates (PIN required)
POST /api/web-bridge/v1/xml/signSigning — the core call
POST /api/web-bridge/v1/xml/inspectVerifies a signature already present in a document

POST /xml/sign always returns 200 — check the ok field:

{ "mode": "sign", "ok": true, "signedXml": "<Invoice>...(signed)...</Invoice>" }

or { "mode": "sign", "ok": false, "error": "Invalid PIN", "errorCode": "SIGN_FAILED" }.

Submitting directly to TTN

Unlike Partner API (where partner tokens are blocked), the local FatooraSigner agent can file signed invoices with TTN directly, using the client's own TTN credentials, entered locally and never sent to your servers:

POST /api/web-bridge/v1/credentials/encrypt-ttn   { "login": "...", "password": "...", "matricule": "..." }
GET /api/web-bridge/v1/ttn/check-availability?ttnMode=PROD
POST /api/web-bridge/v1/ttn/save-efact { "encryptedCredential": "...", "documentEfact": "<base64>", "ttnMode": "PROD" }
POST /api/web-bridge/v1/ttn/consult-efact

Credentials are AES-256-GCM encrypted, in-memory only — never written to disk or sent to Fatoora Cloud.

Next step: Domain and client management.