Skip to main content
POST
/
v0
/
profiles
/
{address}
/
labels
curl --request POST \
  --url https://api.formo.so/v0/profiles/{address}/labels \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "tag_id": "vip",
  "value": "tier-1"
}
'
{
  "address": "<string>",
  "tag_id": "<string>",
  "value": "<string>",
  "chain_id": "<string>",
  "source": "<string>",
  "timestamp": "2023-11-07T05:31:56Z"
}

Historical (backfilled) labels

Each label accepts an optional timestamp (ISO-8601). When set, the label is recorded at that time instead of the server’s current time, so you can backfill historical values (for example, an open_interest reading from a past week). Label-based retention then evaluates each value at the correct point in time.
curl -sS -X POST \
  -H "Authorization: Bearer <your_workspace_api_key>" \
  -H "Content-Type: application/json" \
  -d '{ "tag_id": "open_interest", "value": "1200", "timestamp": "2026-01-05T00:00:00Z" }' \
  "https://api.formo.so/v0/profiles/<address>/labels"
timestamp must not be in the future; future timestamps are rejected with 400. When omitted, the label is recorded at server time. The response echoes the caller-supplied timestamp when present, otherwise the server write time.

Backdated removals (historical tombstones)

To record that a label was removed at a past point in time, set the optional _is_deleted flag to 1 together with a past timestamp. This writes a tombstone: a soft-delete row that marks the label as removed at time T. Point-in-time retention then drops the wallet from that historical week, rather than only from now. _is_deleted is an optional integer, either 0 or 1, and defaults to 0 (a live label) when omitted. A tombstone’s value is irrelevant and can be left out.
curl -sS -X POST \
  -H "Authorization: Bearer <your_workspace_api_key>" \
  -H "Content-Type: application/json" \
  -d '{ "tag_id": "open_interest", "_is_deleted": 1, "timestamp": "2026-03-15T00:00:00Z" }' \
  "https://api.formo.so/v0/profiles/<address>/labels"
A common pattern is to import a label’s full history as a value time series ending in a removal:
curl -sS -X POST \
  -H "Authorization: Bearer <your_workspace_api_key>" \
  -H "Content-Type: application/json" \
  -d '[
    { "tag_id": "open_interest", "value": "high", "timestamp": "2026-01-15T00:00:00Z" },
    { "tag_id": "open_interest", "value": "low",  "timestamp": "2026-02-15T00:00:00Z" },
    { "tag_id": "open_interest", "_is_deleted": 1, "timestamp": "2026-03-15T00:00:00Z" }
  ]' \
  "https://api.formo.so/v0/profiles/<address>/labels"
A tombstone is just a backdated write, so the same rules apply: a future timestamp is rejected with 400 BAD_REQUEST. Order of ingest does not matter, since reads resolve to the value with the latest timestamp: a backdated tombstone never clobbers a newer live value, and a backfilled value never resurrects a label after a later removal.
For a chain-scoped label, the tombstone must carry the same chain_id as the label it removes; chain_id is part of the label’s identity. The storage-only _is_deleted flag is never echoed back in the UserLabel response.
To remove a label at the current server time, use Delete Label instead.

Authorizations

Authorization
string
header
required

Workspace API key (e.g. formo_xxx). Create one in the Formo dashboard under Team Settings > API Keys.

Headers

Idempotency-Key
string

Optional unique value (e.g. a UUID v4) that lets you safely retry POST/PUT/PATCH/DELETE requests. The first request runs normally; subsequent requests with the same key replay the stored response (status + body) for 24 hours, so retries can never double-create or double-charge. Two concurrent requests with the same key return 409 IDEMPOTENCY_IN_PROGRESS. Generate a fresh key per logical operation.

Maximum string length: 255

Path Parameters

address
string
required

Wallet address. Accepts an EVM (0x...) or Solana address, or an ENS name (e.g. vitalik.eth) which is resolved to an address.

Body

application/json
tag_id
string
required

Label identifier (lowercased on write). e.g. vip, airdrop_eligible, coinbase.verified_account

value
string

Optional label value (e.g. tier name, country code)

chain_id
string

Optional chain identifier the label applies to

timestamp
string<date-time>

Optional ISO-8601 event-time for the label. When provided, the label is recorded at this time instead of the server's current time; used to backfill historical values (e.g. an open_interest reading from a past week) so label-based retention can evaluate them at the right point in time. Must not be in the future. Defaults to server time when omitted.

_is_deleted
enum<integer>

Optional tombstone flag for backfilled removals. 1 records the row as a soft-delete (label removed) instead of a live value; pair it with a past timestamp to express "label removed at past time T" so point-in-time retention drops the wallet from that week. The future-timestamp guard still applies. Defaults to 0 (a live label) when omitted.

Available options:
0,
1

Response

Labels upserted. Single label in → bare UserLabel; array in → array of UserLabel. Echoes the normalised entries (lowercased tag_id; the caller-supplied timestamp when present, otherwise the server write time).

Canonical user-label resource. Echoed back from upsert calls so callers can cache the normalised entry without a follow-up read.

address
string
required

Wallet address the label applies to

tag_id
string
required

Label identifier (lowercased + trimmed on write)

value
string
required

Label value, empty string when omitted

chain_id
string
required

Chain identifier, empty string when omitted

source
string
required

Origin of the label (e.g. 'import')

timestamp
string<date-time>
required

Event-time of the label: the caller-supplied timestamp when provided (retrospective backfill), otherwise the server's write time.