Features Solutions Technology Tokenomics Docs About Launch App
Docs / Getting Started / First Integration

First Integration

This guide builds a working integration against what TAG IT actually runs today: a direct on-chain read of asset lifecycle state, plus the live verification endpoints. Every code sample and every response on this page was executed against the live network before publication.

Read This Before You Build

TAG IT is pre-production. Specifically:

  • Testnet only. TAGITCore is deployed to Base Sepolia (chain ID 84532). There is no mainnet deployment.
  • Unaudited. The contracts have not completed a third-party security audit. Do not secure anything of value with them.
  • There is no installable SDK package. @tagit/sdk is not published to the public npm registry — an install command for it will fail. See Not Available Yet.

Nothing on this page requires an API key, an account, or a package install beyond viem.

What Works Today

Docs that describe features ahead of implementation waste your time. So here is the honest inventory. Everything marked Live below was exercised on 2026-07-26 and is documented in this guide; everything marked Not available is covered in the last section.

Capability Status How
Read an asset's owner and lifecycle state Live getAsset() on Base Sepolia via viem
Verification service health Live GET api.tagit.network/health
Paid verification (x402) Live POST api.tagit.network/verify
Physical NFC tap verification Live GET verify.tagit.network/api/verify
Digital Product Passport (W3C VC) Live GET verify.tagit.network/api/dpp/... — requires a tap
Human-readable verification pages Live verify.tagit.network/asset/<tokenId>
Register a product over an API Not available On-chain writes only, badge-gated
Transfer ownership over an API Not available On-chain writes only, badge-gated
Installable SDK package, webhooks, sandbox host Not available Do not exist

Step 1: Read an Asset On-Chain

The blockchain record is the source of truth, and it is public. You do not need TAG IT's permission, an account, or an API key to read it — you need a Base Sepolia RPC endpoint and an ABI fragment. This is the foundation of every integration below.

Install the only dependency:

npm install viem

Then read the asset:

import { createPublicClient, http } from 'viem';
import { baseSepolia } from 'viem/chains';

// TAGITCore on Base Sepolia (chain ID 84532)
const TAGIT_CORE = '0x3aDc7EFDb58Ae85483eFf5D4966D916185f31d1D';

const tagitCoreAbi = [
  {
    type: 'function',
    name: 'getAsset',
    stateMutability: 'view',
    inputs: [{ name: 'tokenId', type: 'uint256' }],
    outputs: [
      { name: 'assetOwner', type: 'address' },
      { name: 'timestamp',  type: 'uint64'  },
      { name: 'state',      type: 'uint8'   },
      { name: 'flags',      type: 'uint8'   },
      { name: 'reserved',   type: 'uint16'  }
    ]
  }
];

const STATE_NAMES = [
  'NONE', 'MINTED', 'BOUND', 'ACTIVATED', 'CLAIMED', 'FLAGGED', 'RECYCLED'
];

const client = createPublicClient({
  chain: baseSepolia,
  transport: http()
});

async function readAsset(tokenId) {
  const [assetOwner, timestamp, state, flags, reserved] = await client.readContract({
    address: TAGIT_CORE,
    abi: tagitCoreAbi,
    functionName: 'getAsset',
    args: [BigInt(tokenId)]
  });

  return {
    tokenId,
    owner: assetOwner,
    state,
    stateName: STATE_NAMES[state] ?? 'UNKNOWN',
    lastStateChange: new Date(Number(timestamp) * 1000).toISOString(),
    flags,
    reserved
  };
}

console.log(await readAsset(50));

Token ID 50 exists on Base Sepolia right now, so you can run the above unchanged. It prints:

{
  tokenId: 50,
  owner: '0x458B4d0c3a55006965Fd13D6af7B8509De51Cb3D',
  state: 4,
  stateName: 'CLAIMED',
  lastStateChange: '2026-07-16T23:56:24.000Z',
  flags: 0,
  reserved: 0
}

The five returned fields are the packed on-chain asset record:

A token ID that has never been minted does not revert. It returns the zero record: owner 0x0000…0000 and state 0 (NONE). Handle that case explicitly — see Handling Results.

Lifecycle States

Every asset moves through a seven-state machine. The state value returned above maps to:

Value State Meaning
0NONEAsset does not exist on this contract
1MINTEDDigital twin created, no physical tag bound yet
2BOUNDNFC tag cryptographically linked to the twin
3ACTIVATEDPassed QA, ready for market
4CLAIMEDOwned by an end consumer
5FLAGGEDUnder lost, stolen or recall investigation
6RECYCLEDEnd of life, terminal state
Writes Are Badge-Gated

The state-changing functions — mint, bindTag, activate, claim, flag, resolve, recycle and transferAsset — each require the caller to hold the corresponding BIDGES capability badge. None of them are open to the public, and there is no self-serve way to obtain a badge today. Your integration can read freely; it cannot write.

Step 2: The Verification API

Two endpoints on api.tagit.network are live. Start with the health check, which needs no authentication:

curl https://api.tagit.network/health

Actual response:

{"status":"ok","timestamp":"2026-07-26T21:53:19.795Z","runtime":"vercel"}

The verification endpoint is metered using the x402 payment protocol. Called without a payment header, it returns HTTP 402 and an envelope describing what payment it will accept:

curl -X POST https://api.tagit.network/verify \
  -H 'content-type: application/json' \
  -d '{}'

Actual response (HTTP 402):

{
  "x402Version": 1,
  "error": "Payment required",
  "accepts": [
    {
      "scheme": "exact",
      "network": "base-sepolia",
      "maxAmountRequired": "10000",
      "resource": "https://api.tagit.network/verify",
      "description": "TAG IT Asset Verification — BOUND state proof + ECDSA signature",
      "mimeType": "application/json",
      "payTo": "0x458B4d0c3a55006965Fd13D6af7B8509De51Cb3D",
      "maxTimeoutSeconds": 30,
      "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
    }
  ]
}

That 402 envelope is the endpoint's real, current contract with clients: an x402-capable client reads accepts, settles the payment on Base Sepolia, and retries with a payment header. The shape of the settled success response is not documented here because it is not yet stable — treat it as subject to change and do not hard-code against it. If all you need is the owner and lifecycle state, the on-chain read in Step 1 gives you that for free and with no payment flow at all.

Step 3: Verifying a Physical Tag

Step 1 tells you about a token ID. It cannot tell you whether the object in someone's hand is the real one. Only a physical NFC tap can do that, because only the chip can produce a fresh cryptographic message authentication code.

When an NTAG 424 DNA tag is tapped, the chip's Secure Dynamic Messaging mirror appends two values to the URL it emits: an encrypted PICC data blob and a CMAC. The verification service checks them:

curl "https://verify.tagit.network/api/verify?picc=<32-hex-chars>&cmac=<16-hex-chars>"

The same tap can be resolved as a W3C Verifiable Credential digital product passport, keyed by GS1 GTIN and serial:

curl "https://verify.tagit.network/api/dpp/01/<GTIN>/21/<serial>?picc=<32-hex-chars>&cmac=<16-hex-chars>"
You Cannot Test This Without a Tag

The picc and cmac values are generated by the chip at tap time using a key you do not have. There is no way to fabricate them, and a copied or replayed pair will not validate — which is exactly the property that makes the tap meaningful. If you have no physical NTAG 424 DNA tag, you cannot exercise this endpoint; use the on-chain read instead and treat tap verification as a later integration phase.

For flows where you want to hand a person a link rather than parse JSON, the following pages on verify.tagit.network render a verification result directly — for example, check asset 50 on-chain:

Handling Results

Your UI needs to handle every state the contract can return, including the zero record. This continues from Step 1 and reuses readAsset():

async function checkAsset(tokenId) {
  const asset = await readAsset(tokenId);

  switch (asset.state) {
    case 0: // NONE
      return {
        display: 'not-found',
        headline: 'No record for this token ID',
        detail: 'This token has never been minted on TAGITCore. A zero record is also '
              + 'what you get if you query the wrong contract or the wrong chain.'
      };

    case 1: // MINTED
    case 2: // BOUND
    case 3: // ACTIVATED
      return {
        display: 'in-production',
        headline: `Asset exists — ${asset.stateName}`,
        detail: 'The digital twin exists but has not been claimed by an end owner yet.'
      };

    case 4: // CLAIMED
      return {
        display: 'claimed',
        headline: 'Claimed by an owner',
        detail: `On-chain owner ${asset.owner}, last state change ${asset.lastStateChange}.`
      };

    case 5: // FLAGGED
      return {
        display: 'flagged',
        headline: 'Flagged',
        detail: 'This asset is under a lost, stolen or recall investigation.'
      };

    case 6: // RECYCLED
      return {
        display: 'recycled',
        headline: 'Recycled',
        detail: 'End of life. This is a terminal state.'
      };

    default:
      return {
        display: 'unknown',
        headline: 'Unrecognised state',
        detail: `Contract returned state ${asset.state}.`
      };
  }
}
Do Not Over-Claim in Your UI

An on-chain read proves a record exists and what state it is in. It does not prove the physical object in front of your user is the one that record refers to — that link is only established by a successful NFC tap (Step 3). Label your states accurately: "record found, state CLAIMED" is honest, "authentic" is not, until a tap has been verified.

Not Available Yet

These are things developers reasonably expect and that TAG IT does not have today. Nothing below is a preview of an imminent release; treat it as a list of things you must design around.

What is missing What to do instead
An installable SDK package. @tagit/sdk is not on the public npm registry, so an install of it fails. The TypeScript source exists in the tagit-sdk repository, but it is a client for the agent identity, reputation and validation contracts plus WTag and Voucher helpers — it does not do product registration, verification or ownership transfer. Its default chain is still the deprecated OP Sepolia (11155420), not Base Sepolia. Use viem directly, as in Step 1. It is the same thing the SDK would do underneath.
A Python SDK, and Swift or Kotlin packages. They do not exist in any form. Call the JSON-RPC endpoint with any language's HTTP or web3 library.
A REST API for registering products or transferring ownership. Those operations are on-chain contract calls, and each is gated on a BIDGES capability badge. No workaround exists. If you need write access, this is a conversation, not an integration.
Webhooks and event subscriptions. There is no endpoint to register a callback URL. Watch contract events yourself with client.watchContractEvent() in viem, or poll getAsset().
A sandbox host. There is no sandbox subdomain; that hostname does not resolve. Base Sepolia is the sandbox. It is free and its faucets are public.
A mainnet deployment. No production chain deployment exists, so there is no testnet-to-mainnet switch to plan for yet. Build against Base Sepolia and keep the contract address and chain ID in configuration, not hard-coded.
Found a Gap?

If you hit something this page claims works and it does not, that is a documentation bug and we want it reported. Raise it in the Discord.

Next Steps

Edit this page on GitHub
Type to search documentation...