Many Biotic columns are codes, not literal values — sex, maturationstage,
specialstage, missiontype, readability, gear, nation, … In the field glossary these
are the rows flagged code? = yes (type KeyType / CompositeTaxa*KeyType). A code like
sex = 1 is meaningless until you resolve it against the NMDreference tables.
Rule: don't guess what a code means, and don't read it off some package's
recode()call — those can be incomplete or stale. Resolve it against the authoritative source: the database's own index tables when present, otherwise the IMR Reference API below.
- In-database index tables first (no network, works offline). Resolution is a SQL
join, so it costs no tokens — you never read the mapping into context:
taxaindex(species),csindex(cruise series),gearindex(gear) — seespecies-and-surveys.md.codeindex— the simple codedKeyTypefields (sex,maturationstage,missiontype,nation, …) in one long table. See Resolving fromcodeindexbelow.
- Cached common codes in this file (the sex table below) — only the few tiny, hot
codes you need in context to phrase an answer, and the offline fallback when an older
database predates
codeindex. Not a full registry; don't grow it into one. - The IMR Reference API — last resort, on-network only, for anything the DB and the cache
don't cover (notably the composite taxa/sex-keyed tables:
specialstage,eggstage,moultingstage,spawningfrequency).
Databases built with BioticExplorerServer ≥ 0.7.0 carry a codeindex table:
reftable | code | shortname | description. Decode a coded column by joining on it — keep the
join lazy in DuckDB and only collect() the small result:
# Decode `sex` on a result without ever hard-coding the mapping
sexcodes <- dplyr::tbl(con, "codeindex") |>
dplyr::filter(reftable == "sex") |>
dplyr::select(sex = code, sexname = shortname)
dplyr::tbl(con, "indall") |>
dplyr::filter(commonname == "torsk") |>
dplyr::mutate(sex = as.character(sex)) |> # codeindex$code is character
dplyr::left_join(sexcodes, by = "sex") |>
dplyr::count(sexname) |>
dplyr::collect()codeindex is built by prepareReferenceCodes() and excludes editor-identity columns by
design. If dplyr::tbl(con, "codeindex") errors, the database is older — fall back to the
cached codes below or the API.
An IMR-internal REST service (reachable on the HI network / VPN — behind the institute firewall). Base:
https://reference-api.hi.no/apis/nmdapi/reference/v2
Reads are unauthenticated; writes are not. GET works without a token. POST/PUT/
DELETE require a bearer token and would modify IMR's shared reference registry.
🔒 Agents must only ever GET. Never call a write method against this API — you would be editing institutional reference data for everyone. There is no BAIT task that writes here. Also: this endpoint is for resolving codes. Never send Biotic data, positions, or any query results to it — the privacy rules in
AGENTS.mdapply unchanged.
curl -s -H "Accept: application/json" \
"https://reference-api.hi.no/apis/nmdapi/reference/v2/tables?version=2.1"Returns ~177 reference tables (sex, maturationstage, nation, missiontype,
specialstage-<taxa>-<sex>, …) under a row[] array, each with a name.value.
# /model/{table}/{code}?lang=en → shortname + description
curl -s -H "Accept: application/json" \
"https://reference-api.hi.no/apis/nmdapi/reference/v2/model/sex/1?version=2.1&lang=en"
# → shortname "Female"Use lang=no for Norwegian, lang=en for English. version=2.1 returns JSON cleanly;
omit the Accept header and you get XML.
resolve_code <- function(table, code, lang = "en") {
url <- sprintf(
"https://reference-api.hi.no/apis/nmdapi/reference/v2/model/%s/%s?version=2.1&lang=%s",
table, code, lang)
x <- jsonlite::fromJSON(url)
x$shortname$value
}
resolve_code("sex", 1) # "Female"This works for any coded column — pass the glossary's column name as table (most match
directly: sex, maturationstage, nation, missiontype, …).
A few tiny, stable lookups kept inline so you can decode them in context without a join or a
network call. Prefer codeindex for anything you're decoding in a pipeline; use these only
when you need the meaning to phrase the answer. Verify against the API if a value looks
off — the registry is the source of truth and can change.
| code | meaning |
|---|---|
| 1 | Female |
| 2 | Male |
| 3 | Intersex |
| 4 | Hermaphroditic |
Codes 1/2 dominate; 3/4 are rare and post-2017. Some packages collapse everything except 1/2 to "Unidentified" — that loses the Intersex/Hermaphroditic distinction the registry makes.
NAsex means not recorded, which is not the same as a code.Greenland halibut (
blåkveite) on the Egga surveys: whensexis missing it has historically been encoded incatchpartnumber("delnummer") instead —1 = female,2 = male. Only apply this back-fill for Greenland halibut on EggaN/EggaS; elsewhere a missingsexis just missing. Seespecies-and-surveys.md.
Don't add more tables here — codeindex in the database is the full offline reference, and
the API is the source of truth. This cache is only for the handful of codes you must reason
about in context to write an answer.
- The API base URL is an internal IMR endpoint (firewalled), included here only so agents on the HI network can resolve codes. It exposes reference metadata, not Biotic data.
- Read-only. Never authenticate or write against it from a BAIT task.
- Do not paste API responses verbatim into committed files if they carry editor identities
(
updatedBy/insertedByhold staff usernames) — keep only thecode → meaningmapping.prepareReferenceCodes()already strips these when buildingcodeindex, so the database table is safe; the caution is for anything you copy by hand.
The Swagger UI's initializer points at …/v2/v3/api-docs/swagger-config, whose url field
gives the OpenAPI spec at …/v2/v3/api-docs. The spec lists GET /tables,
GET /{tablename}, and GET /model/{table}/{code} as the read endpoints (the matching
POST/PUT carry bearerAuth).
field-glossary.md— which columns are codes (code? = yes).species-and-surveys.md—taxaindex/csindex/gearindexin-database lookups.codeindexis built by BioticExplorerServer'sprepareReferenceCodes()(≥ 0.7.0) and written bycompileDatabase(). RuncompileDatabase()once to add it to a legacy database; routine data refreshes useupdateDatabase().../skills/biotic-query/SKILL.md— decode codes when reporting results to the user.