Backfilling history¶
Your automation platform already knows about the runs that happened before you adopted LumaTrack. Import them so the trend lines, cumulative value, and reliability charts start with the truth instead of a cliff.
Two ways in:
- In the app: Automations, Import runs. Paste or upload, see the results per row.
- Over the API:
POST /api/v1/runs/backfill.
Either way, imported runs carry a permanent backfilled provenance flag,
so a chart reader can always tell evidence that arrived live from evidence
that arrived in bulk.
Formats¶
CSV (a template is downloadable in the app at
/automations/import/template.csv):
automation,status,executed_at,duration_seconds,source,external_id
os-patching,success,2026-05-14T02:30:00Z,142,ansible,tower-job-99412
os-patching,failure,2026-05-15T02:30:00Z,98,ansible,tower-job-99511
Or JSONL, one object per line, same fields plus optional metadata:
{"automation": "os-patching", "status": "success", "executed_at": "2026-05-14T02:30:00Z", "external_id": "tower-job-99412", "metadata": {"hosts": 240}}
automation (slug) and executed_at (ISO 8601; naive timestamps are
treated as UTC) are required. A backfill row without a timestamp is
meaningless, so unlike live ingest there is no "default to now".
Over the API¶
curl -s -X POST "$LUMATRACK_URL/api/v1/runs/backfill" \
-H "Authorization: Bearer $LUMATRACK_KEY" \
-H "Content-Type: application/json" \
-d "{\"format\": \"csv\", \"data\": $(jq -Rs . < history.csv)}"
The response is an honest accounting:
{
"processed": 1193,
"created": 1180,
"deduplicated": 12,
"held": 0,
"errors": [
{"line": 481, "error": "March 2026 is closed; its numbers are frozen."}
]
}
processed is the total number of input rows: created (which includes the
held subset) plus deduplicated plus the count of errors. It lets you
reconcile the response against the file you sent in one number.
The rules, in the order each row meets them¶
- The automation slug must exist in your organization.
executed_atmust be present, parse as ISO 8601, and not be in the future.- Rows older than your plan's retention window are refused outright (they would be pruned immediately anyway).
- Rows in frozen months are refused, exactly like live ingest. Closed numbers never move, not even for bulk loads.
external_idreplays dedupe: re-importing the same file is safe and reportsdeduplicatedcounts instead of creating duplicates.- Monthly plan caps apply per target month: rows over a month's cap
are stored as
held, never dropped. - A bad row never aborts the rest of the file. Every rejected row comes
back in
errorswith its line number and the reason.
Rules 5 and 7 together make the sane strategy simple: export everything,
import it all, fix the rows listed in errors, and import the whole file
again.