Bulk Import
Wayd can load planning data in bulk from CSV files through three import endpoints — one for
planning intervals, one for the
objectives on them, and one for risks. Each is a
single POST of one CSV file (multipart/form-data, field name file), and each is gated by a
permission.
Every import described here can be run from Settings → System → Imports,
which lists each file's columns and hands over a template. Files can also be posted directly to the
endpoints below, for example with curl or a script. Exact request/response shapes are also in the
API reference the API serves at /scalar.
However it was submitted, the run is visible in Settings → System → Imports, where you can see what became of each row and act on a run that did not finish. See Imports.
Unlike the PPM and Organization imports, which resolve everything by human-readable natural key, these reference records by GUID — teams, people and planning intervals — and use numeric ids for status, category, impact and likelihood.
A file therefore cannot be authored by hand without first looking those values up, typically from the API. Treat these as machine-generated formats rather than something to type out.
How imports work
A few rules apply to all three:
- A successful post returns the run, not the records it created. The file is applied by a
background worker, and the post waits a few seconds for it: a small file comes back finished
(
200 OK) with its counts, and a large one comes back still queued or running (202 Accepted) with aLocationto follow. Either way the body is the same run — follow it in Settings → System → Imports, or by its id against the import endpoints. - A file can be checked before it is imported. Add
validateOnly=trueto the post, or choose Check File in Settings → System → Imports, and every row goes through the import's own checks without anything being created: each comes back passed or rejected with its reason, and an all-or-nothing file reports every rejection rather than stopping at the first step that finds one. It is advice, not a promise — the data can change before you import — and a finished check can be imported for real from its page without posting the file again. - Files posted together can be grouped. Every endpoint takes an optional
submissionGroupIdquery parameter — any GUID you choose — and record it on the run, so a set of files posted under one value shows as one batch in Settings → System → Imports, with summed counts and each run's page linking to the others. Leave it out to submit a file on its own. An empty GUID is rejected rather than treated as a group. - A malformed file is still rejected whole, before anything is queued. Every row is validated on the way in, so a file with a bad row never becomes a run. What happens once rows start being applied is covered under what a failed run leaves behind.
- Every column must be present, even the optional ones. Required in the tables below means the
cell needs a value; it does not mean the column can be left out. A missing header fails the file with
Header with name 'X' was not found. Include every column and leave unused cells blank. ImportIdis your key for a row. Any value you like — a row number, a key from whatever system the file came out of — provided it is unique within the one file, compared case-insensitively. Row outcomes are reported against it. Leave the column blank and the row's position in the file is used instead, so a hand-authored file works without it.ImportIdis not written onto what the import creates. It identifies a row of your file, not a record in Wayd. The durable link runs the other way: each applied row reports the id of the record it created.- Instant columns are UTC and use ISO 8601 (
2026-01-15T00:00:00Z). Date-only columns takeyyyy-MM-dd— a date, not a timestamp: a cell carrying a time is rejected rather than truncated. - A missing file returns 422, not 400 — the file is a validation failure like any other.
What a failed run leaves behind
All three imports are all-or-nothing. The file is applied as one unit, so a rejected row means none of it is written. Correct the file and submit it again.
You do not need to work out what happened by re-posting. Open the run in Settings → System → Imports to see each row's outcome, then:
- Retry rejected rows once the underlying problem is fixed. Rows that already succeeded are never reapplied, so this cannot duplicate them.
- Resume a run that stopped partway — cancelled, or interrupted — to apply only the rows it never reached.
Retrying a row re-applies the data you submitted, which is held for 30 days after a run finishes and then discarded. After that a row can no longer be retried and the file must be submitted again.
Permissions
| Import | Permission |
|---|---|
| Planning intervals | Import Planning Intervals |
| Planning interval objectives | Import Planning Interval Objectives |
| Risks | Import Risks |
Limits
| Import | Rows per file | Applied |
|---|---|---|
| Planning intervals | 10,000 | All-or-nothing |
| Planning interval objectives | 10,000 | All-or-nothing |
| Risks | 10,000 | All-or-nothing |
A file over its cap is rejected before anything is queued, with a message naming the cap. Split a larger
file and post the parts one at a time, waiting for each run; one submissionGroupId across them keeps the
runs findable as one import. An all-or-nothing file is one database transaction, and 10,000 rows bounds it.
The rules every file shares — request size, ImportId length, how rows are chunked, how long the post
waits, retries, stalls and retention — are in Settings → System → Imports → Limits and rules.
Planning intervals import
POST /api/planning/planning-intervals/import
Loads planning intervals together with the teams that ran them. Planning intervals belong to an agile release train rather than to the organization, so a customer arriving with history brings dozens of them — one row each, with no second file for the teams.
| Column | Required | Notes |
|---|---|---|
ImportId | No | Your key for the row; outcomes are reported against it. Unique within the file. Defaults to the row's position when blank. |
Name | Yes | Max 128 chars. Unique within the file, and rejected if an interval already has it. |
Description | No | Max 2048 chars. |
Start | Yes | yyyy-MM-dd. |
End | Yes | yyyy-MM-dd. On or after Start. |
IterationWeeks | Yes | Length of each generated iteration, in whole weeks. Greater than 0. |
IterationPrefix | No | Prefixes each generated iteration's name. Max 32 chars. |
TeamIds | No | Semicolon-separated GUIDs of the teams that ran the interval. See below — blank means no teams. |
This import only creates, so TeamIds never replaces a roster
A row whose Name already exists is rejected, and since the file is all-or-nothing that rejects the
whole file. Nothing is ever updated. That is what makes TeamIds safe to put on the same row: it can
only be the roster a brand-new interval starts with.
So a blank TeamIds leaves the new interval with no teams. It does not clear anything, because there
is nothing there to clear. Add the teams afterwards with Manage Teams on the interval, or fix the
column and submit the file again.
A row naming a team Wayd has not finished replicating out of Organization is rejected rather than dropped, and names the ids it could not find. Import teams before intervals, and give a team created moments earlier a few seconds to land.
Iterations are generated from the cadence, not imported
There is no column for iterations. Each interval's are generated from IterationWeeks and
IterationPrefix the way a hand-created interval's are: iterations of that length running from Start,
named {IterationPrefix}{number}, with the last one truncated to End and categorized Innovation and
Planning.
An interval whose real iterations ran to irregular lengths, or were named something else entirely, will therefore not import faithfully. Correct it afterwards with Manage Dates on that interval, which is the only place iteration names, categories and date ranges are editable.
Planning interval objectives import
POST /api/planning/planning-intervals/objectives/import
Loads objectives onto planning intervals that already exist —
created by hand or by the planning intervals import above. Each row names
its own interval in PlanningIntervalId, so one file can carry objectives for as many intervals as you
have: an organization's whole planning history goes in a single file rather than one per interval.
Because the file is all-or-nothing, a row naming an interval or a team that does not exist, or an interval whose objectives are locked, rejects the whole file — including the rows for every other interval in it.
| Column | Required | Notes |
|---|---|---|
ImportId | No | Your key for the row; outcomes are reported against it. Unique within the file. Defaults to the row's position when blank. |
PlanningIntervalId | Yes | GUID of the planning interval the objective belongs to. Rows may name different intervals. |
TeamId | Yes | GUID of the team the objective belongs to. |
Name | Yes | Max 256 chars. |
Description | No | Max 1024 chars. |
StatusId | Yes | 1 Not Started · 2 In Progress · 3 Completed · 4 Canceled · 5 Missed. |
Progress | Yes | Percent complete, 0–100. |
StartDate | No | yyyy-MM-dd. Must be before TargetDate where both are given. |
TargetDate | No | yyyy-MM-dd. |
IsStretch | Yes | true or false. |
ClosedDateUtc | Conditional | UTC instant. Required when the status is Completed, Canceled or Missed, and must be empty otherwise. |
Order | No | Display position within the team's objectives. |
Risks import
POST /api/planning/risks/import
Loads risks. A row naming a team, reporter or assignee that Wayd does not have is rejected, which, as the file is all-or-nothing, rejects the whole file.
| Column | Required | Notes |
|---|---|---|
ImportId | No | Your key for the row; outcomes are reported against it. Unique within the file. Defaults to the row's position when blank. |
TeamId | Yes | GUID of the team the risk belongs to. |
Summary | Yes | Max 256 chars. |
Description | No | Max 1024 chars. |
ReportedOnUtc | Yes | UTC instant, and must be in the past. |
ReportedById | Yes | GUID of the employee who reported it. Not nullable — a blank cell fails the file. |
StatusId | Yes | 1 Open · 2 Closed. |
CategoryId | Yes | 1 Resolved · 2 Owned · 3 Accepted · 4 Mitigated. |
ImpactId | Yes | 1 Low · 2 Medium · 3 High. |
LikelihoodId | Yes | 1 Low · 2 Medium · 3 High. |
AssigneeId | No | GUID of the employee it is assigned to. |
FollowUpDate | No | yyyy-MM-dd. |
Response | No | Max 1024 chars. |
ClosedDateUtc | Conditional | Required when StatusId is 2 (Closed), and must be empty otherwise. Must be after ReportedOnUtc and in the past. |
Related
- Planning Intervals — the intervals and objectives these imports populate.
- Risks — the risk register this import populates.
- PPM Bulk Import — portfolios, programs, projects and initiatives.
- Organization Bulk Import — employees, teams and staffing.
- Product Management Bulk Import — the product catalog.