{"openapi":"3.1.0","info":{"title":"Form2Workflow public API","version":"1","summary":"Load forms, submit them to n8n webhooks, and sign in form users.","description":"Form2Workflow (form2wf) forwards form submissions from any website to n8n webhooks.\n\nThese are the endpoints the f2wf.js widget and the JavaScript SDK call. Webhook URLs and secret headers are stored server-side and never returned.\n\nDocumentation: https://form2wf.com/docs · LLM index: https://form2wf.com/llms.txt\n\nTo create or edit forms programmatically, use the MCP server (POST /mcp) with an organization API key; see /docs/mcp."},"servers":[{"url":"https://form2wf.com"}],"externalDocs":{"description":"Form2Workflow documentation","url":"https://form2wf.com/docs"},"tags":[{"name":"Forms","description":"Public form endpoints used by the widget and SDK."},{"name":"Form users","description":"Sign-in for login-protected forms."},{"name":"AI agents","description":"MCP server for creating and managing forms."},{"name":"Service","description":"Health and discovery."}],"paths":{"/api/v1/forms/{orgSlug}/{formSlug}":{"get":{"tags":["Forms"],"operationId":"getForm","summary":"Get a form's public config","description":"Fields, layout and theme. Never includes webhook URLs or headers. Refused (403) from websites not in the form's allowed domains.","parameters":[{"name":"orgSlug","in":"path","required":true,"schema":{"type":"string"},"description":"Organization slug (shown in the embed snippet)."},{"name":"formSlug","in":"path","required":true,"schema":{"type":"string"},"description":"Form slug."}],"responses":{"200":{"description":"Public form config","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicForm"}}}},"403":{"description":"Not allowed: wrong website (allowed domains), no access to this form, or wrong token type","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Form not found or not active","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or monthly submission quota reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/submit/{formId}":{"post":{"tags":["Forms"],"operationId":"submitForm","summary":"Submit a form to its webhook","description":"Forwards the values to the form's submit webhook as `{ data, metadata, endUser, ... }` and returns the webhook's response. Send JSON, or multipart/form-data for file uploads (size limit depends on the plan). Login-protected forms need `Authorization: Bearer <form-user token>`.","parameters":[{"name":"formId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Form id (the `id` from the public form config)."}],"security":[{},{"formUser":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"name":"Ada","email":"ada@example.com","message":"Hello"}},"multipart/form-data":{"schema":{"type":"object","additionalProperties":true}}}},"responses":{"200":{"description":"Delivered","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"const":true},"data":{"description":"What the webhook returned"},"message":{"type":"string"}}}}}},"401":{"description":"The form requires sign-in, or the session expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed: wrong website (allowed domains), no access to this form, or wrong token type","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Form not found or not active","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"411":{"description":"Multipart upload without Content-Length","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Upload larger than the plan allows","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or monthly submission quota reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The webhook failed, timed out, or is on a private network","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/fetch/{formId}":{"get":{"tags":["Forms"],"operationId":"fetchFromForm","summary":"Call a form's webhook to read data","description":"Calls the form's webhook with the query parameters as `params` and returns its response. Same sign-in and website rules as submit.","parameters":[{"name":"formId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Form id (the `id` from the public form config)."}],"security":[{},{"formUser":[]}],"responses":{"200":{"description":"Webhook response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"const":true},"data":{}}}}}},"401":{"description":"The form requires sign-in, or the session expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed: wrong website (allowed domains), no access to this form, or wrong token type","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Form not found or not active","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or monthly submission quota reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The webhook failed, timed out, or is on a private network","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/forms/{orgSlug}/{formSlug}/onload":{"post":{"tags":["Forms"],"operationId":"runOnload","summary":"Run the form's onload webhook","description":"Used to fill fields and tables when the form opens. Returns the webhook's response body unchanged (commonly `{ data: {...} }`).","parameters":[{"name":"orgSlug","in":"path","required":true,"schema":{"type":"string"},"description":"Organization slug (shown in the embed snippet)."},{"name":"formSlug","in":"path","required":true,"schema":{"type":"string"},"description":"Form slug."}],"security":[{},{"formUser":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"params":{"type":"object","additionalProperties":{"type":"string"},"description":"Usually the page's URL query parameters."}}}}}},"responses":{"200":{"description":"Webhook response body"},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"The form requires sign-in, or the session expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed: wrong website (allowed domains), no access to this form, or wrong token type","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Form not found or not active","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or monthly submission quota reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The webhook failed, timed out, or is on a private network","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/forms/{orgSlug}/{formSlug}/action":{"post":{"tags":["Forms"],"operationId":"runAction","summary":"Run a button or table row action","description":"The webhook URL, method and headers come from the stored form; the request only says which button or row action ran. On login-protected forms the verified user is sent to the webhook as X-F2WF-User-Id and X-F2WF-User-Email headers.","parameters":[{"name":"orgSlug","in":"path","required":true,"schema":{"type":"string"},"description":"Organization slug (shown in the embed snippet)."},{"name":"formSlug","in":"path","required":true,"schema":{"type":"string"},"description":"Form slug."}],"security":[{},{"formUser":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"fieldId":{"type":"string","description":"Id of a button field."},"actionId":{"type":"string","description":"Id of a table row action."},"data":{"type":"object"},"rowData":{"type":"object"},"context":{"type":"object"},"params":{"type":"object"}}}}}},"responses":{"200":{"description":"Action ran","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"const":true},"data":{},"message":{"type":"string"}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"The form requires sign-in, or the session expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed: wrong website (allowed domains), no access to this form, or wrong token type","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Form not found or not active","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or monthly submission quota reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The webhook failed, timed out, or is on a private network","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/form-auth/check/{orgSlug}/{formSlug}":{"get":{"tags":["Form users"],"operationId":"checkFormAuth","summary":"Whether a form requires sign-in","parameters":[{"name":"orgSlug","in":"path","required":true,"schema":{"type":"string"},"description":"Organization slug (shown in the embed snippet)."},{"name":"formSlug","in":"path","required":true,"schema":{"type":"string"},"description":"Form slug."}],"responses":{"200":{"description":"Sign-in requirement"},"404":{"description":"Form not found or not active","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/form-auth/login":{"post":{"tags":["Form users"],"operationId":"formUserLogin","summary":"Sign in a form user","description":"Returns a token for login-protected forms. Organization members can sign in here too and get access to their organization's forms.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","password","organization_id"],"properties":{"email":{"type":"string","format":"email"},"password":{"type":"string"},"organization_id":{"type":"string","format":"uuid"}}}}}},"responses":{"200":{"description":"Signed in","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"user":{"type":"object"}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"The form requires sign-in, or the session expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or monthly submission quota reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/form-auth/verify-access":{"post":{"tags":["Form users"],"operationId":"verifyFormAccess","summary":"Check the signed-in user can open a form","security":[{"formUser":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"form_id":{"type":"string","format":"uuid"},"slug":{"type":"string"}}}}}},"responses":{"200":{"description":"Access result","content":{"application/json":{"schema":{"type":"object","properties":{"has_access":{"type":"boolean"}}}}}},"401":{"description":"The form requires sign-in, or the session expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/form-auth/me":{"get":{"tags":["Form users"],"operationId":"formUserProfile","summary":"The signed-in form user","security":[{"formUser":[]}],"responses":{"200":{"description":"Profile"},"401":{"description":"The form requires sign-in, or the session expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/user/register":{"post":{"tags":["Form users"],"operationId":"formUserRegister","summary":"Self-register a form user","description":"Only for organizations that turned on end-user self sign-up; otherwise 403.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","password","organizationId"],"properties":{"email":{"type":"string","format":"email"},"password":{"type":"string","minLength":8},"name":{"type":"string"},"organizationId":{"type":"string","format":"uuid"}}}}}},"responses":{"201":{"description":"Registered, with token"},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed: wrong website (allowed domains), no access to this form, or wrong token type","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or monthly submission quota reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/mcp":{"post":{"tags":["AI agents"],"operationId":"mcp","summary":"MCP server (Streamable HTTP, JSON-RPC 2.0)","description":"Model Context Protocol endpoint for AI agents: list, create and edit forms, read submissions, manage themes, form users and access. Authenticate with an organization API key (f2w_…) created under API Keys in the dashboard. Tool reference: https://form2wf.com/docs/mcp-tools","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"JSON-RPC 2.0 request","properties":{"jsonrpc":{"const":"2.0"},"id":{},"method":{"type":"string","examples":["initialize","tools/list","tools/call"]},"params":{"type":"object"}}}}}},"responses":{"200":{"description":"JSON-RPC response (JSON or text/event-stream)"},"401":{"description":"Missing or invalid API key"}}}},"/health":{"get":{"tags":["Service"],"operationId":"health","summary":"Service health","responses":{"200":{"description":"Healthy"}}}}},"components":{"securitySchemes":{"formUser":{"type":"http","scheme":"bearer","description":"Token from /api/v1/form-auth/login."},"apiKey":{"type":"http","scheme":"bearer","description":"Organization API key (f2w_…), for the MCP server."}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"},"code":{"type":"string"}}},"PublicForm":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"slug":{"type":"string"},"type":{"type":"string","examples":["form","report"]},"org_slug":{"type":"string"},"config":{"type":"object","description":"Fields, layout and settings. See /docs/fields."},"is_active":{"type":"boolean"},"auth_required":{"type":"boolean"},"has_submit_webhook":{"type":"boolean"},"has_onload_webhook":{"type":"boolean"},"theme":{"type":["object","null"]}}}}}}