BIO.RE
Referral

Get Referral Link

Get-or-create the calling user's personal referral link. First call generates a code from their username (or random UUID slice on collision); subsequent calls return the existing one.

GET /api/v1/referral/link โ€” ๐Ÿ”‘ Bearer ยท Kill-switched

Returns the calling user's personal referral link. Get-or-create: first read generates a ReferralLink row (code derived from User.username, falling back to an 8-char UUID slice on collision); every subsequent read returns the existing row. There is at most one active ReferralLink per user (uniqueness enforced on ReferralLink.userId).

Cross-table uniqueness. The ReferralLink.code must NOT collide with any User.referralCode (a separate column on the User table โ€” also a referral identifier) or any ReferralLinkAlias.code (preserved old codes from previous renames). The server retries up to 3 times with random UUID slices on collision; if all 3 retry, the request fails with 400 code_collision.

Username drives the default code AND tracks renames. If the user's username is alice123, their referral link will be bio.re/ref/alice123. When they later rename to alice456, the live ReferralLink.code is repointed to alice456 and the previous code is preserved as a ReferralLinkAlias โ€” both bio.re/ref/alice123 and bio.re/ref/alice456 keep resolving to the same ReferralLink. See Change Username ยท Referral code side-effect for the rules and the collision skip behavior.

Request

No body, no params.

HeaderRequiredNotes
Authorization: Bearer <accessToken>โœ“JWT from POST /auth/login

Response

200 OK โ€” ApiResponseOf<ReferralLinkCodeResponseDto>

{
  "success": true,
  "data": {
    "code": "alice123",
    "link": "bio.re/ref/alice123"
  }
}
FieldTypeNotes
codestringThe referral identifier โ€” pass to POST /referral/click/:code for tracking
linkstringThe full shareable URL โ€” bio.re/ref/<code>

Errors

HTTPcode / i18nKeyReason
400referral.link.code_collisionFailed to generate a unique code after 3 retries (rare โ€” would require username-vs-other-user collision plus 2 UUID slice collisions in a row)
401(guard)Missing / invalid bearer token
503features.referral_disabledAdmin kill switch REFERRAL is active

Side effects

  1. referralLink.findUnique({ where: { userId } }). Existing row โ†’ return { code, link } directly.
  2. No row โ€” load User.username for the candidate code; fallback to randomUUID().slice(0, 8) if null.
  3. Cross-table collision check (up to 3 attempts), run as one Promise.all:
    • user.findUnique({ where: { referralCode: code } }) โ€” clash with someone's User.referralCode.
    • referralLinkAlias.findUnique({ where: { code } }) โ€” clash with a preserved old vanity code from a previous rename.
    • On hit, regenerate with randomUUID().slice(0, 8) and retry.
    • 3rd failure โ†’ throw code_collision.
    • Note: collision against another ReferralLink.code is enforced by the unique constraint at referralLink.create() time and surfaces as Prisma P2002; explicit pre-checks cover the cross-table cases the unique index can't.
  4. referralLink.create({ id, userId, code }).
  5. Return { code, link: 'bio.re/ref/<code>' }.

Alias resolution

Any code passed to a referral lookup (e.g. POST /referral/click/:code, plus all auth/oauth signup callers that accept a referrer code) is resolved through the canonical alias-aware helper:

  1. referralLink.findUnique({ where: { code } }) โ€” direct hit on the live code.
  2. If no direct hit: referralLinkAlias.findUnique({ where: { code }, include: { referralLink: true } }) โ€” fall back to a preserved old vanity code.
  3. Returns the underlying ReferralLink row in either case (or null if neither matches).

This means:

  • A user's link has at most one live code (ReferralLink.code) but may have multiple aliases (one per past vanity rename โ€” see Change Username ยท Referral code side-effect).
  • Old shared URLs never break and keep crediting the original referrer.
  • All counters (clicks, signups, conversions) and reward rows live on ReferralLink. Aliases carry no counters of their own โ€” they only map code โ†’ referralLinkId โ€” so click-traffic on bio.re/ref/<oldcode> and bio.re/ref/<newcode> aggregates to the same ReferralLink, with no double-counting.
  • A code currently held by any of ReferralLink.code, ReferralLinkAlias.code, or User.referralCode cannot be claimed as a new link or rename target.

Code samples

curl https://api.bio.re/api/v1/referral/link \
  -H "Authorization: Bearer $ACCESS_TOKEN"
type ReferralLink = {
  code: string;
  link: string;
};

async function getReferralLink(accessToken: string): Promise<ReferralLink> {
  const res = await fetch('https://api.bio.re/api/v1/referral/link', {
    headers: { Authorization: `Bearer ${accessToken}` },
  });
  const json = await res.json();
  if (!res.ok || !json.success) {
    throw Object.assign(new Error(json?.error?.message ?? 'Referral link fetch failed'), {
      code: json?.error?.code,
    });
  }
  return json.data;
}
import { useQuery } from '@tanstack/react-query';

export const referralKeys = {
  link: () => ['referral', 'link'] as const,
};

export function useReferralLink() {
  return useQuery({
    queryKey: referralKeys.link(),
    queryFn: async () => {
      const res = await fetch('/api/v1/referral/link');
      const json = await res.json();
      if (!res.ok || !json.success) {
        throw Object.assign(new Error(json?.error?.message ?? 'Referral link fetch failed'), {
          code: json?.error?.code,
          i18nKey: json?.error?.i18nKey,
        });
      }
      return json.data as ReferralLink;
    },
    // Code is stable per session but may change after a username rename
    // (vanity sync โ€” see /docs/user/username#referral-code-side-effect).
    // Invalidate ['referral', 'link'] from the username-change mutation's onSuccess.
    staleTime: 60 * 60_000,
    gcTime: 60 * 60_000,
  });
}

Try it

GET
/api/v1/referral/link
AuthorizationBearer <token>

In: header

Response Body

application/json

application/json

curl -X GET "https://loading/api/v1/referral/link"
{
  "success": true,
  "data": {
    "code": "alice123",
    "link": "bio.re/ref/alice123"
  }
}
{
  "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/referral/referral.controller.ts42โ€“47 (getLink)
DTO (response)apps/api-core/src/modules/referral/dto/referral-response.dto.ts298โ€“304 (ReferralLinkCodeResponseDto)
Service (get-or-create)apps/api-core/src/modules/referral/referral.service.ts34โ€“61 (getOrCreateLink)
Service (alias resolver)apps/api-core/src/modules/referral/referral.service.ts23โ€“32 (resolveLinkByCode) โ€” used by trackClick, recordSignup, and auth/oauth signup callers (apps/api-core/src/modules/auth/auth.service.ts:174,191, apps/api-core/src/modules/auth/oauth.service.ts:273)
Service (rename sync)apps/api-core/src/modules/user/user.service.ts209โ€“230 (referral block inside changeUsername transaction)
Kill switchapps/api-core/src/common/guards/kill-switch.guard.tsRequireKillSwitch('REFERRAL') (class-level on ReferralController, line 35)
Prisma modelspackages/prisma/prisma/schema.prismaReferralLink 2103โ€“2119 (unique userId, unique code, has-many aliases), ReferralLinkAlias 2121โ€“2130 (unique code, FK referralLinkId cascade), User.referralCode (cross-table uniqueness check)
Live responseMISSING โ€” to be captured by Lead before publishโ€”

On this page