Status and month-end completeness
This page covers the routes that answer "where does this month stand": the credential echo you call first, the completeness picture the whole product is judged by, the suppliers that are late by their own habits, and how often the digest email goes out.
Completeness is the core promise. Everything else in Syllevo exists to make these numbers reach zero missing.
Routes
| Method | Path | Needs |
|---|---|---|
| GET | /v1/status |
Any credential |
| GET | /v1/completeness |
Any member |
| GET | /v1/profiles/{profileId}/completeness |
View |
| POST | /v1/profiles/{profileId}/completeness/expected |
ManageDocuments |
| POST | /v1/profiles/{profileId}/completeness/expected/{id}/waive |
ManageDocuments |
| POST | /v1/profiles/{profileId}/completeness/recompute |
ManageDocuments |
| GET | /v1/predictions/missing |
Any member |
| GET | /v1/digest-preferences |
Any member |
| PUT | /v1/digest-preferences |
Any member |
GET /v1/status
Tells you who the API thinks you are. Call it first when something is not working.
| Field | Type | Means |
|---|---|---|
actor |
string | How you were recognized |
organizationId |
uuid or null | The organization the request is scoped to |
correlationId |
string or null | The id this request is filed under in the logs |
{
"actor": "apikey",
"organizationId": "11111111-1111-4111-8111-111111111111",
"correlationId": "c0ffee00-1111-4111-8111-111111111111"
}
actor is one of anonymous, session, apikey, mcp, or system. It is a lowercase word,
not a number, which makes it one of only two enums in the whole API that are not integers. See
Conventions.
This route is not organization scoped, so it answers without X-Organization-Id. A session token
sent without that header comes back with organizationId: null, which is the fastest way to tell a
missing header from a bad token. An API key always reports its own organization.
A wrong path does not answer 404 here or anywhere else. It answers 401, because the
authorization fallback runs before routing decides there is nothing to route to. If /v1/status
answers 200 and your route answers 401, check the spelling of the path before you check the
credential. See Authentication.
Completeness, in one paragraph
Syllevo keeps a list of documents it expects for each client and each month, then matches arrivals against it. Three things put a row on that list: a supplier that bills this client most months, a person who typed it in, and a connected source that normally delivers and delivered nothing. Each row is missing, received, or waived. The missing ones are what reminders chase.
GET /v1/completeness
The firm's month in one answer, worst client first.
| Parameter | Type | Means |
|---|---|---|
period |
string | The month as "2026-08". Omitted means the month running now |
| Field | Type | Means |
|---|---|---|
period |
string | The month this answer is about |
profileCount |
integer | How many clients are counted |
profilesWithMissing |
integer | How many of them are still short something |
expectedCount |
integer | Rows expected in total |
receivedCount |
integer | Rows satisfied |
missingCount |
integer | Rows still owed |
waivedCount |
integer | Rows written off |
profiles |
array | One summary per client, worst first |
Each summary carries profileId, profileName, expectedCount, receivedCount, missingCount,
and waivedCount.
There is no permission parameter and no organization parameter. The service narrows the query to the clients this caller was granted, so a member with three clients gets a number covering three clients, not the firm.
expectedCount is not receivedCount + missingCount + waivedCount in every case. Take the four
counts as given rather than deriving one from the others.
GET /v1/profiles/{profileId}/completeness
One client's month, with the actual list.
| Parameter | Type | Means |
|---|---|---|
period |
string | The month as "2026-08". Omitted means the month running now |
| Field | Type | Means |
|---|---|---|
profileId |
uuid | The client |
profileName |
string | Its name |
period |
string | The month |
expectedCount |
integer | Rows expected |
receivedCount |
integer | Rows satisfied |
missingCount |
integer | Rows still owed |
waivedCount |
integer | Rows written off |
missing |
array | The rows still owed |
settled |
array | The rows received or waived |
The expectation shape
| Field | Type | Means |
|---|---|---|
id |
uuid | The expectation |
state |
ExpectedDocumentState |
0 missing, 1 received, 2 waived |
reason |
ExpectedDocumentReason |
Why it is expected. See below |
supplierId |
uuid or null | The supplier it is expected from |
supplierName |
string or null | That supplier's name |
accountId |
uuid or null | The source, on source-gap rows |
accountProviderType |
DataProviderType, null |
What kind of source that is |
accountTag |
string or null | Its label |
description |
string or null | What somebody typed when they marked it |
isRecurring |
boolean | Whether it is asked again next month |
satisfiedAt |
date-time, null | When it was matched |
satisfiedByDocumentId |
uuid or null | The file that satisfied it |
satisfiedByInvoiceId |
uuid or null | The invoice that satisfied it |
waivedAt |
date-time, null | When it was written off |
waiveReason |
string or null | Why |
createdAt |
date-time | When the expectation appeared |
reason:
| Value | Name | Means |
|---|---|---|
0 |
RecurringSupplier | This supplier billed the client in most recent months |
1 |
ManualMark | Somebody said so |
2 |
SourceGap | A source that normally delivers delivered nothing this month |
A 2 row is different from the others: what is missing is not a named supplier but everything that
inbox or e-Factura connection usually carries, which is why it hangs off accountId instead of
supplierId.
A 1 row outranks a 0 row wherever both would produce an expectation for the same supplier and
month. A person who typed it in knows something the invoice history does not.
State 1 received is never set without a link. An expectation that claims a document arrived but
cannot say which one is how a month closes on a document nobody has.
The shipped thresholds behind 0 are three months of lookback and at least two of them present,
and behind 2, two months of lookback with documents in at least two. They are configuration rather
than constants, so a deployment can tune them. They are deliberately forgiving in one direction:
expecting too much costs an accountant ten seconds to waive, and expecting too little says a month
is complete when it is not.
POST /v1/profiles/{profileId}/completeness/expected
Adds an expectation by hand. ManageDocuments. The "this client gets a rent invoice every month,
chase it" route.
| Field | Type | Required | Means |
|---|---|---|---|
supplierId |
uuid or null | no | The supplier it is expected from |
description |
string or null | no | What it is, when no supplier row fits |
period |
string or null | no | The month. Omitted means the month running now |
isRecurring |
boolean | no | Ask again every month |
Answers 201 with the expectation, and a Location header pointing at the month it now belongs
to: /v1/profiles/{profileId}/completeness?period=2026-08. There is no route that reads a single
expectation, so the month is where you look to see what you just created.
This answered 200 before 2026-09-11. If your client checks for 200 specifically, it now
sees a 201 and has to accept it. Check for 2xx. See Changelog.
Marking something the recurring rule had already guessed takes over that row rather than adding a
second one, and the answer is still 201: either way the row in the body is standing now and was
not before the request.
isRecurring: true is the useful one. It carries forward, so the rent invoice is asked for again
next month without anyone marking it again.
POST /v1/profiles/{profileId}/completeness/expected/{id}/waive
Says a document is not coming, so it stops appearing as missing. ManageDocuments.
| Field | Type | Required | Means |
|---|---|---|---|
reason |
string or null | no | Why, kept on the row |
stopRecurring |
boolean | no | Also stop asking in future months |
Answers 200 with the expectation, now in state 2.
Waiving is per month, not forever. Next month asks again unless you send
stopRecurring: true. That is the difference between "no invoice this January" and "we dropped
this supplier".
An expectation id that belongs to another client cannot be addressed at all: the service proves the
row belongs to the client in the path before it acts, so a mismatched pair answers 404.
POST /v1/profiles/{profileId}/completeness/recompute
Works the month out again from invoice history, standing marks, and source activity, and answers
with the result. ManageDocuments. Takes the same optional period query parameter.
You should rarely need this. Completeness recomputes on its own as documents arrive. Call it after you have changed something the heuristics read, a merged supplier or a newly connected source, and you do not want to wait.
GET /v1/predictions/missing
Suppliers that are late by their own habits. Different from completeness: this is not "we expect this document", it is "this supplier bills you every month and it is past the day it usually does".
| Parameter | Type | Means |
|---|---|---|
profileId |
uuid | Narrow to one client |
page |
integer | Default 1 |
pageSize |
integer | Default 25, clamped to 200 |
Most overdue first. Paged like everything else, see Conventions.
| Field | Type | Means |
|---|---|---|
profileId |
uuid | The client being billed |
supplierId |
uuid | The supplier |
supplierName |
string or null | Its name, null only if the supplier row is gone |
usualDay |
integer | The day of the month it usually bills by |
expectedByDay |
integer | The day the grace period ran out |
monthsPresent |
integer | How many lookback months it billed in |
lastInvoiceIssueDate |
date | When it last billed this client |
lastInvoiceTotalAmount |
decimal | What that last invoice came to |
currencyCode |
string | The currency of that invoice, ISO 4217 |
There is no sentence in this response, on purpose. The screen writes "Orange usually arrives by the
5th, it is the 12th, and nothing yet" for itself, from usualDay, expectedByDay, and today's
date, in the reader's own language. An API that ships the sentence ships it in one language.
The rule is conservative. A supplier has to have billed in at least four of the lookback months
before anything is said about its silence, usualDay is the 80th percentile of the days it actually
billed on rather than the median, there are two days of grace on top, and expectedByDay never
lands past the 28th so a short February cannot quietly drop a supplier off the list. A supplier the
rule stays quiet about costs an accountant nothing. A supplier it is wrong about means a client gets
told they forgot something they never owed.
profileId narrows within what the caller can already see. A client the caller cannot see is an
empty page, never a 403.
Digest preferences
The digest is the email that says what is still missing. These two routes set how often the caller gets it, in the organization they are working in. It is a personal setting, not a firm one: two people in the same firm can hold different frequencies.
GET /v1/digest-preferences
{ "frequency": 0 }
PUT /v1/digest-preferences
| Field | Type | Required | Means |
|---|---|---|---|
frequency |
DigestFrequency |
yes | 0, 1, or 2 |
| Value | Name | Means |
|---|---|---|
0 |
Weekly | Monday morning, covering the seven days behind it. The default |
1 |
Daily | Every morning, covering the day behind it |
2 |
Off | Nothing |
Answers 200 with the frequency that now stands.
2 is honored without exception. There is no transactional carve-out that keeps sending digests to
somebody who turned them off.