BIO.RE
Creator

Get KYC Status

Read current KYC state plus the active provider id and completion timestamp. Owner-only. Provider value is opaque to clients.

GET /api/v1/creators/kyc/status — 🔑 Bearer

Returns the calling creator's current KYC state, the active provider identifier (opaque), the provider-side session id (for support trails), and the completion timestamp.

Status lifecycle. Created accounts start at NOT_STARTED. After POST /creators/kyc/start, the status flips to PENDING (or sometimes provider-side IN_REVIEW mapped to PENDING). The provider's webhook (handled internally) then flips it to APPROVED / REJECTED / EXPIRED. There is no client-driven path back to NOT_STARTED once started — re-running POST /creators/kyc/start after an expired or rejected session will start a new attempt with a fresh kycProviderId.

Request

No body, no params. Identity comes from the bearer token.

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

Response

200 OKApiResponseOf<CreatorKycStatusDto>

{
  "success": true,
  "data": {
    "status": "APPROVED",
    "provider": "<provider-id>",
    "providerId": "...",
    "completedAt": "2026-04-29T20:00:00.000Z"
  }
}
FieldTypeNotes
statusenumOne of NOT_STARTED / PENDING / APPROVED / REJECTED / EXPIRED
providerstring | nullActive KYC provider identifier (admin-managed via external.kyc.active_provider). Treat as opaque — don't branch on it, don't display it. null until first /kyc/start.
providerIdstring | nullThe provider's session id. null until first /kyc/start.
completedAtstring (ISO 8601) | nullWhen status flipped to APPROVED / REJECTED / EXPIRED. null while NOT_STARTED / PENDING.

Errors

HTTPcode / i18nKeyReason
401(guard)Missing / invalid bearer token
404error.creator.not_foundNo CreatorProfile for this user — call POST /creators/upgrade first

Side effects

  1. Lookup CreatorProfile by userId; throw not_found if missing.
  2. Delegate to creatorLevelService.getKYCStatus(creator.id)kycService.getStatus(creator.id):
    • Read CreatorProfile.kycStatus + kycProvider + kycProviderId + kycCompletedAt.
    • Return as-is. No mutations — provider state changes happen via webhook, not on read.

Polling guidance

  • Poll while status === 'PENDING' (every 5–10s while a verification dialog is open).
  • Stop polling once status is APPROVED / REJECTED / EXPIRED.
  • Don't poll forever — if a session sits PENDING for hours, surface a "still pending — refresh later" message rather than a tight loop.

Code samples

curl https://api.bio.re/api/v1/creators/kyc/status \
  -H "Authorization: Bearer $ACCESS_TOKEN"
type KycStatus = {
  status: 'NOT_STARTED' | 'PENDING' | 'APPROVED' | 'REJECTED' | 'EXPIRED';
  provider: string | null;
  providerId: string | null;
  completedAt: string | null;
};

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

export const creatorKeys = {
  kycStatus: () => ['creators', 'kyc', 'status'] as const,
};

export function useKycStatus() {
  return useQuery({
    queryKey: creatorKeys.kycStatus(),
    queryFn: async () => {
      const res = await fetch('/api/v1/creators/kyc/status');
      const json = await res.json();
      if (!res.ok || !json.success) {
        throw Object.assign(new Error(json?.error?.message ?? 'KYC status fetch failed'), {
          code: json?.error?.code,
          i18nKey: json?.error?.i18nKey,
        });
      }
      return json.data as KycStatus;
    },
    // Poll while PENDING, stop once terminal
    refetchInterval: (query) => {
      const data = query.state.data;
      if (!data) return 5_000;
      return data.status === 'PENDING' ? 5_000 : false;
    },
    staleTime: 30_000,
  });
}

Try it

GET
/api/v1/creators/kyc/status
AuthorizationBearer <token>

In: header

Response Body

application/json

application/json

application/json

curl -X GET "https://loading/api/v1/creators/kyc/status"
{
  "success": true,
  "data": {
    "status": "APPROVED",
    "provider": "sumsub",
    "providerId": "string",
    "completedAt": "2019-08-24T14:15:22Z"
  }
}
{
  "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"
  }
}
{
  "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/creator.controller.ts293–303 (getKycStatus)
DTO (response)apps/api-core/src/modules/creator/dto/creator-kyc-status-response.dto.ts7–19 (CreatorKycStatusDto)
Serviceapps/api-core/src/modules/creator/creator-level.service.ts157–159 (getKYCStatus — delegates to KycService)
KYC serviceapps/api-core/src/modules/creator/kyc/kyc.service.tsgetStatus()
Webhook (status flips happen here)apps/api-core/src/modules/creator/kyc/kyc-webhook.controller.ts(internal scope, not in this portal)
Config(admin-managed)external.kyc.active_provider ConfigService key
Prisma modelpackages/prisma/prisma/schema.prismaCreatorProfile.kycStatus, CreatorProfile.kycProvider, CreatorProfile.kycProviderId, CreatorProfile.kycCompletedAt

On this page