Authentication and signing
Two-host architecture
Fatoora Cloud (business.fatoora.tn) | FatooraSigner (127.0.0.1:38443) | |
|---|---|---|
| Runs where | Fatoora's servers | End client's own machine (installed agent) |
| Protocol | HTTPS | Plain HTTP — no TLS by default |
| You manage | Domain whitelist, client pre-registration | Nothing — it's the client's local install |
| Enforces | Your allowed_domains whitelist + pre-registered client list | A separate, broader CORS policy (localhost, 127.0.0.1, *.fatoora.tn) |
A session-open call actually passes through three layers, not two:
- Browser → local agent: the agent's own CORS policy (above).
- Local agent → Fatoora Cloud (
GET /api/auth/trust-signer-session): fatooraBusiness's global Expresscors()middleware, driven by the server-sideCORS_ORIGINSenv var — completely separate from, and unaware of, your self-serviceallowed_domainswhitelist. - Cloud business logic: your
allowed_domainscheck, which normally returns403 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.
| Endpoint | Role |
|---|---|
GET /api/web-bridge/v1/tokens/certificates | Lists public certificates on the token — no PIN required |
POST /api/web-bridge/v1/tokens/detect-with-certificates | Detects the token and reads certificates (PIN required) |
POST /api/web-bridge/v1/xml/sign | Signing — the core call |
POST /api/web-bridge/v1/xml/inspect | Verifies 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.