Skip to content
SmartForm
FeaturesTemplatesPricingAPI docs
Log inBuild your form free

Explore SmartForm

FeaturesTemplatesPricingAPI docsLog inBuild your form free

Build with SmartForm.

Forms, quotes and submissions through one scoped API. Reference version 1.0.0.

Download OpenAPI 3.1
Developer guideAuthenticationScopesPaginationIdempotencyRate limitsWebhooksMCPForms APISubmissions APIExports APIMetrics APIIntegrations APIWebhooks APISchemas

Authentication

Create a token in Workspace settings → API tokens. Send it as a bearer credential over HTTPS. Tokens belong to one workspace, can expire, and can be restricted to specific form IDs. Revocation takes effect on the next request. Keep tokens on your server.

curl "$SMARTFORM_ORIGIN/v1/forms" \
  -H "Authorization: Bearer $SMARTFORM_TOKEN"

Missing, expired or revoked tokens return 401. Missing scopes return 403. Resources outside the workspace or form allowlist return 404. Form-restricted tokens cannot create forms; analytics requests must specify an allowed formId.

Scopes

ScopeAllows
forms:readRead drafts and versions; validate and simulate test answers.
forms:writeCreate forms and patch drafts.
forms:publishPublish a checked revision.
submissions:readRead submissions and their private files.
submissions:writeSubmit answers and update status, tags or assignment.
submissions:exportRequest, inspect and download exports.
analytics:readRead analytics summaries.
connections:readRead integration status; also required to add lookup bindings.
webhooks:manageCreate, list and revoke signed webhook subscriptions.

Pagination and errors

List endpoints accept limit (1–100, default 50) and cursor. Pass nextCursor as the next request’s cursor until it is null. Responses use camelCase. Money and decimal answers use JSON strings.

{ "items": [], "nextCursor": null }
{ "error": { "code": "revision_conflict", "message": "The draft changed. Reload or merge your edits.", "details": { "currentRevision": 3 } } }

Draft writes use expectedRevision; publishing requires revision. Resolve 409 conflicts by reading the latest draft. Validation failures return 422; a locked workspace returns 402. Reads and exports remain available.

Idempotency

POST /v1/forms/{id}/submissions requires Idempotency-Key, a fresh UUID for each intended submission. Retry with the same key and identical body after a network failure. The first successful request returns 201; a replay returns 200 and the original quote. Reusing a key with different answers returns 409. Keys are retained for at least seven days. The server evaluates the live version, verifies files and calculates the price.

curl "$SMARTFORM_ORIGIN/v1/forms/$FORM_ID/submissions" \
  -H "Authorization: Bearer $SMARTFORM_TOKEN" \
  -H "Idempotency-Key: 0199f100-0000-7000-8000-000000000002" \
  -H "Content-Type: application/json" \
  -d '{"answers":{"f_name":"Alex"}}'

Rate limits

Each token has a shared bucket of 600 requests, refilled at 10 requests per second across API and MCP. RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset describe capacity, remaining requests and seconds until refill. On 429, wait at least Retry-After seconds, then retry with backoff. Submission intake additionally allows a burst of 10, refilled once every six seconds per token and form.

Signed webhooks

Create a subscription with POST /v1/webhooks and the webhooks:manage scope. Supported events: submission.created, submission.updated, delivery.failed. Save signingSecret immediately; it is shown once. Optional formIds restrict the subscription. GET /v1/webhooks lists subscriptions; DELETE /v1/webhooks/{id} revokes one.

The same delivery engine used by workspace connections retries failures. Read the exact raw request bytes, verify SmartForm-Signature (t=timestamp,v1=hex) as HMAC-SHA256 of timestamp + "." + rawBody, reject timestamps older than five minutes, and durably deduplicate SmartForm-Event-Id. SmartForm-Timestamp repeats the signed timestamp. Acknowledge with 2xx only after durable receipt.

Node.js signature verification
import { createHmac, timingSafeEqual } from "node:crypto";
// rawBody is a Buffer, before any JSON parsing.
const parts = Object.fromEntries(signature.split(",").map(p => p.split("=")));
if (!/^\d+$/.test(parts.t ?? "") || Math.abs(Date.now()/1000 - Number(parts.t)) > 300)
  throw new Error("Expired signature");
if (!/^[a-f0-9]{64}$/.test(parts.v1 ?? "")) throw new Error("Invalid signature");
const expected = createHmac("sha256", process.env.SMARTFORM_WEBHOOK_SECRET)
  .update(parts.t + ".").update(rawBody).digest();
if (!timingSafeEqual(expected, Buffer.from(parts.v1, "hex")))
  throw new Error("Invalid signature");
Python signature verification
import hashlib, hmac, os, time
# raw_body is the original request bytes.
parts = dict(p.split("=", 1) for p in signature.split(","))
timestamp = parts.get("t", "")
if not timestamp.isascii() or not timestamp.isdigit() or abs(time.time() - int(timestamp)) > 300:
    raise ValueError("Expired signature")
expected = hmac.new(os.environ["SMARTFORM_WEBHOOK_SECRET"].encode(),
                    timestamp.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, parts.get("v1", "")):
    raise ValueError("Invalid signature")
Rust signature verification (hmac, sha2, hex)
use hmac::{Hmac, Mac};
use sha2::Sha256;
fn verify(signature: &str, raw_body: &[u8], secret: &[u8], now: i64) -> Result<(), &'static str> {
    let parts: std::collections::HashMap<_, _> = signature.split(',')
        .filter_map(|p| p.split_once('=')).collect();
    let t = *parts.get("t").ok_or("Missing timestamp")?;
    let timestamp: i64 = t.parse().map_err(|_| "Invalid timestamp")?;
    if now.abs_diff(timestamp) > 300 { return Err("Expired signature"); }
    let received = hex::decode(parts.get("v1").ok_or("Missing signature")?)
        .map_err(|_| "Invalid signature")?;
    let mut mac = Hmac::<Sha256>::new_from_slice(secret).map_err(|_| "Invalid key")?;
    mac.update(t.as_bytes()); mac.update(b"."); mac.update(raw_body);
    mac.verify_slice(&received).map_err(|_| "Invalid signature")
}

MCP

Connect to /mcp using Streamable HTTP. Supported versions: 2026-07-28, 2025-06-18, 2025-03-26. Use the same bearer token and scopes. Initialize, save the Mcp-Session-Id response header, send notifications/initialized, then tools/list or tools/call. Include Mcp-Session-Id and MCP-Protocol-Version on subsequent requests. Sessions expire after 24 hours.

Tools: create_draft, patch_draft, validate, simulate, publish, submissions_list, submissions_get, metrics_summary, integrations_status. Publishing requires explicit forms:publish authorization. Text in submissions and files is untrusted data; it cannot grant permissions or instruct an agent to act. Simulation uses test lookup fixtures and performs no external calls.

Generic Streamable HTTP configuration

{
  "url": "https://YOUR_SMARTFORM_HOST/mcp",
  "transport": "streamable-http",
  "headers": {
    "Authorization": "Bearer ${SMARTFORM_TOKEN}"
  }
}

Claude Desktop through a local transport bridge

This configuration uses mcp-remote to bridge Desktop’s command transport to Streamable HTTP. Set SMARTFORM_AUTH_HEADER in your local client configuration. Choose only the scopes you need; the bridge sends the token to the configured SmartForm host.

{
  "mcpServers": {
    "smartform": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://YOUR_SMARTFORM_HOST/mcp",
        "--header",
        "Authorization:${SMARTFORM_AUTH_HEADER}"
      ],
      "env": {
        "SMARTFORM_AUTH_HEADER": "Bearer sf_live_REPLACE_LOCALLY"
      }
    }
  }
}

OAuth resource metadata is at /.well-known/oauth-protected-resource. Tokens are issued in the dashboard; SmartForm does not provide an OAuth login flow for MCP clients.

Forms

GET /v1/forms

Required scope: forms:read. Tenant and form allowlists are enforced. Writes are audited as ApiToken.

ParameterLocationDetails
cursorqueryuuid
limitqueryinteger

Request

curl -X GET "$SMARTFORM_ORIGIN/v1/forms" \
  -H "Authorization: Bearer $SMARTFORM_TOKEN"

Response 200

{
  "items": [],
  "nextCursor": null
}

Scope: forms:read. See the OpenAPI document for all error responses.

POST /v1/forms

Required scope: forms:write. Tenant and form allowlists are enforced. Writes are audited as ApiToken.

Request

curl -X POST "$SMARTFORM_ORIGIN/v1/forms" \
  -H "Authorization: Bearer $SMARTFORM_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Cleaning quote",
  "templateSlug": "cleaning"
}'

Response 201

{
  "id": "0199f100-0000-7000-8000-000000000001",
  "workspaceId": "0199f100-0000-7000-8000-000000000001",
  "name": "string",
  "slug": "string",
  "status": "draft",
  "draft": {
    "revision": 1,
    "schema": {
      "schemaVersion": {},
      "title": {},
      "description": {},
      "locale": {},
      "settings": {},
      "fields": {},
      "steps": {},
      "rules": {},
      "tables": {},
      "computed": {},
      "pricing": {},
      "results": {},
      "lookups": {},
      "routes": {},
      "placeholders": {}
    },
    "validation": {
      "errors": {},
      "warnings": {}
    },
    "changes": [
      {}
    ],
    "updatedAt": "2026-10-22T12:00:00Z"
  },
  "publishedVersion": {
    "id": "0199f100-0000-7000-8000-000000000001",
    "version": 1,
    "revision": 1,
    "publishedAt": "2026-10-22T12:00:00Z",
    "publishedBy": "0199f100-0000-7000-8000-000000000001",
    "closedAt": "2026-10-22T12:00:00Z",
    "acceptsSubmissions": true
  },
  "publicUrl": "string"
}

Scope: forms:write. See the OpenAPI document for all error responses.

GET /v1/forms/{id}

Required scope: forms:read. Tenant and form allowlists are enforced. Writes are audited as ApiToken.

ParameterLocationDetails

Submissions

GET /v1/submissions

Required scope: submissions:read. Tenant and form allowlists are enforced. Writes are audited as ApiToken.

ParameterLocationDetails
formIdqueryuuid
statusquerystring
sincequerydate-time
cursorqueryuuid
limitqueryinteger

Request

curl -X GET "$SMARTFORM_ORIGIN/v1/submissions" \
  -H "Authorization: Bearer $SMARTFORM_TOKEN"

Response 200

{
  "items": [],
  "nextCursor": null
}

Scope: submissions:read. See the OpenAPI document for all error responses.

GET /v1/submissions/{id}

Required scope: submissions:read. Tenant and form allowlists are enforced. Writes are audited as ApiToken.

ParameterLocationDetails
id *pathuuid

Request

curl -X GET "$SMARTFORM_ORIGIN/v1/submissions/$ID" \
  -H "Authorization: Bearer $SMARTFORM_TOKEN"

Response 200

{
  "id": "0199f100-0000-7000-8000-000000000001",
  "number": "string",
  "formId": "0199f100-0000-7000-8000-000000000001",
  "versionId": "0199f100-0000-7000-8000-000000000001",
  "status": "new",
  "tags": [
    "string"
  ],
  "assigneeId": "0199f100-0000-7000-8000-000000000001",
  "activeAnswers": {
    "f_area": "40",
    "f_type": "opt_regular"
  },
  "rawAnswers": {
    "f_area": "40",
    "f_type": "opt_regular"
  },
  "computed": {},
  "quote": {
    "status": "ok",
    "currency": "string",
    "total": "string",
    "lines": [
      {
        "id": {},
        "label": {},
        "amount": {}
      }
    ],
    "message": "string",
    "trace": [
      {}
    ]
  },
  "results": [
    {
      "id": "string",
      "label": "string",
      "value": {
        "type": {},
        "value": {}
      }
    }
  ],
  "attribution": {},
  "files": [
    {}
  ],
  "createdAt": "2026-10-22T12:00:00Z",
  "source": "public"
}

Scope: submissions:read. See the OpenAPI document for all error responses.

Exports

POST /v1/exports

Required scope: submissions:export. Tenant and form allowlists are enforced. Writes are audited as ApiToken.

Request

curl -X POST "$SMARTFORM_ORIGIN/v1/exports" \
  -H "Authorization: Bearer $SMARTFORM_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "format": "csv",
  "filter": {
    "status": "new"
  }
}'

Response 202

{
  "id": "0199f100-0000-7000-8000-000000000001",
  "format": "csv",
  "status": "pending"
}

Scope: submissions:export. See the OpenAPI document for all error responses.

GET /v1/exports/{id}

Required scope: submissions:export. Tenant and form allowlists are enforced. Writes are audited as ApiToken.

ParameterLocationDetails
id *pathuuid

Request

curl -X GET "$SMARTFORM_ORIGIN/v1/exports/$ID" \
  -H "Authorization: Bearer $SMARTFORM_TOKEN"

Response 200

{
  "id": "0199f100-0000-7000-8000-000000000001",
  "format": "csv",
  "status": "pending",
  "rowCount": 1,
  "error": "string",
  "createdAt": "2026-10-22T12:00:00Z",
  "expiresAt": "2026-10-22T12:00:00Z"
}

Scope: submissions:export. See the OpenAPI document for all error responses.

GET /v1/exports/{id}/download

Required scope: submissions:export. Tenant and form allowlists are enforced. Writes are audited as ApiToken.

ParameterLocationDetails
id *pathuuid

Request

curl -X GET "$SMARTFORM_ORIGIN/v1/exports/$ID/download" \
  -H "Authorization: Bearer $SMARTFORM_TOKEN"

Response 200

Success. Binary file download.

Scope: submissions:export. See the OpenAPI document for all error responses.

Metrics

GET /v1/metrics/summary

Required scope: analytics:read. Tenant and form allowlists are enforced. Writes are audited as ApiToken.

ParameterLocationDetails
formIdqueryuuid
fromqueryYYYY-MM-DD or RFC3339; defaults to last 30 days
toqueryYYYY-MM-DD (inclusive day) or RFC3339; maximum 366-day window

Request

curl -X GET "$SMARTFORM_ORIGIN/v1/metrics/summary" \
  -H "Authorization: Bearer $SMARTFORM_TOKEN"

Response 200

{}

Scope: analytics:read. See the OpenAPI document for all error responses.

Integrations

GET /v1/integrations/status

Required scope: connections:read. Tenant and form allowlists are enforced. Writes are audited as ApiToken.

Request

curl -X GET "$SMARTFORM_ORIGIN/v1/integrations/status" \
  -H "Authorization: Bearer $SMARTFORM_TOKEN"

Response 200

{
  "items": [],
  "nextCursor": null
}

Scope: connections:read. See the OpenAPI document for all error responses.

Webhooks

GET /v1/webhooks

Required scope: webhooks:manage. Tenant and form allowlists are enforced. Writes are audited as ApiToken.

ParameterLocationDetails
cursorqueryuuid
limitqueryinteger

Request

curl -X GET "$SMARTFORM_ORIGIN/v1/webhooks" \
  -H "Authorization: Bearer $SMARTFORM_TOKEN"

Response 200

{
  "items": [],
  "nextCursor": null
}

Scope: webhooks:manage. See the OpenAPI document for all error responses.

POST /v1/webhooks

Required scope: webhooks:manage. Tenant and form allowlists are enforced. Writes are audited as ApiToken.

Request

curl -X POST "$SMARTFORM_ORIGIN/v1/webhooks" \
  -H "Authorization: Bearer $SMARTFORM_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "CRM events",
  "url": "https://example.com/smartform",
  "events": [
    "submission.created",
    "submission.updated"
  ]
}'

Response 201

{
  "id": "0199f100-0000-7000-8000-000000000001",
  "name": "string",
  "status": "string",
  "config": {
    "url": "string",
    "events": [
      "submission.created"
    ],
    "formIds": [
      "0199f100-0000-7000-8000-000000000001"
    ]
  },
  "signingSecret": "string"
}

Scope: webhooks:manage. See the OpenAPI document for all error responses.

DELETE /v1/webhooks/{id}

Required scope: webhooks:manage. Tenant and form allowlists are enforced. Writes are audited as ApiToken.

ParameterLocationDetails
id *pathuuid

Request

curl -X DELETE "$SMARTFORM_ORIGIN/v1/webhooks/$ID" \
  -H "Authorization: Bearer $SMARTFORM_TOKEN"

Response 204

Success. No response body.

Scope: webhooks:manage. See the OpenAPI document for all error responses.

Schemas

Main schema types are shared by the editor, API and MCP. Field and option IDs remain stable. Expressions use a restricted AST. Unconfirmed placeholders block publication.

Error
{
  "type": "object",
  "properties": {
    "error": {
      "type": "object",
      "properties": {
        "code": {
          "type": "string"
        },
        "message": {
          "type": "string"
        },
        "details": {
          "type": "object",
          "additionalProperties": true
        }
      },
      "required": [
        "code",
        "message",
        "details"
      ]
    }
  },
  "required": [
    "error"
  ]
}
Answers
{
  "type": "object",
  "additionalProperties": true,
  "description": "Field IDs map to typed answers. Numeric/money values are decimal strings, choices use option IDs; file answers are private upload UUID arrays.",
  "example": {
    "f_area": "40",
    "f_type": "opt_regular"
  }
}
LookupResults
{
  "type": "object",
  "additionalProperties": {
    "type": "object",
    "properties": {
      "status": {
        "enum": [
          "ok",
          "error",
          "timeout",
          "pending"
        ],
        "type": "string"
      },
      "outputs": {
        "type": "object",
        "additionalProperties": true
      }
    }
  }
}
Field
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string"
    },
    "type": {
      "type": "string",
      "enum": [
        "shortText",
        "longText",
        "email",
        "phone",
        "url",
        "name",
        "singleChoice",
        "multiChoice",
        "imageChoice",
        "dropdown",
        "yesNo",
        "consent",
        "integer",
        "decimal",
        "money",
        "percent",
        "quantity",
        "date",
        "datetime",
        "file",
        "heading",
        "paragraph",
        "image",
        "divider",
        "summary",
        "quoteResult",
        "termsLink",
        "hidden"
      ]
    },
    "label": {
      "type": "string"
    },
    "required": {
      "type": "boolean"
    },
    "source": {
      "enum": [
        "visitor",
        "constant",
        "system"
      ],
      "type": "string"
    },
    "options": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "value": {
            "type": [
              "string",
              "null"
            ],
            "description": "Decimal string"
          }
        },
        "required": [
          "id",
          "label"
        ]
      }
    },
    "validation": {
      "type": "object",
      "properties": {
        "min": {
          "type": "string"
        },
        "max": {
          "type": "string"
        },
        "step": {
          "type": "string"
        },
        "maxLength": {
          "type": "integer"
        },
        "maxFiles": {
          "type": "integer"
        },
        "maxSizeMb": {
          "type": "integer"
        },
        "accept": {
          "type": "string"
        }
      }
    },
    "piiKind": {
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "id",
    "type",
    "label"
  ]
}
SmartForm

Whatever’s next,
give it a form.

Build your form free

Product

  • Features
  • Templates
  • Pricing
  • API & MCP

Explore examples

  • Cleaning
  • Moving
  • Catering
  • Repairs

Company

  • Privacy policy
  • Terms of service

Black Tree Market OÜ
Tallinn, Estonia

© 2026 Black Tree Market OÜDescribe. Edit. Publish. Get on with your day.
id *pathuuid

Request

curl -X GET "$SMARTFORM_ORIGIN/v1/forms/$ID" \
  -H "Authorization: Bearer $SMARTFORM_TOKEN"

Response 200

{
  "id": "0199f100-0000-7000-8000-000000000001",
  "workspaceId": "0199f100-0000-7000-8000-000000000001",
  "name": "string",
  "slug": "string",
  "status": "draft",
  "draft": {
    "revision": 1,
    "schema": {
      "schemaVersion": {},
      "title": {},
      "description": {},
      "locale": {},
      "settings": {},
      "fields": {},
      "steps": {},
      "rules": {},
      "tables": {},
      "computed": {},
      "pricing": {},
      "results": {},
      "lookups": {},
      "routes": {},
      "placeholders": {}
    },
    "validation": {
      "errors": {},
      "warnings": {}
    },
    "changes": [
      {}
    ],
    "updatedAt": "2026-10-22T12:00:00Z"
  },
  "publishedVersion": {
    "id": "0199f100-0000-7000-8000-000000000001",
    "version": 1,
    "revision": 1,
    "publishedAt": "2026-10-22T12:00:00Z",
    "publishedBy": "0199f100-0000-7000-8000-000000000001",
    "closedAt": "2026-10-22T12:00:00Z",
    "acceptsSubmissions": true
  },
  "publicUrl": "string"
}

Scope: forms:read. See the OpenAPI document for all error responses.

PATCH /v1/forms/{id}/draft

Required scope: forms:write. Tenant and form allowlists are enforced. Writes are audited as ApiToken.

ParameterLocationDetails
id *pathuuid

Request

curl -X PATCH "$SMARTFORM_ORIGIN/v1/forms/$ID/draft" \
  -H "Authorization: Bearer $SMARTFORM_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "expectedRevision": 1,
  "ops": [
    {
      "op": "updateMeta",
      "set": {
        "title": "My cleaning quote"
      }
    }
  ],
  "summary": "Update title"
}'

Response 200

{
  "revision": 1,
  "schema": {
    "schemaVersion": 1,
    "title": "string",
    "description": "string",
    "locale": {
      "language": "string",
      "currency": "string",
      "timezone": "string"
    },
    "settings": {
      "layout": "steps",
      "submitLabel": "string",
      "successMessage": "string",
      "honeypot": true,
      "pricePolicy": {}
    },
    "fields": [
      {}
    ],
    "steps": [
      {}
    ],
    "rules": [
      {}
    ],
    "tables": [
      {
        "id": {},
        "label": {},
        "valueType": {},
        "confirmed": {},
        "rows": {}
      }
    ],
    "computed": [
      {
        "id": {},
        "label": {},
        "valueType": {},
        "expr": {}
      }
    ],
    "pricing": {
      "enabled": true,
      "currency": "string",
      "lines": [
        {}
      ],
      "totalComputedId": "string"
    },
    "results": [
      {}
    ],
    "lookups": [
      {}
    ],
    "routes": [
      {}
    ],
    "placeholders": [
      "string"
    ]
  },
  "validation": {
    "errors": [
      {}
    ],
    "warnings": [
      {}
    ]
  },
  "changes": [
    {}
  ],
  "updatedAt": "2026-10-22T12:00:00Z"
}

Scope: forms:write. See the OpenAPI document for all error responses.

POST /v1/forms/{id}/validate

Required scope: forms:read. Tenant and form allowlists are enforced. Writes are audited as ApiToken.

ParameterLocationDetails
id *pathuuid

Request

curl -X POST "$SMARTFORM_ORIGIN/v1/forms/$ID/validate" \
  -H "Authorization: Bearer $SMARTFORM_TOKEN"

Response 200

{
  "errors": [],
  "warnings": []
}

Scope: forms:read. See the OpenAPI document for all error responses.

POST /v1/forms/{id}/simulate

Required scope: forms:read. Tenant and form allowlists are enforced. Writes are audited as ApiToken.

ParameterLocationDetails
id *pathuuid

Request

curl -X POST "$SMARTFORM_ORIGIN/v1/forms/$ID/simulate" \
  -H "Authorization: Bearer $SMARTFORM_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "answers": {
    "f_area": "40",
    "f_type": "opt_regular",
    "f_extras": [],
    "f_discount": "opt_none"
  }
}'

Response 200

{
  "evaluation": {
    "visibleFields": [
      "string"
    ],
    "hiddenFields": {},
    "visibleSteps": [
      "string"
    ],
    "stepPath": [
      "string"
    ],
    "requiredFields": [
      "string"
    ],
    "enabledOptions": {},
    "activeAnswers": {
      "f_area": "40",
      "f_type": "opt_regular"
    },
    "droppedAnswers": {},
    "fieldErrors": {},
    "missingRequired": [
      "string"
    ],
    "computed": {},
    "quote": {
      "status": "ok",
      "currency": "string",
      "total": "string",
      "lines": [
        {}
      ],
      "message": "string",
      "trace": [
        {}
      ]
    },
    "results": [
      {
        "id": {},
        "label": {},
        "value": {}
      }
    ],
    "resultError": "string",
    "routes": [
      "string"
    ],
    "block": {},
    "complete": true
  },
  "results": [
    {}
  ],
  "coverage": {}
}

Scope: forms:read. See the OpenAPI document for all error responses.

POST /v1/forms/{id}/publish

Required scope: forms:publish. Tenant and form allowlists are enforced. Writes are audited as ApiToken.

ParameterLocationDetails
id *pathuuid

Request

curl -X POST "$SMARTFORM_ORIGIN/v1/forms/$ID/publish" \
  -H "Authorization: Bearer $SMARTFORM_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "revision": 2
}'

Response 200

{
  "id": "0199f100-0000-7000-8000-000000000001",
  "version": 1,
  "revision": 1,
  "publishedAt": "2026-10-22T12:00:00Z",
  "publishedBy": "0199f100-0000-7000-8000-000000000001",
  "closedAt": "2026-10-22T12:00:00Z",
  "acceptsSubmissions": true
}

Scope: forms:publish. See the OpenAPI document for all error responses.

GET /v1/forms/{id}/versions

Required scope: forms:read. Tenant and form allowlists are enforced. Writes are audited as ApiToken.

ParameterLocationDetails
id *pathuuid
cursorqueryuuid
limitqueryinteger

Request

curl -X GET "$SMARTFORM_ORIGIN/v1/forms/$ID/versions" \
  -H "Authorization: Bearer $SMARTFORM_TOKEN"

Response 200

{
  "items": [],
  "nextCursor": null
}

Scope: forms:read. See the OpenAPI document for all error responses.

POST /v1/forms/{id}/submissions

Required scope: submissions:write. Tenant and form allowlists are enforced. Writes are audited as ApiToken.

ParameterLocationDetails
id *pathuuid
Idempotency-Key *headerReuse the UUID for retries of exactly the same request. Replays return 200; changed payload returns 409.

Request

curl -X POST "$SMARTFORM_ORIGIN/v1/forms/$ID/submissions" \
  -H "Authorization: Bearer $SMARTFORM_TOKEN" \
  -H "Idempotency-Key: $REQUEST_UUID" \
  -H "Content-Type: application/json" \
  -d '{
  "answers": {
    "f_area": "40",
    "f_type": "opt_regular",
    "f_extras": [],
    "f_discount": "opt_none"
  }
}'

Response 200

{
  "submissionId": "0199f100-0000-7000-8000-000000000001",
  "number": "string",
  "versionId": "0199f100-0000-7000-8000-000000000001",
  "quote": {
    "status": "ok",
    "currency": "string",
    "total": "string",
    "lines": [
      {
        "id": "string",
        "label": "string",
        "amount": "string"
      }
    ],
    "message": "string",
    "trace": [
      {}
    ]
  }
}

Scope: submissions:write. See the OpenAPI document for all error responses.

PATCH /v1/submissions/{id}

Required scope: submissions:write. Tenant and form allowlists are enforced. Writes are audited as ApiToken.

ParameterLocationDetails
id *pathuuid

Request

curl -X PATCH "$SMARTFORM_ORIGIN/v1/submissions/$ID" \
  -H "Authorization: Bearer $SMARTFORM_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "status": "in_progress",
  "tags": [
    "priority"
  ],
  "assigneeEmail": "owner@example.com"
}'

Response 200

{
  "id": "0199f100-0000-7000-8000-000000000001",
  "number": "string",
  "formId": "0199f100-0000-7000-8000-000000000001",
  "versionId": "0199f100-0000-7000-8000-000000000001",
  "status": "new",
  "tags": [
    "string"
  ],
  "assigneeId": "0199f100-0000-7000-8000-000000000001",
  "activeAnswers": {
    "f_area": "40",
    "f_type": "opt_regular"
  },
  "rawAnswers": {
    "f_area": "40",
    "f_type": "opt_regular"
  },
  "computed": {},
  "quote": {
    "status": "ok",
    "currency": "string",
    "total": "string",
    "lines": [
      {
        "id": {},
        "label": {},
        "amount": {}
      }
    ],
    "message": "string",
    "trace": [
      {}
    ]
  },
  "results": [
    {
      "id": "string",
      "label": "string",
      "value": {
        "type": {},
        "value": {}
      }
    }
  ],
  "attribution": {},
  "files": [
    {}
  ],
  "createdAt": "2026-10-22T12:00:00Z",
  "source": "public"
}

Scope: submissions:write. See the OpenAPI document for all error responses.

GET /v1/submissions/{id}/files/{fileId}

Required scope: submissions:read. Tenant and form allowlists are enforced. Writes are audited as ApiToken.

ParameterLocationDetails
id *pathuuid
fileId *pathuuid

Request

curl -X GET "$SMARTFORM_ORIGIN/v1/submissions/$ID/files/$FILE_ID" \
  -H "Authorization: Bearer $SMARTFORM_TOKEN"

Response 200

Success. Binary file download.

Scope: submissions:read. See the OpenAPI document for all error responses.

Step
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string"
    },
    "title": {
      "type": "string"
    },
    "fieldIds": {
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  },
  "required": [
    "id",
    "title",
    "fieldIds"
  ]
}
Expr
{
  "type": "object",
  "description": "Restricted expression AST: num (decimal string), str, bool, ref; or op add/sub/mul/div/min/max/round/if/sumOptions/optionValue/table/daysBetween/count/coalesce. No code execution or network.",
  "additionalProperties": true,
  "examples": [
    {
      "ref": "f_area"
    },
    {
      "op": "mul",
      "args": [
        {
          "ref": "f_area"
        },
        {
          "num": "2.00"
        }
      ]
    }
  ]
}
Rule
{
  "type": "object",
  "required": [
    "id",
    "kind",
    "when"
  ],
  "properties": {
    "id": {
      "type": "string"
    },
    "kind": {
      "enum": [
        "show",
        "require",
        "enableOption",
        "goto",
        "block"
      ],
      "type": "string"
    },
    "when": {
      "type": "object"
    },
    "target": {
      "type": "object"
    }
  },
  "additionalProperties": true
}
FormSchema
{
  "type": "object",
  "properties": {
    "schemaVersion": {
      "type": "integer",
      "const": 1
    },
    "title": {
      "type": "string"
    },
    "description": {
      "type": [
        "string",
        "null"
      ]
    },
    "locale": {
      "type": "object",
      "properties": {
        "language": {
          "type": "string"
        },
        "currency": {
          "type": "string"
        },
        "timezone": {
          "type": "string"
        }
      },
      "required": [
        "language",
        "currency",
        "timezone"
      ]
    },
    "settings": {
      "type": "object",
      "properties": {
        "layout": {
          "enum": [
            "steps",
            "oneByOne"
          ],
          "type": "string"
        },
        "submitLabel": {
          "type": "string"
        },
        "successMessage": {
          "type": "string"
        },
        "honeypot": {
          "type": "boolean"
        },
        "pricePolicy": {
          "type": "object"
        }
      }
    },
    "fields": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Field"
      }
    },
    "steps": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Step"
      }
    },
    "rules": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Rule"
      }
    },
    "tables": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "valueType": {
            "type": "string"
          },
          "confirmed": {
            "type": "boolean"
          },
          "rows": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "value": {
                  "type": "string"
                }
              },
              "required": [
                "key",
                "value"
              ]
            }
          }
        },
        "required": [
          "id",
          "rows"
        ]
      }
    },
    "computed": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "valueType": {
            "type": "string"
          },
          "expr": {
            "$ref": "#/components/schemas/Expr"
          }
        },
        "required": [
          "id",
          "expr"
        ]
      }
    },
    "pricing": {
      "type": "object",
      "properties": {
        "enabled": {
          "type": "boolean"
        },
        "currency": {
          "type": "string"
        },
        "lines": {
          "type": "array",
          "items": {
            "type": "object"
          }
        },
        "totalComputedId": {
          "type": [
            "string",
            "null"
          ]
        }
      }
    },
    "results": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/ResultLine"
      }
    },
    "lookups": {
      "type": "array",
      "items": {
        "type": "object"
      }
    },
    "routes": {
      "type": "array",
      "items": {
        "type": "object"
      }
    },
    "placeholders": {
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  },
  "required": [
    "schemaVersion",
    "title",
    "fields",
    "steps"
  ]
}
ResultLine
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string"
    },
    "label": {
      "type": "string"
    },
    "computedId": {
      "type": "string"
    }
  },
  "required": [
    "id",
    "label",
    "computedId"
  ]
}
EvaluatedResult
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string"
    },
    "label": {
      "type": "string"
    },
    "value": {
      "type": "object",
      "properties": {
        "type": {
          "enum": [
            "number",
            "text",
            "bool"
          ],
          "type": "string"
        },
        "value": {
          "type": [
            "string",
            "boolean"
          ]
        }
      }
    }
  },
  "required": [
    "id",
    "label",
    "value"
  ]
}
PatchOp
{
  "type": "object",
  "required": [
    "op"
  ],
  "properties": {
    "op": {
      "enum": [
        "addField",
        "updateField",
        "removeField",
        "moveField",
        "addOption",
        "updateOption",
        "removeOption",
        "addStep",
        "updateStep",
        "removeStep",
        "moveStep",
        "addRule",
        "updateRule",
        "removeRule",
        "addComputed",
        "updateComputed",
        "removeComputed",
        "addTable",
        "updateTable",
        "removeTable",
        "setTableRow",
        "removeTableRow",
        "setPricing",
        "setResults",
        "updateSettings",
        "updateMeta",
        "addRoute",
        "updateRoute",
        "removeRoute",
        "addLookup",
        "removeLookup",
        "markPlaceholder",
        "confirmPlaceholder"
      ],
      "type": "string"
    },
    "id": {
      "type": "string"
    },
    "set": {
      "type": "object"
    },
    "field": {
      "$ref": "#/components/schemas/Field"
    },
    "step": {
      "$ref": "#/components/schemas/Step"
    },
    "rule": {
      "$ref": "#/components/schemas/Rule"
    },
    "pricing": {
      "type": "object"
    },
    "results": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/ResultLine"
      }
    },
    "index": {
      "type": [
        "integer",
        "null"
      ]
    },
    "stepId": {
      "type": "string"
    },
    "fieldId": {
      "type": "string"
    }
  },
  "additionalProperties": true,
  "example": {
    "op": "updateMeta",
    "set": {
      "title": "Updated quote"
    }
  }
}
ValidationReport
{
  "type": "object",
  "properties": {
    "errors": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Issue"
      }
    },
    "warnings": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Issue"
      }
    }
  },
  "required": [
    "errors",
    "warnings"
  ]
}
Issue
{
  "type": "object",
  "properties": {
    "code": {
      "type": "string"
    },
    "severity": {
      "enum": [
        "error",
        "warning"
      ],
      "type": "string"
    },
    "message": {
      "type": "string"
    },
    "path": {
      "type": "string"
    },
    "ids": {
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  },
  "required": [
    "code",
    "severity",
    "message",
    "path",
    "ids"
  ]
}
Quote
{
  "type": "object",
  "properties": {
    "status": {
      "enum": [
        "ok",
        "incomplete",
        "error",
        "requestQuote",
        "declined"
      ],
      "type": "string"
    },
    "currency": {
      "type": "string"
    },
    "total": {
      "type": [
        "string",
        "null"
      ]
    },
    "lines": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "amount": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "label",
          "amount"
        ]
      }
    },
    "message": {
      "type": [
        "string",
        "null"
      ]
    },
    "trace": {
      "type": "array",
      "items": {
        "type": "object"
      }
    }
  },
  "required": [
    "status",
    "currency",
    "total",
    "lines"
  ]
}
Evaluation
{
  "type": "object",
  "properties": {
    "visibleFields": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "hiddenFields": {
      "type": "object"
    },
    "visibleSteps": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "stepPath": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "requiredFields": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "enabledOptions": {
      "type": "object"
    },
    "activeAnswers": {
      "$ref": "#/components/schemas/Answers"
    },
    "droppedAnswers": {
      "type": "object"
    },
    "fieldErrors": {
      "type": "object"
    },
    "missingRequired": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "computed": {
      "type": "object"
    },
    "quote": {
      "$ref": "#/components/schemas/Quote"
    },
    "results": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/EvaluatedResult"
      }
    },
    "resultError": {
      "type": [
        "string",
        "null"
      ]
    },
    "routes": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "block": {
      "type": [
        "object",
        "null"
      ]
    },
    "complete": {
      "type": "boolean"
    }
  },
  "required": [
    "activeAnswers",
    "quote",
    "complete"
  ]
}
DraftState
{
  "type": "object",
  "properties": {
    "revision": {
      "type": "integer",
      "format": "int64"
    },
    "schema": {
      "$ref": "#/components/schemas/FormSchema"
    },
    "validation": {
      "$ref": "#/components/schemas/ValidationReport"
    },
    "changes": {
      "type": "array",
      "items": {
        "type": "object"
      }
    },
    "updatedAt": {
      "type": "string",
      "format": "date-time"
    }
  },
  "required": [
    "revision",
    "schema",
    "validation"
  ]
}
FormVersion
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "version": {
      "type": "integer"
    },
    "revision": {
      "type": "integer"
    },
    "publishedAt": {
      "type": "string",
      "format": "date-time"
    },
    "publishedBy": {
      "type": [
        "string",
        "null"
      ],
      "format": "uuid"
    },
    "closedAt": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time"
    },
    "acceptsSubmissions": {
      "type": "boolean"
    }
  },
  "required": [
    "id",
    "version",
    "revision",
    "acceptsSubmissions"
  ]
}
Form
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "workspaceId": {
      "type": "string",
      "format": "uuid"
    },
    "name": {
      "type": "string"
    },
    "slug": {
      "type": "string"
    },
    "status": {
      "enum": [
        "draft",
        "published",
        "closed",
        "archived"
      ],
      "type": "string"
    },
    "draft": {
      "$ref": "#/components/schemas/DraftState"
    },
    "publishedVersion": {
      "oneOf": [
        {
          "$ref": "#/components/schemas/FormVersion"
        },
        {
          "type": "null"
        }
      ]
    },
    "publicUrl": {
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "id",
    "name",
    "draft"
  ]
}
Submission
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "number": {
      "type": "string"
    },
    "formId": {
      "type": "string",
      "format": "uuid"
    },
    "versionId": {
      "type": "string",
      "format": "uuid"
    },
    "status": {
      "enum": [
        "new",
        "in_progress",
        "closed",
        "spam"
      ],
      "type": "string"
    },
    "tags": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "assigneeId": {
      "type": [
        "string",
        "null"
      ],
      "format": "uuid"
    },
    "activeAnswers": {
      "$ref": "#/components/schemas/Answers"
    },
    "rawAnswers": {
      "$ref": "#/components/schemas/Answers"
    },
    "computed": {
      "type": "object"
    },
    "quote": {
      "$ref": "#/components/schemas/Quote"
    },
    "results": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/EvaluatedResult"
      }
    },
    "attribution": {
      "type": "object"
    },
    "files": {
      "type": "array",
      "items": {
        "type": "object"
      }
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    },
    "source": {
      "type": "string",
      "enum": [
        "public",
        "api"
      ]
    }
  },
  "required": [
    "id",
    "number",
    "formId",
    "status",
    "quote"
  ]
}
Export
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "format": {
      "enum": [
        "csv",
        "xlsx",
        "json"
      ],
      "type": "string"
    },
    "status": {
      "enum": [
        "pending",
        "processing",
        "ready",
        "failed",
        "expired"
      ],
      "type": "string"
    },
    "rowCount": {
      "type": [
        "integer",
        "null"
      ]
    },
    "error": {
      "type": [
        "string",
        "null"
      ]
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    },
    "expiresAt": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time"
    }
  },
  "required": [
    "id",
    "format",
    "status"
  ]
}
Webhook
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "name": {
      "type": "string"
    },
    "status": {
      "type": "string"
    },
    "config": {
      "type": "object",
      "properties": {
        "url": {
          "type": "string",
          "format": "uri"
        },
        "events": {
          "type": "array",
          "items": {
            "enum": [
              "submission.created",
              "submission.updated",
              "delivery.failed"
            ],
            "type": "string"
          }
        },
        "formIds": {
          "type": "array",
          "items": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "required": [
        "url",
        "events"
      ]
    },
    "signingSecret": {
      "type": "string",
      "description": "Only present once, on creation."
    }
  },
  "required": [
    "id",
    "name",
    "config"
  ]
}