# 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
}  
```


---

# 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://sdk.sageapp.xyz/user/membership.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.
