GET /v1/export/decisions
Stream every decision in a window as JSON-Lines (NDJSON), one row per line, terminated by a single inline trailer line that carries an SHA-256 checksum over the body. Designed as the lock-in killer: you can leave any time, with all the data Floopy used to optimize on your behalf.
Endpoint
Section titled “Endpoint”GET https://api.floopy.ai/v1/export/decisionsAuthentication
Section titled “Authentication”Authorization: Bearer <your-floopy-api-key>- Permission:
read_permission. - Plan flag:
has_audit_apiandplan_features.log_retention_days >= 30. Available on Pro and Enterprise plans. - Behind the per-org canary kill switch.
Query parameters
Section titled “Query parameters”| Field | Type | Required | Constraints | Default |
|---|---|---|---|---|
from | ISO8601 | Yes | Inclusive start. | — |
to | ISO8601 | Yes | Inclusive end. to - from ≤ 90 days. | — |
format | string | No | Currently only jsonl. Anything else returns 415. | jsonl |
A pre-flight estimated row-count check rejects exports whose result set would exceed floopy_export_max_rows (default 5_000_000) with 400 estimated_payload_too_large.
Response (200)
Section titled “Response (200)”Content-Type: application/x-ndjson- One JSON object per line, identical shape to
GET /v1/decisions/{request_id}(with the pinnedEXPORT_FIELDSprojection — bodies are never exported). - The last line is an inline JSONL trailer that carries integrity and aggregation metadata.
Header
Section titled “Header”When any decision in the window had used_shared_pool_prior == true, the response also carries:
Floopy-Aggregation-Notice: contains-shared-pool-influenced-decisionsThis is a hint — the authoritative signal lives in the trailer’s aggregation_signal_present field.
Trailer line
Section titled “Trailer line”The last line of the stream is the trailer. It is detected by the literal marker key _floopy_export_trailer: true.
{ "_floopy_export_trailer": true, "outcome": "completed", "row_count": 12345, "byte_count": 18234567, "checksum_sha256": "5f4dcc3b5aa765d61d8327deb882cf99b8adb1c1d2c95b4f9e2c3a4d5e6f7a8b", "aggregation_signal_present": false}| Field | Type | Description |
|---|---|---|
_floopy_export_trailer | boolean | Always true. Use this key as the marker for the trailer line. |
outcome | string | completed, deadline_exceeded, error, or client_disconnect. |
row_count | integer | Number of data rows streamed before the trailer. |
byte_count | integer | Number of body bytes streamed before the trailer (data lines only). |
checksum_sha256 | hex string | SHA-256 over the body bytes preceding the trailer (each data line including its terminating \n). |
aggregation_signal_present | boolean | true if at least one row carried used_shared_pool_prior == true. |
The checksum is computed over the uncompressed body bytes excluding the trailer line itself. A mismatch on the consumer side indicates a truncated transfer or in-flight corruption.
Verifying the trailer
Section titled “Verifying the trailer”A consumer can verify integrity locally by splitting off the trailer line, recomputing SHA-256 over the rest, and comparing.
RESPONSE=$(curl -s \ -H "Authorization: Bearer $FLOOPY_API_KEY" \ "https://api.floopy.ai/v1/export/decisions?from=$FROM&to=$TO")
# The trailer is the last line containing the marker key.TRAILER_LINE=$(echo "$RESPONSE" | tail -n 1)BODY=$(echo "$RESPONSE" | sed '$d') # all but the last line
CLAIMED=$(echo "$TRAILER_LINE" | jq -r '.checksum_sha256')COMPUTED=$(printf '%s\n' "$BODY" | shasum -a 256 | awk '{print $1}')
if [ "$CLAIMED" = "$COMPUTED" ]; then echo "OK"else echo "TAMPERED — claimed=$CLAIMED computed=$COMPUTED" exit 1fi
echo "outcome=$(echo "$TRAILER_LINE" | jq -r '.outcome') rows=$(echo "$TRAILER_LINE" | jq -r '.row_count')"Notes:
- On Linux replace
shasum -a 256withsha256sum. - The gateway terminates each data line with
\n, andprintf '%s\n're-appends a single trailing newline so the recomputation matches what the gateway hashed. - If the trailer’s
outcomeisdeadline_exceeded,error, orclient_disconnect, the export was cut short. The checksum is still valid for the body actually delivered, but the dataset is incomplete; re-issue the request with a narrower window.
Errors
Section titled “Errors”| Status | error code | When |
|---|---|---|
400 | invalid_range | Missing from/to, or from >= to. |
400 | range_too_wide | to - from > 90 days. |
400 | estimated_payload_too_large | Pre-flight row estimate exceeds floopy_export_max_rows. |
403 | plan_required (feature: "audit_api" or "audit_api_retention") | Plan lacks the flag or the 30-day retention floor. |
404 | not_found | Kill switch off for this organisation, or endpoint unmounted. Identical bytes by design. |
415 | unsupported_format | format other than jsonl. |
429 | export_in_progress | Another export is already running for this organisation; carries retry_after_seconds and Retry-After. |
5xx | internal | Upstream failure. |
Curl example
Section titled “Curl example”FROM="2026-04-01T00:00:00Z"TO="2026-05-01T00:00:00Z"curl -s -H "Authorization: Bearer $FLOOPY_API_KEY" \ "https://api.floopy.ai/v1/export/decisions?from=$FROM&to=$TO" \ > decisions.jsonltail -n 1 decisions.jsonl | jq .Limits and behaviour
Section titled “Limits and behaviour”- One concurrent export per organisation. The next call returns
429 export_in_progressuntil the in-flight export finishes (or the 30-minute deadline trips). - 30-minute wall-clock deadline. When tripped, the trailer carries
outcome: "deadline_exceeded". The audit log row recordsaborted_quota. - Pinned field projection. Exports use a fixed allowlist of columns — bodies (
request_body,response_body) are never exported. - Single start per 5 minutes per organisation in addition to the lock, to keep the start rate bounded.
See also
Section titled “See also”- GET /v1/decisions — paginated listing for narrower windows.
- Confidence methodology.
- Baseline-vs-Floopy methodology.