Help centre

How can we help?

Engagement Letters (Practice)

Pro

Last updated 2026-05-04

Why this exists

Under the Tax Agent Services Act 2009 and the TPB Code of Professional Conduct, a registered BAS or tax agent must hold a current written engagement letter scoping the work before they lodge anything on a client's behalf. The letter must be retained for the duration of the engagement plus 5 years.

Frank gives each practice-client relationship a first-class engagement letter record with a full sign-off lifecycle.

Two flows, same record

In-app sign-off (default for new engagements)

  1. From /practice/engagement-letters, click + Send new letter, pick the client, optionally override the default body text, set a "valid until" date if relevant
  2. Frank creates a draft engagement letter row, generates a client_signoffs event, and emails the client a one-click link to /engagement-letter/[id]
  3. Client lands on the review page, reads the body, ticks the declaration, clicks Sign engagement letter
  4. Frank records a signed event (with timestamp + IP + user agent + SHA256 hash of the body text), flips letter status to active, supersedes any prior active letter for that client, and renders a signed PDF artifact for the audit trail

Pre-signed PDF attach (legacy / external e-signature flow)

If the client signed externally — wet signature, DocuSign, Adobe Sign — record it via:

POST /api/practices/{practice_id}/clients/{practice_client_id}/engagement-letter/attach-pdf
{
  "doc_id": "...",
  "signed_at": "2026-05-01",
  "valid_to": "2027-05-01"
}

The PDF must already be uploaded via the standard documents flow. Frank creates an active engagement letter row with synthetic sent + signed events for audit consistency.

Versioning + supersession

Every send creates a new engagement_letters row with version_no incremented and supersedes_id pointing to the previous active letter. When the new one is signed, the old one flips to superseded automatically. The old letter is never deleted — it stays in your records with the original signature event log intact.

So at audit time you can answer "what letter was in force on 2025-03-15?" by walking the supersession chain and matching against valid_from / valid_to.

Audit defensibility

What survives an audit:

  • Immutable signed event log in client_signoff_events (append-only — even service_role can't UPDATE/DELETE these rows; the database trigger raises insufficient_privilege)
  • Frozen body text on the engagement letter row (immutability trigger blocks UPDATE on body_text once status leaves draft)
  • SHA256 hash of the declaration text the client actually rendered at sign time, compared against the hash from the sent event — proves no tampering between send and sign
  • Rendered PDF artifact linked via signed_pdf_doc_id — embeds body text, signer name + email, signed_at, IP, user agent, hash. Set-once: the trigger blocks replacement
  • Forensic context on every event: IP, user agent, request_id, idempotency key

What's not protected today:

  • Supabase managed backups are 7 days. For TPB's 5-year retention, the live database is the system of record — recovery from beyond 7 days requires restoring from your own archival pipeline (see backend/docs/SIGNOFF_RETENTION.md)
  • The signed PDF is in the documents Supabase Storage bucket — RLS prevents public access but a service-role caller could overwrite. Bucket-level versioning is recommended for the next hardening pass

Compliance caveat

Frank captures the body text + the signature with the audit trail. The legal sufficiency of the engagement terms themselves is for you and your TPB-registered partner to confirm. Your practice's standard engagement letter template should be reviewed against the in-app body_text default before relying on this for live engagements.

Was this article helpful?