Skip to content

Briefings & Toolbox Talks

Status: Specified — ready to build Build order: 11 of 13 — depends on Projects & Sites, Personnel & Attendance


Purpose

Briefings is the daily pre-work communication record for a site. It covers Toolbox Talks (TBTs), Point of Work Risk Assessments (POWRAs), plant familiarisation briefings, COSHH briefings, daily task briefings, and any other structured team communication requiring an attendee sign-off record.

A briefing event is opened from a template (or ad hoc), the supervisor runs through the content on site, and each attendee signs digitally. The completed record is locked and forms part of the auditable project record. Under UK H&S legislation, evidence that workers have been briefed on risks before commencing work is a mandatory compliance requirement.


Entities

BriefingTemplate

A reusable briefing script. Templates can be defined at company level (shared across all projects) or project level (project-specific content).

Field Type Required Notes
id UUID System PK, generated
project_id UUID FK → projects.id — NULL = company-wide template
title VARCHAR(255) Template name
category VARCHAR(50) See categories below
content_points JSONB Ordered list of briefing points — see structure below
estimated_duration_mins INTEGER Typical delivery time
status VARCHAR(20) Default: active
custom_fields JSONB Default {}
created_at TIMESTAMPTZ System
updated_at TIMESTAMPTZ System Trigger-maintained
created_by UUID System FK → users.id
updated_by UUID System
deleted_at TIMESTAMPTZ
CREATE INDEX idx_briefing_template_project
    ON briefing_templates(project_id) WHERE deleted_at IS NULL;
CREATE INDEX idx_briefing_template_category
    ON briefing_templates(category) WHERE deleted_at IS NULL;

Briefing categories:

Category Typical use
tbt Toolbox Talk — topic-based safety briefing (working at height, excavations, etc.)
daily_briefing Morning pre-work briefing — tasks for the day, hazards, changes
powra Point of Work Risk Assessment — immediate environment check before task
plant_familiarisation Introduction to specific plant or equipment before first use
coshh_briefing Hazardous substance briefing before use of a specific product
induction_refresher Refresher of site induction content
other General briefing

Template status values:

Status Meaning
active Available for use
archived No longer available for new events

BriefingEvent

A single instance of a briefing delivered on site on a given date.

Field Type Required Notes
id UUID System PK, generated
site_id UUID FK → sites.id
project_id UUID Denormalised
template_id UUID FK → briefing_templates.id — NULL = ad hoc briefing
title VARCHAR(255) Auto-filled from template; editable for ad hoc
category VARCHAR(50) Copied from template or set directly for ad hoc
content_points JSONB Copied from template at creation; editable before sign-off
briefed_at TIMESTAMPTZ Date and time the briefing was delivered
conducted_by UUID FK → users.id — person who delivered the briefing
location VARCHAR(255) Where on site the briefing was held
attendee_count INTEGER System Denormalised; kept in sync with attendee records
status VARCHAR(20) Default: draft
signed_off_at TIMESTAMPTZ Set when status transitions to completed
custom_fields JSONB Default {}
created_at TIMESTAMPTZ System
updated_at TIMESTAMPTZ System Trigger-maintained
created_by UUID System FK → users.id
updated_by UUID System
deleted_at TIMESTAMPTZ
CREATE INDEX idx_briefing_event_site
    ON briefing_events(site_id, briefed_at DESC) WHERE deleted_at IS NULL;
CREATE INDEX idx_briefing_event_project
    ON briefing_events(project_id, briefed_at DESC) WHERE deleted_at IS NULL;
CREATE INDEX idx_briefing_event_status
    ON briefing_events(site_id, status) WHERE deleted_at IS NULL;
CREATE INDEX idx_briefing_event_template
    ON briefing_events(template_id) WHERE deleted_at IS NULL AND template_id IS NOT NULL;

Briefing event status values:

Status Meaning
draft Created; attendees can still be added and content edited
completed Locked — all attendees have signed or the conductor has closed it

BriefingAttendee

One record per person who attended a briefing event. Attendance is confirmed by a digital signature.

Field Type Required Notes
id UUID System PK, generated
event_id UUID FK → briefing_events.id
project_id UUID Denormalised
personnel_id UUID FK → personnel.id — NULL if not in the system
name VARCHAR(255) Display name; auto-filled from Personnel if linked
company VARCHAR(255) Employer or subcontractor name
signature_key VARCHAR(500) S3 object key for signature image — NULL until signed
signed_at TIMESTAMPTZ NULL until signed
created_at TIMESTAMPTZ System
deleted_at TIMESTAMPTZ
CREATE INDEX idx_briefing_attendee_event
    ON briefing_attendees(event_id) WHERE deleted_at IS NULL;
CREATE UNIQUE INDEX idx_briefing_attendee_personnel
    ON briefing_attendees(event_id, personnel_id) WHERE deleted_at IS NULL AND personnel_id IS NOT NULL;

JSONB Field Structures

content_points

[
  {
    "order": 1,
    "point": "Working at height — all operatives must use fall arrest harnesses above 2m. Lanyards to be inspected before use."
  },
  {
    "order": 2,
    "point": "Excavation at Grid E–F — exclusion zone in place. Do not approach within 2m of the edge without a banksman present."
  },
  {
    "order": 3,
    "point": "Traffic management — site road restricted to one-way from 08:00 today. Refer to TMP drawing ref TM-026."
  }
]

Business Rules

  1. A BriefingEvent can be created from a template or ad hoc. When created from a template, title, category, and content_points are copied from the template at creation time. Subsequent changes to the template do not affect existing events.

  2. content_points on a BriefingEvent can be edited while status is draft. Once completed, the event is locked — content and attendees cannot be modified.

  3. Attendees can be added as linked Personnel records or as free-text names (for visitors or workers not yet in the system). A linked attendee can only appear once per event.

  4. Transitioning to completed sets signed_off_at from the server timestamp. The conductor does not need to wait for all attendees to sign — they can close the event and it will record which attendees signed and which did not.

  5. Soft deleting an event soft-deletes all its attendee records in the same transaction.

  6. Company-wide templates (project_id = NULL) are read-only from within a project — they cannot be edited by Project Managers or Site Managers. Only Tenant Admins and Platform Admins can manage company-wide templates.

  7. briefed_at can be set to any past time on the current day, or to an earlier date by Project Manager+ (for retrospective records). Site Managers can only record for today.


API Endpoints

Templates

Method Path Description
GET /projects/{projectId}/briefing-templates List project + company-wide templates
POST /projects/{projectId}/briefing-templates Create project-level template
GET /projects/{projectId}/briefing-templates/{id} Template detail
PATCH /projects/{projectId}/briefing-templates/{id} Update template
DELETE /projects/{projectId}/briefing-templates/{id} Soft delete
GET /briefing-templates List company-wide templates (Tenant Admin+)
POST /briefing-templates Create company-wide template (Tenant Admin+)
PATCH /briefing-templates/{id} Update company-wide template (Tenant Admin+)

Events

Method Path Description
GET /sites/{siteId}/briefings List events (paginated, filterable)
POST /sites/{siteId}/briefings Create event
GET /sites/{siteId}/briefings/{id} Full detail with attendees
PATCH /sites/{siteId}/briefings/{id} Update event (draft only)
DELETE /sites/{siteId}/briefings/{id} Soft delete
POST /sites/{siteId}/briefings/{id}/complete Transition to completed
GET /projects/{projectId}/briefings Events across all sites

Attendees

Method Path Description
GET /sites/{siteId}/briefings/{id}/attendees List attendees
POST /sites/{siteId}/briefings/{id}/attendees Add attendee
POST /sites/{siteId}/briefings/{id}/attendees/{attendeeId}/sign Capture signature (returns presigned PUT URL)
DELETE /sites/{siteId}/briefings/{id}/attendees/{attendeeId} Remove attendee (draft only)

Request / Response Shapes

POST /sites/{siteId}/briefings

// Request — create from template
{
  "template_id": "uuid-template",
  "briefed_at": "2026-05-28T07:30:00Z",
  "location": "Site compound — welfare unit",
  "custom_fields": {}
}

// Request — ad hoc
{
  "template_id": null,
  "title": "Concrete pour — pad foundations P7–P12",
  "category": "daily_briefing",
  "content_points": [
    { "order": 1, "point": "Concrete delivery expected 09:00 — banksman to manage vehicle access." },
    { "order": 2, "point": "Vibration exposure — rotate operatives every 30 minutes on vibrating poker." }
  ],
  "briefed_at": "2026-05-28T07:30:00Z",
  "location": "Grid E–F compound",
  "custom_fields": {}
}

// Response 201
{
  "id": "uuid-event",
  "site_id": "uuid-site",
  "project_id": "uuid-project",
  "template_id": "uuid-template",
  "title": "Working at Height — General Briefing",
  "category": "tbt",
  "content_points": [...],
  "briefed_at": "2026-05-28T07:30:00Z",
  "conducted_by": "uuid-user",
  "conducted_by_name": "James Fletcher",
  "location": "Site compound — welfare unit",
  "attendee_count": 0,
  "status": "draft",
  "custom_fields": {},
  "created_at": "2026-05-28T07:28:00Z"
}

POST /sites/{siteId}/briefings/{id}/attendees

// Request — linked to Personnel
{
  "personnel_id": "uuid-person",
  "company": null
}

// Request — free text (not in system)
{
  "personnel_id": null,
  "name": "Dave Kowalski",
  "company": "Hanson Groundworks Ltd"
}

// Response 201
{
  "id": "uuid-attendee",
  "event_id": "uuid-event",
  "personnel_id": "uuid-person",
  "name": "James Fletcher",
  "company": "Construo Civil Ltd",
  "signed_at": null,
  "signature_key": null
}

GET /sites/{siteId}/briefings

Query params: category, status, from_date, to_date, conducted_by, sort (default -briefed_at), limit, cursor


Permission Matrix

Action Platform Admin Tenant Admin Project Manager Site Manager Viewer
List / view events ✓ assigned ✓ own site ✓ assigned
Create event ✓ assigned ✓ own site
Edit draft event ✓ assigned ✓ own site
Complete event ✓ assigned ✓ own site
Delete event ✓ assigned
Add / remove attendees ✓ assigned ✓ own site (draft only)
Capture signature ✓ assigned ✓ own site
View attendee list ✓ assigned ✓ own site ✓ assigned
Manage company-wide templates
Create project-level templates ✓ assigned

Notifications Triggered

None. The Briefings module does not fire automated notifications in V1. Alerts for missed daily briefings (e.g. "no briefing recorded for today") are a V2 feature.


Offline Sync Scope

Data Syncs to Notes
BriefingTemplates Site Manager Company-wide + project templates
BriefingEvents Site Manager Last 90 days for assigned sites
BriefingAttendees Site Manager Last 90 days
Signature files Not synced Uploaded directly from device on reconnect

Offline briefing flow: - Site Manager opens event on device, adds attendees (linked or free-text) - Attendees sign on device screen — signature stored in device storage - On reconnect: event, attendees, and signatures upload sequentially - If another device has already created an event with the same template on the same date, a duplicate is allowed (multiple briefings may run in parallel)


Cross-Module Dependencies

Upstream

Module Dependency
Projects & Sites site_id, project_id
Personnel personnel_id on attendees (optional link)

Downstream

None. Briefings is a terminal module.


Tests Required

Templates

  • Create company-wide template — Tenant Admin → 201
  • Create project-level template — Project Manager → 201
  • Project Manager cannot create company-wide template → 403
  • List templates includes both company-wide and project-level

Events

  • Create event from template — content_points copied → 201
  • Create ad hoc event — no template → 201
  • Create event with backdated briefed_at by Site Manager to yesterday → 422
  • Create event with backdated briefed_at by Project Manager → 201
  • Update draft event — content editable
  • Update completed event → 422
  • Complete event — signed_off_at set
  • Delete event — soft deletes event and all attendees
  • List events — filter by category, status, from_date, to_date

Attendees

  • Add linked Personnel attendee → 201
  • Add free-text attendee → 201
  • Add duplicate Personnel attendee to same event → 409
  • Capture signature — returns presigned PUT URL
  • Remove attendee — only on draft event
  • Remove attendee from completed event → 422

Cross-tenant isolation (mandatory)

  • GET event from another tenant's site → 404
  • POST attendee to another tenant's event → 404

Permission boundaries

  • Site Manager can create and complete events → 201
  • Site Manager cannot delete events → 403
  • Viewer cannot create events → 403
  • Viewer can list events → 200

Default Tenant Labels

Key Default
module.briefings Briefings
field.briefing.title Briefing Title
field.briefing.category Category
field.briefing.briefed_at Briefing Date & Time
field.briefing.conducted_by Conducted By
field.briefing.content_points Briefing Points
field.briefing.location Location
status.briefing.draft Draft
status.briefing.completed Completed
category.briefing.tbt Toolbox Talk
category.briefing.daily_briefing Daily Briefing
category.briefing.powra POWRA
category.briefing.plant_familiarisation Plant Familiarisation
category.briefing.coshh_briefing COSHH Briefing
category.briefing.induction_refresher Induction Refresher

Foundation Dependencies

Component Location Used for
TenantMiddleware core/middleware.py Sets request.state.tenant
get_db core/db.py Schema-scoped AsyncSession
get_current_user core/security.py Auth
require_permission core/security.py RBAC
EntityMixin core/models.py Universal columns
PaginatedResponse core/pagination.py List endpoint wrapper
ConstruoError hierarchy core/exceptions.py Typed exceptions
presigned_put_url core/storage.py Signature upload
audit_entity_change trigger DB foundation Applied to briefing_events, briefing_attendees
factories.py tests/factories.py create_briefing_template, create_briefing_event, create_briefing_attendee