Formaly REST API Reference
The Formaly REST API is versioned under `/api/v1` and authenticated with a Formaly API key. Base URL: `https://www.formaly.io/api/v1`. A machine-readable OpenAPI 3.1 description of everything on this page is published at [/openapi.json](/openapi.json).
Endpoints
| Method | Path | Scope | Description |
|---|---|---|---|
| GET | /api/v1/forms | forms:read | List forms, paginated |
| POST | /api/v1/forms | forms:write | Create a form |
| GET | /api/v1/forms/{id} | forms:read | Read one form, including its version |
| PATCH | /api/v1/forms/{id} | forms:write | Update a form |
| DELETE | /api/v1/forms/{id} | forms:write | Delete a form permanently |
| GET | /api/v1/forms/{id}/schema | forms:read | Canonical machine-readable form schema |
| GET | /api/v1/forms/{id}/responses | analytics:read | List submitted responses, paginated |
| GET | /api/v1/forms/{id}/analytics | analytics:read | Completion, drop-off, and per-question stats |
| GET | /api/v1/credits | forms:read | Credit balance, cost table, and history |
List forms
GET /api/v1/forms returns the authenticated account's forms, newest first.
curl "https://www.formaly.io/api/v1/forms?status=active&limit=50" \
-H "Authorization: Bearer $FORMALY_API_KEY"| Query parameter | Type | Default | Notes |
|---|---|---|---|
page | integer ≥ 1 | 1 | Page number |
limit | integer 1-100 | 20 | Page size |
status | draft \| active \| closed | all | Filter by publication status |
Create a form
POST /api/v1/forms creates a form from a structured script. Always send an `Idempotency-Key` so a retried request cannot create a duplicate.
title, prompt, and script are required. script.questions must contain at least one question; each question needs an id, a type (open, rating, multiple_choice, or nps), and a prompt.
curl -X POST https://www.formaly.io/api/v1/forms \
-H "Authorization: Bearer $FORMALY_API_KEY" \
-H "Idempotency-Key: 6f1c2b64-1c9e-4c3f-9d6a-3f3f8f1f7a11" \
-H "Content-Type: application/json" \
-d '{
"title": "Onboarding feedback",
"prompt": "Find out where new users get stuck in week one",
"status": "active",
"script": {
"title": "Onboarding feedback",
"opening": "Thanks for trying us out - two quick questions.",
"questions": [
{ "id": "q1", "type": "open", "prompt": "What were you trying to do first?" },
{ "id": "q2", "type": "rating", "prompt": "How easy was setup?", "scale": 5 }
],
"closing": "That is all - thank you."
}
}'Read, update, and delete a form
GET /api/v1/forms/{id} returns the full form plus a version string. Pass that value back as expected_version on the next PATCH (or as an If-Match header) and the update is rejected with CONFLICT (409) if anything changed in between.
PATCH accepts any subset of title, description, prompt, script, status, collectContactInfo, defaultSurveyMode, and systemPrompt. Setting status to active publishes the form; closed stops accepting responses; draft unpublishes it.
DELETE /api/v1/forms/{id} is permanent and removes the form's responses with it. Confirm with a human before calling it.
curl -X PATCH https://www.formaly.io/api/v1/forms/$FORM_ID \
-H "Authorization: Bearer $FORMALY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "status": "active", "expected_version": "3-1751414400000" }'Form schema
GET /api/v1/forms/{id}/schema returns the canonical machine-readable description of a form: its fields, their types, their options, and their validation rules. This is the endpoint to use when you are rendering the form somewhere Formaly does not control - a Slack modal, a Discord thread, a CLI, or an agent constructing an answer set.
Responses and analytics
GET /api/v1/forms/{id}/responses lists submitted responses with page and limit query parameters, including structured answers and the conversation transcript where the respondent used chat mode.
GET /api/v1/forms/{id}/analytics returns aggregate performance: view and completion counts, completion rate, drop-off by question, and per-question breakdowns. Both require the analytics:read scope.
Credits
GET /api/v1/credits returns the current balance, lifetime totals, the cost table for every billable action, and - unless you pass history=false - recent transactions. Reads are free; generating a form costs credits. Check the balance before a batch of writes rather than discovering INSUFFICIENT_CREDITS (402) halfway through.
curl "https://www.formaly.io/api/v1/credits?history=false" \
-H "Authorization: Bearer $FORMALY_API_KEY"