Why this is different from "we have an API too"
Plenty of services have an API that does exactly one thing: fetch submissions. That is enough for a CRM integration and nothing more.
We assumed the caller might be an agent that thinks for itself. That sets a different bar. An agent has to be able to **discover** what it may do, it has to be able to read a form's **schema** before it submits anything, and it has to be able to **retry** without causing duplicate submissions.
Hence three things you rarely see:
- **/api/v1/me** tells a key what it may do and what it is talking to. No digging through documentation to find out your scope is missing.
- **The form schema is machine readable**, including field types, required fields, valid option values and validation rules. An agent therefore knows in advance what will be accepted.
- **Idempotency-Key** on submission. A network timeout where the submission did in fact arrive would otherwise create a second one, and you only notice when the same customer appears twice in the list.
Getting started
Every key starts with frm_live_. Send it as a Bearer token.
# What may this key do?
curl -H "Authorization: Bearer frm_live_..." \
https://formulira.com/api/v1/me
# Read the schema of a form
curl -H "Authorization: Bearer frm_live_..." \
https://formulira.com/api/v1/forms/request-demo
# Create a form from a question set
curl -X POST -H "Authorization: Bearer frm_live_..." \
-H "Content-Type: application/json" \
-d '{"name":"Customer research","fields":[
{"key":"name","type":"short_text","label":"What is your name?","required":true},
{"key":"score","type":"scale","label":"How satisfied are you?","validation":{"min":1,"max":10}}
]}' \
https://formulira.com/api/v1/formsThe form is created as a draft and translated into every active language straight away. Publishing is a separate call, so an agent can never put something live by accident.
Scopes
A key gets exactly what it needs. An integration that fetches answers has no business publishing forms.
| Scope | What it allows |
|---|---|
| forms:read | Read forms and their field schema |
| forms:write | Create, change and publish forms |
| submissions:read | Read responses — this is the scope with personal data |
| submissions:write | Submit responses on behalf of someone |
| invitations:write | Create personal invitation links |
| webhooks:write | Manage webhooks |
What else is in there
Signed webhooks
Polling is slow and expensive. Register a URL and get a push the moment a submission arrives, with an HMAC signature so you can establish that the message is from us and was not altered on the way.
A key per form
A key can be bound to a single form. It then reaches nothing else, not even if it knows the id of another form.
Rate limit per key
A thousand requests per rolling hour, counted per key rather than per IP address. Two integrations on the same office network therefore do not get in each other's way.
Everything in the audit log
Every write through the API lands in the audit log with the key attached. When a question comes up later, you can see which integration did what.