Product Tagging Tutorial
How a physical product becomes a TAG IT digital twin — the parts you can run today, and the parts that are permissioned.
TAG IT contracts are deployed to Base Sepolia testnet (chainId 84532) only. There is no mainnet deployment, and the contracts are not audited. Every lifecycle write — mint, bindTag, activate, claim — is gated by a BIDGES capability badge on the calling address. There is no public API, no self-serve endpoint and no SDK method that registers a product. The read and verification steps in Step 6 are open to everyone and work today.
Tutorial Overview
This tutorial follows a single physical product from a blank NFC tag to a readable on-chain record. The physical work — selecting, personalizing and attaching the tag — is yours. The on-chain lifecycle transitions are executed by an address that holds the matching capability badge. The verification step at the end requires nothing but an RPC endpoint.
What Is Actually Available
| Stage | Mechanism | Who can do it today |
|---|---|---|
| Select and attach an NTAG 424 DNA tag | Physical work, no software | Anyone |
| Personalize the chip (AES keys, SDM mirroring) | PC/SC personalization tooling | Not publicly available — see Step 3 |
Lifecycle writes on TAGITCore (mint, bindTag, activate, claim) |
Direct contract calls on Base Sepolia | Capability-badge holders only |
| Read an asset's owner and lifecycle state | getAsset(uint256) via any Base Sepolia RPC |
Anyone — verified working |
| Verify a physical tap | verify.tagit.network |
Anyone holding the physical tag |
Prerequisites
- NTAG 424 DNA tags — the TAG IT standard chip. No other chip family can be bound.
- The product you want to tag, plus an NFC-enabled phone for read tests
- Node.js 18+ with viem, or Foundry's
cast, for the read and verification steps - For on-chain writes only: a Base Sepolia wallet that holds the relevant BIDGES capability badge, issued through TAGITAccess. Without the badge, every write reverts — see Contract Addresses.
The @tagit/sdk package is not published to npm — installing it fails with a 404. It is TypeScript-only; there is no Python, Swift or Kotlin package. Its scope is agent identity, reputation and validation, plus WTag and Voucher helpers — it contains no product registration, tag encoding, verification or ownership-transfer functions. Everything on this page is done with a plain Ethereum client (viem, ethers or cast) or with physical tooling.
The physical steps take roughly 30-45 minutes. The on-chain steps depend on badge issuance, which is a manual process today.
Step 1: Prepare Your Product
Before attaching an NFC tag, you need to identify the optimal placement location and prepare the surface.
Tag Placement Considerations
Place tags in accessible but protected areas. For luxury goods, common locations include inside packaging, on product labels, or embedded within the product itself.
Choose a location that is:
- Accessible: Users should be able to tap the tag easily with their phone
- Protected: The tag should be shielded from damage and tampering
- Away from metal: Metal surfaces can interfere with NFC signals
- Flat: Curved surfaces may reduce read range
Surface Preparation
- Clean the surface with isopropyl alcohol to remove dust, oil, and debris
- Allow the surface to dry completely (2-3 minutes)
- For porous materials, apply a thin primer coat if necessary
- Mark the exact placement location with a light pencil mark
Never place NFC tags directly on or within 1cm of metal surfaces. Use ferrite shielding if metal placement is unavoidable.
Step 2: Select NFC Tag
Choosing the right NFC tag is crucial for security and durability. TAG IT Network standardizes on tags with hardware cryptographic authentication — the NTAG 424 DNA is the chip for all item-level tagging.
Recommended Tags
| Tag Type | Security Level | Best For |
|---|---|---|
| NTAG 424 DNA | High (AES-128) | All item-level tagging (TAG IT standard) |
| ICODE SLIX2 | Medium | Package and logistics layer |
Note: NTAG 213/215/216 tags are legacy — not supported by TAG IT. They have no on-chip encryption and cannot be bound to an asset.
For detailed tag specifications and purchasing options, see our Chip Selection Guide.
Step 3: Personalize the Tag
A blank NTAG 424 DNA is not yet a TAG IT tag. It first has to be personalized: its AES-128 key set is written, and Secure Dynamic Messaging (SDM) is configured so that every tap emits a freshly encrypted PICC data block and a CMAC over it.
Those two values are what the verifier consumes. A personalized tag produces a request of this shape when tapped:
GET https://verify.tagit.network/api/verify?picc=<32 hex chars>&cmac=<16 hex chars>
The picc and cmac values are generated by the chip's on-board AES engine at tap time and change on every tap. Software cannot produce a valid pair without the chip's secret keys, which is the entire point of using this chip. Chip-level detail lives in the Encoding Guide.
Programmatic Encoding — Not Publicly Available
TAG IT does not publish a tag-personalization app or library. There is no TAG IT encoder listing on the App Store or Google Play, and no npm package that writes keys to a chip. Personalization is currently performed in-house with an ACR1252U-class PC/SC contactless reader driven by the internal TAG IT console, because it involves provisioning secret AES keys. If you need tags personalized for a pilot, contact info@tagit.network — self-serve personalization does not exist yet.
Always test a personalized tag with a smartphone before attaching it to the product. Once a tag is locked and attached, re-personalizing it may be impossible.
Step 4: The On-Chain Lifecycle
A TAG IT asset moves through a seven-state machine on TAGITCore, deployed at 0x3aDc7EFDb58Ae85483eFf5D4966D916185f31d1D on Base Sepolia. Tagging a product means walking the first four transitions.
| Transition | Function | Required capability |
|---|---|---|
| NONE (0) → MINTED (1) | mint(address to, bytes32 metadata) |
keccak256("MINTER") |
| MINTED (1) → BOUND (2) | bindTag(uint256, bytes32, bytes, bytes) |
keccak256("BINDER") |
| BOUND (2) → ACTIVATED (3) | activate(uint256 tokenId) |
keccak256("ACTIVATOR") |
| ACTIVATED (3) → CLAIMED (4) | claim(uint256 tokenId, address newOwner) |
keccak256("CLAIMER") |
Permissioned Write Functions — You Cannot Call These
The signatures below are the real functions on TAGITCore. They are shown so you can read the ABI and understand the flow. Every one of them reverts unless the caller holds the matching BIDGES capability badge, so this block is reference material, not a snippet you can run.
// TAGITCore, Base Sepolia (chainId 84532)
// 0x3aDc7EFDb58Ae85483eFf5D4966D916185f31d1D
// Each function carries requiresCapability(...) — reverts without the badge.
// NONE -> MINTED. metadata is a 32-byte metadata identifier (e.g. an IPFS digest).
function mint(address to, bytes32 metadata)
external
returns (uint256 tokenId);
// MINTED -> BOUND. Binds one personalized chip to one token, permanently.
function bindTag(
uint256 tokenId,
bytes32 tagHash,
bytes calldata challengeResponse,
bytes calldata oracleSignature
) external;
// BOUND -> ACTIVATED. QA sign-off; the asset is now sellable.
function activate(uint256 tokenId) external;
// ACTIVATED -> CLAIMED. Hands the asset to its end owner.
function claim(uint256 tokenId, address newOwner) external;
Why bindTag Needs Four Arguments
bindTag is the step that makes counterfeiting hard, so it is the step with the most checks:
tagHash— the 32-byte identifier of the physical chip. The contract enforces uniqueness: a tag hash already bound to another token is rejected, and a token that is not in MINTED state cannot be bound.challengeResponse— the chip's answer to a challenge issued during the physical scan.oracleSignature— an ECDSA signature from the contract's configuredtrustedOracleoverkeccak256(abi.encodePacked(tokenId, tagHash, challengeResponse)). If no oracle is configured the call reverts withOracleNotSet(); a mismatched signature reverts withInvalidOracleSignature().
In other words, a token cannot be bound to a chip that was never physically scanned by an attested reader — not even by a badge holder.
There is no REST endpoint, hosted service or SDK call that performs any of these writes on your behalf. Onboarding a manufacturer today means issuing capability badges to their address on Base Sepolia and having them submit transactions directly. Gas is paid in Base Sepolia testnet ETH, available from the Base Sepolia faucet.
Later States
Beyond CLAIMED, the state machine continues: flag (CLAIMED → FLAGGED, capability keccak256("FLAGGER")), resolve (FLAGGED → CLAIMED, keccak256("RESOLVER")) and recycle (→ RECYCLED, keccak256("RECYCLER")). One function is not capability-gated: transferAsset(uint256 tokenId, address to) is owner-gated, so the current owner of a CLAIMED asset can resell it themselves. See the Ownership Transfer tutorial.
Step 5: Attach the Tag
With the tag personalized, attach it to the product. Binding on-chain can happen before or after attachment — what matters is that the chip can still be scanned. Do not attach a tag you have not read-tested.
Application Methods
Adhesive Application (Most Common)
- Peel the backing from the adhesive NFC tag
- Align the tag with your marked placement location
- Press firmly from the center outward to remove air bubbles
- Apply pressure for 30 seconds for optimal adhesion
- For added security, apply a tamper-evident overlay sticker
Heat-Sealed Tags
For textile and apparel applications:
- Position the heat-seal tag on the fabric
- Apply heat press at 150-170°C for 10-15 seconds
- Allow to cool before handling
Sewn-In Labels
For garments and accessories:
- Use woven labels with embedded NFC inlays
- Sew into seams or care label areas
- Ensure the antenna is not folded or crimped
Embedded Tags
For products with internal cavities:
- Place the tag inside the product during manufacturing
- Use protective housing if necessary
- Ensure read range is adequate through the product material
Step 6: Verify — This Part Runs Today
Reading an asset needs no badge, no API key and no account. Point any Base Sepolia RPC at TAGITCore and call getAsset.
Read Asset State with viem
// npm install viem
import { createPublicClient, http } from 'viem';
import { baseSepolia } from 'viem/chains';
const TAGIT_CORE = '0x3aDc7EFDb58Ae85483eFf5D4966D916185f31d1D';
const getAssetAbi = [{
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 client = createPublicClient({
chain: baseSepolia, // chainId 84532
transport: http('https://sepolia.base.org')
});
const [assetOwner, timestamp, state] = await client.readContract({
address: TAGIT_CORE,
abi: getAssetAbi,
functionName: 'getAsset',
args: [50n]
});
console.log({ assetOwner, timestamp, state });
Actual Output
Run against token 50 on Base Sepolia:
{
assetOwner: '0x458B4d0c3a55006965Fd13D6af7B8509De51Cb3D',
timestamp: 1784246184n,
state: 4
}
state: 4 is CLAIMED, and timestamp is the Unix time of the last state change — 1784246184 is 2026-07-16T23:56:24Z. The same call with Foundry:
cast call 0x3aDc7EFDb58Ae85483eFf5D4966D916185f31d1D \
"getAsset(uint256)(address,uint64,uint8,uint8,uint16)" 50 \
--rpc-url https://sepolia.base.org
Lifecycle State Values
| Value | State | Meaning |
|---|---|---|
0 | NONE | Token does not exist |
1 | MINTED | Digital twin created, no chip bound |
2 | BOUND | Chip cryptographically bound to the token |
3 | ACTIVATED | QA passed, ready for market |
4 | CLAIMED | Held by an end owner |
5 | FLAGGED | Lost, stolen or under investigation |
6 | RECYCLED | End of life, terminal |
Two other public reads are useful here: getTokenByTag(bytes32 tagHash) returns the token bound to a chip (0 if unbound), and totalSupply() returns the number of digital twins minted.
Human-Readable Verification Pages
These routes render a verification page in a browser, all on the public host verify.tagit.network:
https://verify.tagit.network/asset/{tokenId}— e.g. /asset/50https://verify.tagit.network/tag/{uid}— look up by chip UIDhttps://verify.tagit.network/01/{gtin}/21/{serial}— GS1 Digital Link form
An earlier revision of this page showed https://verify.tagit.network/{tagId}. That route does not exist and returns 404. Use /asset/{tokenId} or /tag/{uid}.
Tap-Backed Verification Endpoints
These two endpoints consume the SDM values a genuine tap produces. They are not callable with invented parameters — that is the security property, not a limitation:
GET https://verify.tagit.network/api/verify?picc=<32 hex>&cmac=<16 hex>
GET https://verify.tagit.network/api/dpp/01/<GTIN>/21/<serial>?picc=<32 hex>&cmac=<16 hex>
The second returns a W3C Verifiable Credential digital product passport for the scanned item.
Verification API Health
The verification API exposes an unauthenticated health check:
curl -s https://api.tagit.network/health
# 200 → {"status":"ok", ...}
The machine verification endpoint is metered with the x402 payment protocol. Calling it without payment returns a 402 carrying the payment envelope, which is the expected first half of the handshake:
curl -s -o /dev/null -w '%{http_code}\n' \
-X POST https://api.tagit.network/verify
# 402
Installation Checklist
- Read test: tap the tag with a phone — the verification page should load.
- Range test: read from several angles; NTAG 424 DNA typically works within 2-4cm.
- State test: call
getAsset(tokenId)and confirm the state is what you expect (2 = BOUND after binding, 3 = ACTIVATED after QA). - Binding test: call
getTokenByTag(tagHash)and confirm it returns your token ID rather than 0.
Best Practices
Durability
- Environmental Protection: Use waterproof encapsulated tags for products exposed to moisture
- UV Resistance: Choose UV-resistant tag materials for outdoor products
- Temperature Range: Standard tags operate -25°C to +85°C; use industrial tags for extreme conditions
- Physical Protection: Apply protective overlays or embed tags in protective housings
Placement
- Consistency: Place tags in the same location across product lines for predictable user experience
- Documentation: Include tag location in user manuals and packaging
- Visual Indicators: Use "NFC Enabled" or "Tap to Verify" icons near the tag location
- Accessibility: Ensure the tag is reachable without disassembling the product
Security
- Use cryptographic tags: Always use the NTAG 424 DNA — legacy non-cryptographic chips cannot be bound
- Never ship factory-default keys: an unpersonalized chip provides no authentication at all
- Tamper evidence: apply tamper-evident seals over tags to make removal visible
- Treat badge-holding keys as production secrets: any address holding a capability badge can move assets through the lifecycle. Keep those keys in a hardware wallet or multisig.
- Re-read after handling: periodically confirm the on-chain state of shipped items still matches expectations
Batch Operations
TAGITCore has batch equivalents for high-volume lines. They carry the same capability gating as their single-item counterparts, so the same "you cannot call these without a badge" caveat applies.
// Capability-gated. MAX_BATCH_SIZE = 100 — larger calls revert with
// BatchTooLarge, mismatched array lengths revert with ArrayLengthMismatch.
function batchMint(address[] calldata recipients, bytes32[] calldata metadata)
external
returns (uint256[] memory tokenIds);
function batchBind(
uint256[] calldata tokenIds,
bytes32[] calldata tagHashes,
bytes[] calldata challengeResponses,
bytes calldata oracleSignature
) external;
function batchActivate(uint256[] calldata tokenIds) external;
You can physically tag a product today, and you can read and verify any asset that already exists on Base Sepolia. Getting your own assets on-chain currently requires TAG IT to issue capability badges to your address — that step is manual, and testnet-only, until the contracts are audited and mainnet is live.
Next Steps
- Contract Addresses - Every deployed address on Base Sepolia
- ABI Reference - Full ABIs for the functions above
- Ownership Transfer Tutorial - The owner-gated
transferAssetpath - Dispute Resolution - The flag / resolve path
- Encoding Guide - Chip-level detail on NTAG 424 DNA and SDM