BIO.RE
Authentication

Validate Reactivation Token

Pre-flight check for an account reactivation token from an email link. Returns account status without performing reactivation.

GET /api/v1/auth/reactivate/validate?token=... — 🌐 Public · Rate limit: 30 req / hour

Pre-flight check: validates a reactivation token without committing reactivation. Returns the account status (paused / pending-deletion / expired / deleted) so the landing page can render appropriate UI before the user clicks the "Reactivate" button.

The actual reactivation happens via POST /users/reactivate (separate endpoint, in the user module). This endpoint is read-only — it lets the UI show "Welcome back, your account is paused — click below to reactivate" vs "This account was deleted and cannot be recovered".

Request

Query parameters

ParamTypeRequiredNotes
tokenstringOpaque reactivation token from the email link

No body, no path params, no headers.

Response

200 OKApiResponseOf<ReactivateValidateResponseDto>

{
  "success": true,
  "data": {
    "valid": true,
    "status": "paused",
    "userMaskEmail": "u***@b***.re",
    "deletionDate": null
  }
}
FieldTypeNotes
validbooleanfalse if token is malformed, unknown, used, or expired
statusenumOne of: paused / pending-deletion / expired / deleted
userMaskEmailstring | nullMasked email (single-letter prefix per maskEmail helper) of the account associated with the token
deletionDatestring (ISO 8601) | nullHard-deletion schedule — present only when status = pending-deletion

Status semantics

statusMeaningUI guidance
pausedAccount is DEACTIVATED, no pending deletion → reactivate eligible"Reactivate your account" CTA
pending-deletionAccount is DEACTIVATED + GDPRRequest DELETION pending in grace window → reactivate eligible"Cancel deletion + reactivate (deadline {deletionDate})" CTA
expiredToken signature valid but expiresAt past → user must re-request link"This link expired — request a new one"
deletedAccount purged (UserStatus.DELETED) → cannot reactivate"This account has been permanently deleted" — terminal

Errors

HTTPcode / i18nKeyReason
429(throttle)Rate limit exceeded (30 req/hour)

The endpoint always returns 200 — invalid / unknown / expired tokens come back with valid: false (not 4xx) so the landing page can render a coherent expired-link UI.

Side effects

  1. Decode + verify token signature.
  2. Look up associated User + GDPRRequest (if any).
  3. Compute status from User.status + GDPRRequest.status + expiresAt.
  4. Mask email for display.
  5. No mutations — purely a read endpoint. The reactivation itself is a separate POST.

Code samples

curl 'https://api.bio.re/api/v1/auth/reactivate/validate?token=abc123-from-email-link'
type ReactivateStatus = 'paused' | 'pending-deletion' | 'expired' | 'deleted';

type ReactivateValidate = {
  valid: boolean;
  status: ReactivateStatus;
  userMaskEmail: string | null;
  deletionDate: string | null;
};

async function validateReactivate(token: string): Promise<ReactivateValidate> {
  const url = new URL('https://api.bio.re/api/v1/auth/reactivate/validate');
  url.searchParams.set('token', token);
  const res = await fetch(url);
  const json = await res.json();
  if (!res.ok || !json.success) {
    throw Object.assign(new Error(json?.error?.message ?? 'Failed'), {
      code: json?.error?.code,
    });
  }
  return json.data;
}
import { useQuery } from '@tanstack/react-query';

export const reactivateKeys = {
  validate: (token: string) => ['auth', 'reactivate', 'validate', token] as const,
};

export function useReactivateValidate(token: string) {
  return useQuery({
    queryKey: reactivateKeys.validate(token),
    queryFn: async () => {
      const url = new URL('/api/v1/auth/reactivate/validate', window.location.origin);
      url.searchParams.set('token', token);
      const res = await fetch(url);
      const json = await res.json();
      if (!res.ok || !json.success) {
        throw Object.assign(new Error(json?.error?.message ?? 'Failed'), {
          code: json?.error?.code,
        });
      }
      return json.data as ReactivateValidate;
    },
    enabled: Boolean(token),
    retry: false, // Token state is deterministic — no point retrying
  });
}

Try it

GET
/api/v1/auth/reactivate/validate

Query Parameters

token*string

Reactivation token from email link

Response Body

application/json

curl -X GET "https://loading/api/v1/auth/reactivate/validate?token=string"
{
  "success": true,
  "data": {
    "valid": true,
    "status": "paused",
    "userMaskEmail": "u***@b***.re",
    "deletionDate": "2019-08-24T14:15:22Z"
  }
}

Source

SourcePathLines
Controllerapps/api-core/src/modules/auth/auth.controller.ts407–423 (reactivateValidate)
DTO (response)apps/api-core/src/modules/auth/dto/reactivate-validate.dto.ts34–45 (ReactivateValidateResponseDto), 21–24 (REACTIVATE_STATUSES)
Serviceapps/api-core/src/modules/auth/challenge.service.tsvalidateReactivationToken()
Prisma modelspackages/prisma/prisma/schema.prismaUser.status, GDPRRequest

On this page