Form
Forms provide a reusable template (kind 30168) that any user can respond to with a Form Response (kind 1069). Responses may be public or encrypted to the form creator using "Encrypted Payload", covering community membership applications, service requests, surveys, event registration, and any other structured data collection on Nostr.
Form Template (kind:30168)
An addressable event that defines the fields and behaviour of a form. Can be updated by re-publishing with the same d tag.
Required Tags
| Tag | Description |
|-----|-------------|
| d | Unique identifier for the form (slug). |
Optional Tags
| Tag | Description |
|-----|-------------|
| name | Human-readable name of the form. |
| description | Explanation of the form’s purpose. |
| field | Defines a form field. Repeat for each field. See [Field Tag Format](#field-tag-format). |
| public | If present, responses are public. Default (absent): responses MUST be encrypted. |
| confirmation_message | Message shown to user after submission (e.g. "Thank you! We’ll review within 48 hours."). |
| auto_response | "true" = automated processing (e.g. hot-key badge awarding); "false" or absent = manual review. |
Field Tag Format
["field", "<field-id>", "<type>", "<label>", "<default-value>", "<options-json>"]
| Parameter | Description |
|-----------|-------------|
| field-id | Unique identifier used in response tags. |
| type | text, textarea, number, email, url, select, checkbox, radio, date, file |
| label | Human-readable label shown to the user. |
| default-value | Optional default value; empty string if none. |
| options-json | JSON string: required (bool), min/max (number/length), options (array for select/radio), accept (file types), placeholder (hint text). |
Event Structure
{
"kind": 30168,
"pubkey": "<form-creator-pubkey>",
"tags": [
["d", "membership-application"],
["name", "Community Membership Application"],
["description", "Apply to join our community"],
["field", "name", "text", "Full Name", "", "{\"required\":true}"],
["field", "reason", "textarea", "Why do you want to join?", "", "{\"required\":true,\"min\":50}"],
["field", "tier", "select", "Membership Tier", "", "{\"required\":true,\"options\":[\"Basic\",\"Premium\"]}"],
["field", "terms", "checkbox", "I agree to the terms of service", "", "{\"required\":true}"],
["confirmation_message", "Thank you! Our admins will review your application within 48 hours."],
["auto_response", "false"]
],
"content": "",
"created_at": 1234567890
}
Form Response (kind:1069)
A user’s submission for a specific form template. References the template via a tag. When the form does not include a public tag, all response data MUST be encrypted using "Encrypted Payload" (NIP-44) and placed in content; only the a and p tags remain in plaintext.
Required Tags
| Tag | Description |
|-----|-------------|
| a | Reference to the Form Template: 30168:<pubkey>:<form-id>. |
| response | Field value: ["response", "<field-id>", "<value>"]. One per field. Omit from tags when encrypted; include in encrypted content instead. |
Optional Tags
| Tag | Description |
|-----|-------------|
| p | Pubkey of the form creator (for routing and notifications). |
| encrypted | Present when content contains encrypted response data. |
Encryption
For private forms (no public tag on the template), the response tags MUST be encrypted:
-
Serialize the response tags as a JSON array.
-
Encrypt using "Encrypted Payload" (NIP-44): sender = respondent’s key, recipient = form creator’s pubkey.
-
Place the ciphertext in
contentand add["encrypted"]to tags. -
Omit all
responsetags from the plaintext tag array.
Event Structure
Public form response:
{
"kind": 1069,
"pubkey": "<user-pubkey>",
"tags": [
["a", "30168:<creator-pubkey>:membership-application"],
["p", "<creator-pubkey>"],
["response", "name", "Bob Smith"],
["response", "reason", "I want to contribute to the community."],
["response", "tier", "Premium"],
["response", "terms", "true"]
],
"content": "",
"created_at": 1234567890
}
Encrypted form response:
{
"kind": 1069,
"pubkey": "<user-pubkey>",
"tags": [
["a", "30168:<creator-pubkey>:membership-application"],
["p", "<creator-pubkey>"],
["encrypted"]
],
"content": "<nip44-ciphertext of JSON array of response tags>",
"created_at": 1234567890
}
The plaintext before encryption is a JSON array of response tags:
[
["response", "name", "Bob Smith"],
["response", "reason", "I want to contribute to the community."],
["response", "tier", "Premium"],
["response", "terms", "true"]
]
Follow Up Communication
<!-- TODO: define how form creators communicate back to respondents (approval, rejection, follow-up questions, invoices). To be developed alongside the nostr-mail spec. -→
Client Recommendations
-
Clients MUST encrypt responses when the form template does not include
public. -
Clients MUST use "Encrypted Payload" (NIP-44) for all encryption.
-
Clients SHOULD validate field requirements (
required,min,max) before submission. -
Clients SHOULD display the
confirmation_messageafter successful submission. -
Clients SHOULD warn users before submitting sensitive data to public forms.
-
Clients SHOULD verify the form template’s pubkey matches the expected creator.
-
Clients MAY notify form creators of new responses.
See Also
-
"Encrypted Payload" — NIP-44 encryption used for private responses
-
"Profile List" — Membership lists that reference forms via
formtag -
"Community" — Communities that use forms for membership applications