Skip to content

Data Model

┌──────────┐ ┌────────────────┐ ┌──────────────┐
│ forms │───1:N─│ form_versions │ │ webhooks │
└────┬─────┘ └────────────────┘ └──────┬───────┘
│ │
│ 1:N │ 1:N
│ │
┌────▼─────┐ ┌────────▼────────┐
│ sessions │ │ webhook_deliveries│
└────┬─────┘ └─────────────────┘
│ 1:1
┌────▼────────┐
│ submissions │
└─────────────┘

Core form entity. Each form has a slug, status, and current version.

ColumnTypeDescription
idTEXTPrimary key, e.g. form_abc123
nameTEXTHuman-readable name
slugTEXTURL-safe identifier, unique per project
descriptionTEXTOptional description
statusTEXTdraft, published, or archived
current_version_idTEXTFK to form_versions.id
created_atTEXTISO 8601
updated_atTEXTISO 8601

Immutable snapshots of a form definition at publish time. Versioning is checksum-based: publishing a form reuses the last version if the definition hasn’t changed.

ColumnTypeDescription
idTEXTPrimary key, e.g. ver_xyz789
form_idTEXTFK to forms.id
versionINTEGERMonotonic version number
definitionTEXTJSON blob of the full form definition
checksumTEXTSHA-256 of the definition for dedup
created_atTEXTISO 8601

When publish() is called with an unchanged definition, the existing version is reused. This means:

  • Consecutive publishes of identical definitions don’t create new versions.
  • form_versions.version skips numbers when a checksum is reused.
  • Submission data always references the correct form_version_id for audit.
ColumnTypeDescription
idTEXTPrimary key, e.g. sess_xyz789
form_idTEXTFK to forms.id
statusTEXTcreated, open, submitted, expired
prefillTEXTJSON blob of prefill values
metadataTEXTJSON blob of arbitrary metadata
expires_atTEXTISO 8601
created_atTEXTISO 8601
ColumnTypeDescription
idTEXTPrimary key, e.g. sub_def456
form_idTEXTFK to forms.id
form_version_idTEXTFK to form_versions.id
session_idTEXTFK to sessions.id
answersTEXTJSON blob of field id → value
partialINTEGER0 or 1
submitted_atTEXTISO 8601
Column (webhooks)TypeDescription
idTEXTPrimary key
urlTEXTHTTPS endpoint
eventsTEXTJSON array of event types
secret_hashTEXTHashed HMAC secret
activeINTEGER0 or 1
created_atTEXTISO 8601
Column (webhook_deliveries)TypeDescription
idTEXTPrimary key
webhook_idTEXTFK to webhooks.id
event_idTEXTThe event being delivered
statusTEXTpending, success, failed
attemptINTEGERRetry attempt number
response_codeINTEGERHTTP status from the target
created_atTEXTISO 8601