Skip to main content
POST
https://api-mainnet.onzks.com
/
v1
/
identity
/
activate
Activate Identity
curl --request POST \
  --url https://api-mainnet.onzks.com/v1/identity/activate \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "tokenId": 123,
  "signature": "<string>",
  "walletAddress": "<string>"
}
'
{
  "success": false,
  "error": "IDENTITY_NOT_FOUND",
  "message": "Identity with tokenId 12345 not found"
}

Overview

Activate a minted identity to make it soulbound (non-transferable). Once activated, the identity becomes permanently bound to the owner’s wallet and can be used as a parameter across all ZKScore APIs. Activation is irreversible.
Activation is permanent! Once an identity is activated, it cannot be transferred or deactivated. Make sure you’re activating the correct identity.

Request Body

tokenId
number
required
The NFT token ID of the identity to activate (obtained from minting)
signature
string
required
Cryptographic signature from the identity owner proving ownership
walletAddress
string
required
The wallet address of the identity owner (must match the minted identity)

Response

success
boolean
Indicates if the activation was successful
identity
object

Examples

curl -X POST https://api.onzks.com/v1/identity/activate \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tokenId": 12345,
    "signature": "0xabc123...",
    "walletAddress": "0x742d35Cc6635C0532925a3b844D1FF4e1321"
  }'

Response Example

{
  "success": true,
  "identity": {
    "tokenId": 12345,
    "name": "alice",
    "ownerAddress": "0x742d35cc6635c0532925a3b844d1ff4e1321",
    "isActivated": true,
    "activatedAt": "2024-01-15T11:00:00Z",
    "transactionHash": "0xdef456..."
  }
}

Error Responses

{
  "success": false,
  "error": "IDENTITY_NOT_FOUND",
  "message": "Identity with tokenId 12345 not found"
}

Signature Generation

Using ethers.js

import { ethers } from 'ethers';

async function generateActivationSignature(tokenId, privateKey) {
  const signer = new ethers.Wallet(privateKey);
  const message = `Activate identity ${tokenId}`;
  const signature = await signer.signMessage(message);
  
  return {
    signature,
    walletAddress: signer.address
  };
}

// Usage
const { signature, walletAddress } = await generateActivationSignature(12345, privateKey);

Using web3.js

import Web3 from 'web3';

async function generateActivationSignature(tokenId, privateKey) {
  const web3 = new Web3();
  const account = web3.eth.accounts.privateKeyToAccount(privateKey);
  const message = `Activate identity ${tokenId}`;
  const signature = await account.sign(message);
  
  return {
    signature: signature.signature,
    walletAddress: account.address
  };
}

Using MetaMask

async function generateActivationSignature(tokenId) {
  if (!window.ethereum) {
    throw new Error('MetaMask not installed');
  }

  const accounts = await window.ethereum.request({ 
    method: 'eth_requestAccounts' 
  });
  
  const walletAddress = accounts[0];
  const message = `Activate identity ${tokenId}`;
  
  const signature = await window.ethereum.request({
    method: 'personal_sign',
    params: [message, walletAddress]
  });

  return { signature, walletAddress };
}

Use Cases

1. Complete Onboarding Flow

Mint and activate an identity in sequence:
async function completeOnboarding(name, walletAddress, privateKey) {
  // Step 1: Mint identity
  const mintResponse = await fetch('https://api.onzks.com/v1/identity/mint', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ name, walletAddress })
  });
  
  const { identity } = await mintResponse.json();
  console.log(`Identity minted: ${identity.name}.zks`);

  // Step 2: Generate signature
  const signer = new ethers.Wallet(privateKey);
  const message = `Activate identity ${identity.tokenId}`;
  const signature = await signer.signMessage(message);

  // Step 3: Activate identity
  const activateResponse = await fetch('https://api.onzks.com/v1/identity/activate', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      tokenId: identity.tokenId,
      signature,
      walletAddress
    })
  });

  const { identity: activatedIdentity } = await activateResponse.json();
  console.log(`Identity activated: ${activatedIdentity.name}.zks`);
  
  return activatedIdentity;
}

2. Activation with User Confirmation

Request user confirmation before activation:
async function activateWithConfirmation(tokenId) {
  const confirmed = await showConfirmationDialog(
    'Activate Identity',
    'This action is permanent and cannot be undone. Continue?'
  );

  if (!confirmed) {
    return null;
  }

  const { signature, walletAddress } = await generateActivationSignature(tokenId);

  const response = await fetch('https://api.onzks.com/v1/identity/activate', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ tokenId, signature, walletAddress })
  });

  return response.json();
}

3. Batch Activation

Activate multiple identities:
async function batchActivate(identities, privateKey) {
  const signer = new ethers.Wallet(privateKey);
  const results = [];

  for (const identity of identities) {
    try {
      const message = `Activate identity ${identity.tokenId}`;
      const signature = await signer.signMessage(message);

      const response = await fetch('https://api.onzks.com/v1/identity/activate', {
        method: 'POST',
        headers: {
          'Authorization': 'Bearer YOUR_API_KEY',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          tokenId: identity.tokenId,
          signature,
          walletAddress: signer.address
        })
      });

      const result = await response.json();
      results.push({ success: true, identity: result.identity });

      // Rate limiting
      await new Promise(resolve => setTimeout(resolve, 1000));
    } catch (error) {
      results.push({ success: false, tokenId: identity.tokenId, error });
    }
  }

  return results;
}

Best Practices

1. Verify Before Activation

Check identity details before activating:
// Get identity details
const identity = await getIdentity(tokenId);

// Verify it's the correct identity
console.log(`About to activate: ${identity.name}.zks`);
console.log(`Owner: ${identity.ownerAddress}`);

// Confirm with user
const confirmed = confirm('Activate this identity?');
if (confirmed) {
  await activateIdentity(tokenId);
}

2. Handle Errors Gracefully

Provide clear error messages:
try {
  await activateIdentity(tokenId);
} catch (error) {
  if (error.code === 'ALREADY_ACTIVATED') {
    console.log('Identity is already activated');
  } else if (error.code === 'INVALID_SIGNATURE') {
    console.log('Signature verification failed. Please try again.');
  } else if (error.code === 'NOT_IDENTITY_OWNER') {
    console.log('You are not the owner of this identity');
  } else {
    console.error('Activation failed:', error.message);
  }
}

3. Store Activation Status

Track activation in your database:
const result = await activateIdentity(tokenId);

await database.update('identities', {
  tokenId: result.identity.tokenId,
  isActivated: true,
  activatedAt: result.identity.activatedAt,
  transactionHash: result.identity.transactionHash
});

4. Verify Signature Locally

Verify signature before sending to API:
import { ethers } from 'ethers';

function verifySignature(message, signature, expectedAddress) {
  const recoveredAddress = ethers.utils.verifyMessage(message, signature);
  return recoveredAddress.toLowerCase() === expectedAddress.toLowerCase();
}

// Before activation
const message = `Activate identity ${tokenId}`;
const isValid = verifySignature(message, signature, walletAddress);

if (!isValid) {
  throw new Error('Invalid signature');
}

// Proceed with activation
await activateIdentity(tokenId, signature, walletAddress);

What Happens After Activation

Once activated, your identity:
  1. Becomes Soulbound: Cannot be transferred to another wallet
  2. Can Be Used as Parameter: Use alice.zks instead of wallet address in all APIs
  3. Enables Features: Unlocks achievements, attestations, and trust scoring
  4. Permanent: Cannot be deactivated or changed

Next Steps

After activating an identity:
  1. Get Score - Check your ZKScore
  2. Get Achievements - View available achievements
  3. Create Attestation - Start building trust

Troubleshooting

”Invalid signature”

Cause: Signature doesn’t match the expected format or signer. Solution:
  • Ensure you’re signing with the correct private key
  • Use the exact message format: Activate identity {tokenId}
  • Verify the wallet address matches the identity owner

”Already activated”

Cause: Identity has already been activated. Solution:
  • Check identity status before attempting activation
  • This is not an error if the identity is already active

”Not identity owner”

Cause: The wallet address doesn’t match the identity owner. Solution:
  • Verify you’re using the correct wallet
  • Check the identity owner address
  • Only the owner can activate their identity

”Transaction failed”

Cause: Blockchain transaction failed. Solution:
  • Ensure wallet has sufficient gas
  • Wait a few minutes and try again
  • Check blockchain network status
  • Contact support if issue persists

Security Considerations

Private Key Safety

Never expose your private key:
// ❌ BAD: Hardcoded private key
const privateKey = '0x1234...';

// ✅ GOOD: Use environment variables
const privateKey = process.env.PRIVATE_KEY;

// ✅ BETTER: Use secure key management
const privateKey = await keyManagementService.getKey();

Signature Verification

Always verify signatures match expected addresses:
const recoveredAddress = ethers.utils.verifyMessage(message, signature);
if (recoveredAddress !== expectedAddress) {
  throw new Error('Signature verification failed');
}

Rate Limiting

Respect rate limits to avoid being blocked:
// Add delay between activations
await new Promise(resolve => setTimeout(resolve, 1000));