Get Privacy Policy
Public read of the platform privacy policy. Content is admin-managed via ConfigService. Returns the body plus the last-updated timestamp.
GET /api/v1/legal/privacy — 🌐 Public
Returns the platform's privacy policy as a single document. Content is admin-managed via the legal.privacy_policy ConfigService key — when the admin updates the policy, the next read serves the new content. The lastUpdated timestamp comes from the config definition's updatedAt field, so the client can show "Last updated: ..." in the UI.
Admin-managed catalog. The policy text lives in ConfigService (admin → "Legal docs" pane). Versioning + change-tracking are platform concerns; this endpoint is read-only and always serves the current version. To track user consent across versions, see POST /users/consent (in the User module).
Default fallback when unconfigured. If the admin has never set legal.privacy_policy, the response carries content: 'Privacy policy not yet published.' and lastUpdated: null. Treat null lastUpdated as "no policy on file" and gate behavior accordingly (e.g. don't show the consent gate until a real policy exists).
Request
No body, no params, no headers required.
Response
200 OK — ApiResponseOf<LegalDocumentDto>
{
"success": true,
"data": {
"content": "<p>Privacy Policy content...</p>",
"lastUpdated": "2026-04-29T20:00:00.000Z"
}
}| Field | Type | Notes |
|---|---|---|
content | string | HTML body of the policy. Render with sanitization (e.g. DOMPurify) if untrusted-author surface is a concern; the platform admins are the only authors here, but defense-in-depth is fine. |
lastUpdated | string (ISO 8601) | null | Timestamp of the most recent admin update, derived from ConfigService.getEffective(key).definition.updatedAt. null when the config key has never been written. |
Errors
This endpoint has no documented error responses beyond HTTP infrastructure failures (5xx). No 4xx — even unconfigured keys return 200 with the default fallback.
Side effects
configService.getWithDefault('legal.privacy_policy', 'Privacy policy not yet published.').configService.getEffective('legal.privacy_policy'). If the result hasdefinition.updatedAt, format as ISO; otherwiselastUpdated = null(with a warn log).- Return
{ content, lastUpdated }. No mutations, no auth, no per-user state.
Code samples
curl https://api.bio.re/api/v1/legal/privacytype LegalDocument = {
content: string;
lastUpdated: string | null;
};
async function getPrivacyPolicy(): Promise<LegalDocument> {
const res = await fetch('https://api.bio.re/api/v1/legal/privacy');
const json = await res.json();
if (!res.ok || !json.success) {
throw Object.assign(new Error(json?.error?.message ?? 'Privacy policy fetch failed'), {
code: json?.error?.code,
});
}
return json.data;
}import { useQuery } from '@tanstack/react-query';
export const legalKeys = {
privacy: () => ['legal', 'privacy'] as const,
};
export function usePrivacyPolicy() {
return useQuery({
queryKey: legalKeys.privacy(),
queryFn: async () => {
const res = await fetch('/api/v1/legal/privacy');
const json = await res.json();
if (!res.ok || !json.success) {
throw Object.assign(new Error(json?.error?.message ?? 'Privacy policy fetch failed'), {
code: json?.error?.code,
i18nKey: json?.error?.i18nKey,
});
}
return json.data as LegalDocument;
},
// Policies change rarely — cache aggressively
staleTime: 60 * 60_000, // 1 hour
gcTime: 24 * 60 * 60_000, // 1 day
});
}Try it
Response Body
application/json
curl -X GET "https://loading/api/v1/legal/privacy"{
"success": true,
"data": {
"content": "<p>Privacy Policy content...</p>",
"lastUpdated": "2019-08-24T14:15:22Z"
}
}Source
| Source | Path | Lines |
|---|---|---|
| Controller | apps/api-core/src/modules/legal/legal.controller.ts | 14–19 (getPrivacyPolicy) |
| DTO (response) | apps/api-core/src/modules/legal/dto/legal-response.dto.ts | 7–13 (LegalDocumentDto) |
| Service | apps/api-core/src/modules/legal/legal.service.ts | 10–20 (getPrivacyPolicy), 34–43 (getLastUpdated) |
| Config keys | (admin-managed via ConfigService) | legal.privacy_policy (string content; default 'Privacy policy not yet published.') |
Get Namespace Bundle
Single-namespace translation bundle. Like the full bundle but scoped to one namespace and with non-prefixed keys. Same ETag + version-pinning semantics.
Get Terms of Service
Public read of the platform terms of service. Content is admin-managed via ConfigService. Same shape as the privacy policy endpoint.