Recurring documents
A recurring rule is a document template paired with a schedule. Once configured, it issues invoices on its own (AUTO mode), prepares drafts for approval (DRAFT) or just sends a reminder to issue one (REMINDER). Through the API you can read rule state and generation history, and pause or resume a rule, for example when a customer cancels their subscription in your system.
The module requires the MAX plan on the Fakturnia account. Without it every endpoint returns 403 with the SUBSCRIPTION_REQUIRED code.
Permission scopes: recurring:browse, recurring:read, recurring:toggle.
Rules are created and edited in the Fakturnia app. The API deliberately does not create them: a rule template is a complete document with line items and automations, and configuring it is simply easier in the editor than in JSON.
Listing rules
curl -s https://prod.api.fakturnia.pl/external/v1/recurring-rules \
-H "Authorization: Bearer $TOKEN"
{
"success": true,
"data": {
"rules": [
{
"id": "665f0c1234567890abcdef12",
"name": "Subscription, Firma Sp. z o.o.",
"status": "ACTIVE",
"mode": "AUTO",
"documentSummary": {
"documentType": "FakturaVAT",
"buyerName": "Firma Sp. z o.o.",
"grossSum": 1230,
"currency": "PLN"
},
"schedule": { "frequency": "MONTHLY", "dayOfMonth": 1, "startDate": "2026-01-01", "endType": "NEVER" },
"nextRunAt": "2026-08-01",
"occurrencesCount": 7,
"documentsIssuedCount": 7
}
],
"summary": {
"total": 3,
"active": 2,
"paused": 1,
"finished": 0,
"nextRunAt": "2026-08-01",
"monthlyRecurringGross": 2460
}
}
}
The ?status=ACTIVE|PAUSED|FINISHED parameter narrows the list. The summary block always describes all rules on the account, regardless of the filter.
monthlyRecurringGross is the monthly gross revenue from active rules, with each frequency converted to a monthly figure (a quarterly rule counts as 1/3 of its amount). Only PLN rules are summed, because the API does not guess exchange rates.
Instead of the full document template, a rule returns documentSummary: document type, buyer name, gross amount and currency. That is enough for listing and reporting, and the generated documents are available through the regular document endpoints.
Rule details
curl -s https://prod.api.fakturnia.pl/external/v1/recurring-rules/{id} \
-H "Authorization: Bearer $TOKEN"
On top of the list fields, the details include the full schedule and automations:
| Field | Meaning |
|---|---|
schedule.frequency |
WEEKLY, MONTHLY, BIMONTHLY, QUARTERLY, SEMIANNUALLY, YEARLY or CUSTOM (with customInterval + customUnit) |
schedule.endType |
NEVER, DATE (until endDate) or COUNT (after endCount occurrences) |
automation |
e-mail delivery to the buyer, sending to KSeF, notifications |
payment |
payment status stamped on generated documents (PAID / UNPAID + deadline) |
nextRunAt |
date of the next generation (null when the rule is paused or finished) |
skipDates |
occurrence dates skipped manually in the app |
Generation history
The journal of rule occurrences, newest first:
curl -s "https://prod.api.fakturnia.pl/external/v1/recurring-rules/{id}/history?itemsPerPage=50" \
-H "Authorization: Bearer $TOKEN"
{
"success": true,
"data": {
"runs": [
{
"id": "666a0d1234567890abcdef56",
"plannedDate": "2026-07-01",
"issueDate": "2026-07-01",
"documentId": "666a0e1234567890abcdef78",
"documentNumber": "FV/7/2026",
"status": "GENERATED",
"emailSent": true,
"ksefQueued": false
}
]
}
}
| Status | Meaning |
|---|---|
GENERATED |
document issued automatically (AUTO mode) |
DRAFT |
a draft was created and awaits approval in the app |
REMINDED |
a reminder to issue the document was sent (REMINDER mode) |
SKIPPED |
occurrence skipped |
FAILED |
generation failed, details in the error field |
PENDING |
occurrence currently being processed |
documentId and documentNumber point to the generated document. The history is paginated like the lists: currentPage, itemsPerPage (default 50, up to 200), with totalCount and totalPages in the response; the older limit parameter still works as an alias of itemsPerPage. The rule list GET /recurring-rules is paginated too (itemsPerPage up to 100), and its summary always covers every rule.
Pausing and resuming
# Pause
curl -s -X PATCH https://prod.api.fakturnia.pl/external/v1/recurring-rules/{id}/toggle \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "active": false }'
# Resume
curl -s -X PATCH https://prod.api.fakturnia.pl/external/v1/recurring-rules/{id}/toggle \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "active": true }'
A paused rule does not generate documents. Resuming recalculates nextRunAt from today: occurrences missed during the pause are not backfilled. This is the safe behaviour for the typical scenario where a customer suspends a subscription and comes back a few months later.
A finished rule (FINISHED, meaning past its end date or occurrence limit) cannot be resumed; the attempt returns 409 CONFLICT.
Best practices
- Listen for the
document.createdwebhook instead of polling the list. Documents issued by rules emitdocument.created(like every other new document, from the API or the app), so your integration receives them immediately. The rule history (/history) then lets you match an occurrence to itsdocumentId. - Monitor
FAILEDoccurrences. Periodically (say, once a day) review the history of active rules; theerrorfield explains why a generation failed. A failed occurrence does not stop the cycle, subsequent dates keep coming. - Pause instead of deleting. When a customer suspends a subscription, call
togglewithactive: false. The rule keeps its history and configuration, and resuming recalculatesnextRunAtwith no risk of issuing overdue invoices. - Store the rule
id, not its name. The name is editable in the app and can change without notice. - Tell the counters apart.
occurrencesCountcounts schedule occurrences (including reminders and skips),documentsIssuedCountonly documents actually issued. Use the latter for sales reporting. - Treat
monthlyRecurringGrossas an approximation. The sum covers only active PLN rules; add foreign-currency rules yourself at your own rate, based ondocumentSummary.