"sendToPhone" at creation, or POST /send with channel "whatsapp": the PDF lands in the customer’s conversation, in the merchant’s name.
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
- 1The merchant opens their Fatora-Bot WhatsApp conversation
- 2They simply write “api”
- 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.
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"
}'{
"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" }
}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.
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"
}'{
"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.
"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.
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" }'{ "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.
/v1/documentsIssue 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
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"
}'{
"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" }
}/v1/documents/{number}/modifyModify 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
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"
}'{
"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…"
}/v1/documents/{number}/sendDeliver 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
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" }'{ "ok": true, "number": "FAT-2026-0042",
"channel": "email", "to": "client@exemple.ma", "status": "sent" }/qr/{token}.pngDownload 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
<!-- 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
HTTP 200 — image/png, 600 × 600 px Cache-Control: public, max-age=86400, immutable
/v1/documentsList documents
Latest documents, newest first. "limit" up to 50, "type" to filter invoices ("invoice") or quotes ("quote").
See the tested example
curl -H "Authorization: Bearer fk_votre_cle" \ "https://bot.swiviq.com/v1/documents?limit=5&type=invoice"
[
{
"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", "...": "..." }
]/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
curl -H "Authorization: Bearer fk_votre_cle" \ https://bot.swiviq.com/v1/documents/FAT-2026-0042
{
"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"
}/v1/accountAccount & quota
Company profile, active plan, quota used/remaining. Also the simplest way to check that a key is valid.
See the tested example
curl -H "Authorization: Bearer fk_votre_cle" \ https://bot.swiviq.com/v1/account
{
"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 }
}/v1/companyUpdate the company
Accepted fields: companyName, ice, if, taxePro, rc, address, email, defaultVatRate, paymentTerms. Only the fields you send are changed.
See the tested example
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"
}'{
"ok": true,
"company": {
"name": "ATLAS SERVICES SARL", "ice": "002233445566778",
"address": "12 rue Oued Fès, Agdal, Rabat", "defaultVatRate": 20
}
}/v1/company/logoUpload 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
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)\" }"{ "ok": true, "logo": "saved",
"appliesTo": "toutes les prochaines factures" }/v1/company/stampUpload 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
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)\" }"{ "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.
400 | Invalid request | Codes: 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. |
401 | Missing or invalid key | missing_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. |
402 | Plan quota reached | quota_exceeded: the response carries used/limit/reason. Quotes don’t count. The merchant upgrades in two WhatsApp messages. |
403 | Account suspended | account_suspended: the merchant’s account is suspended — contact contact@swiviq.com. |
404 | Document not found | not_found: no document with that number on THIS account — each key only sees its own documents. |
409 | Document cancelled | cancelled: the document was cancelled (credit note issued) — it can no longer be modified or resent. Issue a new one. |
413 | Body too large | body_too_large: 8 MB max — compress the logo or stamp before uploading. |
422 | Instruction not understood | instruction_not_understood: the AI could not apply the change safely — rephrase more precisely. Nothing was modified. |
429 | Too many requests | rate_limited: 60 requests/minute per key — slow down or queue, then retry. |
500 | Internal error | internal: retry; if it persists, write to contact@swiviq.com with the response body. |
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.