Skip to content

FluoTest API Reference

FluoTest's public API is served through its MCP endpoint — a JSON-RPC 2.0 API over plain HTTPS that works with curl or any HTTP client, no MCP library required. You authenticate with a personal API key.

Last updated

How do I call the FluoTest API?

Generate an API key in Settings → Security → API keys, then POST JSON-RPC requests to https://fluotest.com/api/mcp with the key in an "Authorization: Bearer" header. Eight tools are available today: list_quizzes, create_quiz, get_quiz_questions, get_quiz_settings, update_quiz, publish_quiz, get_results, and delete_quiz.

Getting an API key#

API keys are personal — every request made with a key acts as your user account, with the same permissions you have in the app.

  1. Open Settings → Security → API keys in your FluoTest dashboard.
  2. Enter a name that tells you where the key will be used (e.g. "Zapier script").
  3. Click Generate key and copy the fluo_… value immediately.
  4. Store it somewhere safe — a password manager, or your tool's secrets storage.
  • Shown once: Only a SHA-256 hash of the key is stored — if you lose it, revoke it and generate a new one.
  • Up to 10 keys: Create separate keys per tool or script so you can revoke one without breaking the others.
  • Revocable anytime: Revoking a key takes effect immediately on the next request.
  • Usage tracking: Each key shows when it was last used, so stale keys are easy to spot and clean up.

Authentication#

Send the key with every request in the Authorization header:

Authorization: Bearer fluo_YOUR_API_KEY

Connecting an AI assistant instead of writing code? See the MCP server guide →

Request format#

The endpoint is https://fluotest.com/api/mcp. Send POST requests with "Content-Type: application/json", an Accept header of "application/json, text/event-stream", and a JSON-RPC 2.0 body. No initialize handshake is required — you can call tools/list and tools/call directly.

Responses come back as a server-sent-events body: the JSON-RPC result is on the "data:" line, and each tool's payload is a JSON string inside result.content[0].text:

event: message
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\n  \"quiz_id\": \"…\",\n  \"status\": \"draft\", …}"}]}}

Examples#

List available tools

curl -X POST https://fluotest.com/api/mcp \
  -H "Authorization: Bearer fluo_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

List your quizzes

curl -X POST https://fluotest.com/api/mcp \
  -H "Authorization: Bearer fluo_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "tools/call",
    "params": { "name": "list_quizzes", "arguments": {} }
  }'

Create a draft quiz

curl -X POST https://fluotest.com/api/mcp \
  -H "Authorization: Bearer fluo_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "create_quiz",
      "arguments": {
        "title": "Customer readiness check",
        "questions": [
          { "text": "Do you have a budget?", "type": "yes_no", "points": 2 },
          {
            "text": "Company size?",
            "type": "multiple_choice",
            "options": [
              { "label": "1-10", "score": 1 },
              { "label": "11-50", "score": 2 },
              { "label": "50+", "score": 3 }
            ]
          }
        ]
      }
    }
  }'

Get a quiz's questions

curl -X POST https://fluotest.com/api/mcp \
  -H "Authorization: Bearer fluo_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 4,
    "method": "tools/call",
    "params": {
      "name": "get_quiz_questions",
      "arguments": { "quiz_id": "Customer readiness check" }
    }
  }'

Get a quiz's settings

curl -X POST https://fluotest.com/api/mcp \
  -H "Authorization: Bearer fluo_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 8,
    "method": "tools/call",
    "params": {
      "name": "get_quiz_settings",
      "arguments": { "quiz_id": "Customer readiness check" }
    }
  }'

Edit a quiz

curl -X POST https://fluotest.com/api/mcp \
  -H "Authorization: Bearer fluo_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 5,
    "method": "tools/call",
    "params": {
      "name": "update_quiz",
      "arguments": {
        "quiz_id": "Customer readiness check",
        "questions": [
          { "text": "Do you have a budget?", "type": "yes_no", "points": 2 },
          { "text": "What's your budget range?", "type": "multiple_choice",
            "options": [
              { "label": "Under $1k", "score": 1 },
              { "label": "$1k-$10k", "score": 2 },
              { "label": "$10k+", "score": 3 }
            ]
          }
        ]
      }
    }
  }'

Publish a draft quiz

curl -X POST https://fluotest.com/api/mcp \
  -H "Authorization: Bearer fluo_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 6,
    "method": "tools/call",
    "params": {
      "name": "publish_quiz",
      "arguments": { "quiz_id": "Customer readiness check" }
    }
  }'

Get quiz results

curl -X POST https://fluotest.com/api/mcp \
  -H "Authorization: Bearer fluo_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": {
      "name": "get_results",
      "arguments": { "quiz_id": "Customer readiness check" }
    }
  }'

Delete a quiz (with confirmation)

curl -X POST https://fluotest.com/api/mcp \
  -H "Authorization: Bearer fluo_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 7,
    "method": "tools/call",
    "params": {
      "name": "delete_quiz",
      "arguments": { "quiz_id": "Customer readiness check", "confirm": true }
    }
  }'

Errors#

Authentication problems use HTTP status codes with an OAuth error body (RFC 6750) and a WWW-Authenticate header. Other HTTP errors (unknown endpoint, rate limit) return application/problem+json (RFC 9457) with a stable code and a hint. Problems inside a tool call come back as a normal JSON-RPC response with isError set and the explanation in the content text.

StatusMeaning
401Missing, invalid, or revoked API key. Check the Authorization header and that the key still exists in Settings.
400Malformed JSON-RPC body. Check the jsonrpc, id, method, and params fields.
404No endpoint at that path. The body is application/problem+json with code not_found.
429Rate limit exceeded. The body is application/problem+json with code rate_limited; wait the number of seconds in Retry-After.
200 + isErrorThe tool call itself failed — for example a quiz name/ID you don't own, a quiz name matching more than one of your quizzes, editing a published quiz without confirm_edit_published: true, deleting a quiz without confirm: true, or a multiple_choice question with fewer than 2 options. The text content explains what to fix.

Error codes (application/problem+json)

Every problem body has type, title, status, code, and usually detail and hint. The type URI links to the matching entry below.

not_found
The path is not an API endpoint. See /openapi.json for the documented endpoints.
rate_limited
Too many requests in the current window. Wait Retry-After seconds, then retry.

Rate limits and versioning#

Each API key or OAuth token can send 120 requests per minute to /api/mcp (requests without a credential are counted per IP address). Every response carries RateLimit-Policy and RateLimit (IETF draft format) plus RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset, so you can slow down before hitting the limit. Over the limit you get a 429 with Retry-After.

HTTP/1.1 429 Too Many Requests
Content-Type: application/problem+json
RateLimit-Policy: "default";q=120;w=60
RateLimit: "default";r=0;t=17
Retry-After: 17

{"type":"https://fluotest.com/en/docs/api-reference#error-rate_limited","title":"Too Many Requests","status":429,"code":"rate_limited","hint":"Wait 17 seconds (Retry-After) before retrying."}

The transport is versioned by the MCP-Protocol-Version request header, negotiated during initialize. The tool surface follows semantic versioning (the version field in /openapi.json): new tools and new optional arguments are additive and never break existing calls.

A breaking change ships as a new tool name. The old tool keeps working for at least 90 days; responses that use it carry a Deprecation header and a Sunset header with the removal date, and the change is announced on the changelog.

FAQ#

Is there a rate limit?

Yes: 120 requests per minute per API key or OAuth token on /api/mcp. Every response includes RateLimit headers with your remaining budget, and a 429 response tells you how long to wait in Retry-After.

Can I use OAuth instead of an API key?

Yes — OAuth is the recommended way to connect AI assistants like Claude, where the user approves access on a consent screen. API keys are the simpler option for scripts and clients that just send headers. See the MCP server guide for the OAuth setup.

What can the API do today?

Eight operations: list_quizzes, create_quiz, get_quiz_questions, get_quiz_settings, update_quiz (with a confirm step to edit published quizzes), publish_quiz, get_results, and delete_quiz (with a confirm step). Every quiz-reference argument accepts a title, slug, or ID. More operations are planned — tell us what you need via the feedback form in the dashboard.