{
  "openapi": "3.0.3",
  "info": {
    "title": "Synctactic FormStudio API",
    "version": "1.0.0",
    "description": "Authenticated workspace synchronization, shared reports, collaboration, previews and main portal data access. Login through the main portal first. The canonical API domain is available over HTTPS."
  },
  "servers": [
    {
      "url": "https://api.synctacticsystems.com",
      "description": "Canonical API service"
    },
    {
      "url": "https://forms.synctacticsystems.com",
      "description": "Live compatibility gateway"
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "summary": "Service health",
        "description": "Liveness only; does not claim that the database is available.",
        "security": [],
        "responses": {
          "200": {
            "description": "Successful response"
          }
        }
      }
    },
    "/ready": {
      "get": {
        "summary": "Database readiness",
        "description": "Checks database connectivity without exposing connection details.",
        "security": [],
        "responses": {
          "200": {
            "description": "Database available"
          },
          "503": {
            "description": "Database unavailable"
          }
        }
      }
    },
    "/v1/formstudio/session": {
      "get": {
        "summary": "Verify portal session",
        "description": "Returns the main portal account response. Does not create accounts or accept passwords.",
        "security": [
          {
            "portalBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response"
          },
          "401": {
            "description": "Missing, invalid, or expired main portal token"
          },
          "403": {
            "description": "Origin or report permission rejected"
          },
          "400": {
            "description": "Invalid request"
          },
          "503": {
            "description": "Dependency temporarily unavailable; retry with backoff"
          }
        }
      }
    },
    "/v1/formstudio/workspace": {
      "get": {
        "summary": "Read account workspace changes",
        "description": "Returns {records,cursor,more}. Pages contain at most 20 records; small values may be inline. For a large record use key, version and part to receive {chunk}.",
        "security": [
          {
            "portalBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response"
          },
          "401": {
            "description": "Missing, invalid, or expired main portal token"
          },
          "403": {
            "description": "Origin or report permission rejected"
          },
          "400": {
            "description": "Invalid request"
          },
          "503": {
            "description": "Dependency temporarily unavailable; retry with backoff"
          }
        },
        "parameters": [
          {
            "name": "after",
            "in": "query",
            "required": false,
            "description": "Revision cursor",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "key",
            "in": "query",
            "required": false,
            "description": "Account-scoped storage key for chunk retrieval",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "version",
            "in": "query",
            "required": false,
            "description": "Expected record version",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "part",
            "in": "query",
            "required": false,
            "description": "Zero-based chunk number",
            "schema": {
              "type": "integer"
            }
          }
        ]
      },
      "post": {
        "summary": "Upload workspace mutations",
        "description": "An upload has key, mutationId UUID, baseVersion, parts, index, deleted and chunk. Chunk limit: 250,000 characters; 512 parts. Retry the same mutation ID and identical chunks. A batch supports 1\u201320 single-part items and total JSON length up to 200,000 characters.",
        "security": [
          {
            "portalBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response"
          },
          "401": {
            "description": "Missing, invalid, or expired main portal token"
          },
          "403": {
            "description": "Origin or report permission rejected"
          },
          "400": {
            "description": "Invalid request"
          },
          "503": {
            "description": "Dependency temporarily unavailable; retry with backoff"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/WorkspaceMutation"
                  },
                  {
                    "type": "object",
                    "required": [
                      "batch"
                    ],
                    "properties": {
                      "batch": {
                        "type": "array",
                        "minItems": 1,
                        "maxItems": 20,
                        "items": {
                          "$ref": "#/components/schemas/WorkspaceMutation"
                        }
                      }
                    }
                  }
                ]
              },
              "example": {
                "key": "formstudio-demo:user%40example.com:BayanPay:custom-report",
                "mutationId": "10000000-0000-4000-8000-000000000001",
                "baseVersion": 0,
                "parts": 1,
                "index": 0,
                "deleted": false,
                "chunk": "{\"name\":\"Example\"}"
              }
            }
          }
        }
      }
    },
    "/v1/formstudio/collaboration": {
      "post": {
        "summary": "Shared reports, history and collaboration",
        "description": "Send an action JSON object. All report actions except list/create require id. See the action reference below for permissions and payloads. Concurrent commits merge against baseVersion and retain previous versions.",
        "security": [
          {
            "portalBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response"
          },
          "401": {
            "description": "Missing, invalid, or expired main portal token"
          },
          "403": {
            "description": "Origin or report permission rejected"
          },
          "400": {
            "description": "Invalid request"
          },
          "503": {
            "description": "Dependency temporarily unavailable; retry with backoff"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "action"
                ],
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "list",
                      "create",
                      "read",
                      "snapshot",
                      "original",
                      "versions",
                      "comments",
                      "presence",
                      "commit",
                      "comment",
                      "resolve",
                      "nameVersion",
                      "member",
                      "linkAccess",
                      "delete",
                      "thumbnail",
                      "read-thumbnail",
                      "upload"
                    ]
                  },
                  "id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "mutation": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "baseVersion": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "template": {
                    "type": "object"
                  },
                  "data": {
                    "type": "object"
                  }
                },
                "additionalProperties": true
              },
              "example": {
                "action": "list",
                "search": "",
                "offset": 0
              }
            }
          }
        }
      }
    },
    "/v1/formstudio/thumbnail": {
      "get": {
        "summary": "Fetch authorized report thumbnail",
        "description": "Returns image/webp. Supports ETag and If-None-Match (304). Returns 404 if no current thumbnail exists. Report membership/link permissions are checked before serving the image.",
        "security": [
          {
            "portalBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response"
          },
          "401": {
            "description": "Missing, invalid, or expired main portal token"
          },
          "403": {
            "description": "Origin or report permission rejected"
          },
          "400": {
            "description": "Invalid request"
          },
          "503": {
            "description": "Dependency temporarily unavailable; retry with backoff"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "description": "Shared report UUID",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "version",
            "in": "query",
            "required": false,
            "description": "Optional client cache version",
            "schema": {
              "type": "integer"
            }
          }
        ]
      }
    },
    "/v1/formstudio/data/{systemId}": {
      "get": {
        "summary": "Read app demo records",
        "description": "Returns the signed-in account\u2019s saved native app workspace. SQL execution and file export remain in the report engine; this endpoint does not accept arbitrary SQL.",
        "security": [
          {
            "portalBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response"
          },
          "401": {
            "description": "Missing, invalid, or expired main portal token"
          },
          "403": {
            "description": "Origin or report permission rejected"
          },
          "400": {
            "description": "Invalid request"
          },
          "503": {
            "description": "Dependency temporarily unavailable; retry with backoff"
          }
        },
        "parameters": [
          {
            "name": "systemId",
            "in": "path",
            "required": true,
            "description": "Registered app system ID",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/v1/formstudio/access/context": {
      "get": {
        "summary": "Read role context",
        "description": "Business list and available report roles.",
        "security": [
          {
            "portalBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response"
          },
          "401": {
            "description": "Missing, invalid, or expired main portal token"
          },
          "403": {
            "description": "Origin or report permission rejected"
          },
          "400": {
            "description": "Invalid request"
          },
          "503": {
            "description": "Dependency temporarily unavailable; retry with backoff"
          }
        },
        "parameters": [
          {
            "name": "appId",
            "in": "query",
            "required": true,
            "description": "App/system ID",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "businessId",
            "in": "query",
            "required": false,
            "description": "Selected business ID",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/v1/formstudio/access/catalog": {
      "get": {
        "summary": "Read included report permissions",
        "description": "Catalog policy and effective permissions for the requested business and app.",
        "security": [
          {
            "portalBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response"
          },
          "401": {
            "description": "Missing, invalid, or expired main portal token"
          },
          "403": {
            "description": "Origin or report permission rejected"
          },
          "400": {
            "description": "Invalid request"
          },
          "503": {
            "description": "Dependency temporarily unavailable; retry with backoff"
          }
        },
        "parameters": [
          {
            "name": "appId",
            "in": "query",
            "required": true,
            "description": "App/system ID",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "businessId",
            "in": "query",
            "required": false,
            "description": "Selected business ID",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/v1/formstudio/access/reports": {
      "get": {
        "summary": "Read accessible custom reports",
        "description": "Returns reports allowed by the portal business/app roles.",
        "security": [
          {
            "portalBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response"
          },
          "401": {
            "description": "Missing, invalid, or expired main portal token"
          },
          "403": {
            "description": "Origin or report permission rejected"
          },
          "400": {
            "description": "Invalid request"
          },
          "503": {
            "description": "Dependency temporarily unavailable; retry with backoff"
          }
        },
        "parameters": [
          {
            "name": "appId",
            "in": "query",
            "required": true,
            "description": "App/system ID",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/v1/formstudio/access/businesses/{businessId}/apps/{appId}/roles": {
      "post": {
        "summary": "Create custom report role",
        "description": "Portal enforces business owner access.",
        "security": [
          {
            "portalBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response"
          },
          "401": {
            "description": "Missing, invalid, or expired main portal token"
          },
          "403": {
            "description": "Origin or report permission rejected"
          },
          "400": {
            "description": "Invalid request"
          },
          "503": {
            "description": "Dependency temporarily unavailable; retry with backoff"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              },
              "example": {
                "label": "Report reviewer",
                "description": "Reviews reports",
                "permissions": [
                  "reports:view"
                ]
              }
            }
          }
        },
        "parameters": [
          {
            "name": "businessId",
            "in": "path",
            "required": true,
            "description": "Business ID",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "appId",
            "in": "path",
            "required": true,
            "description": "App/system ID",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/v1/formstudio/access/businesses/{businessId}/apps/{appId}/catalog/{reportId}": {
      "post": {
        "summary": "Set included report access",
        "description": "Set enabled and grants, or {mode:\"default\"}. Portal enforces business ownership.",
        "security": [
          {
            "portalBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response"
          },
          "401": {
            "description": "Missing, invalid, or expired main portal token"
          },
          "403": {
            "description": "Origin or report permission rejected"
          },
          "400": {
            "description": "Invalid request"
          },
          "503": {
            "description": "Dependency temporarily unavailable; retry with backoff"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              },
              "example": {
                "enabled": true,
                "grants": [
                  {
                    "role": "report-reviewer",
                    "permissions": [
                      "reports:view"
                    ]
                  }
                ]
              }
            }
          }
        },
        "parameters": [
          {
            "name": "businessId",
            "in": "path",
            "required": true,
            "description": "businessId",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "appId",
            "in": "path",
            "required": true,
            "description": "appId",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "reportId",
            "in": "path",
            "required": true,
            "description": "reportId",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/v1/formstudio/access/businesses/{businessId}/apps/{appId}/reports/{reportId}": {
      "post": {
        "summary": "Save custom report access",
        "description": "Set name, grants and template. To delete the registration send {deleted:true}. Portal enforces ownership.",
        "security": [
          {
            "portalBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response"
          },
          "401": {
            "description": "Missing, invalid, or expired main portal token"
          },
          "403": {
            "description": "Origin or report permission rejected"
          },
          "400": {
            "description": "Invalid request"
          },
          "503": {
            "description": "Dependency temporarily unavailable; retry with backoff"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              },
              "example": {
                "name": "Monthly report",
                "grants": [],
                "template": {}
              }
            }
          }
        },
        "parameters": [
          {
            "name": "businessId",
            "in": "path",
            "required": true,
            "description": "businessId",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "appId",
            "in": "path",
            "required": true,
            "description": "appId",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "reportId",
            "in": "path",
            "required": true,
            "description": "reportId",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/v1/formstudio/integrations/keys": {
      "post": {
        "summary": "Manage scoped integration credentials",
        "description": "Main portal bearer session only. Actions: create, list, revoke, audit. Tokens are returned once; stored SHA-256 digests, 1\u201390 day expiry. Audit is paginated using before. No OAuth exchange is implemented.",
        "security": [
          {
            "portalBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "action"
                ],
                "properties": {
                  "action": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "action": "create",
                "name": "Report agent",
                "scopes": [
                  "read",
                  "write"
                ],
                "reports": [
                  "00000000-0000-4000-8000-000000000001"
                ],
                "expiresInDays": 30
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Operation completed"
          },
          "400": {
            "description": "Invalid request"
          },
          "401": {
            "description": "Authentication required"
          },
          "403": {
            "description": "Scope or report permission denied"
          },
          "404": {
            "description": "Resource unavailable"
          },
          "409": {
            "description": "Base version unavailable"
          },
          "413": {
            "description": "Request too large"
          },
          "422": {
            "description": "Invalid template; errors include field paths"
          },
          "429": {
            "description": "Rate limited; Retry-After header"
          },
          "503": {
            "description": "Dependency unavailable"
          }
        }
      }
    },
    "/v1/formstudio/integrations/reports": {
      "post": {
        "summary": "Validated agent report operations",
        "description": "Accepts main portal bearer or scoped fsi_ credential. Supports collaboration actions excluding upload, plus validate and patch. Credentials are bounded to report UUIDs and creation apps. 60 successful requests per minute per credential; current report role still applies. Validation checks structure, geometry and unsafe keys, not formula semantics or SQL execution.",
        "security": [
          {
            "portalBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "action"
                ],
                "properties": {
                  "action": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "action": "patch",
                "id": "00000000-0000-4000-8000-000000000001",
                "baseVersion": 1,
                "mutation": "00000000-0000-4000-8000-000000000002",
                "edits": [
                  {
                    "op": "update",
                    "collection": "canvasItems",
                    "id": "title",
                    "value": {
                      "content": "New title",
                      "color": "#4f46e5"
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Operation completed"
          },
          "400": {
            "description": "Invalid request"
          },
          "401": {
            "description": "Authentication required"
          },
          "403": {
            "description": "Scope or report permission denied"
          },
          "404": {
            "description": "Resource unavailable"
          },
          "409": {
            "description": "Base version unavailable"
          },
          "413": {
            "description": "Request too large"
          },
          "422": {
            "description": "Invalid template; errors include field paths"
          },
          "429": {
            "description": "Rate limited; Retry-After header"
          },
          "503": {
            "description": "Dependency unavailable"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "portalBearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "Main portal session token. Integration report operations also accept a scoped fsi_ credential. Credential management requires a portal session."
      }
    },
    "schemas": {
      "WorkspaceMutation": {
        "type": "object",
        "required": [
          "key",
          "mutationId",
          "baseVersion",
          "parts",
          "index",
          "deleted",
          "chunk"
        ],
        "properties": {
          "key": {
            "type": "string",
            "maxLength": 600
          },
          "mutationId": {
            "type": "string",
            "format": "uuid"
          },
          "baseVersion": {
            "type": "integer",
            "minimum": 0
          },
          "parts": {
            "type": "integer",
            "minimum": 1,
            "maximum": 512
          },
          "index": {
            "type": "integer",
            "minimum": 0,
            "maximum": 511
          },
          "deleted": {
            "type": "boolean"
          },
          "chunk": {
            "type": "string",
            "maxLength": 250000
          }
        }
      }
    }
  },
  "x-collaboration-actions": [
    {
      "action": "list",
      "description": "Accessible reports; search and offset paginate the library",
      "role": "Any signed-in user"
    },
    {
      "action": "create",
      "description": "Create a shared report with template, appId, data, originalTemplate and mutation UUID",
      "role": "Signed-in creator"
    },
    {
      "action": "read",
      "description": "Report metadata, effective role, members (owner only), comments and presence",
      "role": "Viewer or above"
    },
    {
      "action": "snapshot",
      "description": "Version snapshot split into 200,000-character chunks; version and part required",
      "role": "Viewer or above"
    },
    {
      "action": "original",
      "description": "Original report snapshot split into chunks; part required",
      "role": "Viewer or above"
    },
    {
      "action": "versions",
      "description": "Retained version history; before is an exclusive version cursor",
      "role": "Viewer or above"
    },
    {
      "action": "comments",
      "description": "Paginated comments; before accepts {id, at}",
      "role": "Viewer or above"
    },
    {
      "action": "presence",
      "description": "Heartbeat with session UUID, normalized cursor {x,y}, selection, activity; leave=true removes session",
      "role": "Viewer or above"
    },
    {
      "action": "commit",
      "description": "Update template against baseVersion; mutation UUID makes retries idempotent",
      "role": "Editor or owner"
    },
    {
      "action": "comment",
      "description": "Post text and optional selection with mutation UUID",
      "role": "Commenter or above"
    },
    {
      "action": "resolve",
      "description": "Set commentId and resolved boolean",
      "role": "Commenter or above"
    },
    {
      "action": "nameVersion",
      "description": "Set version and label (up to 200 characters)",
      "role": "Editor or owner"
    },
    {
      "action": "member",
      "description": "Set email and role: viewer, commenter, editor, or remove",
      "role": "Owner"
    },
    {
      "action": "linkAccess",
      "description": "Set role: viewer, commenter, editor, or null for invited people only",
      "role": "Owner"
    },
    {
      "action": "delete",
      "description": "Soft-delete report; prior versions stay retained",
      "role": "Owner"
    },
    {
      "action": "thumbnail",
      "description": "Store WebP data URL (43,755-character limit) for current version",
      "role": "Editor or owner"
    },
    {
      "action": "read-thumbnail",
      "description": "Return current thumbnail data URL or null",
      "role": "Viewer or above"
    },
    {
      "action": "upload",
      "description": "Chunk an oversized action with mutation UUID, parts, part, chunk; chunks up to 200,000 characters",
      "role": "Underlying action role"
    }
  ]
}
