Concepts
What a Session, Evidence row, and Audit Chain actually are, in the fewest words possible. Read this once and the API reference will make sense.
Organisation
The top-level tenant. One org corresponds to one customer account. Every other resource (sessions, evidence, audit events, users) is scoped to an Org via row-level security on the underlying Postgres tables. A bug in the application code can't cross-tenant — the database will refuse the query.
An Org has: members (users with role-based access), branding (logo / colours / PDF footer), a billing subscription, optional KYB legal-entity data, optional SSO config, and a retention policy.
Member, Role, Permission
A Membership is the join between a User and an Org. A user can hold memberships in multiple orgs and switch between them (the active org rides as a JWT claim).
Each Org seeds four system roles on creation:
org_admin— full control. Billing, members, branding, retention.inspector— can run inspections, capture evidence, sign reports.observer— read-only access to sessions + audit data.auditor— read-only access plus chain-verify permission.
Org admins can create custom roles by composing the permission catalog. Permissions are slug-checked at the view layer + cross-checked by RLS at the DB layer.
Session
One inspection. The unit of billing (you pay per closed session) and the unit of evidence (an audit chain is per-session, not per-org).
A session has: an operator (your team member who started it), one or more participants (the field user, plus optional observers), consent state, optional GPS position, optional notes, an audit chain, and — once closed — TSA-anchored timestamp tokens.
Participant
One person in a session. There's one operator per session and at least one field user; optional additional observers are supported. Field users join via a one-shot signed URL (no account required); operators + observers are members of the Org.
Evidence
A piece of captured proof during a session. Kinds:
snapshot— still photo from the field user's camera.annotation— drawing overlaid on a snapshot or whiteboard.whiteboard— in-session Excalidraw canvas exported as PNG + state.clip— short video segment.document— uploaded file (used by the chat layer for PDF-for-signature).
Every Evidence row has a SHA-256 of its binary content, stored in the audit chain. Tampering with the file after the fact fails verification.
Audit Chain
The cryptographic backbone. Every event in a session — session creation, consent grant, GPS recording, evidence capture, annotation, whiteboard save, signature, session end — emits an AuditEvent row with:
{
"session": "<uuid>",
"sequence": N,
"occurred_at": "<iso8601>",
"kind": "evidence.snapshot_added",
"actor": { "user": <id|null>, "participant": <id|null> },
"payload": { /* event-specific */ },
"prev_hash": "<sha256 of previous event>",
"hash": "<sha256 of canonical_json of this event>"
}
The first event uses prev_hash = "0" * 64 (genesis). Each
subsequent event uses the previous event's hash as prev_hash
and increments sequence by 1. A Postgres advisory lock
serialises writes per session; an append-only trigger blocks UPDATE
+ DELETE on the table.
TimestampToken (TSA anchor)
At session-end (and on operator-triggered "stamp now"), the current chain head is submitted to three independent timestamp authorities:
- YodaLedger — Tezos blockchain anchor. ~15-20 minute finality. Asynchronous; we get a callback when the block is confirmed.
- FreeTSA — RFC 3161 timestamp. Synchronous; token returned immediately. Swappable to a paid QTSP (DataSure) for eIDAS Art. 42 compliance.
- OpenTimestamps — Bitcoin anchor via the OpenTimestamps calendar protocol. Asynchronous; upgrade path runs on a Celery sweep.
Three is by design — if any one TSA disappears, the other two still anchor the chain. An auditor can verify against any of them independently using public block explorers / verify endpoints.
Signature (SES / AES / QES)
Three eIDAS tiers, all on the same audit-report PDF:
- SES (Simple Electronic Signature) — audit-chain-backed, no signing certificate. Suitable for internal records.
- AES (Advanced Electronic Signature) — identity-bound signing certificate, PAdES B-T anchored. Suitable for most B2B contracts.
- QES (Qualified Electronic Signature) — highest eIDAS tier, legal equivalent of a handwritten signature across the EU. Gated behind KYB verification of the issuing organisation.
Campaign (optional)
A logical grouping of sessions for batched reporting — "Q2 2026 motor claims" or "Site A handover defects". Sessions don't require a campaign; it's a reporting affordance.
Webhook
A customer-registered URL that receives HMAC-signed event POSTs.
Event types: session.created, session.completed,
participant.joined, participant.left,
evidence.added, recording.ready,
audit.anchored, signature.completed, plus a
webhook.test for delivery verification.
Signature: Stripe-style t=...,v1=... header with HMAC-SHA256
over <timestamp>.<body>. The SDK's
constructEvent() helper verifies in constant time with a
5-minute clock-skew tolerance.