API reference
A versioned HTTP API for the two things another system usually wants: evaluate a payload against a form's rules without storing anything, and file a submission. Authenticated by an API key, bounded by exactly the privileges that key was granted.
Base URL and authentication
Every request carries an API key as a bearer token. A session token from the app works identically — the server builds the same caller either way — but a key is what you want for a machine: it is created for one system, granted one explicit set of privileges, and revoked without disturbing anyone's login.
https://faldaro.app/api/platform/v1/…
Authorization: Bearer ffk_your_token_here
Content-Type: application/json
The versioned promise
Everything under /v1 is a frozen contract. Fields are added to
responses, never renamed or removed; a change that would break your integration arrives as
/v2 instead. The shapes are deliberately narrower than what the app itself uses,
so nothing you depend on moves when the interface does.
The service generates a machine-readable OpenAPI description of this surface at
/v3/api-docs. A self-hosted deployment can reach it directly on the service;
it is deliberately not published through the hosted application origin, so on
Faldaro-hosted plans this page is the reference.
Evaluate a payload
Runs the form's live rules over the values you send — computed fields, hidden fields, outcomes and validation problems — and stores nothing. This is the rating call: price a quote, score an intake, or check a payload before filing it.
POST /api/platform/v1/forms/{formId}/evaluate
{"data": {"units": 4}}
{
"valid": false,
"calculatedData": {"total": 100},
"hiddenFields": [],
"outcomes": [],
"problems": [{"field": "applicant", "message": "Applicant is required"}]
}
| Field | Meaning |
|---|---|
valid |
Whether this payload as a whole would be accepted as a complete submission. A missing required value counts — unlike the in-app preview, which is lenient because a form is still being filled in. |
calculatedData | Every computed field, keyed by slug. |
hiddenFields | Slugs the rules hid for this payload. |
outcomes | Named flags the rules raised — id, message, severity. |
problems | Validation failures — field and message. |
Requires submission:write. Evaluation always runs against the live version.
File a submission
Creates a real submission through the same path the app uses: the engine re-runs server-side, a reference number allocates exactly once, and every configured effect — notifications, documents, integrations, delivery — fires as authored.
POST /api/platform/v1/forms/{formId}/submissions
{"data": {"applicant": "Ada Lovelace", "units": 4}}
Answers 201 with the stored submission: id, formId,
formVersionId, workflowState, data,
calculatedData, hiddenFields and outcomes. Send
"draft": true alongside data to save a partial record that
required-field validation does not yet bind.
Requires submission:write.
Rate limits
Every caller has a request budget — 120 a minute by default, and deployments may tune it.
Going over answers 429 with a Retry-After header and a stable
code:
{"error": "Rate limit exceeded. Retry after 34 seconds.", "code": "RATE_LIMITED"}
The budget is counted per key, not per address, so one integration exhausting its window
never slows another — and machine traffic sharing one IP is not punished for it. Honour
Retry-After and continue.
Errors
| Status | Means |
|---|---|
401 | Missing, malformed, expired or revoked credential. |
403 | The key authenticated but lacks the privilege this call needs. |
404 | No such form — including a form that exists but is not yours, and one never published. |
422 | The request was understood and refused; the body says why. |
429 | Over the request budget; wait Retry-After seconds. |
A form belonging to another organisation is reported as missing rather than forbidden — whether it exists is not something an unauthorised caller gets to learn.
Beyond /v1
The app's own API — folders, forms, reports, admin — is reachable with the same key and the
same privilege rules, and is what the API keys guide shows with
curl. It is deliberately not versioned: it changes when the interface
changes. Build integrations you intend to keep on /v1, and tell us what belongs
there next.
If the caller is a model rather than a script, the MCP server wraps this surface as tools — same key, same privileges, with the schemas and annotations an MCP client expects.