Write

Push entity labels (operator keys only)

OPERATOR KEYS ONLY since 19.10.2026: a signed request from a key whose user_id is in ADMIN_USER_IDS. Any other credential gets 403 admin_required (or hmac_required for a bare key). Every entry names the source it is written under — an attribution_trust source, see GET /v1/admin/source-trust — and the row records the writer: written_by (user_id) and written_by_key (key fingerprint). Source and writer are different facts.

An address that nothing else describes is answerable by /v2/enrich IMMEDIATELY; one that a feed already describes waits for the next label-sync cycle (about ten minutes), which decides by source rank who wins. The response says which is which: visible_now and visible_after_sync. Rows from other sources on the same address are reported per entry in competing (with their rank), not refused: rank settles it. Re-pushing under the same source is an ordinary update; an unknown source is refused per entry (unknown source) — create it first.

An entry may name a CLUSTER instead of an address: send cluster_id (the value /v2/enrich returns in axes.membership.group_id) in place of wallet_address and ONE row covers the whole cluster, including members discovered later. Use it for hubs and for multi-address entities — a hub can hold 1.96M addresses, and a claim expanded over them freezes on today's membership. A cluster label participates in attribution with its original source and source rank, just like an address label. Confidence breaks rank ties; an address claim wins an otherwise equal tie. Inherited axis claims carry applies_to: cluster and group_id. No label-sync wait is needed for cluster claims. Sending both keys in one entry is a 400.

ONLY OWNERSHIP CLUSTERS MAY BE LABELLED: unnamed: (a hub) and entity:. community: and cex_origin: are refused — they group addresses by graph behaviour, not by owner, and a name put on one names strangers (the largest community cluster holds 523k addresses, the largest cex_origin 29.6M). Clusters above CLUSTER_LABEL_MAX_MEMBERS members are refused too; check cluster_size from /v1/entities first.

category must exist in the published taxonomy — see GET /v1/categories. An unknown one is refused per entry, with the nearest known names in did_you_mean.

SOLANA (label-only chain) is accepted since 19.10.2026. A Solana row is read by /v2/enrich straight from the label store, so visible_now counts it when your row is the best-ranked claim on the address; a row outranked by a feed is stored but not shown, and no sync cycle changes that. Addresses are case-sensitive: push the exact spelling. cluster_id entries do not apply to Solana (no clusters there).

post/v1/labels

Request

  • Base URL: https://intelapi.publicaml.org
  • URL: https://intelapi.publicaml.org/v1/labels
  • Auth: API key in header X-Signature and API key in header X-API-Key and API key in header X-Timestamp

Request body

Response

Per-entry result.

upsertedinteger

Address labels written.

cluster_upsertedinteger

Cluster labels written. Present only when the batch contained any — one of these covers a whole cluster, so it is not summed with upserted.

visible_nowinteger

Of upserted, how many are already answerable by /v2/enrich.

visible_after_syncinteger

The rest: addresses something else already describes, where precedence is settled by the next label-sync cycle.

errorsobject[]

Per-entry rejections, each with its index. A cluster_id that matches no known cluster is rejected as entity_not_found; a community/cex_origin or oversized cluster is rejected with its cluster_type and cluster_size; an unknown category is rejected with did_you_mean.

Changes

No recorded changes to this operation across all 1 revision of this API.