Skip to main content

Setup Guide (Step by Step)

This guide takes you from sign-up to live verifications, one step at a time. Each step links to the full details. Follow it in order: each step depends on the one before.

What you'll build​

An AttestID integration has two halves:

PartWhere it runsWhat it does
Your backendYour serverHolds the API key and client certificate. Issues short-lived SDK tokens. Verifies results. Receives webhooks
Your frontendThe user's browser or phoneRuns the AttestID SDK: consent, document photo, face check
User's browser / phone Your backend AttestID
────────────────────── ──────────── ────────
1. "Verify me" ──────────────▶ 2. Ask for an SDK token ────────▶ (API key + certificate)
◀────────────── 3. Return the token ◀────────
4. SDK: consent, ID photo, face check ─────────────────────────────────▶ (SDK token)
5. Result ──────────────▶ 6. Verify the signed result, save it
7. Webhook arrives ◀──────── verification.completed

The secrets (API key, certificate key, webhook secret) stay on your backend. The browser or phone only ever holds a 10-minute SDK token.

See the finished result first: the live demo is a working integration built exactly this way. Its source is explained in JavaScript/React Demo App.


Step 1: Create your account​

  1. Register your company with POST /v1/account/register (or the sign-up form).
  2. Click the link in the verification email within 24 hours.
  3. Your first test API key appears once: an api_key (sk_test_...) and an access_key_id (ak_test_...). Copy both into your secrets manager straight away. If you lose the api_key, you'll have to issue a new one.

New accounts start on the Free plan (50 verifications in total).

→ Details: Creating a Company & Getting Your Keys

Step 2: Get a client certificate​

Mutual TLS is required: AttestID rejects every API-key request (including POST /v1/kyc/session) that doesn't present your client certificate, with a 401.

  1. On your server, create a private key and CSR with openssl.
  2. In the dashboard, go to Client Certificates → Issue Certificate and paste the CSR. Issue it while logged in to the same company that owns your API key.
  3. Download client.crt (your browser saves it as attestid-client.crt, not the -ca.pem file), and put it on your server as client.crt, next to client.key. It's shown only once.

→ Details: Client Certificates (mTLS)

Step 3: Test the connection​

Check that your key and certificate work together before writing any code:

curl https://api.attestid.brimsage.co.ls/v1/account/me \
--cert client.crt --key client.key \
-H "X-API-Key: $ATTESTID_API_KEY" \
-H "X-Access-Key-Id: $ATTESTID_ACCESS_KEY_ID"

The headers must be named exactly X-API-Key and X-Access-Key-Id. Older versions of these docs showed API-Key / Access-Key-Id, which the API doesn't accept. The production base URL is https://api.attestid.brimsage.co.ls.

A 200 with your company's details means you're ready. A 401 or 400: see Troubleshooting.

Step 4: Store your secrets​

Your backend needs these, kept in a secrets manager or server-only environment variables:

ATTESTID_API_URL=https://api.attestid.brimsage.co.ls
ATTESTID_API_KEY=sk_test_...
ATTESTID_ACCESS_KEY_ID=ak_test_...
ATTESTID_CLIENT_CERT_FILE=/secure/path/client.crt
ATTESTID_CLIENT_KEY_FILE=/secure/path/client.key
ATTESTID_WEBHOOK_SECRET=whsec_... # from step 8
  • Never put any of these in frontend code or in variables your build exposes to the browser (VITE_*, NEXT_PUBLIC_*, REACT_APP_*), or in a mobile app.
  • Never commit them to Git.

Step 5: Build the SDK-token endpoint on your backend​

Add a route to your backend (for example POST /api/attestid-session) that:

  1. Checks the user is logged in to your app.
  2. Takes the user_id from your own session, never from the request body. Otherwise anyone could start a verification as someone else.
  3. Calls POST /v1/kyc/session with your API key and client certificate.
  4. Returns { sdk_token, expires_in } to the frontend.

The user_id is your own identifier for the user (a database ID, for example). Use the same one every time for the same person.

→ Code: Node.js · other languages

Step 6: Add the SDK to your frontend​

Web (React): install @attestid/sdk and render AttestIDVerifier, pointing getSdkToken at your endpoint from step 5. It handles consent, the document photo, quality checks, the face check and the result screen.

→ Using the JavaScript / React SDK

Mobile (Flutter): install attestid_sdk and point its getSdkToken at the same endpoint.

→ Using the Flutter SDK

Requirements for the frontend:

  • HTTPS. Browsers only allow the camera on https:// pages (or http://localhost while developing).
  • Consent. The user must agree to biometric processing before their photo is taken. AttestIDVerifier shows this screen for you. If you build your own UI, record the moment they agree and pass it as consentGivenAt.
  • Camera permission. Users must allow camera access. Phones give the best results; desktop users are offered a QR code to continue on their phone.

The SDK takes care of the rest for you: it runs the AttestID face check, and the JavaScript SDK also signs captures with a device-bound key and retries failed requests safely with an Idempotency-Key. You don't need to write code for any of this.

Step 7: Check every result on your backend​

When the flow finishes, the SDK gives your frontend a result. Don't trust verified from the browser: it could have been changed there. Send the result to your backend and check its signed attestation:

import { verifyAttestation } from '@attestid/sdk';

const claims = await verifyAttestation(result.attestation, {
apiUrl: process.env.ATTESTID_API_URL!,
verificationId: result.verification_id,
document: result.document,
});
// Only now: claims.verified === true → mark the user as verified in your database

Save at least the verification_id, verified, status and failure_reason with the user's record.

If you need to look at the ID photo or selfie later, request links with GET /v1/kyc/{verification_id}/documents. They expire after 5 minutes and every request is audit-logged, so ask for fresh links each time rather than storing them.

→ Details: Security → Signed verification results

Webhooks tell your backend about outcomes even if the user closes the page before the result reaches you.

  1. Add a route on your backend, for example POST /webhooks/attestid. It must be public and use HTTPS.
  2. Register it: POST /v1/webhooks/configure with only { "url": "..." }, or use the dashboard. AttestID generates the secret; you don't send one.
  3. Copy the whsec_... secret from the response into your secrets manager. It's shown once.
  4. In your route, verify the signature on every delivery with verifyWebhook(), reading the raw body. While a secret is being rotated, the AttestID-Signature header carries two v1= values: accept the delivery if either one matches. Deliveries also carry an AttestID-JWS header signed with AttestID's platform key; pass apiUrl to verifyWebhook() and it checks that too.
  5. Respond with 200 within 10 seconds, then do the work in the background.
  6. Handle the same event arriving more than once (deliveries are retried). Use verification_id to skip events you've already processed.

→ Details: Webhooks

Step 9: Handle every outcome​

What happenedWhat to do
verified: true (checked in step 7)Mark the user as verified
failure_reason: fraud_review_requiredThe verification is held for a person at AttestID to review. Treat the user as not verified yet. If the review confirms fraud, you'll receive a fraud.detected webhook
Failed only at the face check (canRetryLiveness(result) is true)Let the user retry the face check. AttestIDVerifier shows a Retry face check button for you
Any other failureShow the user result.message, which is written for them, and let them start again
402 from the APIYou've used up your plan's verifications. Upgrade in the dashboard
403 fraud_review_required / identity_flagged_fraudulent when startingThis identity is under review or flagged. Don't let the user retry

→ All failure reasons: JavaScript SDK → Error handling

Step 10: Test end to end with test keys​

Run through the whole flow with your test key before going live:

  • Your token endpoint returns a token, and rejects users who aren't logged in.
  • A real ID document and face check complete on a phone, over HTTPS.
  • A bad photo (blurry, dark, wrong document) is caught, with a clear message.
  • Your backend verifies the attestation, and rejects a result you edit by hand.
  • A webhook arrives and passes signature verification. Check Webhook → Delivery History in the dashboard.
  • A face-check failure offers Retry face check.

Step 11: Go live​

  1. Add your company's physical address in the dashboard under Profile. It's required for live access.
  2. As the account owner, open API Keys and click Promote to Live.
  3. Issue a live key (sk_live_... / ak_live_...) on the same page and store it in your secrets manager.
  4. Swap your backend's environment variables to the live key. Your client certificate works for live keys too: no new certificate needed.
  5. Choose a paid plan if you need more than the Free plan's verifications.
  6. Run one real verification in production and check it end to end.

Step 12: Keep it running​

WhenWhat
Every yearRenew your client certificate before its Valid Until date. How
Regularly (and when a staff member with access leaves)Rotate your API key in API Keys → Rotate. The old key keeps working for 24 hours while you deploy the new one
When you need a new webhook secretRotate it with no downtime: both secrets are accepted until the rotation ends (see step 8)
When you suspect a leakRevoke the key, certificate or webhook secret straight away, then issue new ones
When a user asksDelete their data with DELETE /v1/account/me/data?user_id=... (Lesotho Data Protection Act)
WeeklyCheck Webhook → Delivery History for failed deliveries
When we release updatesUpdate @attestid/sdk / attestid_sdk to get the latest fixes and features

Who holds what​

SecretKept byNever put in
api_key (sk_...)Your backend's secrets managerFrontend, mobile app, Git, logs
client.keyYour backend's secrets manager or a protected fileAnywhere else. Not even AttestID needs it
Webhook secret (whsec_...)Your backend's secrets managerFrontend, mobile app, Git
access_key_id (ak_...)Backend config. Not secret, but only useful with the key—
client.crtYour backend. Not secret, but only useful with the key—
sdk_tokenThe user's browser or phone, for 10 minutesLong-term storage

Need help?​

Contact support@brimsage.co.ls. Include your company ID and the verification_id if you're asking about a specific verification. Never send your API key, private key or webhook secret.