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.
TAG IT is pre-production. Specifically:
- Testnet only.
TAGITCoreis deployed to Base Sepolia (chain ID84532). 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/sdkis 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:
assetOwner— the current on-chain owner address.timestamp— Unix seconds of the last state change, not the mint date.state— the lifecycle state,0–6(table below).flags— a bit field reserved for future use; currently0.reserved— reserved for future metadata; currently0.
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 |
|---|---|---|
0 | NONE | Asset does not exist on this contract |
1 | MINTED | Digital twin created, no physical tag bound yet |
2 | BOUND | NFC tag cryptographically linked to the twin |
3 | ACTIVATED | Passed QA, ready for market |
4 | CLAIMED | Owned by an end consumer |
5 | FLAGGED | Under lost, stolen or recall investigation |
6 | RECYCLED | End of life, terminal state |
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>"
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:
https://verify.tagit.network/asset/<tokenId>https://verify.tagit.network/tag/<uid>https://verify.tagit.network/01/<gtin>/21/<serial>
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}.`
};
}
}
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. |
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
- Contract Addresses — deployed addresses and chain IDs
- ABI Reference — the full contract interface, beyond
getAsset() - Platform Overview — how the pieces fit together
- NFC Chip Selection — why NTAG 424 DNA, and what tap verification requires