API reference
Authenticate with an API key, create and finalize invoices, manage customers and recurring schedules. Every endpoint below is live in production.
https://hisab.ma/api/v1The Hisab API is a JSON REST API served over HTTPS. Every request is authenticated with an API key and scoped to your organization.
https://hisab.ma/api/v1Requests are limited per minute and per API key. Past the limit the API returns 429 RATE_LIMIT_EXCEEDED with a Retry-After header.
| Plan | Requests | API keys |
|---|---|---|
| Professional | 120 req/min | 10 |
| Fiduciaire | 300 req/min | Unlimited |
Every response reports your current usage in these headers:
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 119
X-RateLimit-Reset: 1780750800API keys are created from the dashboard: Settings, then API keys. You choose the scopes when creating the key, and the full secret is shown only once.
Send the key as a Bearer token in the Authorization header:
curl https://hisab.ma/api/v1/invoices \
-H "Authorization: Bearer hisab_live_***"hisab_live_***Production key. Acts on your real data.
hisab_test_***Test key. Same format, for staging integrations.
Keys are stored hashed (SHA-256) on our side and can be revoked at any time from the dashboard. A 401 means the key is missing, invalid or revoked:
{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}
}Each key carries an explicit list of scopes. A request that needs a scope the key does not have returns 403 FORBIDDEN, with the required scopes in the error details.
invoices:readinvoices:writeinvoices:finalizeinvoices:sendinvoices:paymentinvoices:voidinvoices:exportrecurring:readrecurring:writerecurring:deletecustomers:readcustomers:writecustomers:deleteorganization:readorganization:writewebhooks:readwebhooks:writeorganizations:readorganizations:writeorganizations:archiveWhen creating a key, the dashboard offers three bundles (read only, invoicing, full access), and you can always pick scopes individually.
List endpoints accept page and per_page (max 100) and return the pagination block inside meta:
{
"success": true,
"data": [ ... ],
"meta": {
"pagination": { "total": 128, "page": 1, "perPage": 20, "totalPages": 7 }
}
}All errors share one shape: success is false, and error carries a stable code, a human-readable message and optional field-level details. The standard codes:
VALIDATION_ERROR | 400 |
INVALID_JSON | 400 |
UNAUTHORIZED | 401 |
FORBIDDEN | 403 |
API_DISABLED | 403 |
SUBSCRIPTION_EXPIRED | 403 |
API_TIER_NOT_SUPPORTED | 403 |
ORG_HEADER_REQUIRED | 400 |
ACCOUNT_KEY_REQUIRED | 403 |
ENTITY_CAP_REACHED | 403 |
NOT_FOUND | 404 |
RATE_LIMIT_EXCEEDED | 429 |
INTERNAL_ERROR | 500 |
Hisab signs and POSTs an event to your endpoint whenever an invoice or a customer changes. Webhooks are configured from the dashboard: Settings, then Webhooks.
invoice.createdinvoice.updatedinvoice.finalizedinvoice.sentinvoice.paidinvoice.voidedcustomer.createdcustomer.updatedcustomer.deletedCreate draft invoices, finalize them to assign the official number, then track sending, payment and voiding. Finalized invoices export as PDF and UBL 2.1 XML.
/api/v1/invoicesReturns your organization's invoices, newest first. Filter by status, customer or issue date.
invoices:readpage | integerdefault: 1 |
per_page | integerdefault: 20 (max 100) |
order | stringdefault: descascdesc |
status | stringdraftfinalizedsentpaidvoid |
customer_id | uuid |
date_from | date (YYYY-MM-DD) |
date_to | date (YYYY-MM-DD) |
curl https://hisab.ma/api/v1/invoices \
-H "Authorization: Bearer hisab_live_***"/api/v1/invoices/:idReturns a single invoice with its customer, line items and totals.
invoices:readcurl https://hisab.ma/api/v1/invoices/{id} \
-H "Authorization: Bearer hisab_live_***"/api/v1/invoicesCreates a draft. Totals and taxes are computed server-side from the line items.
invoices:writeinvoice.createdcustomer_idrequired | uuid |
issue_daterequired | date (YYYY-MM-DD) |
due_date | date (YYYY-MM-DD) |
currency | stringdefault: MAD |
payment_terms | string |
notes | string |
internal_notes | string |
items[].descriptionrequired | string |
items[].quantityrequired | number |
items[].unit_pricerequired | number |
items[].tax_rate | numberdefault: 20 |
curl https://hisab.ma/api/v1/invoices \
-X POST \
-H "Authorization: Bearer hisab_live_***" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "cus_2b81fe",
"issue_date": "2026-06-05",
"items": [
{
"description": "Integration services",
"quantity": 8,
"unit_price": 1300,
"tax_rate": 20
}
]
}'/api/v1/invoices/:idUpdates a draft. Finalized invoices are immutable; the call returns INVOICE_NOT_EDITABLE.
invoices:writeinvoice.updatedcustomer_id | uuid |
issue_date | date (YYYY-MM-DD) |
due_date | date (YYYY-MM-DD) |
currency | string |
payment_terms | string |
notes | string |
items[].description | string |
items[].quantity | number |
items[].unit_price | number |
items[].tax_rate | numberdefault: 20 |
INVOICE_NOT_EDITABLEcurl https://hisab.ma/api/v1/invoices/{id} \
-X PATCH \
-H "Authorization: Bearer hisab_live_***" \
-H "Content-Type: application/json" \
-d '{
"due_date": "2026-08-05"
}'/api/v1/invoices/:id/finalizeAssigns the official sequential number and locks the invoice. This is the legal issuance step.
invoices:finalizeinvoice.finalizedINVOICE_NOT_DRAFTVALIDATION_ERRORcurl https://hisab.ma/api/v1/invoices/{id}/finalize \
-X POST \
-H "Authorization: Bearer hisab_live_***"/api/v1/invoices/:id/sendRecords that the finalized invoice was delivered to the customer.
invoices:writeinvoice.sentINVALID_STATUS_TRANSITIONcurl https://hisab.ma/api/v1/invoices/{id}/send \
-X POST \
-H "Authorization: Bearer hisab_live_***"/api/v1/invoices/:id/payRecords a payment, with an optional method and reference.
invoices:writeinvoice.paidpaid_at | datetime (ISO 8601) |
payment_method | string |
payment_reference | string |
INVALID_STATUS_TRANSITIONcurl https://hisab.ma/api/v1/invoices/{id}/pay \
-X POST \
-H "Authorization: Bearer hisab_live_***" \
-H "Content-Type: application/json" \
-d '{
"payment_method": "bank_transfer",
"payment_reference": "VIR-20260605"
}'/api/v1/invoices/:id/voidCancels a finalized invoice with a mandatory reason. Paid invoices cannot be voided.
invoices:writeinvoice.voidedreasonrequired | string |
CANNOT_VOID_PAIDALREADY_VOIDEDcurl https://hisab.ma/api/v1/invoices/{id}/void \
-X POST \
-H "Authorization: Bearer hisab_live_***" \
-H "Content-Type: application/json" \
-d '{
"reason": "Duplicate billing"
}'/api/v1/invoices/:id/pdfReturns the validated PDF in French, English or Arabic. Available once finalized.
invoices:exportlocale | stringdefault: frfrenar |
PDF_NOT_AVAILABLEcurl https://hisab.ma/api/v1/invoices/{id}/pdf \
-H "Authorization: Bearer hisab_live_***"/api/v1/invoices/:id/xmlReturns the UBL 2.1 XML of the finalized invoice.
invoices:exportXML_NOT_AVAILABLEcurl https://hisab.ma/api/v1/invoices/{id}/xml \
-H "Authorization: Bearer hisab_live_***"B2B and B2C customers with Moroccan identifiers (ICE, RC). Archiving preserves the invoice history.
/api/v1/customersReturns customers, with search across name, email and ICE.
customers:readpage | integerdefault: 1 |
per_page | integerdefault: 20 (max 100) |
order | stringdefault: descascdesc |
search | string |
type | stringb2bb2c |
status | stringactiveinactivearchived |
curl https://hisab.ma/api/v1/customers \
-H "Authorization: Bearer hisab_live_***"/api/v1/customers/:idReturns a single customer with contact details and address.
customers:readcurl https://hisab.ma/api/v1/customers/{id} \
-H "Authorization: Bearer hisab_live_***"/api/v1/customersB2B customers should carry their 15-digit ICE. Duplicate ICEs are rejected within your organization.
customers:writecustomer.creatednamerequired | string |
typerequired | stringb2bb2c |
ice | string (15 digits) |
legal_name | string |
rc | string |
email | string |
phone | string |
address.street | string |
address.city | string |
address.postal_code | string |
address.country | stringdefault: MA |
notes | string |
tags | string[] |
curl https://hisab.ma/api/v1/customers \
-X POST \
-H "Authorization: Bearer hisab_live_***" \
-H "Content-Type: application/json" \
-d '{
"name": "MERIT Sarl",
"type": "b2b",
"ice": "001234567000089",
"email": "compta@example.ma"
}'/api/v1/customers/:idPartial update: send only the fields that change.
customers:writecustomer.updatedname | string |
type | stringb2bb2c |
ice | string (15 digits) |
email | string |
phone | string |
address | object |
notes | string |
tags | string[] |
status | stringactiveinactivearchived |
curl https://hisab.ma/api/v1/customers/{id} \
-X PATCH \
-H "Authorization: Bearer hisab_live_***" \
-H "Content-Type: application/json" \
-d '{
"email": "finance@example.ma"
}'/api/v1/customers/:idSoft delete. The customer is archived and their invoice history stays intact.
customers:deletecustomer.deletedALREADY_ARCHIVEDcurl https://hisab.ma/api/v1/customers/{id} \
-X DELETE \
-H "Authorization: Bearer hisab_live_***"Schedules that generate invoices automatically, weekly to yearly, with optional auto-finalize and auto-send.
/api/v1/recurring-invoicesReturns recurring invoice schedules. Filter by status, customer or frequency.
invoices:readpage | integerdefault: 1 |
per_page | integerdefault: 20 (max 100) |
order | stringdefault: descascdesc |
status | stringactivepausedcompletedcancelled |
customer_id | uuid |
frequency | stringweeklybiweeklymonthlyquarterlybiannuallyyearly |
search | string |
curl https://hisab.ma/api/v1/recurring-invoices \
-H "Authorization: Bearer hisab_live_***"/api/v1/recurring-invoices/:idReturns a schedule with its latest generation history.
invoices:readcurl https://hisab.ma/api/v1/recurring-invoices/{id} \
-H "Authorization: Bearer hisab_live_***"/api/v1/recurring-invoicesDefines the frequency, start date and line items of the future invoices.
invoices:writecustomer_idrequired | uuid |
frequencyrequired | stringweeklybiweeklymonthlyquarterlybiannuallyyearly |
start_daterequired | date (YYYY-MM-DD) |
name | string |
interval_count | integer (1-12)default: 1 |
day_of_month | integer (1-28) |
day_of_week | integer (0-6) |
end_date | date (YYYY-MM-DD) |
max_occurrences | integer |
auto_finalize | booleandefault: false |
auto_send | booleandefault: false |
days_until_due | integer (0-365)default: 30 |
currency | stringdefault: MAD |
items[].descriptionrequired | string |
items[].quantityrequired | number |
items[].unit_pricerequired | number |
items[].tax_rate | numberdefault: 20 |
QUOTA_EXCEEDEDcurl https://hisab.ma/api/v1/recurring-invoices \
-X POST \
-H "Authorization: Bearer hisab_live_***" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "cus_2b81fe",
"frequency": "monthly",
"start_date": "2026-07-01",
"auto_finalize": true,
"items": [
{
"description": "Monthly retainer",
"quantity": 1,
"unit_price": 10400,
"tax_rate": 20
}
]
}'/api/v1/recurring-invoices/:idAdjusts an active or paused schedule. Completed and cancelled schedules are read-only.
invoices:writefrequency | stringweeklybiweeklymonthlyquarterlybiannuallyyearly |
end_date | date (YYYY-MM-DD) |
auto_finalize | boolean |
auto_send | boolean |
days_until_due | integer (0-365) |
items | array |
INVALID_STATEcurl https://hisab.ma/api/v1/recurring-invoices/{id} \
-X PUT \
-H "Authorization: Bearer hisab_live_***" \
-H "Content-Type: application/json" \
-d '{
"auto_send": true
}'/api/v1/recurring-invoices/:idRemoves the schedule and its history. Already generated invoices are not affected.
invoices:writecurl https://hisab.ma/api/v1/recurring-invoices/{id} \
-X DELETE \
-H "Authorization: Bearer hisab_live_***"/api/v1/recurring-invoices/:id/generateCreates the next invoice immediately, honoring auto-finalize and auto-send.
invoices:writeissue_date | date (YYYY-MM-DD)default: today |
INVALID_STATECUSTOMER_INACTIVEcurl https://hisab.ma/api/v1/recurring-invoices/{id}/generate \
-X POST \
-H "Authorization: Bearer hisab_live_***"/api/v1/recurring-invoices/:id/historyLists past runs with their generated invoice numbers and status.
invoices:readlimit | integerdefault: 20 (max 100) |
curl https://hisab.ma/api/v1/recurring-invoices/{id}/history \
-H "Authorization: Bearer hisab_live_***"/api/v1/recurring-invoices/:id/pauseStops future generation until the schedule is resumed.
invoices:writeINVALID_STATEcurl https://hisab.ma/api/v1/recurring-invoices/{id}/pause \
-X POST \
-H "Authorization: Bearer hisab_live_***"/api/v1/recurring-invoices/:id/resumeRestarts a paused schedule, optionally from a new next run date.
invoices:writenext_run_date | date (YYYY-MM-DD) |
INVALID_STATEcurl https://hisab.ma/api/v1/recurring-invoices/{id}/resume \
-X POST \
-H "Authorization: Bearer hisab_live_***"Your organization profile, subscription state and API quotas.
/api/v1/organizationReturns legal identifiers, contact info, subscription and API quotas.
organization:readcurl https://hisab.ma/api/v1/organization \
-H "Authorization: Bearer hisab_live_***"/api/v1/organizationUpdates legal identifiers, contact info and the billing address.
organization:writelegal_name | string |
ice | string |
rc | string |
if_number | string |
vat_number | string |
phone | string |
email | string |
website | string (URL) |
billing_address | object |
curl https://hisab.ma/api/v1/organization \
-X PATCH \
-H "Authorization: Bearer hisab_live_***" \
-H "Content-Type: application/json" \
-d '{
"phone": "+212 5 22 00 00 01"
}'Organization management for multi-entity integrations. These endpoints require an ACCOUNT-level key (hisab_acct_). After provisioning, operate any entity on the regular endpoints by sending the X-Organization-Id header with the same account key. An ICE already registered on the platform cannot be used again, even by another account - it identifies exactly one legal entity.
/api/v1/organizationsReturns every organization the key's owner owns. Archived entities are excluded unless include_archived=true.
organizations:readinclude_archived | booleandefault: false |
ACCOUNT_KEY_REQUIREDcurl https://hisab.ma/api/v1/organizations \
-H "Authorization: Bearer hisab_live_***"/api/v1/organizationsProvisions a new legal entity under your account. Your plan's entity cap applies; contact support to raise it.
organizations:writelegal_namerequired | string |
ice | string (15 digits) |
rc | string |
if_number | string |
phone | string |
email | string |
address_street | string |
address_city | string |
address_postal_code | string |
ACCOUNT_KEY_REQUIREDENTITY_CAP_REACHEDcurl https://hisab.ma/api/v1/organizations \
-X POST \
-H "Authorization: Bearer hisab_live_***" \
-H "Content-Type: application/json" \
-d '{
"legal_name": "Filiale Casablanca SARL",
"ice": "002345678000071"
}'/api/v1/organizations/{id}Returns one owned organization. Unknown or foreign ids return 404.
organizations:readACCOUNT_KEY_REQUIREDcurl https://hisab.ma/api/v1/organizations/{id} \
-H "Authorization: Bearer hisab_live_***"/api/v1/organizations/{id}/archiveSoft-archives the entity: it is frozen everywhere (API, dashboard, crons) but nothing is deleted. Fully reversible.
organizations:archiveACCOUNT_KEY_REQUIREDALREADY_ARCHIVEDcurl https://hisab.ma/api/v1/organizations/{id}/archive \
-X POST \
-H "Authorization: Bearer hisab_live_***"/api/v1/organizations/{id}/restoreReverses an archive; the entity resumes normal operation immediately.
organizations:archiveACCOUNT_KEY_REQUIREDNOT_ARCHIVEDcurl https://hisab.ma/api/v1/organizations/{id}/restore \
-X POST \
-H "Authorization: Bearer hisab_live_***"