Pear Exchange

Activity feed

Mock payloads for the social Activity card. Backed by GET /api/posts/feed (FYP).

Activity feed

The "Activity" tab in the mobile app (the trader-card stream — whale_mike.eth bought Yes: BTC above $70,800 for $2400) is not backed by /api/events/discover. That endpoint is a slim browse index by design. The activity feed is the social posts firehose:

SurfaceEndpointAuthNotes
Activity tab (default)GET /api/posts/feed?scope=globaloptionalPublic FYP. One row per auto_trade post (created on FILLED ≥ $5 USD), manual post, or auto_position_close (PnL highlight).
Activity tab (filter "Following")GET /api/posts/feed?scope=followingrequiredSame shape, scoped to the people you follow. Falls back to coldStart: true when you don't follow anyone.
Home screen "Activity" stripGET /api/homeactivityFeed.items[]requiredThinner trade-row shape today; will be brought in line with the post-feed contract.

Pagination is a cursor on createdAt (ISO timestamp) — pass the previous response's nextCursor back as ?cursor=.... Default limit=25, max 100.

Where each pixel comes from

Mapping the activity card from the design to fields in the response:

Card elementField
Avatar, handle, display nameauthor.{avatarUrl, handle, displayName}
Top 50 rank chipauthor.leaderboardRank (proposed — see §gaps)
82% win rate · 2 trades this weekauthor.{winRatePct, tradesLast7d} (proposed)
Verb (Bought / Sold half of / Won)verb (proposed — derived from trade.side + position delta)
Outcome chip (Yes)trade.outcome
Market label (BTC above $70,800)market.title
for $2400trade.sizeUsd
Event title (Bitcoin price today at 5pm…)event.title
Event description (subtitle in card)event.description (proposed)
Event image (right-edge thumbnail)event.imageUrl
Category chips (Crypto, Bitcoin)event.category (proposed)
Outcome ladder ($71,050 above 4.00x 46% …)event.topMarkets[] (proposed — child-market roll-up)
$2.4M Volume · 80 Marketsevent.{totalVolume24hUsd, marketsCount} (proposed)
Venue chips (Polymarket, Kalshi)event.venues[] (proposed — already on the row)
❤ 5 · 💬 12 · ↗ 2engagement.{likeCount, commentCount, copyCount} (today: top-level)
2m agocreatedAt

Today's response shape — /api/posts/feed?scope=global

This is what the API returns right now (see pear/apps/api/src/routes/posts.ts::shapePost). It carries the trade USD notional and the anchored event/market titles, but doesn't yet carry the trader stats, event metadata, or the outcome ladder. Use this for the v1 of the card:

{
  "items": [
    {
      "id": "post_01HX9WQK4D2A7N6F4W5T3KZQXQ",
      "source": "auto_trade",
      "body": null,
      "trade": {
        "id": "ord_8c1f4a30",
        "venue": "polymarket",
        "venueMarketId": "0x5f7c2e9a8b1d4e6f3a2c8d9e7b1f4a3c6d2e8b9f1a4c7e3d2b6a9f8c1e4d7b3a",
        "outcome": "yes",
        "sizeUsd": 2400.0
      },
      "event": {
        "pearEventId": "pear_btc_5pm_edt",
        "slug": "btc-price-today-5pm-edt",
        "title": "Bitcoin price today at 5pm EDT",
        "imageUrl": "https://cdn.pear.trade/events/btc.png"
      },
      "market": {
        "venue": "polymarket",
        "venueMarketId": "0x5f7c2e9a8b1d4e6f3a2c8d9e7b1f4a3c6d2e8b9f1a4c7e3d2b6a9f8c1e4d7b3a",
        "title": "BTC above $70,800"
      },
      "author": {
        "userId": "did:privy:cmmthssoe00rd0cjsllawl7m9",
        "handle": "whale_mike.eth",
        "displayName": "Whale Mike",
        "avatarUrl": "https://cdn.pear.trade/avatars/whale_mike.png"
      },
      "imageUrl": null,
      "likeCount": 5,
      "commentCount": 12,
      "copyCount": 2,
      "liked": false,
      "visibility": "public",
      "createdAt": "2026-04-24T16:09:30.000Z"
    },
    {
      "id": "post_01HX9WPS3M8N1V2K7H6L9DQXR2",
      "source": "auto_trade",
      "body": null,
      "trade": {
        "id": "ord_b3a91d6c",
        "venue": "polymarket",
        "venueMarketId": "0xa1b2c3d4e5f60718293a4b5c6d7e8f9012345678abcdef0123456789abcdef01",
        "outcome": "yes",
        "sizeUsd": 850.0
      },
      "event": {
        "pearEventId": "pear_az_senate_2026",
        "slug": "az-senate-2026",
        "title": "Will Dem candidate win Arizona Senate?",
        "imageUrl": "https://cdn.pear.trade/events/az-senate.png"
      },
      "market": {
        "venue": "polymarket",
        "venueMarketId": "0xa1b2c3d4e5f60718293a4b5c6d7e8f9012345678abcdef0123456789abcdef01",
        "title": "Democrat wins Arizona Senate 2026"
      },
      "author": {
        "userId": "did:privy:cmm2x1n7w00aa0cjs5g2ftkpd",
        "handle": "kw_trades",
        "displayName": "KW Trades",
        "avatarUrl": "https://cdn.pear.trade/avatars/kw_trades.png"
      },
      "imageUrl": null,
      "likeCount": 8,
      "commentCount": 4,
      "copyCount": 1,
      "liked": false,
      "visibility": "public",
      "createdAt": "2026-04-24T16:09:00.000Z"
    },
    {
      "id": "post_01HX9WP3Y6F1Q2W8E7R4T9YQXS3",
      "source": "auto_position_close",
      "body": null,
      "trade": {
        "id": "ord_4d8c7b1e",
        "venue": "kalshi",
        "venueMarketId": "KXNBASPREAD-26APR-LAL",
        "outcome": "yes",
        "sizeUsd": 1200.0
      },
      "event": {
        "pearEventId": "pear_lakers_spread_apr",
        "slug": "lakers-cover-spread-apr",
        "title": "Will Lakers cover -4.5 spread?",
        "imageUrl": "https://cdn.pear.trade/events/lakers-warriors.png"
      },
      "market": {
        "venue": "kalshi",
        "venueMarketId": "KXNBASPREAD-26APR-LAL",
        "title": "Lakers to cover -4.5"
      },
      "author": {
        "userId": "did:privy:cmm9p4lz500jx0cjslckrnp7v",
        "handle": "alex_l",
        "displayName": "Alex L.",
        "avatarUrl": "https://cdn.pear.trade/avatars/alex_l.png"
      },
      "imageUrl": null,
      "likeCount": 14,
      "commentCount": 3,
      "copyCount": 0,
      "liked": false,
      "visibility": "public",
      "createdAt": "2026-04-24T16:08:00.000Z"
    }
  ],
  "nextCursor": "2026-04-24T16:08:00.000Z",
  "coldStart": false
}

Designer-grade enriched shape (target — what the card needs end-to-end)

This is the composite shape we recommend the FE codes against. Everything marked (proposed) in the table above is folded in below — the designer can build the full card off a single mock without stitching three endpoints together.

{
  "items": [
    {
      "id": "post_01HX9WQK4D2A7N6F4W5T3KZQXQ",
      "source": "auto_trade",
      "verb": "bought",
      "createdAt": "2026-04-24T16:09:30.000Z",
      "body": null,
      "imageUrl": null,
      "author": {
        "userId": "did:privy:cmmthssoe00rd0cjsllawl7m9",
        "handle": "whale_mike.eth",
        "displayName": "Whale Mike",
        "avatarUrl": "https://cdn.pear.trade/avatars/whale_mike.png",
        "winRatePct": 82,
        "tradesLast7d": 2,
        "leaderboardRank": 47,
        "leaderboardTier": "top_50"
      },
      "trade": {
        "id": "ord_8c1f4a30",
        "venue": "polymarket",
        "venueMarketId": "0x5f7c2e9a8b1d4e6f3a2c8d9e7b1f4a3c6d2e8b9f1a4c7e3d2b6a9f8c1e4d7b3a",
        "side": "buy",
        "outcome": "yes",
        "sizeShares": 12000,
        "sizeUsd": 2400.0,
        "avgPrice": 0.20,
        "filledAt": "2026-04-24T16:09:28.000Z"
      },
      "market": {
        "venue": "polymarket",
        "venueMarketId": "0x5f7c2e9a8b1d4e6f3a2c8d9e7b1f4a3c6d2e8b9f1a4c7e3d2b6a9f8c1e4d7b3a",
        "title": "BTC above $70,800",
        "yesPrice": 0.20,
        "noPrice": 0.80
      },
      "event": {
        "pearEventId": "pear_btc_5pm_edt",
        "slug": "btc-price-today-5pm-edt",
        "title": "Bitcoin price today at 5pm EDT",
        "description": "Bitcoin price today at 5pm EDT today",
        "imageUrl": "https://cdn.pear.trade/events/btc.png",
        "category": "Crypto",
        "subCategory": "Bitcoin",
        "venues": ["polymarket", "kalshi"],
        "totalVolume24hUsd": 2400000,
        "marketsCount": 80,
        "closesAt": "2026-04-24T21:00:00.000Z",
        "topMarkets": [
          {
            "venue": "polymarket",
            "venueMarketId": "0x5f7c2e9a8b1d4e6f3a2c8d9e7b1f4a3c6d2e8b9f1a4c7e3d2b6a9f8c1e4d7b3a",
            "title": "$71,050 above",
            "yesPrice": 0.46,
            "multiplier": 4.0,
            "highlighted": false
          },
          {
            "venue": "polymarket",
            "venueMarketId": "0x5f7c2e9a8b1d4e6f3a2c8d9e7b1f4a3c6d2e8b9f1a4c7e3d2b6a9f8c1e4d7b3a",
            "title": "$70,800 above",
            "yesPrice": 0.50,
            "multiplier": 200.0,
            "highlighted": true
          }
        ]
      },
      "engagement": {
        "likeCount": 5,
        "commentCount": 12,
        "copyCount": 2,
        "liked": false
      },
      "visibility": "public"
    },
    {
      "id": "post_01HX9WPS3M8N1V2K7H6L9DQXR2",
      "source": "auto_trade",
      "verb": "sold_half",
      "createdAt": "2026-04-24T16:09:00.000Z",
      "body": null,
      "imageUrl": null,
      "author": {
        "userId": "did:privy:cmm2x1n7w00aa0cjs5g2ftkpd",
        "handle": "kw_trades",
        "displayName": "KW Trades",
        "avatarUrl": "https://cdn.pear.trade/avatars/kw_trades.png",
        "winRatePct": 61,
        "tradesLast7d": 5,
        "leaderboardRank": 92,
        "leaderboardTier": "top_100"
      },
      "trade": {
        "id": "ord_b3a91d6c",
        "venue": "polymarket",
        "venueMarketId": "0xa1b2c3d4e5f60718293a4b5c6d7e8f9012345678abcdef0123456789abcdef01",
        "side": "sell",
        "outcome": "yes",
        "sizeShares": 1545,
        "sizeUsd": 850.0,
        "avgPrice": 0.55,
        "filledAt": "2026-04-24T16:08:58.000Z"
      },
      "market": {
        "venue": "polymarket",
        "venueMarketId": "0xa1b2c3d4e5f60718293a4b5c6d7e8f9012345678abcdef0123456789abcdef01",
        "title": "Democrat wins Arizona Senate 2026",
        "yesPrice": 0.55,
        "noPrice": 0.45
      },
      "event": {
        "pearEventId": "pear_az_senate_2026",
        "slug": "az-senate-2026",
        "title": "Will Dem candidate win Arizona Senate?",
        "description": "Resolves to the certified winner of the 2026 Arizona Senate race.",
        "imageUrl": "https://cdn.pear.trade/events/az-senate.png",
        "category": "Politics",
        "subCategory": "US Midterms 2026",
        "venues": ["polymarket"],
        "totalVolume24hUsd": 380000,
        "marketsCount": 1,
        "closesAt": "2026-11-04T05:00:00.000Z",
        "topMarkets": [
          {
            "venue": "polymarket",
            "venueMarketId": "0xa1b2c3d4e5f60718293a4b5c6d7e8f9012345678abcdef0123456789abcdef01",
            "title": "Yes",
            "yesPrice": 0.55,
            "multiplier": 1.82,
            "highlighted": true
          }
        ]
      },
      "engagement": {
        "likeCount": 8,
        "commentCount": 4,
        "copyCount": 1,
        "liked": false
      },
      "visibility": "public"
    },
    {
      "id": "post_01HX9WP3Y6F1Q2W8E7R4T9YQXS3",
      "source": "auto_position_close",
      "verb": "won",
      "createdAt": "2026-04-24T16:08:00.000Z",
      "body": null,
      "imageUrl": null,
      "author": {
        "userId": "did:privy:cmm9p4lz500jx0cjslckrnp7v",
        "handle": "alex_l",
        "displayName": "Alex L.",
        "avatarUrl": "https://cdn.pear.trade/avatars/alex_l.png",
        "winRatePct": 74,
        "tradesLast7d": 8,
        "leaderboardRank": 318,
        "leaderboardTier": null
      },
      "trade": {
        "id": "ord_4d8c7b1e",
        "venue": "kalshi",
        "venueMarketId": "KXNBASPREAD-26APR-LAL",
        "side": "sell",
        "outcome": "yes",
        "sizeShares": 1200,
        "sizeUsd": 1200.0,
        "avgPrice": 1.00,
        "filledAt": "2026-04-24T16:07:55.000Z",
        "realizedPnlUsd": 540.0
      },
      "market": {
        "venue": "kalshi",
        "venueMarketId": "KXNBASPREAD-26APR-LAL",
        "title": "Lakers to cover -4.5",
        "yesPrice": 1.00,
        "noPrice": 0.00,
        "status": "resolved"
      },
      "event": {
        "pearEventId": "pear_lakers_spread_apr",
        "slug": "lakers-cover-spread-apr",
        "title": "Will Lakers cover -4.5 spread?",
        "description": "NBA · Lakers vs Warriors · Resolved",
        "imageUrl": "https://cdn.pear.trade/events/lakers-warriors.png",
        "category": "Sports",
        "subCategory": "NBA",
        "venues": ["kalshi"],
        "totalVolume24hUsd": 380000,
        "marketsCount": 1,
        "closesAt": "2026-04-24T03:00:00.000Z",
        "topMarkets": [
          {
            "venue": "kalshi",
            "venueMarketId": "KXNBASPREAD-26APR-LAL",
            "title": "Lakers cover -4.5",
            "yesPrice": 1.00,
            "multiplier": 2.20,
            "highlighted": true
          }
        ]
      },
      "engagement": {
        "likeCount": 14,
        "commentCount": 3,
        "copyCount": 0,
        "liked": false
      },
      "visibility": "public"
    }
  ],
  "nextCursor": "2026-04-24T16:08:00.000Z",
  "coldStart": false
}

Field reference (enriched shape)

author

FieldTypeSourceNotes
userIdstringidentity.posts.user_idPrivy DID.
handlestring | nullidentity.user_profiles.handlenull until the user sets it.
displayNamestring | nullidentity.user_profiles.display_name
avatarUrlstring | nullidentity.user_profiles.avatar_url
winRatePctinteger 0–100 | nullidentity.user_stats.win_count / total_tradesRounded to nearest int. null if total_trades < 5.
tradesLast7dinteger | nullidentity.user_stats.trades_7dRolling 7-day window from the stats roll-up worker.
leaderboardRankinteger | nullidentity.leaderboard.rankGlobal rank by total_volume. null if outside top 1000.
leaderboardTier"top_50" | "top_100" | "top_500" | nullderived from leaderboardRankDrives the chip badge.

trade

FieldTypeNotes
idstringThe originating order id.
venue"polymarket" | "kalshi" | "dflow"
venueMarketIdstringPolymarket token id or Kalshi market ticker.
side"buy" | "sell"
outcome"yes" | "no"
sizeSharesnumber
sizeUsdnumberThe "for $X" in the card. sizeShares × avgPrice.
avgPricenumber 0..1
filledAtISO timestamp
realizedPnlUsdnumber | nullOnly present on auto_position_close posts (drives the "Won" / "Lost" chip).

event.topMarkets[]

The outcome-ladder strip in the card (e.g. $71,050 above 4.00x 46%). Server-side we cap to 2–3 child markets, sorted by volume_24h desc, with the trader's own outcome marked highlighted: true.

FieldTypeNotes
venue"polymarket" | "kalshi"
venueMarketIdstring
titlestringThe bucket label, e.g. $71,050 above.
yesPricenumber 0..1Drives the 46% chip.
multipliernumber1 / yesPrice, rounded to one decimal. Drives the 4.00x chip.
highlightedbooleantrue if this is the market the trader actually traded.

verb

ValueTriggered when
boughtauto_trade, trade.side = "buy".
sold_halfauto_trade, trade.side = "sell", position size shrinks to [1%, 99%) of pre-fill size.
sold_allauto_trade, trade.side = "sell", position closes (post-fill size ≤ 1%).
wonauto_position_close, realizedPnlUsd > 0.
lostauto_position_close, realizedPnlUsd ≤ 0.
manualsource = "manual". The card renders body instead of a verb chip.

Empty / cold-start states

{ "items": [], "nextCursor": null, "coldStart": true }

coldStart: true is only returned on scope=following when the caller follows nobody — the FE should swap in a "Find traders to follow" recommendations strip (use GET /api/social/overview.recommendedTraders).

Realtime hook

Subscribe to the social:activity WebSocket topic to prepend new cards as they're created. Frame shape:

{
  "topic": "social:activity",
  "data": {
    "kind": "auto_trade",
    "userId": "did:privy:cmmthssoe00rd0cjsllawl7m9",
    "postId": "post_01HX9WQK4D2A7N6F4W5T3KZQXQ",
    "eventSlug": "btc-price-today-5pm-edt",
    "marketVenue": "polymarket",
    "marketId": "0x5f7c2e9a8b1d4e6f3a2c8d9e7b1f4a3c6d2e8b9f1a4c7e3d2b6a9f8c1e4d7b3a"
  }
}

The frame is a pointer, not the card payload — the FE should refetch GET /api/posts/{postId} (which returns the same item shape as the feed) before inserting the row, so the enriched event/author data is consistent with what's already on screen.

On this page