BIO.RE
Creator

Get Recent Activity

Merged feed of the creator's recent messages, processed payouts, and new bio-page subscribers. Three sources combined, sorted by timestamp DESC, sliced to limit.

GET /api/v1/creators/dashboard/activity — 🔑 Bearer

Returns a merged activity feed of the creator's recent messages received, processed payouts, and new confirmed bio-page email subscribers. Each source pulls up to limit items independently; the union is sorted by timestamp descending and sliced to limit. Default limit is 10, server clamps to [1, 20].

Why bio-email subscribers stand in for "followers". There is no dedicated Follow model today — the closest signal of "someone newly engaged with this creator" is a confirmed BioEmailSubscriber. That choice keeps the activity feed populated for new creators without inventing data. If a Follow model is added later, this endpoint will gain a new activity type rather than replacing this one.

Limit clamps server-side. ?limit=0 becomes 1, ?limit=999 becomes 20. Pagination beyond 20 isn't exposed — design the UI for "recent activity glance" not full history.

Request

Query parameters

ParamTypeDefaultValidationNotes
limitnumber10ParseIntPipe, server-clamped to [1, 20]How many items to return after merge
HeaderRequiredNotes
Authorization: Bearer <accessToken>JWT from POST /auth/login

Response

200 OKApiResponseOf<ActivityResponseDto>

{
  "success": true,
  "data": {
    "items": [
      {
        "type": "message_received",
        "title": "New message from @johndoe",
        "timestamp": "2026-04-29T20:04:00.000Z",
        "meta": { "messageId": "m1a2b3c4-d5e6-7890-abcd-ef1234567890" }
      },
      {
        "type": "payout_processed",
        "title": "Payout of 250.00 processed",
        "timestamp": "2026-04-29T19:50:00.000Z",
        "meta": { "payoutId": "p1a2y3o4-u5t6-7890-abcd-ef1234567890", "amount": "250.00" }
      },
      {
        "type": "follower_new",
        "title": "Alice subscribed to your bio",
        "timestamp": "2026-04-29T19:30:00.000Z",
        "meta": { "subscriberId": "s1u2b3c4-d5e6-7890-abcd-ef1234567890" }
      }
    ]
  }
}

Activity types

typeSourcetitle templatemeta
message_receivedMessage where receiverId = userIdNew message from <senderDisplayName ?? senderUsername ?? 'Someone'>{ messageId }
payout_processedPayoutRequest where creatorId AND status = PROCESSEDPayout of <amount> processed{ payoutId, amount } (amount as string for decimal precision)
follower_newBioEmailSubscriber where bioPage.creatorId AND confirmed = true<name ?? 'Someone'> subscribed to your bio{ subscriberId }

Item fields

FieldTypeNotes
typestringOne of the three values above. Render-by-type, not text parsing.
titlestringServer-rendered English. Localize client-side off type + meta if needed.
timestampstring (ISO 8601)Source-specific timestamp: Message.createdAt / PayoutRequest.processedAt ?? createdAt / BioEmailSubscriber.subscribedAt
metaobjectType-dependent extras (see table). Optional — absent in the rare case of no extras.

Errors

HTTPcode / i18nKeyReason
401(guard)Missing / invalid bearer token
404creator.activity.not_foundNo CreatorProfile for this user

Side effects

  1. Lookup CreatorProfile.id by userId; throw not_found if missing.
  2. Clamp limit to [1, 20] (safeLimit).
  3. In parallel (Promise.all):
    • Message.findMany({ where: receiverId, orderBy createdAt desc, take safeLimit, select id + createdAt + sender { displayName, username } }).
    • PayoutRequest.findMany({ where creatorId AND status PROCESSED, orderBy processedAt desc, take safeLimit, select id + amount + processedAt + createdAt }).
    • BioEmailSubscriber.findMany({ where bioPage.creatorId AND confirmed, orderBy subscribedAt desc, take safeLimit, select id + name + subscribedAt }).
  4. Normalize all rows to the common { type, title, timestamp, meta? } shape.
  5. Sort by timestamp DESC; slice to safeLimit.
  6. Convert Date timestamps to ISO 8601 strings; return { items }. No mutations.

Code samples

curl 'https://api.bio.re/api/v1/creators/dashboard/activity?limit=10' \
  -H "Authorization: Bearer $ACCESS_TOKEN"
type ActivityType = 'message_received' | 'payout_processed' | 'follower_new';

type ActivityItem = {
  type: ActivityType;
  title: string;
  timestamp: string;
  meta?: Record<string, unknown>;
};

async function getActivity(accessToken: string, limit = 10): Promise<ActivityItem[]> {
  const url = new URL('https://api.bio.re/api/v1/creators/dashboard/activity');
  url.searchParams.set('limit', String(limit));
  const res = await fetch(url, {
    headers: { Authorization: `Bearer ${accessToken}` },
  });
  const json = await res.json();
  if (!res.ok || !json.success) {
    throw Object.assign(new Error(json?.error?.message ?? 'Activity fetch failed'), {
      code: json?.error?.code,
    });
  }
  return json.data.items;
}
import { useQuery } from '@tanstack/react-query';

export const creatorKeys = {
  activity: (limit: number) => ['creators', 'dashboard', 'activity', limit] as const,
};

export function useActivity(limit = 10) {
  return useQuery({
    queryKey: creatorKeys.activity(limit),
    queryFn: async () => {
      const url = new URL('/api/v1/creators/dashboard/activity', window.location.origin);
      url.searchParams.set('limit', String(limit));
      const res = await fetch(url);
      const json = await res.json();
      if (!res.ok || !json.success) {
        throw Object.assign(new Error(json?.error?.message ?? 'Activity fetch failed'), {
          code: json?.error?.code,
          i18nKey: json?.error?.i18nKey,
        });
      }
      return json.data.items as ActivityItem[];
    },
    staleTime: 30_000,
  });
}

Try it

GET
/api/v1/creators/dashboard/activity
AuthorizationBearer <token>

In: header

Query Parameters

limit*number

Response Body

application/json

application/json

application/json

curl -X GET "https://loading/api/v1/creators/dashboard/activity?limit=0"
{
  "success": true,
  "data": {
    "items": [
      {
        "type": "message_received",
        "title": "New message from @johndoe",
        "timestamp": "2019-08-24T14:15:22Z",
        "meta": {}
      }
    ]
  }
}
{
  "success": false,
  "error": {
    "code": "AUTH_UNAUTHORIZED",
    "message": "Invalid credentials",
    "i18nKey": "auth.login.invalid_credentials",
    "i18nVars": {
      "field": "email"
    },
    "details": [
      {
        "message": "email must be an email"
      }
    ],
    "correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  }
}
{
  "success": false,
  "error": {
    "code": "AUTH_UNAUTHORIZED",
    "message": "Invalid credentials",
    "i18nKey": "auth.login.invalid_credentials",
    "i18nVars": {
      "field": "email"
    },
    "details": [
      {
        "message": "email must be an email"
      }
    ],
    "correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  }
}

Source

SourcePathLines
Controllerapps/api-core/src/modules/creator/creator.controller.ts112–122 (getActivity)
DTO (response)apps/api-core/src/modules/creator/dto/creator-client-response.dto.ts437–440 (ActivityResponseDto), 423–435 (nested ActivityItemDto)
Serviceapps/api-core/src/modules/creator/creator.service.ts982–1059 (getActivity)
Prisma modelspackages/prisma/prisma/schema.prismaMessage (with User join via sender), PayoutRequest (status PROCESSED), BioEmailSubscriber (confirmed = true), BioPage (relation), CreatorProfile

On this page