BIO.RE
Content

Get Cookie Consent Status

Public endpoint that works for both anonymous and authenticated users. Returns the current policy version, the user's last-saved version (null for anonymous), and a flag telling the UI whether to re-show the consent banner.

GET /api/v1/consent/cookies/status โ€” ๐ŸŒ Public ยท Rate limit: 10 req / minute

Drives the cookie banner. Works for both anonymous and authenticated users โ€” the bearer token is optional. Returns the current platform privacy version (legal.privacy_version admin config, default '1.0'), the user's last-saved consent version (or null if anonymous / never consented), and requiresReConsent: true when the banner should be shown.

Banner display rule: requiresReConsent === true in three cases:

  1. Anonymous request (no bearer) โ†’ always.
  2. Authenticated user, never consented โ†’ userVersion = null.
  3. Authenticated user consented to an older version (userVersion !== currentVersion) โ†’ policy bumped.

When admin updates legal.privacy_version, every existing user's userVersion !== currentVersion until they re-consent.

Tied to privacy policy version, not cookie-specific. The documentVersion stored on ConsentRecord is the same legal.privacy_version config used by AuthService at registration. Cookie consent and privacy consent share a version axis โ€” if you bump the privacy policy, the cookie banner re-shows too. (Designed this way: cookies are a substantive part of the privacy policy.)

Request

No body, no params.

HeaderRequiredNotes
Authorization: Bearer <accessToken>optionalWhen present, fetches the user's saved consent. When absent, treats as anonymous (always requiresReConsent: true).

Response

200 OK

{
  "success": true,
  "data": {
    "currentVersion": "1.2",
    "userVersion": "1.0",
    "requiresReConsent": true
  }
}

(Not strongly DTO-typed โ€” controller returns the bare object; the response interceptor wraps it in { success, data }.)

FieldTypeNotes
currentVersionstringCurrent platform privacy version from admin config legal.privacy_version (default '1.0')
userVersionstring | nullThe user's last-saved ConsentRecord.documentVersion for documentType = 'COOKIE_CONSENT'. null for anonymous OR authenticated-never-consented.
requiresReConsentbooleanuserVersion !== currentVersion โ€” true when the banner should be shown

Errors

HTTPcode / i18nKeyReason
429(throttle)Rate limit exceeded (10 req/min)

Side effects

  1. Read legal.privacy_version from ConfigService (default '1.0').
  2. Anonymous (no userId) โ†’ userVersion = null. Skip DB lookup.
  3. Authenticated โ€” consentRecord.findFirst({ where: { userId, documentType: 'COOKIE_CONSENT' }, orderBy: createdAt desc, select: { documentVersion } }). Use latest?.documentVersion ?? null.
  4. Compute requiresReConsent = (userVersion !== currentVersion).
  5. Return { currentVersion, userVersion, requiresReConsent }. No mutations.

Code samples

curl https://api.bio.re/api/v1/consent/cookies/status
# โ†’ requiresReConsent always true for anonymous
curl https://api.bio.re/api/v1/consent/cookies/status \
  -H "Authorization: Bearer $ACCESS_TOKEN"
type CookieConsentStatus = {
  currentVersion: string;
  userVersion: string | null;
  requiresReConsent: boolean;
};

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

export const consentKeys = {
  cookieStatus: (signedIn: boolean) => ['consent', 'cookies', 'status', signedIn] as const,
};

export function useCookieStatus(signedIn: boolean) {
  return useQuery({
    queryKey: consentKeys.cookieStatus(signedIn),
    queryFn: async () => {
      // Browser auto-attaches the auth cookie/token via shared fetch wrapper
      const res = await fetch('/api/v1/consent/cookies/status');
      const json = await res.json();
      if (!res.ok || !json.success) {
        throw Object.assign(new Error(json?.error?.message ?? 'Status fetch failed'), {
          code: json?.error?.code,
          i18nKey: json?.error?.i18nKey,
        });
      }
      return json.data as CookieConsentStatus;
    },
    // Refresh on focus โ€” admin may have bumped the policy version
    staleTime: 60_000,
    refetchOnWindowFocus: true,
  });
}

Try it

GET
/api/v1/consent/cookies/status

Response Body

application/json

curl -X GET "https://loading/api/v1/consent/cookies/status"
Empty
{
  "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/content/cookie-consent.controller.ts58โ€“85 (getStatus)
Config(admin-managed via ConfigService)legal.privacy_version (default '1.0')
Prisma modelpackages/prisma/prisma/schema.prismaConsentRecord (filter documentType = 'COOKIE_CONSENT', ordered by createdAt desc)

On this page