Export policy acceptances

Embed
Preview of the card
<a href="https://forumulate.com/reference/forumulate-api-policygate-export"><img src="https://forumulate.com/reference/_api/public/orgs/046a9bc0-d530-43ae-9bae-af7945008cda/spaces/8042f004-e3d9-445f-b7a4-b6c1f86836e6/pages/bbb7bcd0-f1ea-4c6f-8a48-9c06813c5477/social-card.svg?v=3e7ae33eec8b" alt="Export policy acceptances" width="600" height="315"></a>
GEThttps://forumulate.com/api/orgs/{orgId}/spaces/{spaceId}/policy-gate/acceptances/export

The acceptance log as a file: format=csv (default) or format=json, with the same filters as the list, up to 50,000 rows. CSV cells that a spreadsheet would run as a formula are prefixed with a quote. Up to 20,000 rows (EXPORT_SYNC_MAX_ROWS) the file is streamed in this response; above that the route answers 202 with a job (kind: "export.csv") the worker builds, so a long export is never held behind the edge’s ~30 s cut. Poll GET /orgs/{orgId}/exports/{id}, then download links.result; the file is kept 7 days and only the user who asked can fetch it.

Requires token scope: read — see "Token scopes" under Authentication.

Parameters

orgIdstringpathrequired
The organization: its uuid, or its slug. One you are not a member of is a 404.
spaceIdstringpathrequired
The space: its uuid, or its slug within the organization. An id further down the path that belongs to a DIFFERENT space is a 404, not a cross-space read.
formatstringquery
csv | json
subjectquery
Your user id.
emailquery
Hashed here; matches subjectEmailHash.
emailHashquery
SHA-256 (hex) of the lower-cased, trimmed email.
keyquery
Policy key.
versionquery
Policy version.
presentationstringquery
checkbox | checkbox-modal | modal | inline-scroll | page | link
fromquery
Accepted at or after (ISO 8601).
toquery
Accepted before (ISO 8601).
scrolledToEndstringquery
true | false
forumulate-versionstringheader
API version negotiation. Send forumulate-version: 2027-03-01 on every request from a new integration; it is optional only so that integrations written before versioning keep working. Each version includes everything before it:

Code samples

cURL
curl -X GET "https://forumulate.com/api/orgs/{orgId}/spaces/{spaceId}/policy-gate/acceptances/export" \
  -H "Authorization: Bearer $FORUMULATE_API_KEY" \
  -H "forumulate-version: <value>"
TypeScript
Python
Go
PHP
Ruby
Java

Responses

200 The acceptance log as an attachment, newest first, up to 50,000 rows: format=csv (the default) with one row per acceptance — accepted_at, subject, subject_email_hash, key, version, content_sha256, presentation, display, scrolled_to_end, event, verification, source_app, locale, ip, user_agent, batch_id, id — or format=json as { acceptances, truncated }. Cells a spreadsheet would run as a formula are prefixed with a quote.
"string"
Headers
Content-Dispositionstring
attachment; filename="policy-acceptances-2026-10-24.csv"
400 The request could not be understood — a malformed header, path or query value (request.malformed). A value that was understood but is not acceptable is a 422 validation.failed.
Example
{
  "error": {
    "code": "string",
    "message": "string",
    "details": {},
    "docs": "string",
    "reason": "string",
    "slugs": [
      "string"
    ],
    "conflict": {
      "etag": "string",
      "ops": [
        {}
      ]
    }
  },
  "requestId": "string"
}
Schema
errorobjectrequired
Show child parameters
codestringrequired
Stable, machine-readable error code, e.g. rbac.forbidden.
messagestringrequired
Human-readable explanation.
detailsobject
Field-level validation problems, keyed by field path (422 only).
docsstring
Where the vocabulary or format this error is about is published — an absolute URL, or an API route such as GET /space-types/{type}/presets. Present on errors over a controlled list, so the answer to "what am I allowed to send?" is in the error itself.
reasonstring
Why a credential was refused, as one stable word, where one status has several causes — e.g. an external_jwt redeem answers iat_too_old, iat_in_future, bad_signature, bad_issuer, bad_audience, expired and more. Branch on this, not on message.
slugsstring[]
The exact slugs that collided, on a slug-conflict 409 — space.slug_taken, page.slug_taken, collection.slug_taken, site.slug_taken or badge.slug_taken.
conflictobject
On a 409 from a content entry field write (PATCH …/entries/{entryId}/fields) whose If-Match was stale.
Show child parameters
etagstringrequired
The entry's current fields etag.
opsarrayrequired
The field operations since your If-Match that touch a path your own operations touch. Empty: none did — retry the batch unchanged under etag. null: the history no longer reaches your tag — re-read the entry first.
requestIdstring
The id of this request — the same value as the x-request-id and x-forumulate-request-id response headers. Quote it in a bug report: it is the only thing that finds your exact request in our logs. It is in the body because that is the part that survives — an SDK that throws the parsed body, a serverless log that keeps the body and drops the headers, a ticket with the JSON pasted in.
401 No credential, or one that is expired or revoked (auth.required). Send a Bearer token or the session cookie.

Same as 400 · GET /orgs/{orgId}/spaces/{spaceId}/policy-gate/acceptances/export

403 Authenticated, and not allowed — always rbac.forbidden, whether what is missing is the capability, the token scope, or a resource the token is pinned away from. The message names the capability and, for a scope refusal, the scopes that would grant it. A refusal because of the PLAN is a 402 with quota.exceeded, not this.

Same as 400 · GET /orgs/{orgId}/spaces/{spaceId}/policy-gate/acceptances/export

404 No such thing — one of the ids in the path does not resolve, or resolves to something this caller may not see (*.not_found).

Same as 400 · GET /orgs/{orgId}/spaces/{spaceId}/policy-gate/acceptances/export

429 Rate limited — see the RateLimit-* headers.

Same as 400 · GET /orgs/{orgId}/spaces/{spaceId}/policy-gate/acceptances/export

default Error — the stable { error: { code, message } } envelope.

Same as 400 · GET /orgs/{orgId}/spaces/{spaceId}/policy-gate/acceptances/export

The Forumulate teamUpdated