Start Bordereau Export Run
Starts a bordereau export run: a background job that produces the FULL
transaction-level premium bordereau as a CSV file, however large it is.
Returns immediately with a runId — poll
GET /policies/bordereau/export-runs/{runId} until the run succeeds,
then fetch the file from
GET /policies/bordereau/export-runs/{runId}/download (CSV or Excel),
or deliver it to Google Drive as a spreadsheet via
POST /policies/bordereau/export-runs/{runId}/drive.
This run family replaces the retired synchronous bordereau CSV download
and Google Sheets export endpoints. Unlike those, a run has no
limit/offset and no row cap: it streams the whole result set into a
stored file in the background.
The request body is OPTIONAL
The body is the bordereau export request:
periodStart/periodEnd— scope to transactions whosetransactionTimestampfalls within the half-open interval[periodStart, periodEnd).actions— transaction types to include (e.g. onlyNEW_BUSINESSandENDORSE).columns— an ordered array that COMPLETELY describes the output columns (seeBordereauColumnSpec): fixed columns become selectable, omittable, and reorderable, and field columns interleave anywhere. Omit it (or send no body at all) for the default column set (the fixed 12): Policy Number, Insured Name, Transaction Action, Policy Version, Effective Date, Transaction Timestamp, Policy Start Date, Policy End Date, Created At, Created By, Policy Premium, Policy Premium Change.filters— policy filters selecting which policies contribute transactions.sortBy/sortDirection— row order.
A body that arrives and cannot be read as JSON is a 400
(UNREADABLE_REQUEST_BODY) — it is never treated as an omitted body,
because that would export different columns and rows than the ones asked
for.
The run snapshots the request AND the company’s current configuration version when it starts, so what a queued export produces cannot drift: a configuration import landing mid-run does not change the file.
Duplicate starts collapse onto the run already in flight
If a run is already in flight (queued or running) producing this exact
export — same request, same configuration version — this endpoint does not
start a second one: it returns 200 with that run’s id and
outcome: "duplicate". That is a normal, successful answer, not an error.
Pass ?force=true to start a new run anyway. A run that already
finished is never deduplicated onto — re-asking for the same export is
how you refresh the file.
At most 3 runs in flight per company
A company may have 3 export runs in flight at once — bordereau and
entity export runs share the limit. A 4th distinct request is refused with
a 429 whose body carries inFlightRunIds — poll those instead of
retrying blind. ?force=true does not lift this limit.
Required permission: company.policy:read
Authorizations
User-principal OAuth 2.0 Bearer authentication. Send a user-scoped Auth0 access token (audience = the app API audience) as Authorization: Bearer <jwt>. The request resolves to the user's identity and is authorized by their Role on the {companyId} in the path — the same role-based permissions the web app enforces. This is the path the MCP connector uses to act on a user's behalf; endpoints that accept it list both BearerAuth and ApiKeyAuth.
Path Parameters
Company identifier
Query Parameters
When true, start a new run even if a run is already in flight producing
this exact export — i.e. skip the duplicate check and always mint a fresh
run. Any value other than the literal true (including omission) is
treated as false. This does not raise the limit of 3 runs in flight
per company.
Body
OPTIONAL. The bordereau export request; omit it entirely (or send {})
to export the default column set (the fixed 12) for the whole book. A
body that cannot be read as JSON is a 400, never a silent default
export.
Inclusive lower bound on transactionTimestamp. Only
transactions at or after this timestamp are included.
Exclusive upper bound on transactionTimestamp. Only
transactions before this timestamp are included.
Transaction actions to include. Valid values: NEW_BUSINESS,
ENDORSE, CANCEL, REINSTATE, RENEW. Omit to include all
actions.
Note: Unrecognized action values are not rejected — they silently match zero rows. Double-check spelling if results are unexpectedly empty.
Ordered column specs that COMPLETELY describe the output
columns: fixed columns become selectable, omittable, and
reorderable, and field columns ({"kind":"field"} entries —
a dot-path into the policy's field data plus a display header,
resolved per row as of the transaction's effective date)
interleave anywhere. A path that does not exist in the policy
data yields an empty cell. Omit for the default column set
(the fixed 12).
One column of a fully caller-ordered bordereau — the single column-selection
mechanism on every bordereau endpoint. On the export-run surface (POST .../policies/bordereau/export-runs, whose finished file downloads as CSV or
Excel and can be delivered to Google Drive) an ordered columns array is the
COMPLETE column set: the fixed columns become selectable, omittable, and
reorderable, and configured field columns (resolved per row as of the
transaction's effective date) interleave anywhere. On the typed-JSON list
endpoint only
field entries are accepted (they select into each row's fieldColumns
map); a fixed entry there is rejected with HTTP 400, since the JSON rows
always carry every fixed property.
Two kinds:
fixed— one of the 12 built-in transaction-metadata columns, addressed by key; the column header comes from the built-in registry (e.g.policyNumber→ "Policy Number").field— a configured bordereau column, addressed by its declared columnkey, plus the displayheaderfor the column. A column the company has not declared on its bordereau surface is ignored; a column whose value cannot be produced yields an empty cell.
- Option 1
- Option 2
Policy filters restricting the export to transactions of the
policies whose CURRENT segment matches every filter — the filter
selects the policies, then all of each matching policy's
transactions in the period are included. Tenant filters must use
fieldType: "declared" and a policy-surface listFilters key.
Fixed system filters remain explicit; implicit field-derived
forms fail at admission with 400 implicit-filters-disabled
(no run is persisted or enqueued). Omit to include every policy.
A filter accepted by FMV1 external list, version, and export endpoints.
Tenant-field filters must use fieldType: declared and the key of a
listFilters definition on the surface being queried. The definition supplies
the SQL expression, result type, and permitted operators; the request supplies
only its key, an exposed operator, and an operand of that operator's shape.
The fixed systemUser, systemDate, and systemId forms remain explicit.
Implicit field-derived forms such as text, number, optionSet, or join
are rejected with 400 and code implicit-filters-disabled.
- Option 1
- Option 2
- Option 3
- Option 4
- Option 5
Field to sort results by. Default transactionTimestamp.
policyNumber, primaryInsuredName, effectiveDate, transactionTimestamp, createdAt Sort direction (default desc).
asc, desc Response
The run to poll. outcome: "enqueued" means a new export was started;
outcome: "duplicate" means runId is a run already in flight producing
this exact export and no second one was started.
The run to poll after starting an export run. outcome is a policy verdict
delivered as a successful 200, not an error: duplicate means a run
producing this exact export (same request, same configuration version) was
already in flight, so runId is that run and no second export was started.
The run to poll via the start endpoint's GET .../export-runs/{runId} sibling
enqueued when a new export was started; duplicate when a run already in flight was producing this exact export (pass ?force=true to start a new run anyway)
enqueued, duplicate 