The API, webhooks and export
A versioned REST API, webhooks you can verify, and an export that does not need us to read it.
REST API
Everything lives under /v1, served separately from the console so an integration cannot accidentally reach an endpoint meant for a signed-in person. The schema is published as OpenAPI 3.1 at /openapi.json, generated from the same definitions the server routes with.
- Scoped keys. A key carries the permissions it was created with, and they are intersected with the role it acts as — so a key cannot outlive the permissions of the person who made it.
- Cursor pagination. Not offset. A record created while you page does not make you skip one or read one twice. The console browses records through the same pagination, so one behaviour is tested twice rather than two behaviours once.
- Idempotency keys on writes. Retrying a create after a timeout returns the original result instead of making a second record.
- Fields you may not read are absent, and the response names them in an
omitted_fieldslist, so an integration can tell the difference between a field that is empty and one it is not allowed to see. - OAuth 2.0 with PKCE for applications acting on behalf of a person. Refresh tokens rotate, and re-use of a spent one revokes the whole grant.
Webhooks
Events are signed with HMAC-SHA256 over the timestamp and the exact request body, in the header layout Stripe uses — so an existing verification routine works unchanged.
- The timestamp is inside the signed string and checked against a tolerance, so a captured request cannot be replayed tomorrow.
- Rotating a secret signs with both for an overlap, so a consumer that has not redeployed keeps working.
- Failed deliveries retry with backoff and then stop, and a dead-lettered delivery can be replayed from the console once the endpoint is fixed.
- Payloads carry only the fields the process explicitly names, and a process that names a restricted field does not publish.
- Slack and Microsoft Teams endpoints post a card instead, carrying a reference rather than the answers.
Getting your data out
Records, answers, files, the process schema and the full audit history export in standard formats. An export is reproducible — the same record exported twice produces the same checksum — which is what makes it usable as evidence rather than just as a copy.
Values you were not allowed to see are withheld rather than silently blanked, and listed, so an export never misrepresents itself as complete.
It runs on managed PostgreSQL with no proprietary database layer, so the underlying data is readable without us.
What is not met
Named rather than implied.
- No published SDKs. The OpenAPI document is the contract; there is no generated client.
- No sandbox environment. There is one set of keys against real data.
- Webhook delivery is at-least-once and unordered. Consumers have to be idempotent, and the event id is what to key on.
- No GraphQL, no bulk endpoints, and no streaming.
How access is decided is on the security page.