Join Nostr
2025-07-03 14:58:39 UTC

Niel Liesmons on Nostr: If any Nostr Product or Service listing just includes a Form event that the Merchant ...

If any Nostr Product or Service listing just includes a Form event that the Merchant needs you to fill in, you solve most of the issues that all the current weirdly specific specs have.

Whether it's an Image generation bot, a T-Shirt, some relay hosting of 2GB for 3 months, a logo design service or a freakin' cucumber, the general pattern is the same:

1️⃣ Merchant announces product / service + adds Form with fields for the data he needs back to create a Bill for you (this data can be a shipping address, a prompt, contact info, filter options, etc...)
2️⃣ Buyer fills out the Form
3️⃣ Merchant sends a Bill
4️⃣ Buyer pays or declines
5️⃣ Merchant confirms and provides follow up communication (Nostr Mail) and optional status updates

A flow like this means that Apps can support marketplaces for anything from the moment they support Forms, Bills (payments) and basic comms.
= Forms

This NIP provides a way to implement forms on Nostr using parametrized replaceable events. They can be used for surveys, voting, product/service purchases, job applications, grants, or other formal applications.

== Form Definition

Event kind `30168` describes a form as a parametrized replaceable event, with field tags that contain the description of each form field.

```json
{
"kind": 30168,
"content": "",
"tags": [
["d", "
"],
["name", "Name of the form"],
["description", "Detailed description of what this form is for"],
["c", "application"],
["t", "job"],
["t", "developer"],
["t", "remote"],
["settings", JSON.stringify({
"description": "Additional form settings and metadata"
})],
["field", "age", "text", "What is your age?", "", JSON.stringify({
"required": true,
"type": "number"
})],
["field", "experience", "option", "Years of experience",
JSON.stringify([
["junior", "0-2 years"],
["mid", "2-5 years"],
["senior", "5+ years"]
]),
JSON.stringify({
"required": true
})
]
],
"pubkey": ""
}
```

The form event uses the following tags:

- `d`: Unique identifier for the form, used to make the event replaceable
- `name`: Display name of the form (for backward compatibility)
- `description`: Detailed explanation of the form's purpose and context
- `c`: Category of the form (poll, purchase, or application)
- `t`: Topic tags for categorizing and searching forms
- `settings`: JSON object containing form-wide settings and metadata
- `field`: One or more field definitions (see Field Tag Structure below)

== Form Response

Event kind `1069` is used to submit responses to a form. The response references the original form using an `a` tag and contains the submitted data in response tags.

```json
{
"kind": 1069,
"content": "",
"tags": [
["a", "30168:form-author:form-identifier", "wss://relay.example.com"],
["p", "form-author-pubkey", "wss://relay.example.com"],
["response", "age", "28", "{}"],
["response", "experience", "mid", "{}"],
["response", "motivation", "I have been following the project for a while and would love to contribute to its development. I have experience with similar technologies and am excited about the opportunity to work with the team.", "{}"],
["response", "skills", "javascript;typescript;react", "{}"],
["response", "availability", "full-time", "{}"],
["response", "timezone", "UTC+1", "{}"],
["response", "portfolio", "https://github.com/username", "{}"],
["response", "references", "{\"name\":\"John Smith\",\"role\":\"Previous Team Lead\",\"contact\":\"john@example.com\"}", "{}"]
],
"pubkey": "Author of Response"
}
```

For multiple-choice fields, the response values are delimited by semicolons. For complex objects like references, the value is stringified JSON.

== Field Tag Structure

A field tag is described as: `["field", FieldId, InputType, Label, Options, Field Settings]`

- `FieldId`: Alphanumeric identifier for the field
- `InputType`: One of "text", "option", "label"
- `Label`: Description or question for the field
- `Options`: Stringified array of options for option-type fields
- `Field Settings`: Stringified JSON with field metadata (required, prerequisites, etc.)

== Options Structure

For option-type fields, the options array is stringified and contains option tags in the format:
`[OptionId, Label, OptionalConfig]`

== Categories

The `c` tag specifies the form category. For example:

- `poll`: For surveys and voting
- `purchase`: For product/service purchases with additional fields
- `application`: For job applications, grants, or other formal applications

== Client Behavior

- Clients SHOULD validate form submissions against the field definitions
- Clients SHOULD respect field settings like "required" and "prerequisites"
- Clients MAY implement additional input types by extending the text type