LIVE · AUDIT-CHAINED · EU-RESIDENT
SYSTEM · 99.99% UPTIME
v 1.0 ↗ MADE IN EU

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.