Awesome-omni-skill tapestry
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.
install
source · Clone the upstream repo
git clone https://github.com/diegosouzapw/awesome-omni-skill
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/development/tapestry" ~/.claude/skills/diegosouzapw-awesome-omni-skill-tapestry && rm -rf "$T"
manifest:
skills/development/tapestry/SKILL.mdsource content
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:
fromuseUnifiedWallet()
provides@/hooks/use-wallet
,address
,walletType
,provider
,isConnectedlogout - State: React Query (
) for server state@tanstack/react-query - Styling: StyleSheet with
bg,#0a0a0a
borders,#232323
fontPlusJakartaSans - API Key:
(server-side only, stored inprocess.env.TAPESTRY_API_KEY
).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 |
|---|---|---|
| <1s | Default. Returns after graph DB write, on-chain tx in background |
| ~5s | Need tx signature but don't need confirmation |
| ~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
- List/search profiles (by wallet, phone, twitter, email)GET /profiles/
- Find or create profilePOST /profiles/findOrCreate
- Get profile by ID or usernameGET /profiles/{id}
- Update profilePUT /profiles/{id}
- Get followersGET /profiles/{id}/followers
- Get followingGET /profiles/{id}/following
- Cross-namespace followersGET /profiles/{id}/followers/global
- Cross-namespace followingGET /profiles/{id}/following/global
- Mutual connectionsGET /profiles/{id}/following-who-follow
- Get linked walletsGET /profiles/{id}/wallets
- Link walletsPATCH /profiles/{id}/wallets
- Unlink walletsDELETE /profiles/{id}/wallets
- Link contactsPATCH /profiles/{id}/contacts
- Unlink contactsDELETE /profiles/{id}/contacts
- Send notificationPOST /profiles/{id}/notification
- Get referral treeGET /profiles/{id}/referrals
- Suggested followsGET /profiles/{id}/suggested-profiles
- Suggestions by wallet/contactGET /profiles/suggested/{identifier}
- Cross-namespace suggestionsGET /profiles/suggested/{identifier}/global
- Token holder profilesGET /profiles/token-owners/{tokenAddress}
- Search profiles by textGET /search/profiles
Social Graph (Followers)
- FollowPOST /followers/add{ startId, endId }
- UnfollowPOST /followers/remove{ startId, endId }
- Check follow stateGET /followers/state?startId=&endId=
Content (Posts)
- List content (with filters, ordering, pagination)GET /contents/
- Aggregate content propertiesGET /contents/aggregation
- Batch get by IDs (max 20)POST /contents/batch/read
- Create contentPOST /contents/findOrCreate{ id, profileId, properties: [{key,value}] }
- Get content detailsGET /contents/{id}
- Update content propertiesPUT /contents/{id}
- Delete contentDELETE /contents/{id}
Comments
- List commentsGET /comments/?contentId=&profileId=&targetProfileId=
- Create commentPOST /comments/{ profileId, text, contentId?, commentId?, targetProfileId? }
- Batch get (max 20)POST /comments/batch/read
- Get comment with repliesGET /comments/{id}
- Update commentPUT /comments/{id}
- Delete commentDELETE /comments/{id}
- Get repliesGET /comments/{id}/replies
Likes
- Get profiles who liked a nodeGET /likes/{nodeId}
- LikePOST /likes/{nodeId}{ startId }
- UnlikeDELETE /likes/{nodeId}{ startId }
Activity Feeds
- User activity feedGET /activity/feed?username=
- Global activity feedGET /activity/global
- Swap activity from followed walletsGET /activity/swap?username=&tokenAddress=
Identities
- Resolve wallets/contacts from IDGET /identities/{id}
- Find profiles across namespacesGET /identities/{id}/profiles
Trades
- Log a tradePOST /trades/
- Get all trades in time periodGET /trades/all-trades
- Wallet tx historyGET /trades/fetch-transaction-history
Wallets
- Connect two walletsPOST /wallets/{address}/connect
- Social counts by walletGET /wallets/{address}/socialCounts
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.