Forms API
API endpoints for managing forms and viewing form submissions.
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"
}
}
| Status | Description |
|---|---|
draft | Form is not accessible to respondents |
published | Form is live and accepting submissions |
closed | Form 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:
| Parameter | Type | Required | Description |
|---|---|---|---|
formId | string | Yes | Form ID |
cursor | string | No | Pagination cursor |
limit | number | No | Results per page (default: 50) |
status | string | No | Filter: 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:
| Parameter | Type | Required | Description |
|---|---|---|---|
formId | string | Yes | The form to attach the flow to |
name | string | Yes | Display name for the flow |
description | string | No | Optional description |
gxpRequired | boolean | No | Enable GxP e-signature mode (default: false) |
nodes | FlowNode[] | No | Graph nodes (start, approval, condition, action, end) |
edges | FlowEdge[] | No | Connections 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
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Flow ID |
name | string | No | Updated name |
description | string | No | Updated description |
gxpRequired | boolean | No | Toggle GxP mode |
nodes | FlowNode[] | No | Updated graph nodes |
edges | FlowEdge[] | No | Updated 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}}
| Parameter | Type | Required | Description |
|---|---|---|---|
flowId | string | Yes | The flow to query requests for |
page | number | No | Page number (default: 1) |
pageSize | number | No | Items per page (default: 20, max: 100) |
status | string | No | Filter 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
| Parameter | Type | Required | Description |
|---|---|---|---|
requestId | string | Yes | The approval request ID |
nodeId | string | Yes | The current approval node ID |
decision | string | Yes | approved, rejected, or returned_for_revision |
comment | string | No | Optional comment |
fieldModifications | array | No | Array 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
| Parameter | Type | Required | Description |
|---|---|---|---|
token | string | Yes | The email approval token |
decision | string | Yes | approved, rejected, or returned_for_revision |
comment | string | No | Optional comment |
fieldModifications | array | No | Field modifications |
ipAddress | string | No | Client IP for audit trail |
userAgent | string | No | Client 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
| Parameter | Type | Required | Description |
|---|---|---|---|
formId | string | Yes | The form to distribute (must be published) |
recipients | array | Yes | Array 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
| Parameter | Type | Required | Description |
|---|---|---|---|
formSlug | string | Yes | The form’s URL slug |
organizationSlug | string | Yes | The organization’s URL slug |
email | string | Yes | Recipient 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
| Parameter | Type | Required | Description |
|---|---|---|---|
formId | string | Yes | The form ID |
email | string | Yes | The email the OTP was sent to |
code | string | Yes | The 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.