name: tapestry description: Social graph protocol integration for Copium app using Tapestry API. Use when building social features - profiles, follows, content/posts, comments, likes, activity feeds, identity resolution, or trades. Covers all 41 Tapestry API endpoints. argument-hint: [feature-or-endpoint]
Tapestry Social Graph Integration
You are building social features for a React Native (Expo) mobile app called Copium - a social trading app using Tapestry and Polymarket.
Project Context
- Framework: React Native with Expo Router (file-based routing)
- Auth: Privy (OAuth + embedded Solana wallet) + Mobile Wallet Adapter (MWA)
- Wallet hook:
useUnifiedWallet()from@/hooks/use-walletprovidesaddress,walletType,provider,isConnected,logout - State: React Query (
@tanstack/react-query) for server state - Styling: StyleSheet with
#0a0a0abg,#232323borders,PlusJakartaSansfont - API Key:
process.env.TAPESTRY_API_KEY(server-side only, stored in.env.local) - Base URL:
https://api.usetapestry.dev/api/v1
API Authentication
All endpoints require apiKey as a query parameter:
GET /profiles/{id}?apiKey=YOUR_API_KEY
The API key is in .env.local as TAPESTRY_API_KEY. Since this is a React Native app (no server-side routes), call the Tapestry API directly from a utility module, keeping the API key secure via process.env.TAPESTRY_API_KEY (bundled at build time but not exposed to web).
Execution Methods (for write operations)
| Method | Speed | Use When |
|---|---|---|
FAST_UNCONFIRMED | <1s | Default. Returns after graph DB write, on-chain tx in background |
QUICK_SIGNATURE | ~5s | Need tx signature but don't need confirmation |
CONFIRMED_AND_PARSED | ~15s | Need full on-chain confirmation |
API Client Pattern
Create API calls in lib/tapestry/ directory. Use this pattern:
// lib/tapestry/client.ts
const TAPESTRY_BASE = 'https://api.usetapestry.dev/api/v1';
const API_KEY = process.env.TAPESTRY_API_KEY!;
export async function tapestryFetch<T>(
endpoint: string,
options?: { method?: string; body?: any; params?: Record<string, string> }
): Promise<T> {
const { method = 'GET', body, params = {} } = options ?? {};
const url = new URL(`${TAPESTRY_BASE}${endpoint}`);
url.searchParams.set('apiKey', API_KEY);
Object.entries(params).forEach(([k, v]) => v && url.searchParams.set(k, v));
const res = await fetch(url.toString(), {
method,
headers: body ? { 'Content-Type': 'application/json' } : undefined,
body: body ? JSON.stringify(body) : undefined,
});
if (!res.ok) {
const err = await res.json().catch(() => ({}));
throw new Error(err.error || `Tapestry API error: ${res.status}`);
}
return res.json();
}
React Hook Pattern
// hooks/use-tapestry-profile.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { tapestryFetch } from '@/lib/tapestry/client';
export function useTapestryProfile(username: string) {
return useQuery({
queryKey: ['tapestry-profile', username],
queryFn: () => tapestryFetch<ProfileResponse>(`/profiles/${username}`),
enabled: !!username,
});
}
All Endpoints Reference
For detailed endpoint specs, see reference.md.
Quick Endpoint Map
Profiles
GET /profiles/- List/search profiles (by wallet, phone, twitter, email)POST /profiles/findOrCreate- Find or create profileGET /profiles/{id}- Get profile by ID or usernamePUT /profiles/{id}- Update profileGET /profiles/{id}/followers- Get followersGET /profiles/{id}/following- Get followingGET /profiles/{id}/followers/global- Cross-namespace followersGET /profiles/{id}/following/global- Cross-namespace followingGET /profiles/{id}/following-who-follow- Mutual connectionsGET /profiles/{id}/wallets- Get linked walletsPATCH /profiles/{id}/wallets- Link walletsDELETE /profiles/{id}/wallets- Unlink walletsPATCH /profiles/{id}/contacts- Link contactsDELETE /profiles/{id}/contacts- Unlink contactsPOST /profiles/{id}/notification- Send notificationGET /profiles/{id}/referrals- Get referral treeGET /profiles/{id}/suggested-profiles- Suggested followsGET /profiles/suggested/{identifier}- Suggestions by wallet/contactGET /profiles/suggested/{identifier}/global- Cross-namespace suggestionsGET /profiles/token-owners/{tokenAddress}- Token holder profilesGET /search/profiles- Search profiles by text
Social Graph (Followers)
POST /followers/add- Follow{ startId, endId }POST /followers/remove- Unfollow{ startId, endId }GET /followers/state- Check follow state?startId=&endId=
Content (Posts)
GET /contents/- List content (with filters, ordering, pagination)GET /contents/aggregation- Aggregate content propertiesPOST /contents/batch/read- Batch get by IDs (max 20)POST /contents/findOrCreate- Create content{ id, profileId, properties: [{key,value}] }GET /contents/{id}- Get content detailsPUT /contents/{id}- Update content propertiesDELETE /contents/{id}- Delete content
Comments
GET /comments/- List comments?contentId=&profileId=&targetProfileId=POST /comments/- Create comment{ profileId, text, contentId?, commentId?, targetProfileId? }POST /comments/batch/read- Batch get (max 20)GET /comments/{id}- Get comment with repliesPUT /comments/{id}- Update commentDELETE /comments/{id}- Delete commentGET /comments/{id}/replies- Get replies
Likes
GET /likes/{nodeId}- Get profiles who liked a nodePOST /likes/{nodeId}- Like{ startId }DELETE /likes/{nodeId}- Unlike{ startId }
Activity Feeds
GET /activity/feed- User activity feed?username=GET /activity/global- Global activity feedGET /activity/swap- Swap activity from followed wallets?username=&tokenAddress=
Identities
GET /identities/{id}- Resolve wallets/contacts from IDGET /identities/{id}/profiles- Find profiles across namespaces
Trades
POST /trades/- Log a tradeGET /trades/all-trades- Get all trades in time periodGET /trades/fetch-transaction-history- Wallet tx history
Wallets
POST /wallets/{address}/connect- Connect two walletsGET /wallets/{address}/socialCounts- Social counts by wallet
Key Integration Points for Copium
1. Profile Creation (on signup/login)
After wallet connection + X login, call findOrCreate:
await tapestryFetch('/profiles/findOrCreate', {
method: 'POST',
body: {
username: twitterHandle, // from Privy OAuth
walletAddress: address, // from useUnifiedWallet
blockchain: 'SOLANA',
image: twitterProfileImage,
bio: twitterBio,
contact: { id: twitterHandle, type: 'TWITTER' },
},
});
2. Follow/Unfollow
await tapestryFetch('/followers/add', {
method: 'POST',
body: { startId: myProfileId, endId: targetProfileId },
});
3. Create a Post (Content)
await tapestryFetch('/contents/findOrCreate', {
method: 'POST',
body: {
id: `post-${Date.now()}`,
profileId: myProfileId,
properties: [
{ key: 'text', value: 'My post content' },
{ key: 'type', value: 'trade_call' },
],
},
});
4. Like Content
await tapestryFetch(`/likes/${contentId}`, {
method: 'POST',
body: { startId: myProfileId },
});
5. Comment on Content
await tapestryFetch('/comments/', {
method: 'POST',
body: {
profileId: myProfileId,
contentId: contentId,
text: 'Great trade!',
},
});
6. Activity Feed
const feed = await tapestryFetch('/activity/feed', {
params: { username: myUsername },
});
7. Search Users
const results = await tapestryFetch('/search/profiles', {
params: { query: searchText },
});
8. Polymarket Integration Pattern
Store Polymarket-related data as content properties:
await tapestryFetch('/contents/findOrCreate', {
method: 'POST',
body: {
id: `prediction-${marketId}-${profileId}`,
profileId: myProfileId,
properties: [
{ key: 'type', value: 'prediction' },
{ key: 'marketId', value: polymarketConditionId },
{ key: 'position', value: 'YES' },
{ key: 'amount', value: 50 },
{ key: 'odds', value: 0.65 },
],
},
});
Response Types
interface TapestryProfile {
id: string;
namespace: string;
created_at: number;
username: string;
bio: string | null;
image: string | null;
}
interface ProfileResponse {
profile: TapestryProfile;
walletAddress: string;
socialCounts: { followers: number; following: number };
namespace: { name: string | null; readableName: string | null };
}
interface ContentResponse {
id: string;
created_at: number;
properties: Record<string, any>;
}
interface CommentResponse {
comment: { id: string; created_at: number; text: string };
author: TapestryProfile;
socialCounts: { likeCount: number };
requestingProfileSocialInfo?: { hasLiked: boolean };
}
interface ActivityItem {
type: 'following' | 'new_content' | 'like' | 'comment' | 'new_follower';
actor_id: string;
actor_username: string;
target_id?: string;
target_username?: string;
timestamp: number;
activity: string;
}
interface PaginatedResponse<T> {
page: number;
pageSize: number;
totalCount: number;
data?: T[];
profiles?: T[];
activities?: T[];
}
File Structure Convention
lib/tapestry/
client.ts # Base fetch wrapper
profiles.ts # Profile API functions
social.ts # Follow/unfollow/like functions
content.ts # Content/posts CRUD
comments.ts # Comments CRUD
activity.ts # Activity feed functions
types.ts # TypeScript interfaces
hooks/
use-tapestry-profile.ts
use-tapestry-feed.ts
use-tapestry-social.ts
use-tapestry-content.ts
When implementing features with $ARGUMENTS, refer to reference.md for full endpoint details.