# 02. GGS-1 Identity MW API

## Identity Management

GGS-1 Identity provides user profile management with privacy-preserving merkle tree verification and blockchain anchoring.

### Overview

**Purpose**: Create and manage verifiable user identities with configurable field privacy levels.

**Key Features**:

* Customizable profile fields (name, bio, social links, etc.)
* Privacy levels per field (private, community, public)
* Merkle tree for verifiable credentials
* Blockchain anchoring for tamper-proof verification
* Profile image upload

**Location**: src/packages/identity/

***

### API Endpoints

#### Get Current User's Identity

Fetch the authenticated user's identity profile.

**Endpoint**: `GET /identity/`

**Authentication**: Required (JWT)

**Implementation**:

```typescript
import axiosInstance from '@/packages/identity/api/axiosInstance'
import type { Identity } from '@/packages/identity/types'

export const getIdentity = async () => {
  try {
    const response = await axiosInstance.get<Identity>('/identity/')
    return response.data
  } catch (error) {
    console.error('Error fetching identity:', error)
    throw error
  }
}
```

**Response**:

```typescript
interface Identity {
  name?: string
  bio?: string
  location?: string
  email?: string
  phone?: string
  website?: string
  x?: string // Twitter/X
  telegram?: string
  discord?: string
  linkedin?: string
  instagram?: string
  medium?: string
  youtube?: string
  git?: string
  mastodon?: string
  image?: string // Profile image URL
  custom_fields?: { [key: string]: string }
  custom_fields_order?: Array<string>
  field_configurations?: {
    [key: string]: {
      visibility?: 'private' | 'community' | 'public'
      pepper: string
    }
  }
}
```

**Example Response**:

```json
{
  "name": "Alice Smith",
  "bio": "Blockchain developer",
  "email": "alice@example.com",
  "x": "@alice",
  "custom_fields": {
    "company": "Web3 Corp"
  },
  "custom_fields_order": ["company"],
  "field_configurations": {
    "name": {
      "visibility": "public",
      "pepper": "abc123..."
    },
    "email": {
      "visibility": "private",
      "pepper": "def456..."
    }
  }
}
```

**Usage**:

```typescript
import { useQuery } from '@tanstack/react-query'
import { getIdentity } from '@/packages/identity/api/queries/identity'

function ProfilePage() {
  const { data: identity, isLoading } = useQuery({
    queryKey: ['identity'],
    queryFn: getIdentity
  })

  if (isLoading) return <div>Loading...</div>

  return (
    <div>
      <h1>{identity?.name}</h1>
      <p>{identity?.bio}</p>
    </div>
  )
}
```

***

#### Get Public Identity by ID

Fetch another user's public identity information.

**Endpoint**: `GET /identity/public/{id}`

**Authentication**: Not required

**Parameters**:

* `id` (path): Identity UUID

**Implementation**:

```typescript
export const getIdentityById = async (id: string) => {
  try {
    const response = await axiosInstance.get<Identity>(`/identity/public/${id}`)
    return response.data
  } catch (error) {
    console.error(`Error fetching identity with id ${id}:`, error)
    throw error
  }
}
```

**Response**: Same structure as `GET /identity/` but only includes fields with `visibility: 'public'`

***

#### Get Identity Profile Image

Download the profile image for a specific identity.

**Endpoint**: `GET /identity/public/{id}/image`

**Authentication**: Not required

**Parameters**:

* `id` (path): Gen6 Address

**Implementation**:

```typescript
export const getIdentityImage = async (id: string) => {
  const response = await axiosInstance.get<Blob>(
    `/identity/public/${id}/image`,
    {
      responseType: 'blob'
    }
  )
  // Convert blob to object URL
  const imageUrl = URL.createObjectURL(response.data)
  return imageUrl
}
```

**Response**: Image object URL string

**Usage**:

```typescript
import { useQuery } from '@tanstack/react-query'

function ProfileImage({ identityId }: { identityId: string }) {
  const { data: imageUrl } = useQuery({
    queryKey: ['identityImage', identityId],
    queryFn: () => getIdentityImage(identityId)
  })

  return imageUrl ? <img src={imageUrl} alt="Profile" /> : null
}
```

***

#### Calculate Identity Merkle Tree

Generate a merkle tree hash for identity verification.

**Endpoint**: `POST /identity/merkle`

**Authentication**: Not required

**Purpose**: Calculate the root hash before creating/updating identity to ensure data integrity.

**Request**:

```typescript
export const calculateIdentityMerkle = async (data: IdentityMerkleRequest) => {
  const response = await axiosInstance.post<IdentityMerkleResponse>(
    '/identity/merkle',
    data,
    {
      headers: { 'Content-Type': 'application/json' }
    }
  )
  return response.data
}
```

**Request Body**:

```typescript
interface IdentityMerkleRequest {
  identity: IdentityData & { g6_address: string }
  field_configurations: IdentityFieldConfigurations
}
```

**Response**:

```typescript
interface IdentityMerkleResponse {
  root_hash: string
  total_nodes: number
  total_levels: number
  all_nodes: string[]
  levels: Array<Record<string, unknown>>
}
```

**Example Request**:

```json
{
  "identity": {
    "name": "Alice Smith",
    "bio": "Developer",
    "email": "alice@example.com"
  },
  "field_configurations": {
    "name": { "visibility": "public", "pepper": "abc123" },
    "bio": { "visibility": "public", "pepper": "def456" },
    "email": { "visibility": "private", "pepper": "ghi789" }
  }
}
```

**Example Response**:

```json
{
  "root_hash": "0x7f8a9b3c...",
  "total_nodes": 7,
  "total_levels": 3,
  "all_nodes": ["0xabc...", "0xdef...", "0x789..."],
  "levels": [{ "0": "0xabc..." }, { "0": "0xdef...", "1": "0x123..." }]
}
```

**Usage Flow**:

1. User fills out identity form
2. Frontend calculates merkle tree via `/identity/merkle`
3. Frontend stores hash on blockchain
4. Frontend creates identity via `/identity/` with the same root\_hash

***

#### Create Identity

Create a new identity profile for the authenticated user.

**Endpoint**: `POST /identity/`

**Authentication**: Required (JWT)

**Request**:

```typescript
export const createIdentity = async (data: IdentityCreateUpdateRequest) => {
  const response = await axiosInstance.post<Identity>('/identity/', data)
  return response.data
}
```

**Request Body**:

```typescript
interface IdentityCreateUpdateRequest {
  identity: IdentityData
  field_configurations: Record<string, FieldConfiguration>
  root_hash: string // From /identity/merkle
  custom_fields_order?: string[] // Order for custom fields
}
```

**Response**: Created `Identity` object

**Usage**:

```typescript
import { useMutation } from '@tanstack/react-query'
import { createIdentity } from '@/packages/identity/api/mutations/identity'

function CreateIdentityForm() {
  const createMutation = useMutation({
    mutationFn: createIdentity,
    onSuccess: () => {
      toast.success('Identity created!')
    }
  })

  const handleSubmit = async (formData) => {
    // 1. Calculate merkle tree
    const merkleResult = await calculateIdentityMerkle(formData)

    // 2. Store on blockchain (see Blockchain section)
    await storeOnBlockchain(merkleResult.root_hash)

    // 3. Create identity with same root_hash
    await createMutation.mutateAsync({
      ...formData,
      root_hash: merkleResult.root_hash
    })
  }

  return <form onSubmit={handleSubmit}>...</form>
}
```

**Important**: Always calculate merkle tree and store on blockchain before creating identity.

***

#### Update Identity

Update an existing identity profile.

**Endpoint**: `PUT /identity/`

**Authentication**: Required (JWT)

**Request**:

```typescript
export const updateIdentity = async (data: IdentityCreateUpdateRequest) => {
  const response = await axiosInstance.put<Identity>('/identity/', data)
  return response.data
}
```

**Request Body**: Same as create

**Response**: Updated `Identity` object

**Note**: Updating identity requires recalculating the merkle tree and storing the new hash on blockchain.

***

#### Upload Profile Image

Upload or update the user's profile image.

**Endpoint**: `PUT /identity/image`

**Authentication**: Required (JWT)

**Request**:

```typescript
export const uploadIdentityImage = async (file: File) => {
  const formData = new FormData()
  formData.append('image', file)

  const response = await axiosInstance.put<{ image_url: string }>(
    '/identity/image',
    formData
  )
  return response.data
}
```

**Request Body**: FormData with image file

**Supported Formats**: JPEG, PNG, GIF, WebP

**File Size Limit**: Check backend configuration (typically 5MB)

**Response**:

```typescript
interface ImageUploadResponse {
  image_url: string // URL to access the uploaded image
}
```

**Usage**:

```typescript
import { useMutation } from '@tanstack/react-query'

function ImageUpload() {
  const uploadMutation = useMutation({
    mutationFn: uploadIdentityImage,
    onSuccess: (data) => {
      toast.success('Image uploaded!')
      console.log('Image URL:', data.image_url)
    }
  })

  const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0]
    if (file) {
      uploadMutation.mutate(file)
    }
  }

  return (
    <input
      type="file"
      accept="image/*"
      onChange={handleFileChange}
      disabled={uploadMutation.isPending}
    />
  )
}
```

***

### Blockchain Integration

#### Store Identity Hash On-Chain

Anchor the identity data hash on the blockchain for tamper-proof verification.

**Extrinsic**: `api.tx.dataRegistry.storeData()`

**Parameters**:

* `projectId`: `20250923` (Identity project ID)
* `dataHash`: Blake2 hash of the full identity object (from `deterministicObjectBlake2Hash`)

**Implementation**:

```typescript
import { useGen6 } from '@/contexts/gen6-context'
import { useSigner } from '@/hooks/useSigner'
import { deterministicObjectBlake2Hash } from '@/packages/identity/utils/deterministicHash'

export function useIdentityBlockchain() {
  const { api, currentAccount } = useGen6()
  const { getSigner } = useSigner()

  const storeIdentityOnChain = async (identityPayload: Identity) => {
    const projectId = 20250923
    const dataHash = deterministicObjectBlake2Hash(identityPayload)

    const signerResult = await getSigner()

    const extrinsic = api.tx.dataRegistry.storeData(projectId, dataHash)

    return new Promise<string>((resolve, reject) => {
      extrinsic
        .signAndSend(
          currentAccount.address,
          { signer: signerResult.signer },
          ({ status, dispatchError, txHash }) => {
            if (status.isFinalized) {
              if (dispatchError) {
                reject(new Error('Transaction failed'))
              } else {
                resolve(txHash.toString())
              }
            }
          }
        )
        .catch(reject)
    })
  }

  return { storeIdentityOnChain }
}
```

**Usage**:

```typescript
function IdentityForm() {
  const { storeIdentityOnChain } = useIdentityBlockchain()

  const handleSubmit = async (identityPayload) => {
    // 1. Calculate merkle tree for database storage
    const merkleResult = await calculateIdentityMerkle({
      identity: identityPayload,
      field_configurations: fieldConfigurations
    })

    // 2. Store identity hash on blockchain
    const txHash = await storeIdentityOnChain(identityPayload)

    // 3. Create identity in database with merkle root
    await createIdentity({
      identity: identityPayload,
      field_configurations: fieldConfigurations,
      root_hash: merkleResult.root_hash
    })

    toast.success('Identity created and verified on blockchain!')
  }

  return <form onSubmit={handleSubmit}>...</form>
}
```

**Transaction Flow**:

1. User submits identity form
2. Frontend calculates Blake2 hash of full identity object for blockchain
3. Frontend calculates merkle root for database verification
4. Frontend stores hash on blockchain
5. Frontend creates identity in database with merkle root

**Verification**: Anyone can verify the identity by:

1. Recalculating the Blake2 hash from the identity data
2. Comparing with the on-chain hash
3. Additionally, the merkle root in the database can be used for field-level verification

***

### Privacy Levels

Identity fields support three visibility levels:

#### Private

* **Who can see**: Only the identity owner
* **Use case**: Sensitive data (email, phone, private notes)
* **Merkle tree**: Included in hash but not revealed

#### Community

* **Who can see**: Gen6 community members (authenticated users)
* **Use case**: Semi-public data (Discord, Telegram handles)
* **Merkle tree**: Included in hash and partially revealed

#### Public

* **Who can see**: Everyone (no authentication required)
* **Use case**: Public profile (name, bio, social links)
* **Merkle tree**: Included in hash and fully revealed

**Configuration Example**:

```typescript
const fieldConfigurations = {
  name: { visibility: 'public', pepper: generatePepper() },
  email: { visibility: 'private', pepper: generatePepper() },
  telegram: { visibility: 'community', pepper: generatePepper() }
}
```

***

### Custom Fields

Add arbitrary key-value pairs to identity profiles.

**Implementation**:

```typescript
const identity = {
  name: 'Alice',
  custom_fields: {
    'Favorite Color': 'Blue',
    'Programming Language': 'Rust',
    'Years of Experience': '5'
  }
}

const customFieldsOrder = [
  'Favorite Color',
  'Programming Language',
  'Years of Experience'
]
```

**Privacy**: Each custom field has its own visibility configuration.

**Ordering**: Use `custom_fields_order` to control display order.

***

### Complete Identity Creation Flow

#### Step-by-Step Guide

**Step 1: Prepare Identity Data**

```typescript
const identityData = {
  name: 'Alice Smith',
  bio: 'Blockchain developer',
  email: 'alice@example.com',
  x: '@alice'
}

const fieldConfigurations = {
  name: { visibility: 'public', pepper: generatePepper() },
  bio: { visibility: 'public', pepper: generatePepper() },
  email: { visibility: 'private', pepper: generatePepper() },
  x: { visibility: 'public', pepper: generatePepper() }
}
```

**Step 2: Calculate Merkle Tree for Database**

```typescript
const merkleResult = await calculateIdentityMerkle({
  identity: identityData,
  field_configurations: fieldConfigurations
})

console.log('Merkle root hash:', merkleResult.root_hash)
```

**Step 3: Store Identity Hash on Blockchain**

```typescript
const txHash = await storeIdentityOnChain(identityData)
console.log('Blockchain transaction hash:', txHash)
```

**Step 4: Create Identity in Database**

```typescript
await createIdentity({
  identity: identityData,
  field_configurations: fieldConfigurations,
  root_hash: merkleResult.root_hash
})
```

**Step 5: Upload Profile Image (Optional)**

```typescript
if (profileImage) {
  await uploadIdentityImage(profileImage)
}
```

***

### Query Keys

**TanStack Query Keys**:

```typescript
// Current user's identity
;['identity'][
  // Public identity by ID
  ('identity', 'public', id)
][
  // Identity image
  ('identityImage', id)
][
  // Merkle calculation
  ('identityMerkle', data)
]
```

***

### Error Handling

#### Common Errors

**"Identity already exists"**

* **Cause**: User already has an identity
* **Solution**: Use `updateIdentity()` instead of `createIdentity()`

**"Transaction failed"**

* **Cause**: Blockchain transaction rejected
* **Solution**: Check wallet balance and network status

***

### Best Practices

1. **Always calculate merkle tree first** - Ensures data integrity
2. **Store on blockchain before database** - Prevents orphaned hashes
3. **Set appropriate visibility levels** - Protect sensitive data
4. **Handle errors gracefully** - Show user-friendly messages

***

### Next Steps

* Real-Seal - Sign documents with verified identity
* Ncrypt - Send messages with identity verification


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://wiki.gen6.life/developer-resources/sdk-and-tooling/g6-mw-and-chain-api-guide/02.-ggs-1-identity-mw-api.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
