Skip to content

Security

Keep the minimum, encrypt it, and let it expire.

KYCVerify is a hosted service at kycverify.me. The controls below are what the service does, taken from its code. We hold no security certification and this page claims none.

Architecture

How the service is built

Browsers talk only to the Next.js app at kycverify.me, which proxies /api to the Rust API on the same host. Your backend calls https://kycverify.me/api with a key. Vision models and the sanctions index run in-process, so no third-party verification or model API sees an image. The only outbound calls are one-time code emails, your webhook URL and the official sanctions-list downloads.

  • End user's browser

    hosted flow at kycverify.me/verify/{token}

  • Console users

    reviewers, admins, owners

  • Your backend

    x-api-key calls to kycverify.me/api/v1/…

KYCVerify · kycverify.me

HTTPS front

terminates TLS; /api → Rust API, everything else → Next.js

Next.js app

console, hosted flow; proxies /api/* to the API

kyc-api

axum; engine, webhook, AML and retention workers

  • SQLite

    data/kyc.db

  • Uploads

    AES-256-GCM, data/files

  • Models

    YuNet, SFace, ocrs

  • UIDAI certs

    certs/uidai

Only outbound calls

  • Email codes

    from no-reply@kycverify.me, if the email step is on

  • Your webhook URL

    signed; private addresses refused

  • OFAC and UN list URLs

    public sanctions files; checked every 6 h, re-downloaded when older than 7 days

Everything inside the dashed line is the KYCVerify service at kycverify.me. No third-party verification API, no external model API.

Controls

What the software does to protect data.

Built in and on by default, for every organisation.

  • Encryption at rest

    Uploads are encrypted with AES-256-GCM before they reach disk, under a master key kept outside the database, and served only to signed-in console users of the owning organisation, decrypted per request with cache-control: private, no-store.

  • Secrets are hashed

    Passwords use Argon2id. API keys, console session tokens and hosted-flow tokens are stored as SHA-256 hashes; keys are compared in constant time. A leaked database does not leak working credentials.

  • Minimal identity

    Full document and Aadhaar numbers are never stored in clear fields. Identities keep a masked number and a keyed hash; Aadhaar keeps the last four digits. Card images are encrypted and purged on schedule.

  • Per-organisation isolation

    Owner, admin, reviewer and viewer roles. Every console query is scoped to the caller's organisation and every API query to the key's app, so one customer's sessions are never visible to another.

  • Audit log

    Sign-ins, API key creation and revocation, workflow changes, review decisions, purges, team changes and AML list refreshes are recorded.

  • Retention and erasure

    A worker purges files, embeddings and identity data after each app's retention period (90 days by default). Any session can be purged on demand.

  • Signed webhooks

    HMAC-SHA256 over a timestamp and the raw body, with a 5-minute replay window for receivers and a per-app secret you can rotate.

  • Abuse limits

    60 requests a minute per hosted-flow token, 10 sign-in attempts a minute per IP and email, 10 MB uploads sniffed by content, images over 8,192 pixels a side or 24 megapixels refused before decoding.

Encryption at rest

Every upload sealed to its own name.

Documents, selfies, liveness frames and Aadhaar files are encrypted before they touch disk, with the file's id bound into the authentication tag.

  • AES-256-GCM with a fresh random 96-bit nonce per file.
  • The file id is the associated data: copy a file over another on disk and it fails authentication instead of showing the wrong person.
  • Decrypted only for a signed-in console user, per request, sent with cache-control: private, no-store. The public API never returns images.
  • Face embeddings and identities live in the database; document and Aadhaar numbers are stored only masked, plus a keyed fingerprint. A photographed card shows its full number, so its image is encrypted like every other file and purged on schedule.

data/files/ses_…/fil_01JD….bin

Key

The service's master key: 32 bytes, kept outside the database

Associated data

the file id (fil_…): a file moved or swapped on disk fails to decrypt

Served

only to signed-in console users, decrypted per request, private, no-store

One upload on disk. Without the service's master key it is noise; with the key but under another file id, it fails to decrypt.

Secrets and tokens

A copy of the database is not a set of keys.

Everything that grants access is random, and almost everything is stored only as a hash.

Every credential in KYCVerify, its form and how it is stored
CredentialFormStored asNotes
Console passwordchosen by the userArgon2id hash with a random saltVerified with the Argon2 crate; never logged or returned.
API keykyc_test_ / kyc_live_ + 24 random bytesFirst 12 characters + SHA-256Shown once. Compared by hash; revocation is immediate.
Console session32 random bytes in an HttpOnly, SameSite=Lax cookieSHA-2567 days. Secure flag always set (HTTPS only).
Hosted-flow link token32 random bytes, URL-safeSHA-256Dies with the session: expiry, submit or decision.
One-time email code6 digitsSHA-256 with the session id10 minutes, 5 tries, 5 sends per session.
Webhook secretwhsec_ + 24 random bytesServer-side, to sign deliveriesShown masked (whsec_****1a2b); rotation is audited.
Master key32 bytes, base64Only in the environment, never in the databaseThe API refuses to start without it outside development.

Access control and audit

Four roles, two walls, one ledger.

Every console query is scoped to the caller's organisation and every API query to the key's app. A session in another app is simply not found.

Roles and audit events
What each console role may do; each role includes the ones before it
PermissionViewerReviewerAdminOwner
Read sessions, decisions, evidence, statistics and the audit log
Decide reviews, create verification links, screen names–
Apps, API keys, workflows, webhooks, AML refresh, purge––
Team members and roles–––

Audited: sign-ins and failed sign-ins, key creation and revocation, app and workflow changes, webhook secret rotation, tests and redeliveries, review decisions, purges, team changes and AML refreshes, each with actor, target and IP.

Outbound requests

Webhooks that cannot be aimed inward.

  • Outside development mode, the webhook host is resolved before connecting; any loopback, private, link-local, CGNAT (100.64.0.0/10), unspecified or otherwise non-public address is refused.
  • The connection is pinned to the addresses that passed, so a DNS rebinding answer cannot slip in between check and connect.
  • Redirects are never followed and each attempt times out after 10 seconds.
  • Deliveries are signed with HMAC-SHA256 over a timestamp and the raw body; receivers reject anything older than 5 minutes.

Abuse limits

Rate and size limits.

Hosted-flow requests
60 / minute / link token
Console sign-in
10 / minute / IP + email
Failed sign-ins
20 / hour / account
Sign-up
20 / hour / IP · 200 / hour across the service
Email codes
10 / hour / address · 50 sandbox, 500 live / hour / org
Upload size
10 MB each, sniffed by content
Image dimensions
8,192 px a side, 24 megapixels, checked before decoding
Request body
40 MB

The public /v1 API has no per-key rate limit today. If you plan sustained high volumes, tell us first.

Retention and erasure

Evidence expires on schedule.

Each app keeps evidence for its own retention period, 90 days unless you change it. A worker checks every 10 minutes.

  1. Purged

    Encrypted files, face embeddings, document fingerprints, identity, contact email, IP and user agent, review notes, warning text, and the Decision inside stored webhook deliveries.

  2. Kept

    The session row with status, decision reason, timestamps and purged_at, so references and statistics stay whole.

  3. On request

    DELETE /v1/sessions/{id} or the console purges one session at once, and the purge is audited.

Your part

Integrating it securely.

Some safeguards live on your side of the API. They are short.

KYCVerify holds no ISO 27001, SOC 2 or PAD certification, and this page makes no compliance claim. It does not promise a data-residency region either. Ask us if your review needs a specific answer.

  1. 01Call the API only from your backend. Keep kyc_live_ keys in your secret store and never ship one to a browser or app.
  2. 02Verify x-kyc-signature on every webhook against the raw body, and reject deliveries older than 5 minutes.
  3. 03Confirm a result with the signed webhook or GET /v1/sessions/{id}, never with the return URL's status parameter.
  4. 04Give each console user the lowest role that works, and remove people when they leave.
  5. 05Set each app's retention period to the shortest your business allows, and purge sessions you no longer need.

Check limits

Know what each check proves.

Security includes not over-trusting a result. These are stated in the product, too.

  • Liveness is active challenge-response, not certified presentation-attack detection (ISO/IEC 30107-3).
  • PAN validation is structural; there is no government database lookup.
  • AML screening covers OFAC SDN and the UN consolidated list only: no PEP, adverse media or ongoing monitoring.
  • Document checks rely on the printed MRZ and the image; NFC chips are not read.

Responsible disclosure

Reporting a vulnerability

Found a security issue in KYCVerify? Email us with the details and steps to reproduce. Please give us reasonable time to fix it before disclosing it publicly.

Security reports

hello@kycverify.me

Include

The component (API, hosted flow, console) and URL or endpoint, steps to reproduce, and the impact you see.

In scope

The KYCVerify service at kycverify.me: the API, the hosted flow, the console and the marketing site.

Out of scope

Our customers' own applications and webhook receivers. Report those to the organisation running them.

Please

Do not access other people's data, degrade a service or publish before a fix is available. There is no bug bounty programme.