Overview
ProctorSafe's integration model is designed for developers who need programmatic access to proctoring session data — whether that's routing alerts to a student information system, building custom dashboards, or automating post-exam workflows.
All session data is delivered via webhook — outbound HTTPS POST requests to your endpoint. ProctorSafe does not offer a pull-based REST API for session data; this ensures that no data is held in memory longer than necessary and that delivery is guaranteed even if your system is temporarily unavailable.
Webhook endpoint requirements
Your endpoint must:
- Accept
POSTrequests withContent-Type: application/json - Respond with
HTTP 200within 5 seconds to acknowledge receipt - Be reachable over HTTPS (HTTP endpoints are rejected)
- Be reachable from ProctorSafe's outbound IPs (whitelist provided on request)
ProctorSafe retries failed deliveries using exponential backoff: 3 attempts at 30s, 5m, and 30m intervals. After 3 failures, the event is marked as undelivered and can be re-requested via the Events Replay API for up to 72 hours.
Event types
session.started
Fired when a student begins a proctored exam.
{
"event": "session.started",
"timestamp": 1718000000,
"exam_id": "EX-2024-0412",
"session_id": "sess_8f3k2m",
"institution_id": "inst_mougins",
"student_identifier": null,
"metadata": {
"browser_ua": "Mozilla/5.0...",
"screen_resolution": "1920x1080",
"timezone": "Europe/Paris",
"sdk_version": "1.4.2"
}
}
Note:
student_identifierisnullunless your integration passes it via LTI launch. If you rely on LMS roster sync, the identifier is resolved asynchronously within 5 minutes.
proctoring_event.tab_switch
Fired when the student switches away from the exam tab.
{
"event": "proctoring_event.tab_switch",
"timestamp": 1718000120,
"session_id": "sess_8f3k2m",
"duration_ms": 3200,
"count_this_session": 3
}
Important: tab_switch events include a duration_ms field. A 3-second switch to check a reference document is a legitimate use case. Whether to flag it is a policy decision on your side — ProctorSafe does not enforce a threshold here, it just delivers the signal.
proctoring_event.face_absent
Fired when the SDK detects no face in the webcam frame for longer than the configured threshold (default: 5 seconds).
{
"event": "proctoring_event.face_absent",
"timestamp": 1718000450,
"session_id": "sess_8f3k2m",
"duration_ms": 8200,
"confidence": 0.97
}
confidence is the SDK's assessment that no face is present (0.0–1.0). Values below 0.85 are typically caused by poor lighting and are worth reviewing before escalation.
proctoring_event.face_mismatch
Fired when the face detected during the exam does not match the face enrolled at session start.
{
"event": "proctoring_event.face_mismatch",
"timestamp": 1718000500,
"session_id": "sess_8f3k2m",
"confidence": 0.12,
"re_enrolled": false
}
Security note:
face_mismatchis a high-severity event by default. Your endpoint should treat it as requiring immediate review, not auto-dismissal.
proctoring_event.audio_anomaly
Fired when ambient audio exceeds the configured threshold (if audio monitoring is enabled).
{
"event": "proctoring_event.audio_anomaly",
"timestamp": 1718000600,
"session_id": "sess_8f3k2m",
"peak_db": 68,
"duration_ms": 4500,
"count_this_session": 1
}
Audio level is reported in dB. The default threshold is 65dB. Note that this event only fires if your institution has explicitly enabled audio monitoring in the ProctorSafe admin panel.
proctoring_event.devtools_open
Fired if the browser developer tools are opened during the session.
{
"event": "proctoring_event.devtools_open",
"timestamp": 1718000800,
"session_id": "sess_8f3k2m"
}
session.ended
Fired when the student submits the exam or the session is terminated.
{
"event": "session.ended",
"timestamp": 1718010000,
"session_id": "sess_8f3k2m",
"exit_reason": "submitted",
"total_duration_seconds": 3600,
"events_summary": {
"tab_switches": 4,
"face_absences": 2,
"face_mismatches": 0,
"audio_anomalies": 0,
"devtools_opens": 0
},
"risk_score": 0.3
}
risk_score is a composite score (0.0–1.0) computed by the ProctorSafe analysis engine. It is not a verdict — it's a signal for your reviewer. A score above 0.7 warrants closer review; above 0.9 should trigger an escalation workflow.
Signature verification
Every webhook delivery includes an X-ProctorSafe-Signature header for verification:
X-ProctorSafe-Signature: sha256=a1b2c3d4e5f6...
The signature is computed as:
HMAC-SHA256(timestamp + "." + raw_body, webhook_secret)
Where webhook_secret is the value assigned to your integration in the ProctorSafe developer portal.
Verification steps in your handler:
- Extract
X-ProctorSafe-Timestamp— reject if older than 5 minutes (replay attack protection) - Compute the expected signature
- Compare using a constant-time comparison function — never use
==for HMAC comparison
import hmac
import hashlib
import time
def verify_signature(payload_body, timestamp_header, signature_header, secret):
# Reject events older than 5 minutes
if abs(time.time() - int(timestamp_header)) > 300:
raise ValueError("Webhook timestamp too old")
expected = hmac.new(
secret.encode(),
f"{timestamp_header}.{payload_body}".encode(),
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(expected, signature_header):
raise ValueError("Invalid webhook signature")
Alert escalation logic
ProctorSafe provides a default alert pipeline, but the escalation rules are configurable per institution. Here's how the default logic works:
Event captured (on-device)
→ Encrypted signal sent to ProctorSafe servers
→ Signal stored in session log (anonymous, no PII)
→ Alert evaluated against institution thresholds
IF risk_score >= escalation_threshold:
→ Route to reviewer dashboard
→ Optionally fire webhook to your SIS (if configured)
→ Optional: send notification to instructor (email/in-app)
IF risk_score >= critical_threshold:
→ Alert marked as HIGH priority
→ Optional: auto-pause exam session (configurable)
→ Optional: webhook to your SIS with flag: urgent: true
The escalation_threshold and critical_threshold are set in the ProctorSafe admin panel and are scoped per exam type.
Testing your integration
Use the ProctorSafe webhook testing tool in the developer portal to:
- Replay any historical event — replay a specific event type with a specific payload
- Send a test event — fire a custom payload to your endpoint without affecting live sessions
- View delivery logs — see retry attempts, HTTP status codes, and failure reasons
For local development, use a tool like ngrok or smee.io to expose a local endpoint.
Event Replay API
If your endpoint was unavailable and you missed events, use the Events Replay API:
GET /v1/events/replay?session_id={session_id}&from={timestamp}&to={timestamp}
Authorization: Bearer {api_key}
Returns all events for the specified session within the time window. Events are available for replay for 72 hours after session end.
SDK vs. webhook scope
A common integration question: where does the SDK end and the webhook begin?
| What | Where |
|---|---|
| Face detection | On-device SDK |
| Tab switching detection | On-device SDK |
| Audio level monitoring | On-device SDK |
| Event signal encryption & delivery | ProctorSafe servers |
| Alert log storage | ProctorSafe servers |
| Webhook delivery to your endpoint | ProctorSafe servers |
| Dashboard, review workflow, SIS integration | Your systems |
The SDK never talks directly to your systems — all data flows through ProctorSafe's server layer, which applies encryption, anonymisation, and access control before any event reaches your webhook endpoint.
EdTech developers: full API documentation, SDK reference, and sandbox credentials are available at proctorsafe.eu/docs. For integration support, contact the team with your institution ID and a description of your use case.