Skip to main content

Overview

Social endpoints enable community features: liking stories, following creators, and receiving notifications.
All social endpoints require authentication and enforce ownership validation.

Toggle Like

Like or unlike a story. This endpoint automatically toggles the like state.

Request Body

storyId
string
required
UUID of the story to like/unlike

Response

success
boolean
Always true on successful operation
data
object
Like operation result
message
string
"Story liked!" or "Story unliked."

Example Request

curl -X POST https://istory.vercel.app/api/social/like \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"storyId": "123e4567-e89b-12d3-a456-426614174000"}'

Example Response (Like)

{
  "success": true,
  "data": {
    "storyId": "123e4567-e89b-12d3-a456-426614174000",
    "isLiked": true,
    "totalLikes": 5,
    "timestamp": "2026-03-04T10:30:00.000Z"
  },
  "message": "Story liked!"
}

Example Response (Unlike)

{
  "success": true,
  "data": {
    "storyId": "123e4567-e89b-12d3-a456-426614174000",
    "isLiked": false,
    "totalLikes": 4,
    "timestamp": "2026-03-04T10:30:15.000Z"
  },
  "message": "Story unliked."
}

Behavior

  1. Like - If not already liked:
    • Creates a record in likes table
    • Increments stories.likes counter
    • Creates a notification for the story author (unless you’re liking your own story)
  2. Unlike - If already liked:
    • Deletes the record from likes table
    • Decrements stories.likes counter (floor at 0)
    • No notification

Notifications

When you like another user’s story, they receive a notification:
{
  "type": "like",
  "title": "New Like",
  "message": "Alice liked your story",
  "story_id": "123e4567-e89b-12d3-a456-426614174000"
}

Error Responses


Follow User

Follow or unfollow a user by wallet address. This endpoint automatically toggles the follow state.

Request Body

follower_wallet
string
required
Your Ethereum address (must match authenticated user’s wallet)
followed_wallet
string
required
Target user’s Ethereum address

Response

isFollowing
boolean
Current follow state after toggle
followers_count
number
Updated follower count for the target user

Example Request

curl -X POST https://istory.vercel.app/api/social/follow \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "follower_wallet": "0x742d35cc6634c0532925a3b844bc9e7595f0beb",
    "followed_wallet": "0x1234567890abcdef1234567890abcdef12345678"
  }'

Example Response (Follow)

{
  "isFollowing": true,
  "followers_count": 42
}

Example Response (Unfollow)

{
  "isFollowing": false,
  "followers_count": 41
}

Behavior

  1. Follow - If not already following:
    • Creates a record in follows table
    • Increments users.followers_count for the target user
    • Creates a notification for the followed user
  2. Unfollow - If already following:
    • Deletes the record from follows table
    • Decrements users.followers_count for the target user (floor at 0)
    • No notification

Notifications

When you follow a user, they receive a notification:
{
  "type": "follow",
  "title": "New Follower",
  "message": "Alice started following you"
}

Error Responses


Check Follow Status

Check if you’re following one or more users.

Query Parameters

follower_wallet
string
required
Your Ethereum address
followed_wallets
string
required
Comma-separated list of wallet addresses to check

Response

following
object
Map of wallet addresses to follow status
{
  [walletAddress: string]: boolean
}

Example Request

curl "https://istory.vercel.app/api/social/follow?follower_wallet=0x742d35cc6634c0532925a3b844bc9e7595f0beb&followed_wallets=0x1234...5678,0xabcd...ef01" \
  -H "Authorization: Bearer YOUR_TOKEN"

Example Response

{
  "following": {
    "0x1234567890abcdef1234567890abcdef12345678": true,
    "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd": false
  }
}

Error Responses


Database Schema

likes table

CREATE TABLE likes (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID REFERENCES users(id) ON DELETE CASCADE,
  story_id UUID REFERENCES stories(id) ON DELETE CASCADE,
  created_at TIMESTAMP DEFAULT NOW(),
  UNIQUE(user_id, story_id)
);

follows table

CREATE TABLE follows (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  follower_wallet TEXT NOT NULL,
  followed_wallet TEXT NOT NULL,
  created_at TIMESTAMP DEFAULT NOW(),
  UNIQUE(follower_wallet, followed_wallet)
);

notifications table

CREATE TABLE notifications (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID REFERENCES users(id) ON DELETE CASCADE,
  type TEXT NOT NULL,  -- 'like', 'follow', 'comment'
  title TEXT NOT NULL,
  message TEXT NOT NULL,
  story_id UUID REFERENCES stories(id) ON DELETE SET NULL,
  is_read BOOLEAN DEFAULT FALSE,
  created_at TIMESTAMP DEFAULT NOW()
);

Best Practices

Toggle Pattern

Both like and follow use toggle logic - no need to check current state before calling

Idempotent Operations

Calling the same operation twice is safe (uses upsert for likes, check-before-insert for follows)

Batch Follow Checks

Use comma-separated wallets to check multiple follow statuses in one request

Notification Awareness

Liking and following trigger notifications - consider this in UX design

Example: Social Feed Component

// Like a story
async function toggleLike(storyId: string) {
  const response = await fetch('/api/social/like', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ storyId })
  });
  
  const { data } = await response.json();
  return data; // { isLiked, totalLikes }
}

// Follow a user
async function toggleFollow(targetWallet: string) {
  const response = await fetch('/api/social/follow', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      follower_wallet: myWallet,
      followed_wallet: targetWallet
    })
  });
  
  const { isFollowing, followers_count } = await response.json();
  return { isFollowing, followers_count };
}

// Check follow status for multiple users
async function checkFollowStatus(wallets: string[]) {
  const params = new URLSearchParams({
    follower_wallet: myWallet,
    followed_wallets: wallets.join(',')
  });
  
  const response = await fetch(`/api/social/follow?${params}`, {
    headers: { 'Authorization': `Bearer ${token}` }
  });
  
  const { following } = await response.json();
  return following; // { [wallet]: boolean }
}

Next Steps

API Overview

Return to API overview

Authentication

Review authentication methods

Build docs developers (and LLMs) love