API Reference
Base URL: http://localhost:8080/api/v1
All request and response bodies are JSON. All endpoints return Content-Type: application/json.
Health
GET /health
Returns 200 if the database is reachable.
Response:
{"status": "ok"}
Returns 503 if the database ping fails.
Reference Data
Reference data (departments and GL accounts) must be created before budget lines or actuals, as they are foreign-key parents.
Departments
POST /department/create
Create a department.
Request body:
{
"code": "ENG",
"name": "Engineering",
"cost_center": "idk",
"active": true
}
GET /department/list
List all departments.
Response:
[
{"id": 1, "code": "ENG", "name": "Engineering", "cost_center": "CC-1100", "active": true},
{"id": 2, "code": "MKT", "name": "Marketing", "cost_center": "CC-1100", "active": true}
]
GET /department/
Getting department by identifier.
Request bodys examples:
Only one may be used, as no conflict is permitted in same request
[
{ "code": "ENG"},
]
[
{ "name": "Marketing"},
]
[
{ "cost_center": "CC-1100"},
]
POST /department/activity
Setting activity by identifier.
Request bodys examples:
Only one may be used, as no conflict is permitted in same request
[
{
"code": "ENG",
"active": false
},
]
[
{
"name": "Marketing",
"active": true
},
]
[
{
"cost_center": "CC-1100",
"active": true
},
]
DELETE /departments/delete
Request body:
{
"id": "2",
}
Delete a department by ID.
GL Accounts
POST /gl-accounts
Create a GL account.
Request body:
{
"code": "6100",
"description": "Salaries & Wages",
"type": "headcount",
"favour_high": false
}
favour_high drives the favourability logic. Set to true for revenue accounts (higher actual = good), false for cost accounts (lower actual = good).
Account types: revenue, cogs, opex, capex, headcount
GET /gl-accounts
List all GL accounts.
DELETE /gl-accounts/delete
Delete a GL account by ID.
Budgets
POST /budget/create
Create a budget line.
Request body:
{
"department_id": 1,
"gl_account_id": 3,
"fiscal_year": 2024,
"fiscal_period": 9,
"version": "original",
"amount": 4200000,
"currency": "DKK",
"notes": "Headcount plan P09"
}
Budget versions: original, forecast1, forecast2, forecast3
PUT /budgets/update
Update a budget line's amount or notes.
Request body:
{
"amount": 4350000,
"notes": "Revised after headcount change"
}
DELETE /budgets/delete
Delete a budget line by ID.
Actuals
POST /actuals/ingest
Upsert an actual. Idempotent by (fiscal_year, fiscal_period, department_id, gl_account_id) - posting the same period twice updates the amount rather than creating a duplicate.
Request body:
{
"department_id": 1,
"gl_account_id": 3,
"fiscal_year": 2024,
"fiscal_period": 9,
"amount": 4380000,
"currency": "DKK",
"source": "SAP_EXPORT"
}
Designed to accept bulk ERP export feeds. POST each line individually or build a batch wrapper around the endpoint.
Variance
Query Parameters (both variance endpoints)
| Parameter | Example | Description |
|---|---|---|
year |
2024 |
Fiscal year (required) |
period |
9 |
Fiscal period 1-12 (required) |
dept |
ENG |
Department code - omit to return all departments |
version |
original |
Budget version (defaults to original) |
GET /variance
Full variance report for the given filters.
Example:
GET /api/v1/variance?year=2024&period=9&dept=ENG
Response:
{
"department": "ENG",
"fiscal_year": 2024,
"fiscal_period": 9,
"version": "original",
"currency": "DKK",
"total_budget": 6740000,
"total_actual": 7074600,
"total_variance": -334600,
"total_variance_pct": -4.97,
"lines": [
{
"gl_code": "6100",
"gl_description": "Salaries & Wages",
"gl_type": "headcount",
"budget": 4200000,
"actual": 4380000,
"variance_abs": -180000,
"variance_pct": -4.29,
"status": "unfavourable",
"currency": "DKK"
},
{
"gl_code": "5000",
"gl_description": "Cloud Infrastructure",
"gl_type": "cogs",
"budget": 850000,
"actual": 791000,
"variance_abs": 59000,
"variance_pct": 6.94,
"status": "favourable",
"currency": "DKK"
}
]
}
variance_abs = actual - budget. Positive means over-budget for costs, under-budget for revenue.
status is "favourable" or "unfavourable" based on the account's favour_high flag - see Finance Concepts.
GET /variance/alerts
Returns only the GL lines where absolute variance percentage exceeds the threshold.
Additional query parameter:
| Parameter | Example | Description |
|---|---|---|
threshold |
15 |
Alert threshold as a percentage (default: 10) |
Example:
GET /api/v1/variance/alerts?year=2024&period=9&threshold=15
Returns the same line-level structure as the full variance report, filtered to lines where abs(variance_pct) > threshold.