Bird Detection & Birdex
Bird Detection & Birdex
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:
``jsonc ["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:
https scheme, host www.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 i tag with a k tag 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 the k tag: every i tag on a kind 2473 event is known by the kind contract to be a Wikidata entity URI, so a k tag would carry no information beyond what the i value's URL shape already reveals. Queries over these events filter by the specific Wikidata URI (or by kinds: [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.
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.
``jsonc ["n", "Turdus migratorius"] ``
The scientific name is advisory, not authoritative — the i tag is the canonical species identity. If the two disagree (e.g. because a taxon was renamed between publishing and rendering), clients SHOULD trust the i tag's Q-id and treat n as 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.
``jsonc ["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 g tag 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 authors filter 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>"]. i tags not followed by an n tag 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.