Developer documentation
Build on verified humans.
KYCPort is reusable identity infrastructure. Your users verify once and you read their assurance — KYC status and tier — instead of re-collecting documents. Standard OAuth2 / OpenID Connect, a thin BFF on your side, no tokens in the browser.
Concept
Verify once, reuse everywhere
You are a tenant (relying party). You register an OAuth/OIDC client and receive a client_id (and a client_secret for confidential clients). You never receive a user's documents — only the verification result.
Every verified human carries a reusable assurance: a status (verified · pending · rejected · unverified · expired) and an assurance tier (t0…t3). You request the tier you need; KYCPort tells you whether the human meets it.
client_secret live only on your server; the browser holds an httpOnly session cookie. Never store a token in localStorage or ship a secret to the client. HTTPS (TLS 1.2+) everywhere.Quickstart
Sign in with KYCPort
The Authorization Code flow with PKCE is the only supported browser flow.
1. Discover endpoints (always start here, never hardcode):
GET https://www.kycport.com/api/oidc/.well-known/openid-configuration
# -> issuer, authorization_endpoint, token_endpoint, userinfo_endpoint, jwks_uri2. Redirect the user to authorize with PKCE + state:
GET /authorize
?response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=https://app.example.com/callback # exact-match registered, HTTPS
&scope=openid email kyc
&state=RANDOM_CSRF
&nonce=RANDOM_NONCE
&code_challenge=BASE64URL(SHA256(verifier))
&code_challenge_method=S2563. Exchange the returned code for tokens on your server:
POST /api/oidc/token/
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=AUTH_CODE
&redirect_uri=https://app.example.com/callback
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET # confidential clients
&code_verifier=ORIGINAL_VERIFIER
# -> { id_token, access_token, token_type: "Bearer", expires_in }
# Tokens are JWTs signed EdDSA (Ed25519); access tokens carry typ: at+jwt.4. Validate every token — signature via jwks_uri, iss == https://www.kycport.com, aud == client_id, exp, and the nonce — then create your session.
Auth rules
Non-negotiable
- PKCE is required. S256 only;
plainis rejected. - state + nonce. Bind
stateto the session and reject mismatches; validatenoncein the ID token. - Least-privilege scopes.
openid(required), plusemail,profile,kycas needed. - Exact redirect URIs. HTTPS, registered ahead of time, exact match.
Reusable identity
Read a user's verification status
With the kyc scope the status is in the ID token / userinfo (no extra call). Or fetch it server-to-server with your tenant API key:
GET /api/v1/kyc/{person_id}/status/
Authorization: Bearer YOUR_TENANT_API_KEY
# -> { "kyc_status": "verified", "kyc_tier": "t2", "aml_clear": true }
# Field names are exactly kyc_status and kyc_tier.If the user isn't at your required tier, send them through verification and re-check — don't collect documents yourself.
Optional
Host your own capture
If you run the capture UI instead of redirecting, use the tenant-key case API:
POST /api/v1/verification/cases/ # { person_id, level } -> opens a case
POST /api/v1/verification/cases/{id}/upload/ # multipart: document, selfie, document_back?, liveness_mode?, applicant_*?
POST /api/v1/verification/cases/{id}/decide/ # -> { outcome, risk_level, human_verified, automation_tier }The engine runs real document forensics, liveness / presentation-attack detection, and face-match with 1:N dedup. Never submit a precomputed verdict — the score is always computed server-side.
Events
Webhooks
Subscribe to verification and status-change events. Each delivery is signed (HMAC over the raw body) — verify the signature before trusting it, make handlers idempotent (deliveries can repeat), return 2xx quickly, and let KYCPort retry with backoff on failures.
Ship safely
Best-practices checklist
- Confidential client + client_secret on the server only; rotate periodically.
- PKCE S256 on every authorize; state + nonce validated on callback.
- Validate iss, aud, exp, and EdDSA signature (via jwks) on every token.
- Browser holds only an httpOnly, Secure, SameSite=Lax session cookie — never a token.
- Least-privilege scopes; cache the tier, re-check on sensitive actions.
- Idempotent webhook handlers with signature + replay protection.
- All redirect URIs HTTPS and exact-match registered.
- Act on the JSON error field; retry only idempotent GET/5xx with backoff; respect 429.
AI coding assistant? Fetch /llms.txt — it's the same guidance in a literal, copy-paste-ready form.