Overview

Forms allow collecting data from users (including external users without a ProBeya account) and automatically creating items on a board from submissions. The form builder is powered by form.io.

List Forms

List all forms associated with a board.

GET /api/trpc/forms.list?input={"json":{"boardId":"brd_01HXK5..."}}

Response:

{
  "result": {
    "data": {
      "json": {
        "forms": [
          {
            "id": "frm_01HXK5QJBN3YZXM8KJP2RSNV4C",
            "boardId": "brd_01HXK5...",
            "name": "Bug Report",
            "description": "Submit a bug report",
            "status": "published",
            "publicUrl": "https://acme.probeya.com/forms/frm_01HXK5...",
            "submissionCount": 34,
            "fieldCount": 8,
            "requireAuth": false,
            "createdBy": "usr_01HXK5...",
            "createdAt": "2026-02-20T10:00:00.000Z",
            "updatedAt": "2026-03-10T14:00:00.000Z"
          }
        ]
      }
    }
  }
}

Get Form

Retrieve a form with its field definitions and column mappings.

GET /api/trpc/forms.get?input={"json":{"id":"frm_01HXK5..."}}

Response:

{
  "result": {
    "data": {
      "json": {
        "id": "frm_01HXK5QJBN3YZXM8KJP2RSNV4C",
        "boardId": "brd_01HXK5...",
        "name": "Bug Report",
        "description": "Submit a bug report for the platform",
        "status": "published",
        "schema": {
          "components": [
            {
              "type": "textfield",
              "key": "title",
              "label": "Bug Title",
              "validate": { "required": true }
            },
            {
              "type": "textarea",
              "key": "description",
              "label": "Description",
              "validate": { "required": true }
            },
            {
              "type": "select",
              "key": "severity",
              "label": "Severity",
              "data": {
                "values": [
                  { "label": "Critical", "value": "critical" },
                  { "label": "Major", "value": "major" },
                  { "label": "Minor", "value": "minor" },
                  { "label": "Trivial", "value": "trivial" }
                ]
              }
            },
            {
              "type": "file",
              "key": "screenshots",
              "label": "Screenshots",
              "multiple": true
            }
          ]
        },
        "mappings": [
          { "formField": "title", "columnId": null, "target": "item_name" },
          { "formField": "description", "columnId": null, "target": "item_description" },
          { "formField": "severity", "columnId": "col_01HXK5...", "target": "column" },
          { "formField": "screenshots", "columnId": "col_01HXK6...", "target": "column" }
        ],
        "settings": {
          "successMessage": "Thank you! Your bug report has been submitted.",
          "redirectUrl": null,
          "notifyOnSubmit": ["usr_01HXK5..."],
          "groupId": "grp_01HXK5...",
          "requireAuth": false,
          "allowMultiple": true
        },
        "publicUrl": "https://acme.probeya.com/forms/frm_01HXK5...",
        "createdAt": "2026-02-20T10:00:00.000Z"
      }
    }
  }
}

Create Form

Create a new form linked to a board.

POST /api/trpc/forms.create

Request:

{
  "json": {
    "boardId": "brd_01HXK5...",
    "name": "Feature Request",
    "description": "Submit a feature request",
    "schema": {
      "components": [
        {
          "type": "textfield",
          "key": "title",
          "label": "Feature Title",
          "validate": { "required": true }
        },
        {
          "type": "textarea",
          "key": "description",
          "label": "Describe the feature"
        },
        {
          "type": "select",
          "key": "priority",
          "label": "How important is this?",
          "data": {
            "values": [
              { "label": "Must have", "value": "critical" },
              { "label": "Nice to have", "value": "low" }
            ]
          }
        }
      ]
    },
    "mappings": [
      { "formField": "title", "target": "item_name" },
      { "formField": "description", "target": "item_description" },
      { "formField": "priority", "columnId": "col_01HXK5...", "target": "column" }
    ],
    "settings": {
      "groupId": "grp_01HXK5...",
      "requireAuth": false,
      "successMessage": "Thank you for your suggestion!"
    }
  }
}

Update Form

POST /api/trpc/forms.update
{
  "json": {
    "id": "frm_01HXK5...",
    "name": "Updated Feature Request Form",
    "status": "published"
  }
}
StatusDescription
draftForm is not accessible to respondents
publishedForm is live and accepting submissions
closedForm is visible but no longer accepting submissions

Delete Form

POST /api/trpc/forms.delete
{
  "json": {
    "id": "frm_01HXK5..."
  }
}

Deleting a form does not delete items created from its submissions. The items remain on the board.

List Submissions

View submissions for a specific form.

GET /api/trpc/forms.submissions.list?input={"json":{"formId":"frm_01HXK5..."}}

Input Parameters:

ParameterTypeRequiredDescription
formIdstringYesForm ID
cursorstringNoPagination cursor
limitnumberNoResults per page (default: 50)
statusstringNoFilter: new, reviewed, archived

Response:

{
  "result": {
    "data": {
      "json": {
        "submissions": [
          {
            "id": "sub_01HXK5...",
            "formId": "frm_01HXK5...",
            "data": {
              "title": "Add dark mode support",
              "description": "It would be great to have a dark mode option...",
              "priority": "critical"
            },
            "itemId": "itm_01HXK5...",
            "status": "new",
            "submittedBy": null,
            "submittedAt": "2026-03-19T14:30:00.000Z"
          }
        ],
        "nextCursor": null
      }
    }
  }
}

The submittedBy field is null for anonymous submissions (when requireAuth is false). When authentication is required, it contains the submitting user’s ID.

Submit Form (Public)

Submit a form response. This endpoint is publicly accessible for published forms that do not require authentication.

POST https://acme.probeya.com/api/forms/frm_01HXK5.../submit
Content-Type: application/json
{
  "title": "Mobile app crashes on login",
  "description": "The app crashes when I try to log in with Google OAuth...",
  "severity": "critical",
  "screenshots": ["file_01HXK5..."]
}

Response:

{
  "success": true,
  "message": "Thank you! Your bug report has been submitted.",
  "submissionId": "sub_01HXK5..."
}

Form Approval Flows API

Approval flows define multi-step review workflows for form submissions. All management endpoints require organization-level authentication (orgProcedure).

Create Approval Flow

Create a new approval flow for a form. If no nodes/edges are provided, a default start-to-end flow is created.

POST /api/trpc/formApprovalFlows.create

Input:

ParameterTypeRequiredDescription
formIdstringYesThe form to attach the flow to
namestringYesDisplay name for the flow
descriptionstringNoOptional description
gxpRequiredbooleanNoEnable GxP e-signature mode (default: false)
nodesFlowNode[]NoGraph nodes (start, approval, condition, action, end)
edgesFlowEdge[]NoConnections between nodes
{
  "json": {
    "formId": "frm_01HXK5...",
    "name": "QA Review Workflow",
    "gxpRequired": true,
    "nodes": [
      { "id": "start-1", "type": "start", "position": { "x": 250, "y": 50 }, "data": {} },
      { "id": "approval-1", "type": "approval", "position": { "x": 250, "y": 200 }, "data": { "approverIds": ["usr_01..."], "approvalType": "any" } },
      { "id": "end-1", "type": "end", "position": { "x": 250, "y": 400 }, "data": { "status": "approved" } }
    ],
    "edges": [
      { "id": "e1", "source": "start-1", "target": "approval-1" },
      { "id": "e2", "source": "approval-1", "target": "end-1" }
    ]
  }
}

Update Approval Flow

Update flow metadata and/or graph structure. Only provided fields are updated.

POST /api/trpc/formApprovalFlows.update
ParameterTypeRequiredDescription
idstringYesFlow ID
namestringNoUpdated name
descriptionstringNoUpdated description
gxpRequiredbooleanNoToggle GxP mode
nodesFlowNode[]NoUpdated graph nodes
edgesFlowEdge[]NoUpdated graph edges

Delete Approval Flow

Soft-delete a flow (sets isActive = false). Fails if the flow has active (pending/in_progress) requests.

POST /api/trpc/formApprovalFlows.delete
{ "json": { "id": "flow_01HXK5..." } }

Activate / Deactivate Flow

Toggle a flow’s active state. Activating a flow deactivates any other active flow for the same form (only one flow active per form).

POST /api/trpc/formApprovalFlows.activate
POST /api/trpc/formApprovalFlows.deactivate
{ "json": { "id": "flow_01HXK5..." } }

Activating a flow validates graph integrity first. Flows with missing start nodes, orphan nodes, or disconnected subgraphs cannot be activated.

Get Flow by ID

Returns the flow definition with request statistics (counts by status).

GET /api/trpc/formApprovalFlows.getById?input={"json":{"id":"flow_01HXK5..."}}

List Flows by Form

Returns all flows for a form with request counts.

GET /api/trpc/formApprovalFlows.listByForm?input={"json":{"formId":"frm_01HXK5..."}}

Approval Requests API

Approval requests are created automatically when a submission enters an active flow. These endpoints manage and query requests.

Get Requests

Paginated list of approval requests for a flow.

GET /api/trpc/formApprovalFlows.getRequests?input={"json":{"flowId":"flow_01HXK5...","page":1,"pageSize":20}}
ParameterTypeRequiredDescription
flowIdstringYesThe flow to query requests for
pagenumberNoPage number (default: 1)
pageSizenumberNoItems per page (default: 20, max: 100)
statusstringNoFilter by status: pending, in_progress, approved, rejected, expired

Get Request by ID

Full request detail including decisions, version history, and parent flow definition.

GET /api/trpc/formApprovalFlows.getRequestById?input={"json":{"id":"req_01HXK5..."}}

Submit Decision (Authenticated)

Submit an approval decision as an authenticated user. Requires orgProcedure authentication.

POST /api/trpc/formApprovalFlows.submitDecisionAuthenticated
ParameterTypeRequiredDescription
requestIdstringYesThe approval request ID
nodeIdstringYesThe current approval node ID
decisionstringYesapproved, rejected, or returned_for_revision
commentstringNoOptional comment
fieldModificationsarrayNoArray of { fieldId, newValue } to modify submission fields

Response:

{
  "decisionId": "dec_01HXK5...",
  "stepComplete": true,
  "stepResult": "approved"
}

Submit Decision (By Token)

Submit a decision via email token. Public endpoint — no session required.

POST /api/trpc/formApprovalFlows.submitDecisionByToken
ParameterTypeRequiredDescription
tokenstringYesThe email approval token
decisionstringYesapproved, rejected, or returned_for_revision
commentstringNoOptional comment
fieldModificationsarrayNoField modifications
ipAddressstringNoClient IP for audit trail
userAgentstringNoClient user agent for audit trail

Return for Revision

Send a submission back to the respondent for edits. Requires a comment explaining what needs to change.

POST /api/trpc/formApprovalFlows.returnForRevision
{
  "json": {
    "requestId": "req_01HXK5...",
    "comment": "Please update the batch number -- it should be 6 digits."
  }
}

Distribution API

Distribution enables sending personalized form links to specific recipients with engagement tracking.

Create Distribution

Send a form to a list of email recipients. Each recipient gets a unique tracking token.

POST /api/trpc/forms.createDistribution
ParameterTypeRequiredDescription
formIdstringYesThe form to distribute (must be published)
recipientsarrayYesArray of { email: string, name?: string }

Response:

{
  "success": true,
  "sentCount": 15,
  "message": "Form distributed to 15 recipient(s)"
}

Get Distribution Status

Returns all recipients with their engagement status and aggregate statistics.

GET /api/trpc/forms.getDistributionStatus?input={"json":{"formId":"frm_01HXK5..."}}

Response:

{
  "recipients": [
    {
      "id": "dr_01HXK5...",
      "email": "[email protected]",
      "name": "Maria Santos",
      "status": "completed",
      "sentAt": "2026-04-10T09:00:00.000Z",
      "openedAt": "2026-04-10T09:15:00.000Z",
      "completedAt": "2026-04-10T09:30:00.000Z",
      "reminderCount": 0
    }
  ],
  "stats": {
    "total": 15,
    "opened": 12,
    "started": 10,
    "completed": 8,
    "responseRate": 53.3
  }
}

Send Reminders

Send reminder emails to all recipients who have not completed the form.

POST /api/trpc/forms.sendReminders
{ "json": { "formId": "frm_01HXK5..." } }

Response:

{
  "success": true,
  "remindersSent": 7,
  "message": "Sent 7 reminder(s)"
}

OTP API

One-time password verification for anonymous form respondents.

Request OTP

Send a 6-digit OTP code to an email address for form access verification. Always returns { sent: true } to prevent email enumeration.

POST /api/trpc/forms.requestOtp
ParameterTypeRequiredDescription
formSlugstringYesThe form’s URL slug
organizationSlugstringYesThe organization’s URL slug
emailstringYesRecipient email address

Response:

{ "sent": true }

Verify OTP

Verify a one-time password code and receive a session token for form access.

POST /api/trpc/forms.verifyOtp
ParameterTypeRequiredDescription
formIdstringYesThe form ID
emailstringYesThe email the OTP was sent to
codestringYesThe 6-digit OTP code

Response (success):

{
  "valid": true,
  "sessionToken": "clx9abc..."
}

Response (failure):

{
  "valid": false,
  "error": "Invalid code"
}

OTP codes expire after 10 minutes. A maximum of 5 verification attempts are allowed per code. After 5 failed attempts, the code is invalidated and a new one must be requested.