Included in every plan — no extra fee

Your software invoices. Fatora does the rest.

Your SaaS, CRM or business app has no Moroccan invoicing module? One button on your side, one HTTP call on ours: Fatora issues the invoice or quote in the merchant’s own numbering, with their stamp, logo and every mention required by article 145 of the tax code — PDF included, delivered by WhatsApp, email or QR code.

  • DGI-compliant invoices and quotes: ICE, tax ID, itemised VAT, amount in words, continuous sequence
  • Delivery your way: WhatsApp, email, or a printable QR code — the customer scans and downloads
  • Free-text modification: write the change, the AI applies it
  • Logo, stamp and company details manageable by API

Getting the API key

  1. 1The merchant opens their Fatora-Bot WhatsApp conversation
  2. 2They simply write “api”
  3. 3The bot replies with their personal key (fk_…) — paste it into your app

The key belongs to each merchant and is included in their plan, from the free trial onwards. “api reset” rotates it at any time. API invoices consume the plan quota; quotes are free and unlimited.

Up and running in 5 minutes

A single request issues a document. Prices are net of tax by default; pass "priceIsTtc": true if your amounts are tax-inclusive — Fatora rebuilds the base.

Request
curl -X POST https://bot.swiviq.com/v1/documents \
  -H "Authorization: Bearer fk_votre_cle" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "invoice",
    "client": { "name": "ATLAS DIGITAL SARL", "ice": "001122334455667", "type": "company" },
    "items": [
      { "description": "Abonnement CRM — pack annuel", "quantity": 1, "unitPrice": 4800 },
      { "description": "Formation équipe (2 jours)", "quantity": 2, "unitPrice": 1500 }
    ],
    "sendToEmail": "client@exemple.ma"
  }'
Response — 201
{
  "number": "FAT-2026-0042",
  "type": "invoice",
  "status": "issued",
  "issueDate": "2026-08-15",
  "client": { "name": "ATLAS DIGITAL SARL", "ice": "001122334455667", "type": "company" },
  "items": [
    { "description": "Abonnement CRM — pack annuel", "quantity": 1, "unitPrice": 4800, "vatRate": 20 },
    { "description": "Formation équipe (2 jours)", "quantity": 2, "unitPrice": 1500, "vatRate": 20 }
  ],
  "totals": { "ht": 7800, "vat": 1560, "ttc": 9360, "currency": "MAD" },
  "pdfUrl": "https://bot.swiviq.com/f/a1b2c3d4e5",
  "qrcodeUrl": "https://bot.swiviq.com/qr/a1b2c3d4e5.png",
  "whatsapp": { "owner": "sent", "client": "skipped" },
  "email": { "client": "sent" }
}
★ Unique to Fatora

Free-text modification

No patch schema to learn. Your user types the correction in a text field, you pass it through as-is — the same AI engine that understands merchants in Darija on WhatsApp decodes and applies it.

Request
curl -X POST https://bot.swiviq.com/v1/documents/FAT-2026-0042/modify \
  -H "Authorization: Bearer fk_votre_cle" \
  -H "Content-Type: application/json" \
  -d '{
    "instruction": "le client est en fait STE ATLAS DIGITAL MAROC SARL,
                    et remplace la formation par 3 jours à 1200 dh le jour"
  }'
Response — 200
{
  "number": "FAT-2026-0042",
  "client": { "name": "STE ATLAS DIGITAL MAROC SARL", "ice": "001122334455667", "type": "company" },
  "items": [
    { "description": "Abonnement CRM — pack annuel", "quantity": 1, "unitPrice": 4800, "vatRate": 20 },
    { "description": "Formation équipe", "quantity": 3, "unitPrice": 1200, "vatRate": 20 }
  ],
  "totals": { "ht": 8400, "vat": 1680, "ttc": 10080, "currency": "MAD" },
  "pdfUrl": "https://bot.swiviq.com/f/a1b2c3d4e5",
  "applied": "le client est en fait STE ATLAS DIGITAL MAROC SARL, et remplace…"
}

The document keeps its number (no gap in the sequence), the PDF is regenerated, totals recomputed. If the instruction cannot be understood, the API answers 422 with an explanation — never a hazardous guess.

Delivery: WhatsApp, email or QR code

Issuing is not enough — the end customer must receive their document. Three channels, combinable, reusable at will: a lost invoice is resent in one call.

WhatsApp

"sendToPhone" at creation, or POST /send with channel "whatsapp": the PDF lands in the customer’s conversation, in the merchant’s name.

Email

"sendToEmail" at creation, or channel "email": the customer gets a polished message with the invoice attached — the bot’s own template.

QR code

Every document exposes "qrcodeUrl": a PNG to show at the counter, print on a receipt or embed in your UI. The customer scans, the invoice downloads.

Request
curl -X POST https://bot.swiviq.com/v1/documents/FAT-2026-0042/send \
  -H "Authorization: Bearer fk_votre_cle" \
  -H "Content-Type: application/json" \
  -d '{ "channel": "email", "to": "client@exemple.ma" }'
Response — 200
{ "ok": true, "number": "FAT-2026-0042",
  "channel": "email", "to": "client@exemple.ma", "status": "sent" }

Endpoint reference

✓ Every example below was replayed as-is against the production API on 15/08/2026 — the responses shown are the real ones.

POST/v1/documents

Issue an invoice or quote

"type": "invoice" (default) or "quote". Options: "priceIsTtc", per-line or global "vatRate", "sendToPhone" and "sendToEmail" for immediate delivery, "notify": false to skip notifying the merchant. Responds 201 with number, totals, PDF link and QR code.

See the tested example
Request
curl -X POST https://bot.swiviq.com/v1/documents \
  -H "Authorization: Bearer fk_votre_cle" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "invoice",
    "client": { "name": "ATLAS DIGITAL SARL", "ice": "001122334455667", "type": "company" },
    "items": [
      { "description": "Abonnement CRM — pack annuel", "quantity": 1, "unitPrice": 4800 },
      { "description": "Formation équipe (2 jours)", "quantity": 2, "unitPrice": 1500 }
    ],
    "sendToEmail": "client@exemple.ma"
  }'
Response
{
  "number": "FAT-2026-0042",
  "type": "invoice",
  "status": "issued",
  "issueDate": "2026-08-15",
  "client": { "name": "ATLAS DIGITAL SARL", "ice": "001122334455667", "type": "company" },
  "items": [
    { "description": "Abonnement CRM — pack annuel", "quantity": 1, "unitPrice": 4800, "vatRate": 20 },
    { "description": "Formation équipe (2 jours)", "quantity": 2, "unitPrice": 1500, "vatRate": 20 }
  ],
  "totals": { "ht": 7800, "vat": 1560, "ttc": 9360, "currency": "MAD" },
  "pdfUrl": "https://bot.swiviq.com/f/a1b2c3d4e5",
  "qrcodeUrl": "https://bot.swiviq.com/qr/a1b2c3d4e5.png",
  "whatsapp": { "owner": "sent", "client": "skipped" },
  "email": { "client": "sent" }
}
POST/v1/documents/{number}/modify

Modify in free text

The body carries a single free-text "instruction" (French, Darija or English). Same number, PDF regenerated, an "applied" field echoes the understood instruction.

See the tested example
Request
curl -X POST https://bot.swiviq.com/v1/documents/FAT-2026-0042/modify \
  -H "Authorization: Bearer fk_votre_cle" \
  -H "Content-Type: application/json" \
  -d '{
    "instruction": "le client est en fait STE ATLAS DIGITAL MAROC SARL,
                    et remplace la formation par 3 jours à 1200 dh le jour"
  }'
Response
{
  "number": "FAT-2026-0042",
  "client": { "name": "STE ATLAS DIGITAL MAROC SARL", "ice": "001122334455667", "type": "company" },
  "items": [
    { "description": "Abonnement CRM — pack annuel", "quantity": 1, "unitPrice": 4800, "vatRate": 20 },
    { "description": "Formation équipe", "quantity": 3, "unitPrice": 1200, "vatRate": 20 }
  ],
  "totals": { "ht": 8400, "vat": 1680, "ttc": 10080, "currency": "MAD" },
  "pdfUrl": "https://bot.swiviq.com/f/a1b2c3d4e5",
  "applied": "le client est en fait STE ATLAS DIGITAL MAROC SARL, et remplace…"
}
POST/v1/documents/{number}/send

Deliver or resend a document

{ "channel": "whatsapp" | "email", "to": number or address }. The PDF is regenerated if missing — reusable as many times as needed.

See the tested example
Request
curl -X POST https://bot.swiviq.com/v1/documents/FAT-2026-0042/send \
  -H "Authorization: Bearer fk_votre_cle" \
  -H "Content-Type: application/json" \
  -d '{ "channel": "email", "to": "client@exemple.ma" }'
Response
{ "ok": true, "number": "FAT-2026-0042",
  "channel": "email", "to": "client@exemple.ma", "status": "sent" }
GET/qr/{token}.png

Download QR code (public)

The QR PNG pointing at the PDF. No authentication: the token is the secret. Embed it straight into an <img> tag or print it on a receipt.

See the tested example
Request
<!-- directement dans votre page -->
<img src="https://bot.swiviq.com/qr/a1b2c3d4e5.png" width="180" alt="QR facture">

# ou en téléchargement
curl -o facture-qr.png https://bot.swiviq.com/qr/a1b2c3d4e5.png
Response
HTTP 200 — image/png, 600 × 600 px
Cache-Control: public, max-age=86400, immutable
GET/v1/documents

List documents

Latest documents, newest first. "limit" up to 50, "type" to filter invoices ("invoice") or quotes ("quote").

See the tested example
Request
curl -H "Authorization: Bearer fk_votre_cle" \
  "https://bot.swiviq.com/v1/documents?limit=5&type=invoice"
Response
[
  {
    "number": "FAT-2026-0042",
    "type": "invoice",
    "status": "issued",
    "issueDate": "2026-08-15",
    "client": { "name": "STE ATLAS DIGITAL MAROC SARL", "ice": "001122334455667" },
    "totals": { "ht": 8400, "vat": 1680, "ttc": 10080, "currency": "MAD" },
    "pdfUrl": "https://bot.swiviq.com/f/a1b2c3d4e5",
    "qrcodeUrl": "https://bot.swiviq.com/qr/a1b2c3d4e5.png"
  },
  { "number": "FAT-2026-0041", "...": "..." }
]
GET/v1/documents/{number}

Read a document

Lines, totals, PDF link, QR code — handy to render the state in your UI or verify a modification.

See the tested example
Request
curl -H "Authorization: Bearer fk_votre_cle" \
  https://bot.swiviq.com/v1/documents/FAT-2026-0042
Response
{
  "number": "FAT-2026-0042",
  "type": "invoice",
  "status": "issued",
  "issueDate": "2026-08-15",
  "client": { "name": "STE ATLAS DIGITAL MAROC SARL", "ice": "001122334455667", "type": "company" },
  "items": [
    { "description": "Abonnement CRM — pack annuel", "quantity": 1, "unitPrice": 4800, "vatRate": 20 },
    { "description": "Formation équipe", "quantity": 3, "unitPrice": 1200, "vatRate": 20 }
  ],
  "totals": { "ht": 8400, "vat": 1680, "ttc": 10080, "currency": "MAD" },
  "pdfUrl": "https://bot.swiviq.com/f/a1b2c3d4e5",
  "qrcodeUrl": "https://bot.swiviq.com/qr/a1b2c3d4e5.png"
}
GET/v1/account

Account & quota

Company profile, active plan, quota used/remaining. Also the simplest way to check that a key is valid.

See the tested example
Request
curl -H "Authorization: Bearer fk_votre_cle" \
  https://bot.swiviq.com/v1/account
Response
{
  "company": {
    "name": "ATLAS SERVICES SARL", "ice": "002233445566778",
    "address": "Agdal, Rabat", "defaultVatRate": 20,
    "taxRegime": "normal", "hasLogo": true, "hasStamp": true
  },
  "plan": { "key": "p100", "label": "100 factures / mois", "priceMAD": 90 },
  "quota": { "used": 12, "limit": 100, "remaining": 88, "ok": true }
}
PUT/v1/company

Update the company

Accepted fields: companyName, ice, if, taxePro, rc, address, email, defaultVatRate, paymentTerms. Only the fields you send are changed.

See the tested example
Request
curl -X PUT https://bot.swiviq.com/v1/company \
  -H "Authorization: Bearer fk_votre_cle" \
  -H "Content-Type: application/json" \
  -d '{
    "address": "12 rue Oued Fès, Agdal, Rabat",
    "paymentTerms": "Virement à 30 jours"
  }'
Response
{
  "ok": true,
  "company": {
    "name": "ATLAS SERVICES SARL", "ice": "002233445566778",
    "address": "12 rue Oued Fès, Agdal, Rabat", "defaultVatRate": 20
  }
}
POST/v1/company/logo

Upload the logo

Body: { "image": "<base64 or data-URI png/jpeg>" } (6 MB max). Cleaned and cropped automatically, then applied to upcoming PDFs.

See the tested example
Request
curl -X POST https://bot.swiviq.com/v1/company/logo \
  -H "Authorization: Bearer fk_votre_cle" \
  -H "Content-Type: application/json" \
  -d "{ \"image\": \"data:image/png;base64,$(base64 -w0 logo.png)\" }"
Response
{ "ok": true, "logo": "saved",
  "appliesTo": "toutes les prochaines factures" }
POST/v1/company/stamp

Upload the stamp

Same format as the logo. Background removed by ink extraction (a photo of the stamp on paper is enough), then stamped on every PDF.

See the tested example
Request
curl -X POST https://bot.swiviq.com/v1/company/stamp \
  -H "Authorization: Bearer fk_votre_cle" \
  -H "Content-Type: application/json" \
  -d "{ \"image\": \"data:image/png;base64,$(base64 -w0 cachet.jpg)\" }"
Response
{ "ok": true, "stamp": "saved",
  "appliesTo": "toutes les prochaines factures" }

Error codes

Every error returns a JSON with two constant fields: "error" (stable machine code to test in your code) and "message" (plain-language explanation, displayable as-is). The 402 adds "used", "limit" and "reason" so you can build your own quota screen.

400Invalid requestCodes: invalid_json (unreadable body), empty_body (empty body — check no line break is corrupting your headers), missing_client, missing_items, invalid_channel, invalid_phone, invalid_email, invalid_image. The message says exactly which field to fix.
401Missing or invalid keymissing_api_key: add the Authorization: Bearer fk_… header · invalid_api_key: unknown key, or revoked by “api reset” — ask the merchant for the new one.
402Plan quota reachedquota_exceeded: the response carries used/limit/reason. Quotes don’t count. The merchant upgrades in two WhatsApp messages.
403Account suspendedaccount_suspended: the merchant’s account is suspended — contact contact@swiviq.com.
404Document not foundnot_found: no document with that number on THIS account — each key only sees its own documents.
409Document cancelledcancelled: the document was cancelled (credit note issued) — it can no longer be modified or resent. Issue a new one.
413Body too largebody_too_large: 8 MB max — compress the logo or stamp before uploading.
422Instruction not understoodinstruction_not_understood: the AI could not apply the change safely — rephrase more precisely. Nothing was modified.
429Too many requestsrate_limited: 60 requests/minute per key — slow down or queue, then retry.
500Internal errorinternal: retry; if it persists, write to contact@swiviq.com with the response body.
Response
HTTP 402
{
  "error": "quota_exceeded",
  "reason": "trial",
  "used": 2,
  "limit": 2,
  "message": "Quota d'essai atteint (2 factures). Le commerçant peut passer
              au plan supérieur depuis son WhatsApp Fatora."
}

Integration questions

How much does the API cost?

Nothing beyond the merchant’s Fatora plan, free trial included. API invoices count in the plan quota exactly like WhatsApp ones; quotes are free and unlimited.

Who owns the key: me (the vendor) or my user?

Each merchant has their own key, tied to their account and numbering. Your app stores the key the user pastes — no central account to manage, each customer pays their own plan.

My amounts are tax-inclusive — what then?

Add "priceIsTtc": true. Fatora rebuilds the net base by dividing by one plus the rate, line by line.

The merchant is an auto-entrepreneur, no VAT?

Their tax regime is stored on their account and enforced by the API: whatever your app sends, their documents come out without VAT, with the proper mention.

What happens when the quota is reached?

The API answers 402 with used, limit and a display-ready message. The merchant upgrades in two WhatsApp messages.