BIO.RE
Creator

Track Bio Link Click

Append a click record + increment BioLink.clickCount. Two writes happen in parallel; a missing link silently no-ops the increment but still records the view.

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

Records a click on a bio link. Two writes run in parallel:

  1. BioLink.clickCount += 1 (silently swallows errors — analytics is non-critical)
  2. Append a BioPageView row with linkClicked = linkId

Both are best-effort fire-and-forget — a non-existent linkId won't fail the request; the view row is still inserted (with linkClicked referencing the bad id) for forensic visibility.

The endpoint records the click on the bio page scope, not the link scope — that's why bioPageId is the path param and linkId is in the body. A single page can hold many links, and the analytics table tracks everything against the page so the breakdowns aggregate cleanly.

Request

Path parameters

ParamTypeValidationNotes
bioPageIdstring (UUID)ParseUUIDPipeThe page hosting the clicked link

Body — TrackClickDto

FieldTypeRequiredValidationNotes
linkIdstring (UUID)IsUUID()The link that was clicked. A bad id silently no-ops the clickCount increment but the view row is still recorded.
visitorIdstringoptionalIsString()Stable client-generated id (cookie / localStorage UUID) for unique-visitor de-duplication

The DTO does not carry referrer — the controller passes it as undefined to the service and the row is inserted without it.

No headers required.

Response

200 OKApiResponseOf<TrackResponseDto>

{
  "success": true,
  "data": {
    "tracked": true
  }
}
FieldTypeNotes
trackedbooleanAlways true on 200

Errors

HTTPcode / i18nKeyReason
400(DTO validation)bioPageId not UUID; linkId missing or not UUID
429(throttle)Rate limit exceeded (60 req/min)

Side effects

In parallel (Promise.all):

  1. prisma.bioLink.update({ where: { id: linkId }, data: { clickCount: { increment: 1 } } }) — wrapped in .catch(() => {}) so a non-existent or already-deleted link doesn't fail the request.
  2. prisma.bioPageView.create({ id: randomUUID(), bioPageId, visitorId, referrer: undefined, linkClicked: linkId }).

Both writes are independent — failure of one doesn't roll back the other.

Code samples

curl -X POST 'https://api.bio.re/api/v1/bio/b1a2b3c4-d5e6-7890-abcd-ef1234567890/click' \
  -H 'Content-Type: application/json' \
  -d '{
    "linkId": "l1a2b3c4-d5e6-7890-abcd-ef1234567890",
    "visitorId": "cookie-uuid-12345"
  }'
type TrackClickInput = {
  linkId: string;
  visitorId?: string;
};

async function trackBioClick(bioPageId: string, input: TrackClickInput): Promise<void> {
  const res = await fetch(`https://api.bio.re/api/v1/bio/${bioPageId}/click`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(input),
  });
  if (!res.ok) {
    console.warn('Bio click tracking failed:', res.status);
  }
}
import { useMutation } from '@tanstack/react-query';

export function useTrackBioClick() {
  return useMutation({
    mutationFn: async (vars: { bioPageId: string; input: TrackClickInput }) => {
      await fetch(`/api/v1/bio/${vars.bioPageId}/click`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(vars.input),
      });
      // Don't throw — analytics failures must not block the actual link navigation
    },
    retry: false,
  });
}

// Usage: fire the mutation in onClick, then perform the navigation immediately
// (don't await — the click should happen even if tracking is slow)

Try it

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

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/click" \  -H "Content-Type: application/json" \  -d '{    "linkId": "009f739c-6620-43b0-978e-b245e723c57a"  }'
{
  "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.ts113–124 (trackClick)
DTO (request)apps/api-core/src/modules/creator/dto/bio-analytics.dto.ts14–17 (TrackClickDto)
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.ts207–224 (trackLinkClick)
Prisma modelspackages/prisma/prisma/schema.prismaBioLink.clickCount (counter), BioPageView (with linkClicked field)

On this page