Form Availability
Registration opens Monday. The workshop seats forty. The survey closes at the end of the quarter. Each of those is a rule about when a form accepts answers, and each one otherwise becomes a calendar reminder to go and edit the config by hand.
availability states the rule once and lets the server enforce it:
forms:
- id: 1
name: workshop-signup
title: Spring Workshop
submitTo: { table: registrations }
availability:
opensAt: '2026-03-01T09:00:00Z'
closesAt: '2026-03-20T23:59:59Z'
maxSubmissions: 40
fields:
- { kind: table-field, column: email, required: true }
Availability Properties
Every property is optional. Omit the block and the form is open, forever, with no cap.
| Property | Description | Default |
|---|---|---|
opensAt |
ISO 8601 timestamp at which the form starts accepting submissions. | Open immediately. |
closesAt |
ISO 8601 timestamp at which it stops. | Never closes. |
maxSubmissions |
Positive integer cap. The submission after the cap is refused. | Unlimited. |
closedPage |
Custom page shown while the form is closed, for any of the three reasons. | Title + boilerplate copy. |
When both timestamps are set, opensAt must be strictly before closesAt. A window that closes before it opens accepts nothing and is almost always a typo, so it fails when the config is decoded, naming the form and both timestamps.
What a Closed Form Answers
Opening a closed form is not an error — the visitor gets a page explaining the situation. Posting to one is refused with a structured body so a programmatic client can tell the three cases apart.
| State | GET /forms/{name} |
POST status |
Error body |
|---|---|---|---|
Before opensAt |
Closed page | 403 |
{ error: 'form not yet open', opensAt } |
After closesAt |
Closed page | 403 |
{ error: 'form closed', closedAt } |
At maxSubmissions |
Form still renders | 403 |
{ error: 'submission limit reached', maxSubmissions, currentCount } |
A refused submission writes nothing — no ledger row, no table row, no automation run.
The Closed Page
Left unconfigured, the closed page shows the form's own title and a sentence explaining that it is not yet open (quoting opensAt) or no longer accepting answers. closedPage replaces that with your own copy and, optionally, a way forward:
availability:
closesAt: '2026-03-20T23:59:59Z'
closedPage:
title: Registration has closed
message: Spring places are full. The autumn cohort opens in July.
cta:
label: Join the waiting list
href: /waiting-list
| Property | Description | Default |
|---|---|---|
title |
Page heading. | The form's title. |
message |
Body copy. | Default explanation for the reason. |
cta.label |
Link text. Requires cta.href. |
No link. |
cta.href |
Link target. Requires cta.label. |
No link. |
type |
Reserved discriminator; only page is accepted. |
page |
The same page serves all three closed states, so write copy that reads sensibly whether the form has not opened yet or has filled up.
Only real submissions consume a cap slot. A submission rejected as spam — a tripped honeypot, a rate-limit miss — and one that failed downstream never count toward maxSubmissions. A bot cannot exhaust your forty workshop seats in a burst, and a failed write does not quietly cost a genuine registrant their place. The cap is also enforced atomically, so concurrent submissions racing for the last slots cannot overshoot it.
Related Pages
- Anti-Spam & Attribution — the spam classification that is excluded from the cap.
- Access Control — the other reason a form may refuse a submitter.
- Submissions — the ledger status a refused submission does not create.
- Forms Overview — where
availabilitysits in the full form schema.
Last updated July 27, 2026
This documentation was written with AI, so errors or outdated content are possible. Sovrium is in beta — contributions and corrections are welcome.