{
 "openapi": "3.0.3",
 "info": {
  "title": "Bramble Forms API",
  "version": "2026-11-13",
  "description": "Bramble Forms: forms with pages, fields and logic; collaborators; publishing; responses with file uploads, summaries and CSV export; links from responses to records in other apps; and the public endpoints people fill forms in with. Works with an API key.\n"
 },
 "servers": [
  {
   "url": "https://{org}.brambleit.cloud/api",
   "variables": {
    "org": {
     "default": "your-org"
    }
   }
  },
  {
   "url": "https://{org}.brambleit.cloud/forms",
   "variables": {
    "org": {
     "default": "your-org"
    }
   }
  }
 ],
 "components": {
  "securitySchemes": {
   "apiKey": {
    "type": "apiKey",
    "in": "header",
    "name": "X-API-Key"
   },
   "session": {
    "type": "apiKey",
    "in": "cookie",
    "name": "sso_session"
   },
   "bearer": {
    "type": "http",
    "scheme": "bearer",
    "bearerFormat": "JWT"
   }
  }
 },
 "tags": [
  {
   "name": "Forms",
   "description": "Creating and editing forms (pages, fields, logic), publishing and closing them, and statistics."
  },
  {
   "name": "Responses",
   "description": "Responses, their uploaded files, summaries, CSV export, and links from a response to a record in another app."
  },
  {
   "name": "Collaborators",
   "description": "Who else can edit a form and see its responses, and handing a form to a new owner."
  },
  {
   "name": "Public form endpoints",
   "description": "Reading a published form and submitting a response."
  },
  {
   "name": "Images",
   "description": "Images used in forms."
  }
 ],
 "paths": {
  "/v1/app/forms/forms": {
   "get": {
    "tags": [
     "Forms"
    ],
    "summary": "List this tenant's forms with response counts",
    "operationId": "get-v1-app-forms-forms",
    "description": "Requires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`). Licence: Forms.",
    "parameters": [],
    "responses": {
     "200": {
      "description": "Success",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "forms": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "id": {
              "type": "integer"
             },
             "tenant_id": {
              "type": "integer"
             },
             "slug": {
              "type": "string"
             },
             "title": {
              "type": "string"
             },
             "description": {
              "type": "string",
              "nullable": true
             },
             "status": {
              "type": "string",
              "enum": [
               "draft",
               "published",
               "closed"
              ]
             },
             "require_login": {
              "type": "boolean"
             },
             "allow_multiple_responses": {
              "type": "boolean"
             },
             "auto_link_project_id": {
              "type": "integer",
              "nullable": true
             },
             "accent_color": {
              "type": "string"
             },
             "background_color": {
              "type": "string"
             },
             "background_image_id": {
              "type": "integer",
              "nullable": true
             },
             "background_preset": {
              "type": "string",
              "enum": [
               "none",
               "soft_clouds",
               "waves",
               "gradient_warm",
               "gradient_cool",
               "bramble"
              ]
             },
             "font_family": {
              "type": "string"
             },
             "button_style": {
              "type": "string",
              "enum": [
               "rounded",
               "square",
               "pill"
              ]
             },
             "card_radius": {
              "type": "string",
              "enum": [
               "rounded",
               "sharp"
              ]
             },
             "layout_style": {
              "type": "string",
              "enum": [
               "classic",
               "title_on_background",
               "card_left",
               "card_right",
               "compact"
              ]
             },
             "notify_on_submission": {
              "type": "boolean"
             },
             "created_by": {
              "type": "integer"
             },
             "created_at": {
              "type": "string",
              "format": "date-time"
             },
             "updated_at": {
              "type": "string",
              "format": "date-time"
             },
             "deleted_at": {
              "type": "string",
              "format": "date-time",
              "nullable": true
             },
             "response_count": {
              "type": "boolean"
             },
             "is_owner": {
              "type": "boolean"
             }
            }
           }
          }
         }
        }
       }
      }
     },
     "500": {
      "description": "Internal Server Error"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ]
   },
   "post": {
    "tags": [
     "Forms"
    ],
    "summary": "Create a draft form with one default page",
    "operationId": "post-v1-app-forms-forms",
    "description": "Requires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`). Licence: Forms.",
    "parameters": [],
    "responses": {
     "201": {
      "description": "Success",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "form": {}
         }
        }
       }
      }
     },
     "500": {
      "description": "Internal Server Error"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "title": {
          "type": "string"
         }
        }
       }
      }
     }
    }
   }
  },
  "/v1/app/forms/forms/{id}": {
   "get": {
    "tags": [
     "Forms"
    ],
    "summary": "Full structure for the builder (pages, fields, logic)",
    "operationId": "get-v1-app-forms-forms-id",
    "description": "Requires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`). Licence: Forms.",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Success",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "form": {
           "type": "object",
           "properties": {
            "is_owner": {
             "type": "boolean"
            },
            "admin_access": {
             "type": "boolean"
            }
           }
          },
          "pages": {
           "type": "array",
           "items": {}
          }
         }
        }
       }
      }
     },
     "404": {
      "description": "Form not found"
     },
     "500": {
      "description": "Internal Server Error"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ]
   },
   "patch": {
    "tags": [
     "Forms"
    ],
    "summary": "Update title/description/status/require_login/theme",
    "operationId": "patch-v1-app-forms-forms-id",
    "description": "Requires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`). Licence: Forms.",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Success",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "form": {
           "type": "object",
           "properties": {
            "id": {
             "type": "integer"
            },
            "tenant_id": {
             "type": "integer"
            },
            "slug": {
             "type": "string"
            },
            "title": {
             "type": "string"
            },
            "description": {
             "type": "string",
             "nullable": true
            },
            "status": {
             "type": "string",
             "enum": [
              "draft",
              "published",
              "closed"
             ]
            },
            "require_login": {
             "type": "boolean"
            },
            "allow_multiple_responses": {
             "type": "boolean"
            },
            "auto_link_project_id": {
             "type": "integer",
             "nullable": true
            },
            "accent_color": {
             "type": "string"
            },
            "background_color": {
             "type": "string"
            },
            "background_image_id": {
             "type": "integer",
             "nullable": true
            },
            "background_preset": {
             "type": "string",
             "enum": [
              "none",
              "soft_clouds",
              "waves",
              "gradient_warm",
              "gradient_cool",
              "bramble"
             ]
            },
            "font_family": {
             "type": "string"
            },
            "button_style": {
             "type": "string",
             "enum": [
              "rounded",
              "square",
              "pill"
             ]
            },
            "card_radius": {
             "type": "string",
             "enum": [
              "rounded",
              "sharp"
             ]
            },
            "layout_style": {
             "type": "string",
             "enum": [
              "classic",
              "title_on_background",
              "card_left",
              "card_right",
              "compact"
             ]
            },
            "notify_on_submission": {
             "type": "boolean"
            },
            "created_by": {
             "type": "integer"
            },
            "created_at": {
             "type": "string",
             "format": "date-time"
            },
            "updated_at": {
             "type": "string",
             "format": "date-time"
            },
            "deleted_at": {
             "type": "string",
             "format": "date-time",
             "nullable": true
            }
           }
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "No fields to update"
     },
     "404": {
      "description": "Form not found"
     },
     "500": {
      "description": "Internal Server Error"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "key": {
          "type": "string"
         }
        }
       }
      }
     }
    }
   },
   "delete": {
    "tags": [
     "Forms"
    ],
    "summary": "Soft delete",
    "operationId": "delete-v1-app-forms-forms-id",
    "description": "Requires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`). Licence: Forms.",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Success",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean"
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "Only the form's owner can delete it"
     },
     "404": {
      "description": "Form not found"
     },
     "500": {
      "description": "Internal Server Error"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ]
   }
  },
  "/v1/app/forms/forms/{id}/structure": {
   "put": {
    "tags": [
     "Forms"
    ],
    "summary": "Bulk save: replaces pages/fields/logic in one transaction (delete-then-reinsert)",
    "operationId": "put-v1-app-forms-forms-id-structure",
    "description": "Bulk save: replaces pages/fields/logic in one transaction (delete-then-reinsert). Simplest robust way to persist a nested builder structure without diffing client vs. server state.\n\nBody: { pages: [{ clientId, title, subtitle, fields: [{ clientId, type, label, help_text, required, options, config, logic: [{ operator, value, source_field_clientId }] }], logic: [...] }] }\n\nRequires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`). Licence: Forms.",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Success",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean"
          }
         }
        }
       }
      }
     },
     "404": {
      "description": "Form not found"
     },
     "500": {
      "description": "Internal Server Error"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "pages": {
          "type": "array",
          "items": {}
         }
        }
       }
      }
     }
    }
   }
  },
  "/v1/app/forms/forms/{id}/publish": {
   "post": {
    "tags": [
     "Forms"
    ],
    "summary": "Go live at /f/:slug",
    "operationId": "post-v1-app-forms-forms-id-publish",
    "description": "Requires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`). Licence: Forms.",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Success"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ]
   }
  },
  "/v1/app/forms/forms/{id}/close": {
   "post": {
    "tags": [
     "Forms"
    ],
    "summary": "Stop accepting new responses",
    "operationId": "post-v1-app-forms-forms-id-close",
    "description": "Requires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`). Licence: Forms.",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Success"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ]
   }
  },
  "/v1/app/forms/forms/{id}/collaborators": {
   "get": {
    "tags": [
     "Collaborators"
    ],
    "summary": "Owner + current collaborator list",
    "operationId": "get-v1-app-forms-forms-id-collaborators",
    "description": "Owner + current collaborator list. Visible to anyone with access (owner or collaborator), so a collaborator can see who else is on the form; only the owner can add/remove/transfer.\n\nRequires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`). Licence: Forms.",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Success",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "owner": {
           "type": "object",
           "properties": {
            "id": {
             "type": "integer"
            }
           }
          },
          "collaborators": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "id": {
              "type": "integer"
             },
             "user": {
              "type": "object"
             },
             "created_at": {
              "type": "string",
              "format": "date-time"
             }
            }
           }
          }
         }
        }
       }
      }
     },
     "404": {
      "description": "Form not found"
     },
     "500": {
      "description": "Internal Server Error"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ]
   },
   "post": {
    "tags": [
     "Collaborators"
    ],
    "summary": "Add a collaborator. Owner only",
    "operationId": "post-v1-app-forms-forms-id-collaborators",
    "description": "Requires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`). Licence: Forms.",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "201": {
      "description": "Success",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "user_id is required"
     },
     "403": {
      "description": "Only the form's owner can manage collaborators"
     },
     "404": {
      "description": "Form not found"
     },
     "500": {
      "description": "Internal Server Error"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "user_id": {
          "type": "integer"
         }
        }
       }
      }
     }
    }
   }
  },
  "/v1/app/forms/forms/{id}/collaborators/available": {
   "get": {
    "tags": [
     "Collaborators"
    ],
    "summary": "Tenant users not already the owner or a collaborator, for the \"add collaborator\" picker",
    "operationId": "get-v1-app-forms-forms-id-collaborators-available",
    "description": "Tenant users not already the owner or a collaborator, for the \"add collaborator\" picker. Owner only, same reasoning as the mutation routes below (search reveals tenant users).\n\nRequires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`). Licence: Forms.",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "q",
      "in": "query",
      "required": false,
      "schema": {}
     }
    ],
    "responses": {
     "200": {
      "description": "Success",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "users": {
           "type": "array",
           "items": {}
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "Only the form's owner can manage collaborators"
     },
     "404": {
      "description": "Form not found"
     },
     "500": {
      "description": "Internal Server Error"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ]
   }
  },
  "/v1/app/forms/forms/{id}/collaborators/{collabId}": {
   "delete": {
    "tags": [
     "Collaborators"
    ],
    "summary": "Owner only",
    "operationId": "delete-v1-app-forms-forms-id-collaborators-collabid",
    "description": "Requires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`). Licence: Forms.",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "collabId",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Success",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean"
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "Only the form's owner can manage collaborators"
     },
     "404": {
      "description": "Form not found"
     },
     "500": {
      "description": "Internal Server Error"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ]
   }
  },
  "/v1/app/forms/forms/{id}/owner": {
   "patch": {
    "tags": [
     "Collaborators"
    ],
    "summary": "Transfer ownership. Owner only; the new owner must already be a collaborator (promotes them and drops the old owner down to collaborator, so they don't lose …",
    "operationId": "patch-v1-app-forms-forms-id-owner",
    "description": "Transfer ownership. Owner only; the new owner must already be a collaborator (promotes them and drops the old owner down to collaborator, so they don't lose access outright).\n\nRequires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`). Licence: Forms.",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Success",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "user_id is required"
     },
     "403": {
      "description": "Only the form's owner can transfer ownership"
     },
     "404": {
      "description": "Form not found"
     },
     "500": {
      "description": "Internal Server Error"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "user_id": {
          "type": "integer"
         }
        }
       }
      }
     }
    }
   }
  },
  "/v1/app/forms/forms/{id}/submissions": {
   "get": {
    "tags": [
     "Responses"
    ],
    "summary": "Paginated list",
    "operationId": "get-v1-app-forms-forms-id-submissions",
    "description": "Requires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`). Licence: Forms.",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "page",
      "in": "query",
      "required": false,
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "pageSize",
      "in": "query",
      "required": false,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Success",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "submissions": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "id": {
              "type": "integer"
             },
             "tenant_id": {
              "type": "integer"
             },
             "form_id": {
              "type": "integer"
             },
             "respondent_user_id": {
              "type": "integer",
              "nullable": true
             },
             "is_anonymous": {
              "type": "boolean"
             },
             "respondent_email": {
              "type": "string",
              "nullable": true
             },
             "submitted_at": {
              "type": "string",
              "format": "date-time"
             },
             "ip_address": {
              "type": "string",
              "nullable": true
             },
             "user_agent": {
              "type": "string",
              "nullable": true
             }
            }
           }
          },
          "total": {
           "type": "integer"
          },
          "page": {
           "type": "integer"
          },
          "pageSize": {}
         }
        }
       }
      }
     },
     "404": {
      "description": "Form not found"
     },
     "500": {
      "description": "Internal Server Error"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ]
   },
   "delete": {
    "tags": [
     "Responses"
    ],
    "summary": "Bulk delete, body: { ids: [1,2,3] }",
    "operationId": "delete-v1-app-forms-forms-id-submissions",
    "description": "Bulk delete, body: { ids: [1,2,3] }. Registered before the single-:subId route below isn't necessary (different HTTP method/no path collision), but kept next to it for readability. Cascades to form_submission_answers/form_uploads/form_submission_links.\n\nRequires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`). Licence: Forms.",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Success",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean"
          },
          "deleted": {
           "type": "integer"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "ids (array of submission ids) is required"
     },
     "404": {
      "description": "Form not found"
     },
     "500": {
      "description": "Internal Server Error"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ]
   }
  },
  "/v1/app/forms/forms/{id}/submissions/export": {
   "get": {
    "tags": [
     "Responses"
    ],
    "summary": "CSV export (register before the :subId route below so \"export\" isn't parsed as a submission id)",
    "operationId": "get-v1-app-forms-forms-id-submissions-export",
    "description": "Requires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`). Licence: Forms.",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "File, page or redirect"
     },
     "404": {
      "description": "Form not found"
     },
     "500": {
      "description": "Internal Server Error"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ]
   }
  },
  "/v1/app/forms/forms/{id}/submissions/{subId}": {
   "get": {
    "tags": [
     "Responses"
    ],
    "summary": "Full detail",
    "operationId": "get-v1-app-forms-forms-id-submissions-subid",
    "description": "Requires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`). Licence: Forms.",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "subId",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Success",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "submission": {},
          "answers": {
           "type": "array",
           "items": {}
          },
          "uploads": {
           "type": "array",
           "items": {}
          }
         }
        }
       }
      }
     },
     "404": {
      "description": "Form not found"
     },
     "500": {
      "description": "Internal Server Error"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ]
   },
   "delete": {
    "tags": [
     "Responses"
    ],
    "summary": "Single delete",
    "operationId": "delete-v1-app-forms-forms-id-submissions-subid",
    "description": "Requires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`). Licence: Forms.",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "subId",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Success",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean"
          }
         }
        }
       }
      }
     },
     "404": {
      "description": "Form not found"
     },
     "500": {
      "description": "Internal Server Error"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ]
   }
  },
  "/v1/app/forms/forms/{id}/stats": {
   "get": {
    "tags": [
     "Forms"
    ],
    "summary": "Response dashboard stat tiles",
    "operationId": "get-v1-app-forms-forms-id-stats",
    "description": "Requires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`). Licence: Forms.",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Success",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "total": {
           "type": "integer"
          },
          "today": {},
          "lastSubmittedAt": {}
         }
        }
       }
      }
     },
     "404": {
      "description": "Form not found"
     },
     "500": {
      "description": "Internal Server Error"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ]
   }
  },
  "/v1/app/forms/forms/{id}/summary": {
   "get": {
    "tags": [
     "Forms"
    ],
    "summary": "Per-question breakdown for the responses \"Summary\" tab: option counts for choice-type fields, top keywords for free-text ones",
    "operationId": "get-v1-app-forms-forms-id-summary",
    "description": "Requires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`). Licence: Forms.",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Success",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "total": {
           "type": "integer"
          },
          "summaries": {
           "type": "array",
           "items": {}
          }
         }
        }
       }
      }
     },
     "404": {
      "description": "Form not found"
     },
     "500": {
      "description": "Internal Server Error"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ]
   }
  },
  "/v1/app/forms/forms/{id}/uploads/{uploadId}": {
   "get": {
    "tags": [
     "Responses"
    ],
    "summary": "Auth-gated file-answer download",
    "operationId": "get-v1-app-forms-forms-id-uploads-uploadid",
    "description": "Requires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`). Licence: Forms.",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "uploadId",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Success"
     },
     "404": {
      "description": "Not found"
     },
     "500": {
      "description": "Internal Server Error"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ]
   }
  },
  "/v1/app/forms/images": {
   "post": {
    "tags": [
     "Images"
    ],
    "summary": "Upload a theme background/banner image (multipart, field \"image\")",
    "operationId": "post-v1-app-forms-images",
    "description": "Requires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`). Licence: Forms.",
    "parameters": [],
    "responses": {
     "201": {
      "description": "Success",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "id": {
           "type": "integer"
          },
          "url": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "No image file provided"
     },
     "500": {
      "description": "Internal Server Error"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ]
   }
  },
  "/v1/app/forms/images/{id}": {
   "get": {
    "tags": [
     "Images"
    ],
    "summary": "Public, no auth (theme images embedded via <img>/CSS background-image)",
    "operationId": "get-v1-app-forms-images-id",
    "description": "Requires: None: public, or protected by a token in the address or body.",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "File, page or redirect"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ]
   }
  },
  "/v1/app/forms/public/{slug}": {
   "get": {
    "tags": [
     "Public form endpoints"
    ],
    "summary": "A published form's structure/theme (404 if draft/closed) optionalAuth so alreadySubmitted can be reported for one-response-per-user forms without requiring t…",
    "operationId": "get-v1-app-forms-public-slug",
    "description": "A published form's structure/theme (404 if draft/closed) optionalAuth so alreadySubmitted can be reported for one-response-per-user forms without requiring the caller to be signed in for forms that don't need it.\n\nRequires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`).",
    "parameters": [
     {
      "name": "slug",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Success",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "form": {},
          "pages": {
           "type": "array",
           "items": {}
          },
          "logic": {},
          "alreadySubmitted": {}
         }
        }
       }
      }
     },
     "404": {
      "description": "Form not found"
     },
     "500": {
      "description": "Internal Server Error"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ]
   }
  },
  "/v1/app/forms/public/{slug}/submit": {
   "post": {
    "tags": [
     "Public form endpoints"
    ],
    "summary": "Submit a response. FormData shape: `answers` = JSON string { [fieldId]: value|value[] }, plus one file part per file-type field named `file_<fieldId>`",
    "operationId": "post-v1-app-forms-public-slug-submit",
    "description": "Requires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`).",
    "parameters": [
     {
      "name": "slug",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "201": {
      "description": "Success",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "You must be signed in to submit this form"
     },
     "404": {
      "description": "Form not found"
     },
     "409": {
      "description": "You've already submitted a response to this form"
     },
     "500": {
      "description": "Internal Server Error"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ],
    "requestBody": {
     "content": {
      "multipart/form-data": {
       "schema": {
        "type": "object",
        "properties": {
         "answers": {
          "type": "array",
          "items": {}
         }
        }
       }
      }
     }
    }
   }
  },
  "/v1/app/forms/forms/{id}/submissions/{subId}/links": {
   "get": {
    "tags": [
     "Responses"
    ],
    "summary": "A response's links to other records",
    "operationId": "get-v1-app-forms-forms-id-submissions-subid-links",
    "description": "Requires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`). Licence: Forms.",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "subId",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Success",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "links": {
           "type": "array",
           "items": {}
          }
         }
        }
       }
      }
     },
     "404": {
      "description": "Form not found"
     },
     "500": {
      "description": "Internal Server Error"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ]
   },
   "post": {
    "tags": [
     "Responses"
    ],
    "summary": "Link a response to a record in another app",
    "operationId": "post-v1-app-forms-forms-id-submissions-subid-links",
    "description": "Requires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`). Licence: Forms.",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "subId",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "201": {
      "description": "Success",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "link": {}
         }
        }
       }
      }
     },
     "400": {
      "description": "target_app must be 'project' or 'todo'"
     },
     "404": {
      "description": "Form not found"
     },
     "500": {
      "description": "Internal Server Error"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "target_app": {
          "type": "string",
          "enum": [
           "project",
           "todo"
          ],
          "description": "The app (e.g. `psa`)."
         },
         "target_id": {
          "type": "number",
          "description": "The record's id there."
         },
         "note": {
          "type": "string",
          "description": "A note."
         }
        },
        "required": [
         "target_id"
        ]
       }
      }
     }
    }
   }
  },
  "/v1/app/forms/forms/{id}/submissions/{subId}/links/{linkId}": {
   "delete": {
    "tags": [
     "Responses"
    ],
    "summary": "Remove a link",
    "operationId": "delete-v1-app-forms-forms-id-submissions-subid-links-linkid",
    "description": "Requires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`). Licence: Forms.",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "subId",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "linkId",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Success",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean"
          }
         }
        }
       }
      }
     },
     "404": {
      "description": "Form not found"
     },
     "500": {
      "description": "Internal Server Error"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ]
   }
  },
  "/v1/app/forms/forms/links": {
   "post": {
    "tags": [
     "Responses"
    ],
    "summary": "Same as the nested create above, but keyed by submission_id alone (no form_id needed in the URL)",
    "operationId": "post-v1-app-forms-forms-links",
    "description": "Same as the nested create above, but keyed by submission_id alone (no form_id needed in the URL). BRM-Project/BRM-ToDo know their own target_id but not which form a submission came from, so they can't hit the nested route above — this is the one they call to create a link from their side.\n\nRequires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`). Licence: Forms.",
    "parameters": [],
    "responses": {
     "201": {
      "description": "Success",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "link": {}
         }
        }
       }
      }
     },
     "400": {
      "description": "target_app must be 'project' or 'todo'"
     },
     "404": {
      "description": "Submission not found"
     },
     "500": {
      "description": "Internal Server Error"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "submission_id": {
          "type": "number"
         },
         "target_app": {
          "type": "string",
          "enum": [
           "project",
           "todo"
          ]
         },
         "target_id": {
          "type": "number"
         },
         "note": {
          "type": "string"
         }
        },
        "required": [
         "submission_id",
         "target_id"
        ]
       }
      }
     }
    }
   }
  },
  "/v1/app/forms/forms/links/for-target": {
   "get": {
    "tags": [
     "Responses"
    ],
    "summary": "The reverse lookup, called from BRM-Project/BRM-ToDo to show what submissions link to a given work package/task",
    "operationId": "get-v1-app-forms-forms-links-for-target",
    "description": "The reverse lookup, called from BRM-Project/BRM-ToDo to show what submissions link to a given work package/task. Not nested under /forms/:id since the caller doesn't know (or care) which form a submission came from.\n\nRequires: Signed-in session, `X-API-Key`, or a Bramble app access token (`Authorization: Bearer`).",
    "parameters": [
     {
      "name": "target_app",
      "in": "query",
      "required": false,
      "schema": {
       "type": "string",
       "enum": [
        "project",
        "todo"
       ]
      }
     },
     {
      "name": "target_id",
      "in": "query",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Success",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "links": {
           "type": "array",
           "items": {}
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "target_app ('project'|'todo') and target_id are required"
     },
     "500": {
      "description": "Internal Server Error"
     }
    },
    "security": [
     {
      "apiKey": []
     },
     {
      "session": []
     },
     {
      "bearer": []
     }
    ]
   }
  }
 }
}