Close tracker
The close calendar of an entity (owner, the working day each step is due on), its own month-end tasks and the close status of a period: the steps, the tasks, who did what and what is overdue.
| Operation | Summary | Scope | Idempotency-Key |
|---|---|---|---|
PATCH /v1/close-tasks/{task_id} |
Change a close task | draft |
|
GET /v1/entities/{entity}/close-schedule |
Get the close calendar of an entity | read |
|
PUT /v1/entities/{entity}/close-schedule |
Set the close calendar of an entity | draft |
|
GET /v1/entities/{entity}/close-tasks |
List the close tasks of an entity | read |
|
POST /v1/entities/{entity}/close-tasks |
Add a close task | draft |
required |
GET /v1/periods/{period_id}/close-status |
Get where the close of a period stands | read |
|
POST /v1/periods/{period_id}/close-tasks/{task_id}/complete |
Record that a close task was done | draft |
required |
POST /v1/periods/{period_id}/close-tasks/{task_id}/withdraw |
Withdraw that a close task was done | draft |
PATCH /v1/close-tasks/{task_id}
Change a close task
Change a task's title, owner, working day or check, or take it out of the close
(active: false; a task is never deleted, so the periods it was done for keep it). Omitted
fields keep their value. A person's act (manage_close). 404 when it does not exist.
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | update_close_task_v1_close_tasks__task_id__patch |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | task_id |
string (uuid) | yes | Id of the close task (GET /v1/entities/{entity}/close-tasks). |
Request body
application/json (required): TaskUpdate
| Field | Type | Required | Description |
|---|---|---|---|
title |
string or null | New title. (min length 1; max length 200) | |
owner_user_id |
string (uuid) or null | New owner; null: the close owner. | |
due_day |
integer or null | New working day. (min 0; max 30) | |
check_id |
string or null | New check; null for none. (pattern ^[a-z][a-z0-9_]{1,63}$) |
|
active |
boolean or null | false takes the task out of the close of the open periods for good (a task is never deleted, and a deactivated task is not reactivated: 409; create a new task). |
Example request
curl -X PATCH "$ERP_URL/v1/close-tasks/8e3f4051-6273-4c84-9d95-afb6c7d8e920" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Content-Type: application/json" \
-d '{
"due_day": 3
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | TaskOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 409 | The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"title": "string",
"owner": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"email": "string",
"name": "string"
},
"due_day": 1,
"check_id": "string",
"active": true,
"created_by": {},
"created_at": "2026-09-21T09:30:00Z"
}
GET /v1/entities/{entity}/close-schedule
Get the close calendar of an entity
Who owns the entity's month-end close and the working day after the period end each step
is due on (the defaults until the entity sets its own: configured is false). Read-only.
| Required scope | read (admin implies every scope) |
| Operation id | get_close_schedule_v1_entities__entity__close_schedule_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
Example request
curl "$ERP_URL/v1/entities/CH01/close-schedule" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | ScheduleOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"automated_steps": [
"recurring_invoices",
"checklist"
],
"configured": true,
"entity": "CH01",
"owner": {
"email": "alice@example.com",
"id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"name": "Alice"
},
"steps": [
{
"due_day": 0,
"step": "recurring_invoices",
"title": "Recurring invoices made"
},
{
"due_day": 1,
"step": "depreciation",
"title": "Depreciation run"
}
],
"updated_at": "2026-10-04T08:00:00Z",
"updated_by": {
"id": "user:alice",
"name": "Alice",
"type": "human"
}
}
PUT /v1/entities/{entity}/close-schedule
Set the close calendar of an entity
Set the close owner, the working days of the steps and which steps the scheduled close
job runs for the entity (automated_steps: recurring_invoices, bank_matching,
depreciation, fx_revaluation, deferred_tax, checklist, close_review and
forecast_roll; empty, the default, leaves the entity to people); what the body leaves out
keeps its value. The job runs once a day, as the organization's system actor, and never
approves or closes anything; taking a step out stops it at the next run. A person's act:
the policy action manage_close denies agents (403, naming the rule). 422 for an owner who is
not a person of the organization, a day outside 0 to 30, or a step the job does not run.
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | set_close_schedule_v1_entities__entity__close_schedule_put |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
Request body
application/json (required): ScheduleInput
| Field | Type | Required | Description |
|---|---|---|---|
owner_user_id |
string (uuid) or null | The person who owns the close (GET /v1/people); null for nobody. Omitted: unchanged. |
|
step_days |
map of integer or null | The working day after the period end each step is due on (0: the period's last day; Monday to Friday count). Only the steps named change; omitted: unchanged. | |
automated_steps |
array of "bank_matching" | "checklist" | "close_review" | "deferred_tax" | "depreciation" | "forecast_roll" | "fx_revaluation" | "recurring_invoices" or null |
The steps the scheduled close job runs for the entity: every day from a period's last day until it is closed, recurring_invoices (the invoices due through the period end), bank_matching (the deterministic matcher over the statement lines booked through the period end), depreciation, fx_revaluation and deferred_tax (each period-end run once, from its due day, when the checklist says it is due; what it posts waits for a person under the policy rule close_job_runs_need_a_person), checklist (the close checklist, kept in the job's log) and close_review (the close review agent writes the period's close memo, once, when no check fails or from the step's due day; it reads and records issues, it runs no adjustment); and once a period is closed, forecast_roll: a new draft of each forecast of the entity that has no version made since the latest close (POST /v1/plans/{plan_id}/roll with its defaults; a person reviews and approves it). The job never approves or closes anything. Empty: the job leaves the entity alone (the default). Omitted: unchanged. |
|
step_owners |
map of string (uuid) or null or null | Who does each step (GET /v1/people); null for the close owner. Only the steps named change; omitted: unchanged. |
Example request
curl -X PUT "$ERP_URL/v1/entities/CH01/close-schedule" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Content-Type: application/json" \
-d '{
"owner_user_id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"step_days": {
"close": 4,
"depreciation": 1
}
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | ScheduleOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 409 | The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"automated_steps": [
"recurring_invoices",
"checklist"
],
"configured": true,
"entity": "CH01",
"owner": {
"email": "alice@example.com",
"id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"name": "Alice"
},
"steps": [
{
"due_day": 0,
"step": "recurring_invoices",
"title": "Recurring invoices made"
},
{
"due_day": 1,
"step": "depreciation",
"title": "Depreciation run"
}
],
"updated_at": "2026-10-04T08:00:00Z",
"updated_by": {
"id": "user:alice",
"name": "Alice",
"type": "human"
}
}
GET /v1/entities/{entity}/close-tasks
List the close tasks of an entity
The entity's own month-end tasks, by the working day they are due on. Read-only.
| Required scope | read (admin implies every scope) |
| Operation id | list_close_tasks_v1_entities__entity__close_tasks_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
| query | include_inactive |
boolean | Also the tasks that are no longer part of the close. (default false) |
|
| query | limit |
integer | How many tasks, the earliest due first. (min 1; max 1000; default 200) |
Example request
curl "$ERP_URL/v1/entities/CH01/close-tasks" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | array of TaskOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
[
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"title": "string",
"owner": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"email": "string",
"name": null
},
"due_day": 1,
"check_id": "string",
"active": true,
"created_by": {},
"created_at": "2026-09-21T09:30:00Z"
}
]
POST /v1/entities/{entity}/close-tasks
Add a close task
A task of every month end of the entity: what, who and on which working day after the
period end. With check_id the task is decided by that close check (done when it passes or
does not apply), so nobody marks it done by hand. A person's act (manage_close). 422 for
an unknown check or an owner outside the organization. Needs an Idempotency-Key.
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | create_close_task_v1_entities__entity__close_tasks_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
Request body
application/json (required): TaskInput
| Field | Type | Required | Description |
|---|---|---|---|
title |
string | yes | What is to be done. (min length 1; max length 200) |
owner_user_id |
string (uuid) or null | Who does it (GET /v1/people); null: the close owner. |
|
due_day |
integer | yes | The working day after the period end it is due on (0: the last day). (min 0; max 30) |
check_id |
string or null | A close check that decides the task (the id of a check of GET /v1/periods/{period_id}/close-checklist): the task is done when the check passes or does not apply, and nobody marks it done by hand. Null for a task a person marks done. (pattern ^[a-z][a-z0-9_]{1,63}$) |
Example request
curl -X POST "$ERP_URL/v1/entities/CH01/close-tasks" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"due_day": 2,
"owner_user_id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"title": "Reconcile the credit card account"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 | Created | TaskOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 409 | The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"title": "string",
"owner": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"email": "string",
"name": "string"
},
"due_day": 1,
"check_id": "string",
"active": true,
"created_by": {},
"created_at": "2026-09-21T09:30:00Z"
}
GET /v1/periods/{period_id}/close-status
Get where the close of a period stands
The close of one period: each step (recurring invoices, depreciation, FX revaluation,
deferred tax, the checklist, the close) and each of the entity's own tasks with the day it is
due and whether it is done, open, overdue or n/a, who did a task and with what
evidence, the checklist's failing checks, and what the scheduled close job did for the period
(job_log). The close checklist decides every step and every task linked to a check, so this
never says done over a failing check. Read-only.
| Required scope | read (admin implies every scope) |
| Operation id | get_close_status_v1_periods__period_id__close_status_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | period_id |
string (uuid) | yes | Fiscal period id (GET /v1/entities/{entity}/periods). |
| query | as_of |
string (date) or null | The day overdue is judged on (ISO 8601); default: today where the entity is. |
Example request
curl "$ERP_URL/v1/periods/3fa85f64-5717-4562-b3fc-2c963f66afa6/close-status" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | CloseStatus |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"as_of": "2026-10-02",
"done": 4,
"entity": "CH01",
"failing_checks": [
"bank_reconciled"
],
"overdue": 1,
"owner": {
"email": "alice@example.com",
"id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"name": "Alice"
},
"period": "2026-09",
"period_end": "2026-09-30",
"period_id": "4f5e6d7c-8b9a-4c0d-9e1f-2a3b4c5d6e7f",
"period_status": "open",
"ready": false,
"steps": [
{
"detail": "3 assets due",
"due_date": "2026-10-01",
"status": "done",
"step": "depreciation",
"title": "Depreciation run"
}
],
"tasks": [
{
"detail": "not done yet",
"due_date": "2026-10-02",
"status": "open",
"task": {
"active": true,
"created_at": "2026-09-01T08:00:00Z",
"created_by": {
"id": "user:alice",
"name": "Alice",
"type": "human"
},
"due_day": 2,
"entity": "CH01",
"id": "8e3f4051-6273-4c84-9d95-afb6c7d8e920",
"title": "Reconcile the credit card account"
}
}
],
"total": 7
}
POST /v1/periods/{period_id}/close-tasks/{task_id}/complete
Record that a close task was done
Record that a person did the task for the period, with what shows it (evidence). A
person's act (manage_close): the record is evidence and is never deleted, only withdrawn.
409 for a task decided by a close check, a task no longer part of the close, one already
done for the period, or a closed period (close_on_record: its close stays as it was closed;
reopen the period to record the task); 404 when the task is not one of the period's entity.
Needs an Idempotency-Key.
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | complete_close_task_v1_periods__period_id__close_tasks__task_id__complete_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | period_id |
string (uuid) | yes | Fiscal period id (GET /v1/entities/{entity}/periods). |
| path | task_id |
string (uuid) | yes | Id of the close task (GET /v1/entities/{entity}/close-tasks). |
Request body
application/json (required): CompletionInput
| Field | Type | Required | Description |
|---|---|---|---|
evidence |
string or null | What shows it was done (a reference or a link). (max length 1000) | |
note |
string or null | A note. (max length 1000) |
Example request
curl -X POST "$ERP_URL/v1/periods/3fa85f64-5717-4562-b3fc-2c963f66afa6/close-tasks/8e3f4051-6273-4c84-9d95-afb6c7d8e920/complete" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"evidence": "Statement 2026-09 reconciled, workpaper CC-09",
"note": "Two items carried forward"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | TaskStatus |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 409 | The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"task": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"title": "string",
"owner": {
"id": null,
"email": null,
"name": null
},
"due_day": 1,
"check_id": "string",
"active": true,
"created_by": {},
"created_at": "2026-09-21T09:30:00Z"
},
"due_date": "2026-09-21",
"status": "done",
"detail": "string",
"reason": {
"words": "string",
"code": "string",
"details": {},
"text": "string"
},
"completion": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"evidence": "string",
"note": "string",
"completed_by": {},
"completed_at": "2026-09-21T09:30:00Z"
}
}
POST /v1/periods/{period_id}/close-tasks/{task_id}/withdraw
Withdraw that a close task was done
Take back a completion, with the reason: it stays on record as withdrawn and the task is
open again for the period. A person's act (manage_close). 409 when the task is not done
for the period, or the period is closed (close_on_record).
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | withdraw_close_task_v1_periods__period_id__close_tasks__task_id__withdraw_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | period_id |
string (uuid) | yes | Fiscal period id (GET /v1/entities/{entity}/periods). |
| path | task_id |
string (uuid) | yes | Id of the close task (GET /v1/entities/{entity}/close-tasks). |
Request body
application/json (required): WithdrawInput
| Field | Type | Required | Description |
|---|---|---|---|
reason |
string | yes | Why it is withdrawn. (min length 3; max length 1000) |
Example request
curl -X POST "$ERP_URL/v1/periods/3fa85f64-5717-4562-b3fc-2c963f66afa6/close-tasks/8e3f4051-6273-4c84-9d95-afb6c7d8e920/withdraw" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Content-Type: application/json" \
-d '{
"reason": "Reconciliation found a missing statement"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | TaskStatus |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 409 | The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"task": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"title": "string",
"owner": {
"id": null,
"email": null,
"name": null
},
"due_day": 1,
"check_id": "string",
"active": true,
"created_by": {},
"created_at": "2026-09-21T09:30:00Z"
},
"due_date": "2026-09-21",
"status": "done",
"detail": "string",
"reason": {
"words": "string",
"code": "string",
"details": {},
"text": "string"
},
"completion": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"evidence": "string",
"note": "string",
"completed_by": {},
"completed_at": "2026-09-21T09:30:00Z"
}
}
Schemas
CloseStatus
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Entity code. |
period |
string | yes | The period as YYYY-NN. |
period_id |
string (uuid) | yes | Period id. |
period_end |
string (date) | yes | The period's last day: working day 0. |
period_status |
string | yes | open or closed. |
as_of |
string (date) | yes | The day overdue is judged on. |
owner |
Person or null |
yes | Who owns the close. |
ready |
boolean | yes | The close checklist has no failing check. |
failing_checks |
array of string | yes | The ids of the checks that fail. |
done |
integer | yes | Steps and tasks that are done or do not apply. |
total |
integer | yes | Steps and tasks. |
overdue |
integer | yes | Steps and tasks that are overdue. |
steps |
array of StepStatus |
yes | The steps, in the order of the close. |
tasks |
array of TaskStatus |
yes | The entity's tasks: the active ones, and an inactive one done for this period. |
job_log |
array of JobLogEntry |
What the scheduled close job did for this period, the latest first (at most 60 entries). |
Completion
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | Completion id. |
evidence |
string or null | yes | What shows it was done. |
note |
string or null | yes | The note. |
completed_by |
object | yes | Who did it (actor object). |
completed_at |
string (date-time) | yes | When it was recorded (UTC). |
CompletionInput
| Field | Type | Required | Description |
|---|---|---|---|
evidence |
string or null | What shows it was done (a reference or a link). (max length 1000) | |
note |
string or null | A note. (max length 1000) |
JobLogEntry
| Field | Type | Required | Description |
|---|---|---|---|
step |
"bank_matching" | "checklist" | "close_review" | "deferred_tax" | "depreciation" | "forecast_roll" | "fx_revaluation" | "recurring_invoices" |
yes | The step the job ran. |
run_date |
string (date) | yes | The day it ran for: today where the entity is. |
outcome |
"done" | "failed" |
yes | done, or failed (the next run tries again). |
attempts |
integer | yes | How many times it was tried that day. |
summary |
object | yes | What it made: for recurring_invoices made, failed and more_due; for checklist ready, the failing check ids and the checks counted by status. |
error |
string or null | yes | Why it failed. |
finished_at |
string (date-time) | yes | When the last attempt ended (UTC). |
ScheduleInput
| Field | Type | Required | Description |
|---|---|---|---|
owner_user_id |
string (uuid) or null | The person who owns the close (GET /v1/people); null for nobody. Omitted: unchanged. |
|
step_days |
map of integer or null | The working day after the period end each step is due on (0: the period's last day; Monday to Friday count). Only the steps named change; omitted: unchanged. | |
automated_steps |
array of "bank_matching" | "checklist" | "close_review" | "deferred_tax" | "depreciation" | "forecast_roll" | "fx_revaluation" | "recurring_invoices" or null |
The steps the scheduled close job runs for the entity: every day from a period's last day until it is closed, recurring_invoices (the invoices due through the period end), bank_matching (the deterministic matcher over the statement lines booked through the period end), depreciation, fx_revaluation and deferred_tax (each period-end run once, from its due day, when the checklist says it is due; what it posts waits for a person under the policy rule close_job_runs_need_a_person), checklist (the close checklist, kept in the job's log) and close_review (the close review agent writes the period's close memo, once, when no check fails or from the step's due day; it reads and records issues, it runs no adjustment); and once a period is closed, forecast_roll: a new draft of each forecast of the entity that has no version made since the latest close (POST /v1/plans/{plan_id}/roll with its defaults; a person reviews and approves it). The job never approves or closes anything. Empty: the job leaves the entity alone (the default). Omitted: unchanged. |
|
step_owners |
map of string (uuid) or null or null | Who does each step (GET /v1/people); null for the close owner. Only the steps named change; omitted: unchanged. |
ScheduleOut
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Entity code. |
configured |
boolean | yes | false while the entity has no schedule of its own: the defaults are shown. |
owner |
Person or null |
yes | Who owns the close. |
steps |
array of StepDay |
yes | Every step, in the order of the close. |
automated_steps |
array of "bank_matching" | "checklist" | "close_review" | "deferred_tax" | "depreciation" | "forecast_roll" | "fx_revaluation" | "recurring_invoices" |
The steps the scheduled close job runs; empty: none. | |
updated_by |
object or null | yes | Who set the schedule last. |
updated_at |
string (date-time) or null | yes | When (UTC). |
StepDay
| Field | Type | Required | Description |
|---|---|---|---|
step |
"checklist" | "close" | "close_review" | "deferred_tax" | "depreciation" | "forecast_roll" | "fx_revaluation" | "recurring_invoices" |
yes | The step. |
title |
string | yes | What the step is. |
due_day |
integer | yes | The working day after the period end it is due on. |
owner |
Person or null |
Who does the step; null: the close owner. |
StepStatus
| Field | Type | Required | Description |
|---|---|---|---|
step |
"checklist" | "close" | "close_review" | "deferred_tax" | "depreciation" | "forecast_roll" | "fx_revaluation" | "recurring_invoices" |
yes | The step. |
title |
string | yes | What the step is. |
due_date |
string (date) | yes | The day it is due. |
status |
"done" | "n/a" | "open" | "overdue" |
yes | done, open (not done, not yet due), overdue (not done and its due date has passed) or n/a (nothing to do this period). |
detail |
string | yes | Why, in words (the check's result where a check decides it). |
reason |
Reason or null |
detail by a stable code, for the screens to word in the person's language (closeTracker.result, or the deciding check's periodClose.result); null where the check gave no code. |
|
owner |
Person or null |
Who does the step: its own owner, else the close owner. | |
done_by |
object or null | Who did it (actor object), where a record says so: the run of a period-end step, the close memo, the close, the latest forecast version; null otherwise. | |
done_at |
string (date-time) or null | When (UTC). |
TaskInput
| Field | Type | Required | Description |
|---|---|---|---|
title |
string | yes | What is to be done. (min length 1; max length 200) |
owner_user_id |
string (uuid) or null | Who does it (GET /v1/people); null: the close owner. |
|
due_day |
integer | yes | The working day after the period end it is due on (0: the last day). (min 0; max 30) |
check_id |
string or null | A close check that decides the task (the id of a check of GET /v1/periods/{period_id}/close-checklist): the task is done when the check passes or does not apply, and nobody marks it done by hand. Null for a task a person marks done. (pattern ^[a-z][a-z0-9_]{1,63}$) |
TaskOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | Task id. |
entity |
string | yes | Entity code. |
title |
string | yes | What is to be done. |
owner |
Person or null |
yes | Who does it; null: the close owner. |
due_day |
integer | yes | The working day after the period end it is due on. |
check_id |
string or null | yes | The close check that decides it. |
active |
boolean | yes | Part of the close. |
created_by |
object | yes | Who made it. |
created_at |
string (date-time) | yes | When (UTC). |
TaskStatus
| Field | Type | Required | Description |
|---|---|---|---|
task |
TaskOut |
yes | The task. |
due_date |
string (date) | yes | The day it is due. |
status |
"done" | "n/a" | "open" | "overdue" |
yes | done, open (not done, not yet due), overdue (not done and its due date has passed) or n/a (nothing to do this period). |
detail |
string | yes | Why, in words. |
reason |
Reason or null |
detail by a stable code (closeTracker.result, or the deciding check's periodClose.result); null where the check gave no code. |
|
completion |
Completion or null |
yes | Who did it and with what evidence (a task a person marks done). |
TaskUpdate
| Field | Type | Required | Description |
|---|---|---|---|
title |
string or null | New title. (min length 1; max length 200) | |
owner_user_id |
string (uuid) or null | New owner; null: the close owner. | |
due_day |
integer or null | New working day. (min 0; max 30) | |
check_id |
string or null | New check; null for none. (pattern ^[a-z][a-z0-9_]{1,63}$) |
|
active |
boolean or null | false takes the task out of the close of the open periods for good (a task is never deleted, and a deactivated task is not reactivated: 409; create a new task). |
WithdrawInput
| Field | Type | Required | Description |
|---|---|---|---|
reason |
string | yes | Why it is withdrawn. (min length 3; max length 1000) |