Get Creator Dashboard
Earnings + level + wallet + DM + response-rate summary in a single response. Computed from CreatorProfile, Wallet, PayoutRequest, Message, SocialAccount, and ConfigService thresholds.
GET /api/v1/creators/dashboard — 🔑 Bearer
Returns the full creator dashboard payload — level + commission rate, lifetime totals, wallet balance + frozen flag, pending payouts count, DM config, response rate (computed from message statuses), next-level progress + remaining requirements, and hasSocialAccounts flag for the onboarding nudge. Heavy aggregate read — call once on dashboard mount and cache.
commissionRate is admin-managed. Per-level commission rates are stored in ConfigService keys (creator.commission_bronze, creator.commission_silver, etc) with a 20% default. The response includes the rate that applies to this creator's current level — not a list of all rates.
Response rate semantics. responseRate = round((COMPLETED + REPLIED) / (COMPLETED + REPLIED + REJECTED + EXPIRED) * 100). Pending / in-flight messages are not counted because the creator hasn't yet had a chance to act. Returns 100 when there are zero resolvable messages (so a brand-new creator doesn't start at 0%).
Request
No body, no params.
| Header | Required | Notes |
|---|---|---|
Authorization: Bearer <accessToken> | ✓ | JWT from POST /auth/login |
Response
200 OK — ApiResponseOf<DashboardResponseDto>
{
"success": true,
"data": {
"level": "BRONZE",
"commissionRate": 20,
"totalMessages": 47,
"totalEarnings": "1250.00",
"walletBalance": "350.00",
"walletFrozen": false,
"pendingPayouts": 1,
"dmActive": true,
"dmType": "SINGLE_PAY",
"dmPrice": "5.00",
"responseRate": 95,
"nextLevel": "SILVER",
"nextLevelProgress": 47,
"nextLevelRequirements": {
"messages": { "current": 47, "required": 100 },
"earnings": { "current": 1250, "required": 5000 }
},
"hasSocialAccounts": true,
"avatarUrl": "https://cdn.bio.re/avatars/abc.webp"
}
}Fields
| Field | Type | Notes |
|---|---|---|
level | enum | BRONZE / SILVER / GOLD / PLATINUM |
commissionRate | number | Percentage (e.g. 20 = 20%). Pulled from creator.commission_<level> admin config; defaults to 20 if unset. |
totalMessages | number | Lifetime message count (CreatorProfile.totalMessages) |
totalEarnings | string (decimal) | Lifetime earnings (decimal as string for precision) |
walletBalance | string (decimal) | number | From Wallet.balance where type = CREATOR; falls back to 0 if no wallet row exists |
walletFrozen | boolean | Wallet.frozen — true blocks payout requests |
pendingPayouts | string | number | Count of PayoutRequest rows with status = PENDING for this creator |
dmActive / dmType / dmPrice | boolean / enum | null / string | null | Mirrors CreatorProfile.dm* fields |
responseRate | number | Integer 0–100 (see Response rate callout above) |
nextLevel | enum | null | One step above level; null when already PLATINUM |
nextLevelProgress | number | Integer 0–100. Computed as min(messageProgress, earningsProgress) — both gates must be met. |
nextLevelRequirements | object | null | { messages: { current, required }, earnings: { current, required } }. null when nextLevel is null. |
hasSocialAccounts | boolean | true if count(SocialAccount where userId) > 0 |
avatarUrl | string | null | Mirror of User.avatarUrl |
Errors
| HTTP | code / i18nKey | Reason |
|---|---|---|
401 | (guard) | Missing / invalid bearer token |
404 | creator.dashboard.not_found | No CreatorProfile for this user — call POST /creators/upgrade first |
Side effects
- Lookup
CreatorProfilebyuserId; thrownot_foundif missing. - In parallel (
Promise.all): readWallet, countPayoutRequest PENDING, readUser.avatarUrl, countSocialAccount, count messages withstatus IN (COMPLETED, REPLIED)("responded"), count messages withstatus IN (COMPLETED, REPLIED, REJECTED, EXPIRED)("resolvable"). - Read
creator.commission_<level>fromConfigService(default 20). - Compute
responseRate = totalResolvable > 0 ? round(responded / totalResolvable * 100) : 100. - Compute
nextLevel+nextLevelProgress+nextLevelRequirementsviacalculateLevelProgress():- Reads
creator.<nextLevel>_min_messages+creator.<nextLevel>_min_earningsfromConfigService(with built-in defaults per level). - Progress =
min(messageProgress, earningsProgress)(both gates).
- Reads
- Return assembled object. No mutations — pure read.
Code samples
curl https://api.bio.re/api/v1/creators/dashboard \
-H "Authorization: Bearer $ACCESS_TOKEN"type CreatorLevel = 'BRONZE' | 'SILVER' | 'GOLD' | 'PLATINUM';
type LevelRequirementDetail = {
current: number;
required: number;
};
type Dashboard = {
level: CreatorLevel;
commissionRate: number;
totalMessages: number;
totalEarnings: string;
walletBalance: string | number;
walletFrozen: boolean;
pendingPayouts: string | number;
dmActive: boolean;
dmType: 'FREE' | 'SINGLE_PAY' | 'PER_MESSAGE' | null;
dmPrice: string | null;
responseRate: number;
nextLevel: CreatorLevel | null;
nextLevelProgress: number;
nextLevelRequirements: { messages: LevelRequirementDetail; earnings: LevelRequirementDetail } | null;
hasSocialAccounts: boolean;
avatarUrl: string | null;
};
async function getDashboard(accessToken: string): Promise<Dashboard> {
const res = await fetch('https://api.bio.re/api/v1/creators/dashboard', {
headers: { Authorization: `Bearer ${accessToken}` },
});
const json = await res.json();
if (!res.ok || !json.success) {
throw Object.assign(new Error(json?.error?.message ?? 'Dashboard fetch failed'), {
code: json?.error?.code,
});
}
return json.data;
}import { useQuery } from '@tanstack/react-query';
export const creatorKeys = {
dashboard: () => ['creators', 'dashboard'] as const,
};
export function useDashboard() {
return useQuery({
queryKey: creatorKeys.dashboard(),
queryFn: async () => {
const res = await fetch('/api/v1/creators/dashboard');
const json = await res.json();
if (!res.ok || !json.success) {
throw Object.assign(new Error(json?.error?.message ?? 'Dashboard fetch failed'), {
code: json?.error?.code,
i18nKey: json?.error?.i18nKey,
});
}
return json.data as Dashboard;
},
staleTime: 30_000, // 30s — dashboard is action-driven
});
}Try it
Authorization
bearer In: header
Response Body
application/json
application/json
application/json
curl -X GET "https://loading/api/v1/creators/dashboard"{
"success": true,
"data": {
"level": "BRONZE",
"commissionRate": 0.2,
"totalMessages": 47,
"totalEarnings": "1250.00",
"walletBalance": "0.00",
"walletFrozen": true,
"pendingPayouts": "0.00",
"dmActive": true,
"dmType": "FREE",
"dmPrice": "5.00",
"responseRate": 95.5,
"nextLevel": "BRONZE",
"nextLevelProgress": 0.47,
"nextLevelRequirements": {
"messages": {
"current": 47,
"required": 100
},
"earnings": {
"current": 47,
"required": 100
}
},
"hasSocialAccounts": true,
"avatarUrl": "string"
}
}{
"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
| Source | Path | Lines |
|---|---|---|
| Controller | apps/api-core/src/modules/creator/creator.controller.ts | 94–101 (getDashboard) |
| DTO (response) | apps/api-core/src/modules/creator/dto/creator-client-response.dto.ts | 343–391 (DashboardResponseDto), 321–339 (nested LevelRequirementDetailDto, NextLevelRequirementsDto) |
| Service | apps/api-core/src/modules/creator/creator.service.ts | 750–812 (getCreatorDashboard), 818–858 (calculateLevelProgress) |
| Config keys | apps/api-core/src/modules/config/config.service.ts | creator.commission_<level>, creator.<level>_min_messages, creator.<level>_min_earnings (admin-managed) |
| Prisma models | packages/prisma/prisma/schema.prisma | CreatorProfile, Wallet, PayoutRequest, Message (statuses COMPLETED/REPLIED/REJECTED/EXPIRED), SocialAccount, User.avatarUrl |
List Connected Social Accounts
Read up to 50 connected social accounts for the current creator. Returns platform, public username, verification flag, and connection timestamp. OAuth tokens are NOT exposed.
Get Setup Checklist
Six-item creator onboarding checklist with completion flags. Static set today (avatar, bio, link, DM config, KYC, social) — drives the post-upgrade getting-started UI.