Skip to main content
There are two event types written to Redis. Both use the same key structure and TTL. Your server reads both via client.getWebhookEvents().

Event 1 — assessment.sent

Fired from white-collar-assessment-portal-backend when an assessment is sent to candidates and emailNotificationsEnabled is false on the API key.
This event fires once per assessment, batching all candidates into a single payload. It tells your app “these people have been invited to take this assessment at this URL.”

Full JSON stored in Redis

Field reference

Source code (assessment.service.ts)

When this fires vs email

This event only fires when emailNotificationsEnabled = false on the API key.

Event 2 — assessment.completed

Fired from record-assessment-backend when a candidate submits the exam. See What Gets Stored in Redis — Completed for the full payload. When a candidate submits an assessment, the backend writes two things into Redis instantly, then your server reads them via client.getWebhookEvents().

Redis key structure (same for both events)

Two keys are written per event:
keyId = your API key’s internal ID (from ApiKeyModel — not the VFN_TEST_xxx value itself, but the keyId field on the key document). eventId = a randomUUID() generated at store time.

How the sorted set is used

When you call getWebhookEvents({ since, limit }):
When you acknowledgeWebhookEvents([...ids]):

Required environment variable

Also accepted: REDIS_URI (fallback). If neither is set, Redis won’t connect and all webhook storage is silently skipped — assessments still complete, but no events are queued.

What triggers the write

Inside submitSession() in exam.controller.ts, after the session is saved:
storeEvent then adds two more fields automatically:
  • eventIdrandomUUID()
  • storedAtnew Date().toISOString()

Exact JSON stored in Redis

This is the complete object that gets JSON.stringify()-ed and written to webhook:events:{keyId}:{eventId}:

Field-by-field source mapping


⚠️ Known gaps

aiFeedback is almost always null when the event is first stored.AI feedback is generated asynchronously after the session is submitted. The webhook event is stored immediately — before the AI has finished. If you need the feedback, either:
  • Poll the result again later via GET /exam/session/result
  • Accept that aiFeedback will be null for most events at polling time
recording.url may be null at polling time.The recording chunks are combined into a final .mp4 asynchronously after submission. The recording.status will be "processing" until the combine job finishes. The URL only appears once status reaches "ready".

Redis connection setup (full .env)


What happens if Redis is not configured

If REDIS_URL is not set:
  • Redis client is null
  • storeEvent throws "Redis is not connected"
  • The throw is caught in the fire-and-forget block inside submitSession
  • Assessments still complete normally — the session is saved to MongoDB
  • No webhook events are queued
  • Your poller gets { events: [], pendingCount: 0 } every time
You will see this in server logs: