{"openapi":"3.1.0","info":{"title":"FormSnapp API","version":"1.0.0","description":"Server-to-server access to your forms and responses.\n\nAuthenticate with `Authorization: Bearer <key>`. Keys are created in Settings → API & webhooks and are shown once.\n\nThere is deliberately no CORS: a key in browser JavaScript ships to every visitor. Call this from your own server."},"servers":[{"url":"https://app.formsnapp.com"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Forms"},{"name":"Responses"},{"name":"Webhooks","description":"Deliveries carry `X-FormSnapp-Signature: t=<unix>,v1=<hex hmac>`, computed over `\"{timestamp}.{raw body}\"` with the endpoint's signing secret.\nReject anything whose timestamp is more than 5 minutes from now — that is what stops a captured delivery being replayed.\n\nEvents:\n- `response.created` — a response is submitted\n- `form.published` — a form is published\n- `form.closed` — a form stops accepting responses"}],"paths":{"/v1/me":{"get":{"summary":"Identify the key","description":"Returns the workspace a key belongs to and what it may do. The first thing to call when a request is failing and it isn't clear whether the key or the path is at fault.","operationId":"get_v1_me","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Identity"}}}},"401":{"description":"Missing, unknown, revoked or expired key. All four look identical.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"The workspace's plan no longer includes API access.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key is valid but lacks the scope this endpoint needs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such resource in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The body failed validation. `field` names the first problem.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/forms":{"get":{"summary":"List forms","operationId":"get_v1_forms","security":[{"bearerAuth":[]}],"x-scope":"forms:read","parameters":[{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"Continue from a previous page's `next_cursor`."},{"name":"limit","in":"query","required":false,"schema":{"type":"string"},"description":"Rows per page, 1–100. Defaults to 25."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormPage"}}}},"401":{"description":"Missing, unknown, revoked or expired key. All four look identical.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"The workspace's plan no longer includes API access.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key is valid but lacks the scope this endpoint needs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such resource in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The body failed validation. `field` names the first problem.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create a form","description":"Creates a draft. Send `Idempotency-Key` and a retry will return the first response instead of creating a second form.","operationId":"post_v1_forms","security":[{"bearerAuth":[]}],"x-scope":"forms:write","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":200},"questions":{"default":[],"maxItems":200,"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["short_text","long_text","email","number","phone","url","time","multiple_choice","checkboxes","dropdown","multi_select","image_choice","ranking","matrix","date","rating","nps","slider","address","signature","legal","file_upload"]},"title":{"type":"string","minLength":1,"maxLength":300},"required":{"default":false,"type":"boolean"},"options":{"maxItems":100,"type":"array","items":{"type":"string","minLength":1,"maxLength":200}}},"required":["type","title","required"],"additionalProperties":false}}},"required":["title","questions"],"additionalProperties":false}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Form"}}}},"401":{"description":"Missing, unknown, revoked or expired key. All four look identical.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"The workspace's plan no longer includes API access.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key is valid but lacks the scope this endpoint needs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such resource in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The body failed validation. `field` names the first problem.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/forms/{id}":{"get":{"summary":"Retrieve a form","operationId":"get_v1_forms__id_","security":[{"bearerAuth":[]}],"x-scope":"forms:read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The resource id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Form"}}}},"401":{"description":"Missing, unknown, revoked or expired key. All four look identical.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"The workspace's plan no longer includes API access.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key is valid but lacks the scope this endpoint needs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such resource in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The body failed validation. `field` names the first problem.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"summary":"Update a form","description":"Omitting `questions` leaves them untouched. Sending them replaces every question, including their ids — answers already collected still refer to the old ones. Refused with 409 on a form that uses conditional logic, multiple steps or a Studio layout, since all three reference questions by id.","operationId":"patch_v1_forms__id_","security":[{"bearerAuth":[]}],"x-scope":"forms:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The resource id."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":200},"questions":{"default":[],"maxItems":200,"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["short_text","long_text","email","number","phone","url","time","multiple_choice","checkboxes","dropdown","multi_select","image_choice","ranking","matrix","date","rating","nps","slider","address","signature","legal","file_upload"]},"title":{"type":"string","minLength":1,"maxLength":300},"required":{"default":false,"type":"boolean"},"options":{"maxItems":100,"type":"array","items":{"type":"string","minLength":1,"maxLength":200}}},"required":["type","title","required"],"additionalProperties":false}}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Form"}}}},"401":{"description":"Missing, unknown, revoked or expired key. All four look identical.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"The workspace's plan no longer includes API access.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key is valid but lacks the scope this endpoint needs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such resource in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The body failed validation. `field` names the first problem.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/forms/{id}/publish":{"post":{"summary":"Publish a form","description":"Makes it live. A slug is assigned on the first publish only.","operationId":"post_v1_forms__id__publish","security":[{"bearerAuth":[]}],"x-scope":"forms:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The resource id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Form"}}}},"401":{"description":"Missing, unknown, revoked or expired key. All four look identical.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"The workspace's plan no longer includes API access.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key is valid but lacks the scope this endpoint needs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such resource in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The body failed validation. `field` names the first problem.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/forms/{id}/close":{"post":{"summary":"Close a form","description":"Stops accepting responses. The form must already be published.","operationId":"post_v1_forms__id__close","security":[{"bearerAuth":[]}],"x-scope":"forms:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The resource id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Form"}}}},"401":{"description":"Missing, unknown, revoked or expired key. All four look identical.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"The workspace's plan no longer includes API access.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key is valid but lacks the scope this endpoint needs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such resource in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The body failed validation. `field` names the first problem.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/forms/{id}/responses":{"get":{"summary":"List a form's responses","operationId":"get_v1_forms__id__responses","security":[{"bearerAuth":[]}],"x-scope":"responses:read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The resource id."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"Continue from a previous page's `next_cursor`."},{"name":"limit","in":"query","required":false,"schema":{"type":"string"},"description":"Rows per page, 1–100. Defaults to 25."},{"name":"since","in":"query","required":false,"schema":{"type":"string"},"description":"ISO 8601 timestamp. Returns only responses submitted after it."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponsePage"}}}},"401":{"description":"Missing, unknown, revoked or expired key. All four look identical.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"The workspace's plan no longer includes API access.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key is valid but lacks the scope this endpoint needs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such resource in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The body failed validation. `field` names the first problem.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/responses/{id}":{"get":{"summary":"Retrieve a response","operationId":"get_v1_responses__id_","security":[{"bearerAuth":[]}],"x-scope":"responses:read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The resource id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}}}},"401":{"description":"Missing, unknown, revoked or expired key. All four look identical.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"The workspace's plan no longer includes API access.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key is valid but lacks the scope this endpoint needs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such resource in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The body failed validation. `field` names the first problem.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"summary":"Delete a response","description":"Also deletes any files it collected. This cannot be undone.","operationId":"delete_v1_responses__id_","security":[{"bearerAuth":[]}],"x-scope":"responses:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The resource id."}],"responses":{"204":{"description":"Deleted."},"401":{"description":"Missing, unknown, revoked or expired key. All four look identical.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"The workspace's plan no longer includes API access.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key is valid but lacks the scope this endpoint needs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such resource in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The body failed validation. `field` names the first problem.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. See `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Your API key."}},"schemas":{"Identity":{"type":"object","properties":{"workspace":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"plan":{"type":"string"}},"required":["id","name","plan"]},"key":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"scopes":{"type":"array","items":{"type":"string","enum":["forms:read","forms:write","responses:read","responses:write"]}}},"required":["id","name","scopes"]}},"required":["workspace","key"]},"Form":{"type":"object","description":"A deliberate projection of the form, not the stored document. Styling, theme, Studio layout and logic rules are internal and never appear here.","properties":{"id":{"type":"string"},"title":{"type":"string"},"status":{"type":"string","enum":["draft","published","closed"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"response_count":{"type":"integer"},"url":{"type":"string","format":"uri"},"questions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"title":{"type":"string"},"required":{"type":"boolean"},"options":{"type":"array","items":{"type":"string"}}},"required":["id","type","title","required"]}}},"required":["id","title","status","created_at","updated_at","response_count","url","questions"]},"FormPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Form"}},"has_more":{"type":"boolean"},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` to fetch the next page. Null on the last one."}},"required":["data","has_more","next_cursor"]},"Response":{"type":"object","properties":{"id":{"type":"string"},"form_id":{"type":"string"},"submitted_at":{"type":"string","format":"date-time"},"answers":{"type":"array","items":{"type":"object","properties":{"question_id":{"type":"string"},"label":{"type":"string","description":"The question's wording at the time it was answered."},"value":{"description":"A string, number, boolean or array, depending on the question."}},"required":["question_id","label","value"]}},"variables":{"type":"object","additionalProperties":{"type":"number"},"description":"Scores and totals the form computed. Empty when it has none."},"outcome_id":{"type":["string","null"]}},"required":["id","form_id","submitted_at","answers","variables","outcome_id"]},"ResponsePage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Response"}},"has_more":{"type":"boolean"},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` to fetch the next page. Null on the last one."}},"required":["data","has_more","next_cursor"]},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"field":{"type":"string"}},"required":["code","message"]}},"required":["error"]}}}}