Attribute Types Reference
Version: 1.0.0
This document describes the seven attribute types supported by notifito, their wire formats, validation rules, and matching behavior.
Overview
notifito uses a closed set of seven attribute types. This set is shared by three consumers:
- Widget: Renders form inputs based on type
- Server-side validation: Validates submissions against type rules
- Matching engine: Compares subscription and event attributes
Adding an eighth type requires teaching all three consumers about it, so the set does not grow.
The Seven Types
1. enum
Single or multiple selection from a predefined list of options.
Wire Format (Scalar):
"42"
Wire Format (Multiple):
["41", "42"]
Definition:
{
"key": "size",
"label": "Size",
"type": "enum",
"options": ["40", "41", "42", "43"],
"required": true,
"matchable": true,
"multiple": false
}
Validation Rules:
- Must be one of the declared options
- If multiple: true, must be an array of options
- If multiple: false, must be a single string
- Empty arrays are rejected
Matching Behavior: - Single: Exact equality - Multiple: Set intersection (at least one value matches)
Widget Rendering:
- Single: <select> dropdown
- Multiple: Multi-select or checkboxes
Examples:
// Single enum
{"size": "42"}
// Multiple enum
{"colors": ["red", "blue"]}
2. integer
Whole numbers with optional range constraints.
Wire Format (Scalar):
42
Wire Format (Interval):
{"min": 40, "max": 44}
Definition:
{
"key": "guests",
"label": "Number of Guests",
"type": "integer",
"required": true,
"matchable": true
}
Validation Rules:
- Must be a whole number (no decimals)
- Strings are coerced to integers
- Interval: min and max are optional (nullable)
- Interval: min must not exceed max
Matching Behavior:
- Scalar: Exact equality
- Interval: Value falls within range (inclusive)
- Open interval: {"min": null, "max": 4} matches anything ≤ 4
- Open interval: {"min": 2, "max": null} matches anything ≥ 2
Widget Rendering:
- <input type="number" step="1">
Examples:
// Scalar
{"guests": 2}
// Interval
{"guests": {"min": 2, "max": 4}}
// Open interval
{"guests": {"min": null, "max": 4}}
3. decimal
Floating-point numbers with optional range constraints.
Wire Format (Scalar):
10.5
Wire Format (Interval):
{"min": 10.0, "max": 12.5}
Definition:
{
"key": "price",
"label": "Price",
"type": "decimal",
"required": true,
"matchable": true
}
Validation Rules: - Must be numeric (integer or float) - Strings are coerced to floats - Interval: Same rules as integer
Matching Behavior: - Scalar: Exact equality - Interval: Value falls within range (inclusive)
Widget Rendering:
- <input type="number" step="any">
Examples:
// Scalar
{"price": 10.5}
// Interval
{"price": {"min": 10.0, "max": 12.5}}
4. date
ISO 8601 date values.
Wire Format:
"2026-09-01"
Definition:
{
"key": "appointment",
"label": "Appointment Date",
"type": "date",
"required": true,
"matchable": true
}
Validation Rules:
- Must be ISO 8601 format: YYYY-MM-DD
- Must be a valid date (e.g., 2026-02-30 is rejected)
- Time component is not allowed
Matching Behavior:
- Exact equality only
- No range comparison (use date_range for ranges)
Widget Rendering:
- <input type="date">
Examples:
{"appointment": "2026-09-01"}
5. date_range
Start and end dates representing a range.
Wire Format:
{
"from": "2026-09-01",
"to": "2026-09-05"
}
Definition:
{
"key": "stay",
"label": "Stay Dates",
"type": "date_range",
"required": true,
"matchable": true
}
Validation Rules:
- Both from and to must be ISO 8601 dates
- from must not be after to
- Either bound can be null (open-ended)
- Bare date string is normalized to {"from": d, "to": d}
Matching Behavior:
- Overlap: Ranges must overlap
- Touching ranges (e.g., Sep 1-5 and Sep 5-10) match
- Open-ended: {"from": null, "to": "2026-09-05"} matches anything ending before Sep 5
Widget Rendering: - Two date pickers (from and to)
Examples:
// Closed range
{"stay": {"from": "2026-09-01", "to": "2026-09-05"}}
// Open-ended
{"stay": {"from": "2026-09-01", "to": null}}
// Bare date (normalized)
{"stay": "2026-09-01"}
6. boolean
True/false values.
Wire Format:
true
Definition:
{
"key": "gift_wrap",
"label": "Gift Wrap",
"type": "boolean",
"required": false,
"matchable": true
}
Validation Rules:
- Must be true or false
- Strings like "yes", "1", "true" are rejected
- null is treated as absent (not a value)
Matching Behavior:
- Exact equality: true matches true, false matches false
Widget Rendering:
- <input type="checkbox">
Examples:
{"gift_wrap": true}
7. text
Free-text values with a maximum length.
Wire Format:
"Leave at door"
Definition:
{
"key": "notes",
"label": "Special Notes",
"type": "text",
"required": false,
"matchable": false
}
Validation Rules:
- Must be a string
- Maximum 500 characters
- null is treated as absent
Matching Behavior:
- Exact equality (case-sensitive)
- Typically matchable: false for free-text notes
Widget Rendering:
- <input type="text" maxlength="500">
Examples:
{"notes": "Leave at door"}
Wire Format Summary
| Type | Scalar Form | Interval Form | Nullable |
|---|---|---|---|
enum |
"42" |
— | No |
enum (multiple) |
["41", "42"] |
— | No |
integer |
42 |
{"min": 40, "max": 44} |
Yes (bounds) |
decimal |
10.5 |
{"min": 10, "max": 12.5} |
Yes (bounds) |
date |
"2026-09-01" |
— | No |
date_range |
{"from": "...", "to": "..."} |
— | Yes (bounds) |
boolean |
true |
— | No |
text |
"hello" |
— | No |
Validation Rules Summary
| Type | Rules |
|---|---|
enum |
Must be one of declared options |
integer |
Must be whole number |
decimal |
Must be numeric |
date |
Must be ISO 8601 format (YYYY-MM-DD) |
date_range |
Must have from/to dates, from ≤ to |
boolean |
Must be true/false |
text |
Max 500 characters |
Matching Behavior Summary
| Type | Matching Rule |
|---|---|
enum |
Exact equality (single) or set intersection (multiple) |
integer |
Exact equality or range containment |
decimal |
Exact equality or range containment |
date |
Exact equality |
date_range |
Range overlap |
boolean |
Exact equality |
text |
Exact equality (case-sensitive) |
Wildcard Rules
- Subscription empty: Consumer doesn't care → Match anything
- Event empty: Seller forgot field → Wildcard, match everything
This ensures sellers never under-notify due to missing attributes.
Schema Definition
Attribute Definition Structure
{
"key": "size",
"label": "Size",
"type": "enum",
"options": ["40", "41", "42"],
"required": true,
"matchable": true,
"multiple": false
}
| Field | Type | Required | Description |
|---|---|---|---|
key |
string | Yes | Lowercase snake_case identifier |
label |
string | Yes | Display label |
type |
string | Yes | One of the seven types |
options |
array | Conditional | Required for enum type |
required |
boolean | No | Default: false |
matchable |
boolean | No | Default: true |
multiple |
boolean | No | Default: false (enum only) |
Key Rules
- Must be lowercase snake_case
- Must start with a letter
- Can contain letters, numbers, underscores
- Examples:
size,guest_count,date_range
Examples by Industry
E-commerce (Sneaker Store)
[
{
"key": "size",
"label": "Size",
"type": "enum",
"options": ["40", "41", "42", "43"],
"required": true,
"matchable": true,
"multiple": false
},
{
"key": "color",
"label": "Color",
"type": "enum",
"options": ["red", "blue", "black"],
"required": false,
"matchable": true,
"multiple": false
}
]
Hospitality (Hotel)
[
{
"key": "stay",
"label": "Stay Dates",
"type": "date_range",
"required": true,
"matchable": true
},
{
"key": "guests",
"label": "Number of Guests",
"type": "integer",
"required": true,
"matchable": true
}
]
Healthcare (Clinic)
[
{
"key": "appointment",
"label": "Appointment Date",
"type": "date",
"required": true,
"matchable": true
},
{
"key": "practitioner",
"label": "Practitioner",
"type": "enum",
"options": ["Dr Ng", "Dr Okafor", "Any"],
"required": false,
"matchable": true
}
]
Related Documentation
- Data Model — Database schema
- Widget Integration — Widget rendering
- Edge API Reference — API endpoints