NIP-85 to Primal Server API Mapping

This document maps NIP-85 Trusted Assertion tags to PrimalServer methods and response fields.

Summary

PrimalServer provides limited support for NIP-85 statistics:

- ✅ Supported: User follower counts, user scores/ranks, and comprehensive event-level statistics (likes, replies, reposts, zaps)
- ❌ Not Supported: User post counts, reply counts, zap totals, reports, topics, active hours, addressable event stats, and NIP-73 identifier stats

The supported fields are accessed through methods like getUserInfos(), getUserProfile(), getFeed(), getEvents(), and getThreadView().

Kind 30382: User-level Statistics

| NIP-85 Tag | NIP-85 Description | PrimalServer Method | Response Field |
|------------|-------------------|---------------------|----------------|
| followers | Follower Count | getUserInfos(), getUserProfile() | UserInfo.followerCount, UserProfileResponse.profile.followers_count (kind 10000133) |
| rank | User Rank (0-100) | getUserInfos() | UserInfo.score (kind 10000108) |

Kind 30383: Event-level Statistics

| NIP-85 Tag | NIP-85 Description | PrimalServer Method | Response Field |
|------------|-------------------|---------------------|----------------|
| rank | Event Rank (0-100) | getFeed(), getEvents(), getThreadView() | FeedPost.stats.score, EventsResponse.eventStats[id].score (kind 10000100) |
| comment_cnt | Event Comment Count | getFeed(), getEvents(), getThreadView() | FeedPost.stats.replies, EventsResponse.eventStats[id].replies |
| repost_cnt | Event Repost Count | getFeed(), getEvents(), getThreadView() | FeedPost.stats.reposts, EventsResponse.eventStats[id].reposts |
| reaction_cnt | Event Reaction Count | getFeed(), getEvents(), getThreadView() | FeedPost.stats.likes, EventsResponse.eventStats[id].likes |
| zap_cnt | Event Zap Count | getFeed(), getEvents(), getThreadView() | FeedPost.stats.zaps, EventsResponse.eventStats[id].zaps |
| zap_amount | Event Zap Amount (sats) | getFeed(), getEvents(), getThreadView() | FeedPost.stats.satszapped, EventsResponse.eventStats[id].satszapped |

Kind 30384: Addressable Event Statistics

Not supported by PrimalServer - no methods available for addressable event statistics.

Kind 30385: NIP-73 Identifier Statistics

Not supported by PrimalServer - no methods available for NIP-73 identifier statistics.

Additional Primal-Specific Fields

Primal includes some fields not in NIP-85:

| Field | Description | PrimalServer Method | Response Field |
|-------|-------------|---------------------|----------------|
| mentions | Number of times event is mentioned | getFeed(), getEvents(), getThreadView() | FeedPost.stats.mentions, EventsResponse.eventStats[id].mentions |
| score24h | 24-hour engagement score | getFeed(), getEvents(), getThreadView() | FeedPost.stats.score24h, EventsResponse.eventStats[id].score24h |
| bookmarks | Number of bookmarks | getFeed(), getEvents(), getThreadView() | FeedPost.stats.bookmarks, EventsResponse.eventStats[id].bookmarks |
| membership | Premium membership information (kind 10000169) | getUserProfile(), getUserInfos() | UserProfileResponse.membership, UserInfo.membership |

Primal Server Custom Event Kinds

The Primal API uses custom event kinds (10000000+) for enriched responses:

- 10000100 - EVENT_STATS: Event statistics (likes, replies, zaps, etc.)
- 10000105 - USER_PROFILE: User profile information
- 10000107 - REFERENCED_EVENT: Referenced events in threads
- 10000108 - USER_SCORES: User scores/rankings
- 10000113 - RANGE: Response range metadata
- 10000115 - EVENT_ACTIONS_COUNT: User's actions on events
- 10000117 - DIRECTMSG_COUNT: Direct message counts
- 10000119 - MEDIA_METADATA: Media metadata
- 10000128 - LINK_METADATA: Link preview metadata
- 10000129 - ZAP_EVENT: Zap event details
- 10000133 - USER_FOLLOWER_COUNTS: Follower counts
- 10000134 - DIRECTMSG_COUNT_2: DM counts v2
- 10000136 - NOSTR_STATS: Network statistics
- 10000138 - USER_PUBKEY: User public key info
- 10000141 - EVENT_RELAYS: Event relay information
- 10000158 - USER_PRIMAL_NAMES: Primal verified names
- 10000168 - MEMBERSHIP_LEGEND_CUSTOMIZATION: Premium member customization
- 10000169 - MEMBERSHIP_COHORTS: Membership tier info

Key Differences

1. No direct NIP-85 implementation: Primal uses custom event kinds (10000100+) instead of NIP-85's 30382-30385 range
2. Limited user-level statistics: Most NIP-85 user statistics (post counts, reply counts, zap totals, etc.) are not exposed through PrimalServer methods
3. Event-level statistics well-supported: Event stats (likes, replies, reposts, zaps) are available through multiple methods
4. Additional granularity: Primal has score24h, bookmarks, and mentions not in NIP-85
5. No addressable/NIP-73 stats: These would need to be implemented

Database Schema References

Event Stats Table

CREATE TABLE event_stats (
  event_id bytea PRIMARY KEY,
  author_pubkey bytea NOT NULL,
  created_at bigint NOT NULL,
  likes bigint NOT NULL,
  replies bigint NOT NULL,
  mentions bigint NOT NULL,
  reposts bigint NOT NULL,
  zaps bigint NOT NULL,
  satszapped bigint NOT NULL,
  score bigint NOT NULL,
  score24h bigint NOT NULL
)

Pubkey Followers Count Table

CREATE TABLE pubkey_followers_cnt (
  key bytea PRIMARY KEY,  -- pubkey
  value bigint NOT NULL   -- follower count
)

Pubkey Zapped Table

CREATE TABLE pubkey_zapped (
  pubkey bytea PRIMARY KEY,
  zaps bigint NOT NULL,
  satszapped bigint NOT NULL
)

Pubkey Events Table

CREATE TABLE pubkey_events (
  pubkey bytea NOT NULL,
  event_id bytea NOT NULL,
  created_at bigint NOT NULL,
  is_reply bigint NOT NULL
)

Example Usage

NIP-85 Format (Kind 30382)

{
  "kind": 30382,
  "tags": [
    ["d", "e88a691e98d9987c964521dff60025f60700378a4879180dcbbb4a5027850411"],
    ["rank", "89"],
    ["followers", "1234"]
  ],
  "content": ""
}

PrimalServer Equivalent

import { PrimalServer } from './PrimalServer.ts';

const server = new PrimalServer('wss://cache.primal.net/v1');

// Get user statistics (followers, rank/score)
const userInfo = await server.getUserInfos([
'e88a691e98d9987c964521dff60025f60700378a4879180dcbbb4a5027850411'
]);

const user = userInfo.users.get('e88a691e98d9987c964521dff60025f60700378a4879180dcbbb4a5027850411');
console.log('Rank/Score:', user?.score); // 89
console.log('Followers:', user?.followerCount); // 1234

NIP-85 Event Stats Format (Kind 30383)

{
  "kind": 30383,
  "tags": [
    ["d", "event_id_here"],
    ["rank", "95"],
    ["comment_cnt", "42"],
    ["reaction_cnt", "156"],
    ["zap_cnt", "12"],
    ["zap_amount", "5000"]
  ],
  "content": ""
}

PrimalServer Equivalent

// Get event statistics
const events = await server.getEvents(['event_id_here'], true);
const stats = events.eventStats.get('event_id_here');

console.log('Rank/Score:', stats?.score); // 95
console.log('Comments:', stats?.replies); // 42
console.log('Reactions:', stats?.likes); // 156
console.log('Zaps:', stats?.zaps); // 12
console.log('Sats Zapped:', stats?.satszapped); // 5000