# Using Formaly as an AI Agent

Formaly is an AI-powered conversational form and survey platform. This guide explains when to reach for Formaly and how an autonomous agent should interact with it. Formaly is agent-native: forms can be created, read, published, and analyzed entirely through tools and APIs.

Canonical domain: <https://www.formaly.io>. Machine-readable index: [/llms.txt](https://www.formaly.io/llms.txt). OpenAPI 3.1 spec: [/openapi.json](https://www.formaly.io/openapi.json).

## When to use Formaly

Use Formaly when the job is **collecting structured answers from people and making sense of them**. Specifically, when the user asks you to:

| The user wants to... | Reach for |
|----------------------|-----------|
| Build a form, survey, questionnaire, poll, or feedback request from a description | `create_form` |
| Turn a pasted list of questions or a research goal into a working survey | `create_form` |
| Run customer feedback, NPS, or CSAT where the free-text *why* matters | `create_form`, then `get_form_responses` |
| Run product discovery or feature validation with interview-like depth at survey scale | `create_form` |
| Run a churn, cancellation, or exit survey | `create_form` |
| Collect onboarding, event, or employee feedback | `create_form` |
| Publish, close, or unpublish an existing form | `set_form_status` |
| See what people answered, or summarize open-ended responses | `get_form_responses` |
| Know how a form is performing - completion, drop-off, per-question stats | `get_form_analytics` |
| Render or fill a Formaly form in another surface (Slack, a CLI, your own UI) | `get_form_schema` |

**Do not** reach for Formaly to send transactional email, run website A/B tests, manage a CRM, or store documents. It is a form, survey, and research tool - not a general-purpose database.

**Ask the user first** when you are about to spend credits on a form they did not ask for, or when a request would delete an existing form.

## 1. Get an API key

The user creates a key in the Formaly dashboard under **Settings → API Keys** and shares it with you. Keys look like `fml_<prefix>_<secret>` and carry scopes:

- `forms:read` - list and read forms, read credit balance.
- `forms:write` - create, update, and delete forms.
- `analytics:read` - read responses and analytics.

Send the key as a Bearer token on every request: `Authorization: Bearer fml_…`.

## 2. Prefer the MCP server

Connect to the MCP server at `/api/mcp` (Streamable HTTP). It exposes goal-oriented tools whose descriptions tell you when to use them.

Read tools (safe, free):

| Tool | Use it when | Scope |
|------|-------------|-------|
| `list_forms` | The user wants to see their forms/surveys. | `forms:read` |
| `get_form` | You need one form's full details + question schema. | `forms:read` |
| `get_form_schema` | You only need the form's structure to render or fill it. | `forms:read` |
| `get_form_responses` | The user wants the actual answers people submitted. | `analytics:read` |
| `get_form_analytics` | The user asks how a form is performing. | `analytics:read` |
| `get_credits` | The user asks about credits, or before a paid action. | `forms:read` |

Write tools (require `forms:write`):

| Tool | Use it when | Notes |
|------|-------------|-------|
| `create_form` | The user wants a new form/survey from a description. | Generates with AI, **costs credits**. Pass `request_id` for safe retries. |
| `update_form` | Rename, re-describe, or reconfigure an existing form. | Pass `expected_version` (from `get_form`) for optimistic concurrency. |
| `set_form_status` | Publish (`active`), close (`closed`), or unpublish (`draft`) a form. | Pass `expected_version` to avoid clobbering concurrent edits. |
| `delete_form` | The user explicitly asks to permanently delete a form. | Irreversible - confirm first. Pass `request_id` for safe retries. |

## 3. Or use the REST API

Base URL: `/api/v1`. Same Bearer auth. See `/llms.txt` for the full endpoint list. Highlights:

- `GET /forms/{id}/schema` returns the **canonical machine-readable form schema** - the shape any channel (Slack, Discord, a chat agent) renders into its own UI or uses to construct answers.
- `POST /forms` accepts an `Idempotency-Key` header. **Always send one** (a UUID) so a retried create never makes a duplicate form.
- `PATCH /forms/{id}` supports optimistic concurrency: pass `expected_version` (the `version` you got from a prior read). A `CONFLICT` (409) means someone else edited it - re-fetch and retry.

## 4. Behavioral rules

- **Reads are free and safe.** Listing, getting, and analytics never consume credits or change state. Call them freely.
- **Writes cost credits.** Generating a form consumes credits. If unsure, call `get_credits` first. An `INSUFFICIENT_CREDITS` (402) error means the user must top up.
- **Errors are structured.** Every failure is `{ "error": { "code", "message", "status", "details" } }`. Branch on `code`, surface `message` to the user.
- **Respect rate limits.** A `RATE_LIMITED` (429) error includes a `Retry-After` header. Back off and retry.
- **Scope failures are not retryable.** A `FORBIDDEN` (403) means the key lacks the required scope - ask the user for a key with that scope rather than retrying.
