Features Solutions Technology Tokenomics Docs About Launch App
Docs / SDK & Libraries / Mobile SDKs

Mobile SDKs

There is no TAG IT mobile SDK package to install. This page documents what a mobile app can actually do today: read the chip with the platform NFC API, verify the tap server-side, and read asset state directly from the contract.

No mobile SDK is published

TAG IT does not publish a Swift package, a CocoaPod, a Maven/Gradle artifact, a React Native package or a Flutter plugin. Nothing named TagItSDK, network.tagit:sdk, @tagit/react-native-sdk or tagit_sdk exists in any registry. There is also no published @tagit/sdk npm package: the registry returns a 404 for it, so any install command you may have seen for it cannot succeed.

What does exist is a first-party scanner app plus two public HTTP surfaces and the contract itself. Everything below is verified working; the rest of this page is deliberately short because short and true beats long and plausible.

What exists today

Everything on TAG IT is currently deployed to the Base Sepolia testnet (chain ID 84532), and the contracts are unaudited. Do not treat any of the following as production infrastructure.

Component What it is Status
ORACULAR app First-party React Native (Expo) scanner. Tap a chip, verify, view the asset. Internal builds only — not on the App Store or Google Play, and not distributed as a library
verify.tagit.network/api/verify Server-side verification of an NTAG 424 DNA SUN tap Live
verify.tagit.network/api/dpp/... Digital Product Passport (W3C Verifiable Credential) for a GS1 Digital Link tap Live
api.tagit.network Verification API — GET /health and a paid POST /verify (x402) Live
TAGITCore contract Direct on-chain read of asset owner, timestamp and lifecycle state Live on Base Sepolia
Mobile SDK package Installable iOS / Android / React Native / Flutter library Does not exist

Why a tap is verified server-side

TAG IT uses the NXP NTAG 424 DNA chip in SUN (Secure Unique NFC) mode. On every tap the chip regenerates its NDEF URL and appends two query parameters:

A phone reading the tag gets ciphertext and a MAC, and nothing else. It cannot decrypt the PICC or validate the CMAC on-device, because doing so requires the SDM key — and shipping that key inside an app binary would hand every attacker the ability to mint valid taps for every chip in the fleet. So the mobile app forwards picc and cmac to the verifier, which holds the key server-side, checks the CMAC, rejects replays via the tap counter, and resolves the on-chain twin.

This is why there is no offline verification

Offline SUN verification is not an unimplemented feature — it is cryptographically impossible under this design without putting the SDM key on the device. An app with no connectivity can still show a cached previous result, but it cannot establish that a fresh tap is genuine.

The tap → verify flow

  1. Read the NDEF URI record from the chip using the platform NFC API.
  2. Parse picc and cmac out of the URL's query string.
  3. GET https://verify.tagit.network/api/verify?picc=<32 hex>&cmac=<16 hex>
  4. Render the JSON response.

Chips are personalized to a SUN landing URL that carries those parameters, for example:

https://verify.tagit.network/sun?picc=<32 hex>&cmac=<16 hex>

You can exercise the verification endpoint right now. With parameters that are not a genuine tap it returns HTTP 200 and a structured rejection:

curl -s "https://verify.tagit.network/api/verify?picc=00000000000000000000000000000000&cmac=0000000000000000"
{
  "verified": false,
  "reason": "unexpected PICC tag 0xba",
  "chain": { "id": 84532, "name": "Base Sepolia" }
}

A genuine tap returns "verified": true together with the resolved asset. The fields the first-party app reads from that response are verified, uid, tapCounter, chain.id, chain.name, and an asset object containing tokenId, stateCode, lifecycleState, owner and timestamp — see src/services/sunVerify.ts in the ORACULAR app. Producing a verified: true response requires a physical chip: there is no way to synthesize a valid picc/cmac pair without one.

Reading the chip on device

Chip reading uses the ordinary platform NFC stack — there is no TAG IT layer involved, and nothing TAG IT-specific to install.

iOS

Use Core NFC (NFCNDEFReaderSession). NFC tag reading requires iOS 13+ and an iPhone 7 or later, and your app must declare the usage description:

<key>NFCReaderUsageDescription</key>
<string>Used to verify the authenticity of your TAG IT-chipped items.</string>

Android

Use android.nfc.NfcAdapter with foreground dispatch or the reader-mode API. Declare the permission in your manifest:

<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="true" />

React Native

The ORACULAR app is Expo + React Native and reads chips with the third-party react-native-nfc-manager package. This is that app's actual read path, condensed:

import NfcManager, { NfcTech, Ndef } from 'react-native-nfc-manager';

await NfcManager.start();

async function readTagUrl() {
  await NfcManager.requestTechnology(NfcTech.Ndef, {
    alertMessage: 'Hold your TAG IT chip near the top of your iPhone',
  });
  try {
    const tag = await NfcManager.getTag();
    if (!tag?.ndefMessage?.length) throw new Error('No NDEF message found on tag');

    for (const record of tag.ndefMessage) {
      if (record.tnf === Ndef.TNF_WELL_KNOWN) {
        const uri = Ndef.uri.decodePayload(new Uint8Array(record.payload));
        if (uri) return uri;
      }
    }
    throw new Error('No URL record found in NDEF message');
  } finally {
    NfcManager.cancelTechnologyRequest().catch(() => {});
  }
}
Note

NFC does not work in the iOS Simulator, the Android emulator or Expo Go. You need a native development build on a physical device to test any of this.

Parsing the SUN URL and verifying

Once you have the URL string, the rest is a query-string parse and one fetch. This runs anywhere with URL and fetch — React Native, a browser, or Node 18+:

async function verifyTap(rawUrl) {
  const params = new URL(rawUrl).searchParams;
  const picc = params.get('picc');   // 32 hex — encrypted UID + counter
  const cmac = params.get('cmac');   // 16 hex — truncated AES-CMAC
  if (!picc || !cmac) throw new Error('Not a SUN URL: missing picc or cmac');

  const res = await fetch(
    `https://verify.tagit.network/api/verify?picc=${picc}&cmac=${cmac}`,
    { headers: { Accept: 'application/json' } }
  );
  return res.json();
}

That is the whole client-side integration. There is no API key, no SDK object and no initialization step, because the endpoint is public and the chip's CMAC is the credential.

Reading asset state directly on-chain

If you already know a token ID — from a verified tap, from a deep link, or from your own records — you can read its state straight from the contract with no TAG IT service in the path. TAGITCore is deployed on Base Sepolia at 0x3aDc7EFDb58Ae85483eFf5D4966D916185f31d1D.

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,
  transport: http('https://sepolia.base.org'),
});

const [assetOwner, timestamp, state] = await client.readContract({
  address: TAGIT_CORE,
  abi: getAssetAbi,
  functionName: 'getAsset',
  args: [50n],
});

// Real result for token 50 on Base Sepolia:
//   assetOwner 0x458B4d0c3a55006965Fd13D6af7B8509De51Cb3D
//   timestamp  1784246184n
//   state      4  (CLAIMED)
React Native needs crypto polyfills for viem

Hermes ships without crypto.getRandomValues and TextEncoder/TextDecoder, and viem fails deep inside its encoding paths without them. Install react-native-get-random-values and fast-text-encoding, and import both at the very top of your app entry file, before anything that touches viem.

Lifecycle states

The state byte returned by getAsset maps to the seven-state asset lifecycle:

Value State Meaning
0NONEToken does not exist
1MINTEDDigital twin created, no chip bound yet
2BOUNDNFC chip cryptographically linked to the twin
3ACTIVATEDPassed QA, ready for market
4CLAIMEDOwned by an end consumer
5FLAGGEDLost, stolen or under investigation
6RECYCLEDEnd of life, terminal

All state-changing functions — mint, bindTag, activate, claim, flag, resolve, recycle and transferAsset — are gated on a BIDGES capability badge. None of them are open to arbitrary callers, so a consumer-facing mobile app is a read-and-verify client only.

Digital Product Passport

Chips personalized as a GS1 Digital Link resolve to a passport endpoint that returns a W3C Verifiable Credential for the tapped item:

curl -s "https://verify.tagit.network/api/dpp/01/<GTIN>/21/<serial>?picc=<32 hex>&cmac=<16 hex>"

Like /api/verify, it requires a real tap. Without valid SUN parameters it returns the same structured rejection:

{
  "verified": false,
  "reason": "unexpected PICC tag 0xba"
}

Human-readable fallback

Because a TAG IT chip emits a plain HTTPS URL, a tap on a phone with no app installed simply opens the verifier in the browser. These pages are live and are a legitimate destination for deep links out of your own app:

Not available yet

The following do not exist. They are listed explicitly so that nobody plans around them:

Testnet, unaudited

Contracts are deployed to Base Sepolia (84532) and have not completed a third-party audit. Token IDs, addresses and endpoints shown here are testnet artifacts and may be reset. Do not build production or custody-bearing flows on them.

Need Help?

Deployed addresses are listed on the Contract Addresses page. For anything else, ask in our Discord community — if a capability you need is missing, tell us rather than assuming it is coming.

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