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
UUID of the story to like/unlike
Response
Always true on successful operation
Like operation result Current like state after toggle
ISO 8601 timestamp of the operation
"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
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)
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
{ "error" : "Story ID is required" }
Follow User
Follow or unfollow a user by wallet address. This endpoint automatically toggles the follow state.
Request Body
Your Ethereum address (must match authenticated user’s wallet)
Target user’s Ethereum address
Response
Current follow state after toggle
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
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
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
Show 400 Bad Request - Missing Fields
{ "error" : "Missing follower_wallet or followed_wallet" }
Show 400 Bad Request - Self Follow
{ "error" : "Cannot follow yourself" }
The follower_wallet doesn’t match the authenticated user’s wallet.
Check Follow Status
Check if you’re following one or more users.
Query Parameters
Comma-separated list of wallet addresses to check
Response
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
{ "error" : "Missing follower_wallet or followed_wallets" }
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