API Reference

Technical reference for building on Verus. Identity structure, contentmultimap, encryption, vault security, and RPC methods.

Identity Structure

Every VerusID is an on-chain object returned by getidentity. Here's what it contains:

// getidentity "veruscx@"
{
  "name": "veruscx",
  "identityaddress": "i6QEbNsGThm2JHUwpLwxSu77DwzFWvfHv6",
  "primaryaddresses": ["RAhD9vgrWZ3ALkq541xdqL2oLYEvsLNrzn"],
  "minimumsignatures": 1,
  "revocationauthority": "i6QEb...",  // can revoke access
  "recoveryauthority": "i6QEb...",   // can recover identity
  "flags": 0,                           // 0=unlocked, 2=locked
  "timelock": 0,                        // unlock delay (blocks)
  "contentmultimap": {},               // on-chain data store
  "contentmap": {}
}
FieldPurpose
nameHuman-readable identity name (unique on-chain)
primaryaddressesAddresses that can spend/sign for this identity
minimumsignaturesMulti-sig threshold (1 = single sig)
revocationauthorityIdentity that can revoke (lock) this ID. Can be self or another ID
recoveryauthorityIdentity that can recover (update keys). Can be self or another ID
flagsBit 1 = locked. When locked, no transactions can be sent
timelockMinimum block height before unlocking takes effect
contentmultimapKey-value data store (VDXF keys to arrays of values)

Content Multimap

Every VerusID has a built-in on-chain data store called the contentmultimap. It maps VDXF keys to arrays of values — each value up to ~4KB. Think of it as a blockchain-native key-value database attached to your identity.

Writing data

Data is written via updateidentity. Values are typically hex-encoded JSON stored under a VDXF key.

# Store data in your identity
DATA=$(echo -n '{"type":"post","title":"Hello"}' | xxd -p | tr -d '\n')

./verus updateidentity '{
  "name": "yourname",
  "parent": "iParentIdHere",
  "contentmultimap": {
    "iVDXFKeyHere": ["'$DATA'"]
  }
}'

Reading data

Read via getidentity and decode the hex values. From a browser, call the public RPC:

const resp = await fetch('https://api.verus.services', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '1.0', id: '1',
    method: 'getidentity',
    params: ['veruscx@']
  })
});
const { result } = await resp.json();
const cmm = result.identity.contentmultimap;
// Decode hex values: hex → bytes → UTF-8 → JSON

Encryption

Data can be encrypted using Verus's built-in Sapling (zk-SNARK) addresses. Encrypt with signdata using an encrypttoaddress parameter, then store the encrypted descriptor in the contentmultimap. Decrypt with decryptdata — only the holder of the z-address private key can read it.

This enables selective disclosure: encrypt each field separately, share viewing keys per-field to reveal only what's needed.

Vault Security

Every VerusID has built-in security features that don't exist in normal crypto wallets.

Lock & Revoke

Lock your identity so no transactions can be sent — even if your private key is stolen. Only the revocation authority can unlock, and only after a timelock delay.

# Lock an identity
./verus updateidentity '{"name":"veruscx","flags":2}'

# Unlock (starts timelock countdown)
./verus updateidentity '{"name":"veruscx","flags":0}'

Recovery

Lost your key? Your recovery authority can update the identity to use a new primary address. Set it to yourself (self-sovereign) or a trusted party like your spouse, company, or friend.

This is impossible with Bitcoin, Ethereum, or any standard crypto wallet. With VerusID, losing your key doesn't mean losing your funds.

Timelock

Set a minimum block height before an unlock takes effect. Even if an attacker has your revocation key, they can't move funds instantly — you have time to notice and respond.

Key RPC Methods

MethodPurpose
getidentityLook up any identity by name or i-address
updateidentityUpdate primary addresses, flags, contentmultimap, authorities
registernamecommitmentFirst step to register a new VerusID
registeridentityComplete VerusID registration
signdataSign or encrypt data with an identity (supports MMR, z-addr encryption)
decryptdataDecrypt data encrypted to a z-address
verifydataVerify a signature from an identity
signmessageSign a message with an address (used for challenges)
verifymessageVerify a signed message

Reading Identity Data

getidentity — confirmed vs mempool

The height parameter controls what state you see. Use -1 to include unconfirmed (mempool) identity updates.

// Confirmed state only (default)
getidentity "alice@"

// Include mempool — see updates before they confirm (~1 min faster)
getidentity "alice@" -1

// State at a specific block height (historical)
getidentity "alice@" 4000000

Use -1 when you need fast detection — e.g. checking if a user just wrote subscription metadata to their identity. The data appears in seconds instead of waiting for a block.

Used in: Subscriptions, KYC, Blog

getidentityhistory — full change log

Returns every version of an identity across a block range. Useful for tracking when contentmultimap data was added or changed.

// All changes from block 0 to current tip
getidentityhistory "alice@" 0 -1

Returns an array of identity states, each with a height and the full identity object at that point. Useful for audit trails and version history.

Used in: KYC (audit trail), Subscriptions (detecting new subscribers)

Reading contentmultimap

Hex decoding

Values in the contentmultimap are hex-encoded. Decode to UTF-8, then parse as JSON.

// JavaScript
const id = await rpc("getidentity", ["alice@"]);
const cmm = id.identity.contentmultimap;

// Decode a hex value
const hex = cmm["iVDXFKeyHere"][0];
const json = JSON.parse(Buffer.from(hex, "hex").toString("utf8"));

Used in: Blog, Social, Subscriptions

VDXF keys

Keys in the contentmultimap are VDXF i-addresses — deterministic identifiers generated from qualified names. Same name always produces the same key on every node.

// Generate a VDXF key
getvdxfid "vrsc::identity.blog.post"
// Returns: { "vdxfid": "iGTUp2Tnd2eYAih717dyFHzbokhy6DgrH2", ... }

// Standard keys (same on every node)
vrsc::identity.firstname     → iLB8SG7ErJtTYcG1f4w9RLuMJPpAsjFkiL
vrsc::identity.email         → iJ4pq4DCymfbu8SAuXyNhasLeSHFNKPr23
vrsc::identity.blog.post     → iGTUp2Tnd2eYAih717dyFHzbokhy6DgrH2

Writing contentmultimap

Read-merge-write pattern

updateidentity replaces the entire contentmultimap. Always read the current state first, merge your changes, then write back.

// 1. Read current identity
const id = await rpc("getidentity", ["alice@"]);
const existing = id.identity.contentmultimap || {};

// 2. Add your new data (preserve existing keys)
const newHex = Buffer.from(JSON.stringify(myData)).toString("hex");
existing["iMyVDXFKey"] = [newHex];

// 3. Write back the full contentmultimap
await rpc("updateidentity", [{
  name: "alice",
  parent: "iParentId",
  contentmultimap: existing
}]);

Gotchas

  • updateidentity fails if another update for the same identity is unconfirmed — wait for prior updates to confirm
  • ~5500 byte limit per updateidentity transaction — keep payloads compact, store large data off-chain
  • Use name + parent fields, never fullyqualifiedname
  • Always verify revocationauthority and recoveryauthority are not set to SELF before revoking

Encryption & Selective Disclosure

signdata — encrypt per-field

Encrypt data to a Sapling z-address. Each field gets its own ephemeral key, enabling per-field selective disclosure.

// Encrypt and sign data
const result = await rpc("signdata", [{
  address: "alice@",
  createmmr: true,
  encrypttoaddress: "zs1your_z_address_here...",
  mmrdata: [{
    vdxfdata: {
      "iDDKeyHere": {
        version: 1,
        label: "iFieldVDXFKey",
        mimetype: "text/plain",
        objectdata: { message: "secret data" }
      }
    }
  }]
}]);
// result.mmrdescriptor_encrypted → store this on-chain
// result.signaturedata → attestation signature

Used in: KYC (per-field encryption), Social (tiered post encryption)

decryptdata — selective disclosure

Decrypt individual fields from an encrypted MMR descriptor. Each field has its own ephemeral public key (EPK) — share the EPK for just the fields you want to disclose.

// Decrypt a single field from the encrypted descriptor
const decrypted = await rpc("decryptdata", [{
  datadescriptor: descriptor.datadescriptors[0]
}]);
// Only the z-address holder can decrypt

Viewing keys

Share a viewing key to let someone decrypt all data encrypted to a z-address — without giving them spending rights.

// Export the viewing key for a z-address
z_exportviewingkey "zs1your_z_address..."
// Give this key to a verifier — they can decrypt but not spend

For tiered access (public/followers/close friends), use a different z-address per tier. Share the viewing key only with people in that tier. Rotate the z-address to revoke access.

Used in: Social (4-tier access control), KYC (viewing key disclosure)

Chain Queries

getaddressdeltas — transaction history for an address

Returns all balance changes for an address. Positive satoshis = received, negative = spent. Useful for verifying payments without trusting a local database.

// All transactions for an address
getaddressdeltas { "addresses": ["RAddressHere"] }

// Filter by block range
getaddressdeltas { "addresses": ["RAddr"], "start": 4000000, "end": 4001000 }

// Each delta: { txid, index, address, satoshis, height, blocktime }
// Negative satoshis = spent from this address

Verifying payments on-chain

Instead of maintaining a local database of who paid, query the chain directly. Count outgoing transactions from a dedicated address to the recipient.

// Get all outgoing txids from a dedicated address
const deltas = await rpc("getaddressdeltas", [{ addresses: [dedicatedAddr] }]);
const outTxids = [...new Set(deltas.filter(d => d.satoshis < 0).map(d => d.txid))];

// Verify each TX pays the right destination
for (const txid of outTxids) {
  const tx = await rpc("getrawtransaction", [txid, 1]);
  const paysProvider = tx.vout.some(v =>
    v.scriptPubKey?.addresses?.includes(providerIAddr)
  );
}

This is how the subscription demo verifies payments — purely chain-based, no local state. The server stores nothing about the subscriber.

Used in: Subscriptions (chain-based access verification, GDPR-compliant)

Currency Operations

sendcurrency

Send native or reserve currencies. Returns an operation ID — poll z_getoperationresult for the actual txid.

// Send VRSC
sendcurrency "fromAddress" '[{"address":"RDest","amount":1.0}]'

// Send a reserve currency (e.g. vETH)
sendcurrency "fromAddress" '[{"address":"RDest","amount":0.01,"currency":"vETH"}]'

// Poll for result
z_getoperationresult '["opid-abc123..."]'
// Returns: [{ status: "success", result: { txid: "abc..." } }]

Warning: Never use * as the from address — always use a specific wallet address. Using wildcard can cause unexpected behaviour.

Time-locked transactions (nLockTime)

Create transactions that can't be broadcast until a specific block height. Used for scheduled payments and subscriptions.

// Create a transaction locked until block 4100000
createrawtransaction \
  '[{"txid":"fundTxid","vout":0}]' \
  '{"iPaymentAddr": 0.0099}' \
  4100000  // nLockTime — earliest block to broadcast \
  4200000  // expiryheight

// Sign it now, broadcast later when locktime is reached
signrawtransaction "rawTxHex"

// The network rejects early broadcasts — consensus enforced

createrawtransaction requires i-addresses, not friendly names. Resolve with getidentity first.

Used in: Subscriptions (pre-signed recurring payments)

Oracle (REST)

USD pricing for every Verus currency, derived from on-chain basket reserves. Hosted at scan.verus.cx. JSON, no auth.

GET /api/oracle/prices

List of every currency the oracle currently prices (filtered to currencies with at least $10k in stable backing depth).

// curl https://scan.verus.cx/api/oracle/prices
[
  {
    "currencyId": "i5w5MuNik5NtLcYmNzcvaoixooEebB6MGV",
    "currencyName": "VRSC",
    "usdPrice": 0.7646,
    "emaPrice": 0.7649,
    "source": "reserve",
    "sourceBlock": 4032417,
    "sourceBasket": "Pure",
    "confidence": 50,
    "status": "healthy",
    "totalDepth": 9426614.83,
    "updatedAt": "2026-04-20T07:52:46.835Z"
  }
]

GET /api/oracle/prices/:id

Detailed view for one currency: current price, every basket sourcing it, guard-rail check against external feeds (CoinGecko / Binance), and recent price history.

:id accepts any of: i-address (i5w5MuNik5...), name (VRSC, case-insensitive), or fully-qualified name (tBTC.vETH). Bare DAI resolves to DAI.vETH.

// curl https://scan.verus.cx/api/oracle/prices/i5w5MuNik5NtLcYmNzcvaoixooEebB6MGV
{
  "currencyId": "i5w5Mu...",
  "currencyName": "VRSC",
  "currentPrice": { "usdPrice": 0.7646, "sourceBasket": "Pure", ... },
  "liveCalculation": { "weightedPrice": ..., "baskets": [ ... ] },
  "guardRail": { "externalPrice": ..., "deviationPct": ... },
  "priceHistory": [ ... last 50 snapshots ... ]
}

GET /api/oracle/prices/:id/history

Time-series price snapshots for charting. Same :id resolution as above (i-address, name, or fully-qualified). Query params: from=<block>, to=<block>, limit=<n> (default 200, max 1000).

// curl 'https://scan.verus.cx/api/oracle/prices/i5w5Mu.../history?limit=5'
{
  "currencyId": "i5w5Mu...",
  "count": 5,
  "data": [
    { "usdPrice": 0.7731, "blockHeight": 4032417,
      "basket": "Pure", "confidence": 50,
      "createdAt": "2026-04-20T07:52:46.835Z" }
  ]
}

GET /api/oracle/conversions/:txid

USD value at execution time for a given conversion (one row per vout). Pricing rule: stablecoin side first if present, otherwise amount × oracle_price of the to-currency. Returns null for the rare basket-to-basket case.

// curl https://scan.verus.cx/api/oracle/conversions/<txid>
{
  "txid": "7e93a938...",
  "count": 1,
  "data": [{
    "chainId": "vdex",
    "fromCurrencyId": "iGBs4DWz... (DAI)",
    "toCurrencyId":   "i9nwxtKu... (vETH)",
    "amountIn":  10,
    "amountOut": 0.00440481,
    "usdValue":  10,
    "rule":      "stable_in",
    "pricedCurrencyId":  "iGBs4DWz... (DAI)",
    "pricedCurrencyUsd": 1
  }]
}

Possible rule values:

stable_inFrom-side is DAI/vUSDT/vUSDC — value = amount_in
stable_outTo-side is DAI/vUSDT/vUSDC — value = amount_out
to_basket_fromTo-side is a basket — priced via the from-side's USD price
to_sideBoth crypto, neither basket — priced via the to-side's USD price
unpricedRare basket-to-basket case; usdValue is null

Libraries

verus-typescript-primitives — Core types: LoginConsentRequest, VerusPayInvoice, TransferDestination, VDXF keys.

verusid-ts-client — VerusID interface for signing, verifying, and RPC interaction.

verus-connect — Drop-in VerusID login and payments for any website.