> For the complete documentation index, see [llms.txt](https://sdk.sageapp.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://sdk.sageapp.xyz/user/membership.md).

# Membership

Following a user will only succeed for a Sui address that has [an associated Sage user.](/user/create.md) Unfollowing will only succeed for&#x20;

## Follow a Sage user

```tsx
import { useSignAndExecuteTransaction } from '@mysten/dapp-kit';
import { useUser } from '@sage-app/sdk/react';

interface JoinUserInput {
  amounts: number[];           // payment amounts set to [0, 0]
  self: string;                // user's wallet address
  soulId: string;              // address of user's owned soul
  userId: string;              // user's object id
}

...

const { mutateAsync: signAndExecuteTransaction } = useSignAndExecuteTransaction();
const { join } = useUser();

...

const followUser = async (data: JoinUserInput) => {
  const { ok, err, transaction } = join(data);
    
  const { digest } = await signAndExecuteTransaction({
    transaction
  });
};
```

## Error Codes

**Authentication** module

<table><thead><tr><th width="104">Code</th><th width="221">Value</th><th>Meaning</th></tr></thead><tbody><tr><td>370</td><td>ENotAuthenticated</td><td>User soul object does not exist, is the wrong type, or not owned by the wallet.</td></tr></tbody></table>

**User Fees** module

<table><thead><tr><th width="106">Code</th><th width="231.63067626953125">Value</th><th>Meaning</th></tr></thead><tbody><tr><td>370</td><td>EIncorrectCoinType</td><td>Custom payment type does not match configured custom payment coin.</td></tr><tr><td>371</td><td>EIncorrectCustomPayment</td><td>Incorrect custom payment value.</td></tr><tr><td>372</td><td>EIncorrectSuiPayment</td><td>Incorrect sui payment value.</td></tr></tbody></table>

## Unfollow a Sage user

```tsx
import { useSignAndExecuteTransaction } from '@mysten/dapp-kit';
import { useUser } from '@sage-app/sdk/react';

interface LeaveUserInput {
  amounts: number[];           // payment amounts set to [0, 0]
  self: string;                // user's wallet address
  userId: string;              // user's object id
}

...

const { mutateAsync: signAndExecuteTransaction } = useSignAndExecuteTransaction();
const { leave } = useUser();

...

const unfollowUser = async (data: LeaveUserInput) => {
  const { ok, err, transaction } = leave(data);
    
  const { digest } = await signAndExecuteTransaction({
    transaction
  });
};
```

## Error Codes

**User Fees** module

<table><thead><tr><th width="106">Code</th><th width="231.63067626953125">Value</th><th>Meaning</th></tr></thead><tbody><tr><td>370</td><td>EIncorrectCoinType</td><td>Custom payment type does not match configured custom payment coin.</td></tr><tr><td>371</td><td>EIncorrectCustomPayment</td><td>Incorrect custom payment value.</td></tr><tr><td>372</td><td>EIncorrectSuiPayment</td><td>Incorrect sui payment value.</td></tr></tbody></table>

## Querying for User Membership Events

```typescript
import { EventId, SuiClient, getFullnodeUrl } from '@mysten/sui/client';
import { SageClient } from '@sage-app/sdk/client';

...

interface QueryInput {
  cursor?: EventId;
}

const appId = '<YOUR_APP_ID>';
const network = 'mainnet' | 'testnet';
const suiClient = new SuiClient({ url: getFullnodeUrl(network) });

const sageClient = new SageClient({ appId, network, suiClient });

...

const pollUserMembershipChanges = async ({ cursor }) => {
  const {
    eventCount,
    events,
    hasNextPage,
    nextCursor
  } = await sageClient.queryUserMembershipEvents({ cursor }: QueryInput);
  
  // do something with events
  
  setTimeout(() => pollUserMembershipChanges({ cursor: nextCursor }), 5000);
};
```

In this case events will take the shape of an array of UserMembershipEvent:

```typescript
interface UserMemberEvent {
  accountType: "OBJECT" | "WALLET";
  followedUser: string;               // followed user's wallet address
  message: "JOIN" | "LEAVE";
  updatedAt: string;
  user: string;                       // user's wallet address
}  
```
