30817:bird-detection-birdex
Bird Detection & Birdex
- signed by
- npub1q3sle...6cd26p
- published
- 2026-06-28
- event
- e0d371eecb...de30
This NIP describes birding events on Nostr.
Kind 2473 — Bird Detection
A regular event representing a single identified bird observation. Regular events (1000 ≤ kind < 10000) are stored permanently by relays and are not deduplicated.
The schema deliberately relies on existing NIPs (NIP-31 alt, NIP-73 i)
rather than inventing parallel vocabulary, so that non-birding clients can
still display and aggregate detections usefully.
Rationale
A bird detection is an observation log entry: a timestamped, immutable record that "species X was heard at time T", optionally annotated with a free-form human note.
We reviewed existing NIPs before introducing a new kind:
- NIP-52 (Calendar Events) models planned events at specific future times, not observation logs. RSVPs and addressability make no sense for a detection.
- NIP-32 (Labeling) labels another event — it has no standalone meaning. A detection is a primary artifact, not a label applied to something else.
- Kind 1 text notes can contain "I heard a robin!" but provide no structured data, so other clients cannot aggregate or filter by species.
- The unofficial Geocaching kinds (7516/7517) follow a similar observation-log pattern but are tightly coupled to geocache listings.
No existing kind covers a structured, standalone observation log, so we define a new regular event kind.
content
Free-form human-readable note about the detection. MAY be empty.
Examples: "", "Heard from my balcony near the oak tree."
Structured data MUST be placed in tags, not in content.
Tags
Required
-
alt(NIP-31) — human-readable summary, e.g."Bird detection: American Robin (Turdus migratorius)". Lets clients that don't implement this NIP still render something meaningful. -
i(NIP-73) — an external ID pointing at the species' Wikidata entity URI. This is the canonical identity of the species, the primary aggregation key, and the only species identifier in the event:["i", "https://www.wikidata.org/entity/Q26825"]Wikidata is language-neutral: a single Q-id designates the species across every Wikipedia edition, taxonomy database, and localized common name. A client rendering the event in any language can follow the entity URI to get labels, images, and the sitelink to its preferred Wikipedia edition.
Requirements:
- The URL MUST match
https://www.wikidata.org/entity/Q<digits>exactly:httpsscheme, hostwww.wikidata.org, path/entity/Q<digits>, no fragment, no query string, no trailing slash. - The Q-id SHOULD resolve to a taxon-like entity (a species, subspecies, or similarly specific rank). Publishers SHOULD NOT emit a detection with a Q-id that has no corresponding taxon.
Deviation from NIP-73. NIP-73 pairs every
itag with aktag naming the ID kind ("web","isbn","geo", etc.) so that clients can query "all events with any external ID of kind X". Birdstar deliberately omits thektag: everyitag on a kind 2473 event is known by the kind contract to be a Wikidata entity URI, so aktag would carry no information beyond what theivalue's URL shape already reveals. Queries over these events filter by the specific Wikidata URI (or bykinds: [2473]), never by the generic#k: ["web"]bucket which would return every blog post, book, and podcast on the relay. The trade-off — Birdstar detections won't show up in generic NIP-73 web-content aggregators — is intentional; a species Q-id is not a webpage in the sense those aggregators care about. - The URL MUST match
-
n— the species' scientific (binomial) name as a single string, e.g."Turdus migratorius". Lets clients render the detection without round-tripping Wikidata just to get a species label.["n", "Turdus migratorius"]The scientific name is advisory, not authoritative — the
itag is the canonical species identity. If the two disagree (e.g. because a taxon was renamed between publishing and rendering), clients SHOULD trust theitag's Q-id and treatnas a stale label.
Optional
-
g(NIP-52) — coarse geohash of where the detection was made. Opt-in only: Birdstar publishes this tag exclusively when the user has explicitly enabled "Publish location" in Settings. Off by default.["g", "dp3"]Birdstar emits a length-3 geohash (~156 km × 156 km cells), which is the coarsest precision still useful for region-scale aggregation without revealing the observer's neighborhood. Clients receiving a detection MAY display it on a map, use it to filter "detections near me", or ignore it entirely; the rest of the event is self-contained without it.
The
gtag value MUST match/^[0-9bcdefghjkmnpqrstuvwxyz]+$/— the standard geohash base-32 alphabet (digits 0-9 plus the consonants b-z with a, i, l, o removed). Publishers SHOULD NOT emit geohashes finer than length 8 (~20 m) on a detection event — the act of stamping a GPS-precise coordinate onto a signed, public, permanent record is almost never what the observer intends.
Publishers SHOULD NOT attach a per-event numeric confidence to a detection. Publishing a kind 2473 event IS the publisher's confidence signal: the act of authoring and signing the event asserts that the observation was reliable enough to commit to the record.
Example
{
"kind": 2473,
"content": "Heard from my balcony around 7am — very clear song.",
"tags": [
["alt", "Bird detection: American Robin (Turdus migratorius)"],
["i", "https://www.wikidata.org/entity/Q26825"],
["n", "Turdus migratorius"]
]
}
With the optional g tag, when the user has enabled location publishing
in Settings:
{
"kind": 2473,
"content": "",
"tags": [
["alt", "Bird detection: American Robin (Turdus migratorius)"],
["i", "https://www.wikidata.org/entity/Q26825"],
["n", "Turdus migratorius"],
["g", "dp3"]
]
}
Query Patterns
All detections of a given species across the network:
{
"kinds": [2473],
"#i": ["https://www.wikidata.org/entity/Q26825"]
}
Dropping the kinds filter broadens this to all Nostr content about the
species — bird detections, comments, long-form posts, etc. — since the
Wikidata entity URI is a shared NIP-73 identifier:
{ "#i": ["https://www.wikidata.org/entity/Q26825"] }
All detections by a specific observer:
{ "kinds": [2473], "authors": ["<hex-pubkey>"], "limit": 50 }
Validation
Clients SHOULD ignore an event as a bird detection if:
kind !== 2473- No
itag with a normalizedhttps://www.wikidata.org/entity/Q<digits>URL is present
Publishing
Birdstar publishes detections automatically for logged-in users as birds are identified, so that a user's sightings are preserved across sessions and devices via their own Nostr relays.
To avoid spamming relays when the same bird is heard repeatedly, publishers
SHOULD apply a per-species cooldown: once a detection event has been
published for a given i tag, no new event for that same URL should be
published by the same author for at least 10 minutes.
Detections from logged-out users are never published.
Detections heard while offline are not lost. When a publish fails (no
connection, relay timeout, etc.) the detection is persisted to a local
queue and retried when connectivity returns, repeatedly if necessary,
until the event lands on a relay. The event's created_at always
carries the original heard time, so a sighting published hours or days
late is still timestamped to when the bird was actually heard — readers
should expect kind 2473 events whose created_at precedes their arrival
by an arbitrary amount, and SHOULD order detections by created_at
rather than receipt time.
Birdstar does not publish any location information on detections by
default. The optional g tag is only attached when the user has
explicitly enabled "Publish location" in the Birds-mode Settings
sheet, and is computed from the same coarse (1°-rounded) lat/lon that
drives the birdex's region filter — no additional precision is
introduced at publish time.
Deletion
Users can retract a previously published detection by issuing a standard NIP-09 deletion request (kind 5) referencing the detection's event id:
{
"kind": 5,
"tags": [
["e", "<kind-2473-event-id>"],
["k", "2473"]
],
"content": ""
}
Birdstar hides any detection whose id appears in an e tag of a kind 5
event authored by the same pubkey.
Kind 12473 — Birdex
A replaceable event (kind 12473, chosen as 1 prefixed onto the
2473 detection kind for mnemonic kinship) representing the author's
Birdex: the cumulative set of every distinct bird species they
have ever published a kind 2473 detection for.
Replaceable events (10000 ≤ kind < 20000) are deduplicated by relays on
pubkey + kind, so each author has at most one Birdex on the network at
any time. Republishing with a newer created_at replaces the prior
copy.
Rationale
Kind 2473 detections are the permanent log; the Birdex is the index derived from them. The log-vs-index split is deliberate:
- Without the Birdex, reconstructing a user's species list requires paginating their entire kind 2473 history across all relays — expensive for clients, unreliable when some relays drop old events, and impossible to express as a single filter.
- With it, any client can fetch one replaceable event and render the user's full species count, first-seen ordering, and profile-page "lifer badges" in a single round-trip.
- A new kind (rather than a generic list via NIP-51) is warranted because the entries are tightly constrained (Wikidata taxon URIs only) and the event has domain-specific semantics (first-seen ordering, tied to the kind 2473 author's own detection history).
content
Free-form human-readable description of the Birdex. MAY be empty.
Non-birding clients rendering this event SHOULD prefer the alt tag.
Tags
Required
alt(NIP-31) — human-readable summary, e.g."Birdex: 37 species". Lets clients that don't implement this NIP still render something meaningful.
Species entries
Each species on the Birdex is represented by an i tag (NIP-73)
immediately followed by an n tag carrying the scientific name — the
same vocabulary used on kind 2473 detections:
["i", "https://www.wikidata.org/entity/Q26825"],
["n", "Turdus migratorius"]
The same URL-shape rules as kind 2473 apply to the i value: https
scheme, host www.wikidata.org, path /entity/Q<digits>, no fragment,
no query string, no trailing slash. The n value is a free-form
single-string scientific name (binomial), advisory only — see the
kind 2473 n description for the trust model.
As with kind 2473, the Birdex deliberately omits the k tag that
NIP-73 normally pairs with i. See the kind 2473 rationale — every
i tag on a Birdex is known by the kind contract to be a Wikidata
entity URI, and pairing with k would only add noise.
On positional pairing. Each n tag is paired with the preceding
i tag by position in the event's tag array. Publishers MUST emit
every species as exactly ["i", "..."] followed immediately by
["n", "..."] with no other tags interleaved between the pair. The
pairing is positional rather than keyed because a Wikidata URI is not
a unique-enough identifier to use as the n tag's second element
without reconstructing most of the i value; positional pairing keeps
the wire format terse and the "N species on my Birdex = 2N entries"
invariant trivially checkable. Clients that need name-first lookups
should build an index once at parse time.
Entry ordering
The i/n pairs MUST be emitted in chronological order of first
detection, oldest species first. The position of a species' pair in
the tag array is its ordinal rank on the Birdex ("the 7th species I
ever identified"). Clients rendering a timeline of an author's birding
journey rely on this ordering — it cannot be reconstructed from the
replaceable event alone once the underlying kind 2473 events have aged
off relays.
Each species MUST appear at most once. Republishing the event to add a
newly confirmed species means appending the new i/n pair to the
end of the existing tag list, preserving the historical order of all
previous entries.
Example
An author whose Birdex holds three species — first a robin, then a cardinal, then a chickadee:
{
"kind": 12473,
"content": "",
"tags": [
["alt", "Birdex: 3 species"],
["i", "https://www.wikidata.org/entity/Q26825"], // American Robin
["n", "Turdus migratorius"],
["i", "https://www.wikidata.org/entity/Q27014"], // Northern Cardinal
["n", "Cardinalis cardinalis"],
["i", "https://www.wikidata.org/entity/Q335669"], // Black-capped Chickadee
["n", "Poecile atricapillus"]
]
}
Query Patterns
A specific user's Birdex:
{ "kinds": [12473], "authors": ["<hex-pubkey>"], "limit": 1 }
All users whose Birdex includes a given species:
{ "kinds": [12473], "#i": ["https://www.wikidata.org/entity/Q26825"] }
Validation
Clients SHOULD ignore an event as a Birdex if:
kind !== 12473- It is not authored by the pubkey whose Birdex is being queried
(replaceable events are per-author; the
authorsfilter is the trust boundary — never trust a 12473 event on its content alone).
When parsing species entries, clients SHOULD:
- Walk the tag array in order.
- For each
itag whose value matches the canonical Wikidata entity URI regex, take its scientific name from the next tag if that tag is["n", "<non-empty string>"].itags not followed by anntag are still accepted — the species is known by its Q-id but has no local scientific-name label. - Deduplicate by Wikidata URI, keeping the first occurrence — the chronologically earliest entry wins.
Publishing
Birdstar publishes the Birdex automatically for logged-in users. The publish flow is tied to kind 2473 detections:
- When a kind 2473 detection is about to be published for a species, check whether that species' Wikidata URI is already present in the author's existing Birdex (loaded via a single kind 12473 query on login).
- If absent, publish the kind 2473 detection as normal, then publish a new kind 12473 event containing every previously-known URI plus the new one appended at the end.
- If present, only the kind 2473 event is (re)published subject to the usual 10-minute cooldown; the Birdex does not change.
This couples Birdex growth to confirmed detections so the Birdex can never diverge from the underlying log: every entry has a corresponding kind 2473 event from the same author.
Deletion
To retract a species from the Birdex, republish the event with that
species' i/n pair removed. Because kind 12473 is replaceable, the
new event supersedes the old. Separately issuing a NIP-09 deletion on
the underlying kind 2473 events is also recommended if the user wants
to remove the detection log, not just the Birdex entry.
Discussion
Connect a key to comment.