BIO.RE
Creator

Track Bio Page View

Append a view record. Server parses User-Agent and resolves country from CDN headers when the client doesn't supply them. Creator-side analytics aggregate from this stream.

POST /api/v1/bio/:bioPageId/view — 🌐 Public · Rate limit: 60 req / minute

Appends a BioPageView row for analytics. Public — no auth required (the bio page itself is public). The server always re-derives userAgent from the request header (client-supplied is ignored), and falls back to server-side UA parsing for device / browser and GeoIP + CDN-header lookup for country when the client doesn't supply them.

Server-side override semantics:

  • userAgentalways from the request header. The userAgent field in the body is read but immediately overwritten.
  • device / browser — body wins when present; otherwise UAParser-derived from the request UA header.
  • country — body wins when present; otherwise GeoIP lookup against the resolved client IP, falling back to the platform CDN's country header (e.g. cf-ipcountry style header).

Don't rely on tampered values to be persisted — the server treats the request headers as truth for things it can derive itself.

IP handling is privacy-preserving. The raw client IP is resolved (priority: cf-connecting-ip style CDN header → first entry of x-forwarded-forreq.ip), used only for the GeoIP lookup, and never persisted or logged on failure paths. The BioPageView row stores country (a 2-letter code), not the IP.

Request

Path parameters

ParamTypeValidationNotes
bioPageIdstring (UUID)ParseUUIDPipeThe page being viewed

Body — TrackViewDto

All fields optional. Send what your client knows; the server fills the rest.

FieldTypeValidationNotes
visitorIdstringIsString()Stable client-generated id (e.g. cookie / localStorage UUID) — used to de-duplicate "unique visitors"
referrerstringIsString()The referring URL the client navigated from
userAgentstringIsString()Ignored — server overwrites from request header
countrystringIsString()2-letter country code; if absent, server resolves via GeoIP / CDN header
devicestringIsString()mobile / tablet / desktop; if absent, server derives from UA
browserstringIsString()Browser name; if absent, server derives from UA
isEmbedbooleanIsBoolean()Set to true when fired from inside the iframe embed (so analytics can split embed vs direct)

No headers required.

Response

200 OKApiResponseOf<TrackResponseDto>

{
  "success": true,
  "data": {
    "tracked": true
  }
}
FieldTypeNotes
trackedbooleanAlways true on 200 — the row was created

Errors

HTTPcode / i18nKeyReason
400(validation)bioPageId not a valid UUID; field type mismatches
429(throttle)Rate limit exceeded (60 req/min)

Side effects

  1. Server UA derivation: read req.headers['user-agent']; parse via UAParser:
    • device = body.device ?? (parsed.device.type === 'mobile' ? 'mobile' : 'tablet' if tablet else 'desktop').
    • browser = body.browser ?? parsed.browser.name.
  2. Country resolution (when body.country absent):
    • Resolve client IP: prefer cf-connecting-ip style CDN header, fall back to first entry of x-forwarded-for, fall back to req.ip.
    • GeoIP lookup against the resolved IP (failures swallowed — raw IP never logged).
    • Fallback: read the platform CDN's country header (e.g. cf-ipcountry style) when GeoIP returned nothing.
  3. prisma.bioPageView.create({ id, bioPageId, visitorId, referrer, userAgent, country, device, browser, isEmbed: false })isEmbed defaults to false if omitted.
  4. Return { tracked: true }. No mutations beyond the single insert.

Code samples

curl -X POST 'https://api.bio.re/api/v1/bio/b1a2b3c4-d5e6-7890-abcd-ef1234567890/view' \
  -H 'Content-Type: application/json' \
  -d '{
    "visitorId": "cookie-uuid-12345",
    "referrer": "https://twitter.com/johndoe",
    "isEmbed": false
  }'
type TrackViewInput = {
  visitorId?: string;
  referrer?: string;
  country?: string;       // server resolves if absent
  device?: string;        // server derives from UA if absent
  browser?: string;       // server derives from UA if absent
  isEmbed?: boolean;
  // userAgent: omit — server always uses request header
};

async function trackBioView(bioPageId: string, input: TrackViewInput = {}): Promise<void> {
  const res = await fetch(`https://api.bio.re/api/v1/bio/${bioPageId}/view`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(input),
  });
  // Fire-and-forget pattern is safe — analytics shouldn't block UI
  if (!res.ok) {
    console.warn('Bio view tracking failed:', res.status);
  }
}
import { useMutation } from '@tanstack/react-query';

export function useTrackBioView() {
  return useMutation({
    mutationFn: async (vars: { bioPageId: string; input?: TrackViewInput }) => {
      await fetch(`/api/v1/bio/${vars.bioPageId}/view`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(vars.input ?? {}),
      });
      // Don't throw — analytics failures should not surface to the user
    },
    retry: false, // Already a fire-and-forget — don't double-count on retry
  });
}

Try it

POST
/api/v1/bio/{bioPageId}/view

Path Parameters

bioPageId*string

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

curl -X POST "https://loading/api/v1/bio/string/view" \  -H "Content-Type: application/json" \  -d '{}'
{
  "success": true,
  "data": {
    "tracked": true
  }
}
{
  "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/bio-analytics.controller.ts72–111 (trackView — UA + GeoIP + CDN header logic)
DTO (request)apps/api-core/src/modules/creator/dto/bio-analytics.dto.ts4–12 (TrackViewDto)
DTO (response)apps/api-core/src/modules/creator/dto/bio-analytics-response.dto.ts214–217 (TrackResponseDto)
Serviceapps/api-core/src/modules/creator/bio-analytics.service.ts180–202 (trackView)
GeoIPpackages/geoip/GeoIPService.lookup()
Prisma modelpackages/prisma/prisma/schema.prismaBioPageView

On this page