Tracking Certified Mail Delivery for Violation Notices
This guide sits under automating violation notice generation and delivery, part of the Violation Tracking & Compliance Enforcement track, and covers the step that turns a mailed document into proven, dated service. Generating and sending a notice is not service; service is a fact you must be able to prove, and its proven date is what starts the cure clock.
The focused task is to model certified-mail proof-of-service as a state machine, ingest delivery-status events from the carrier, resolve an authoritative served date, and toll the cure clock from that date — all while retaining evidence that survives an appeal. The stakes are concrete: if a respondent contests a fine, the hearing turns on whether the municipality can show the notice was delivered (or lawfully refused, or returned unclaimed) and on what date. A green card lost in a drawer, a tracking number that never reconciles, or a cure deadline computed from the mailing date instead of the delivery date each void the enforcement action. The inputs are a dispatched certified article (a tracking number and, often, a return-receipt / green-card identifier) and a stream of scan events from the carrier; the outputs are a single authoritative served date, a tolled deadline, and an append-only evidentiary record.
Permalink to this section Step 1 — Model the Proof-of-Service State Machine
Certified mail is not a boolean “sent / delivered”; it is a small state machine whose terminal state determines both whether service occurred and on what date. Model it explicitly so every case’s legal posture is a value you can query, not an inference from raw carrier text.
from __future__ import annotations
from dataclasses import dataclass
from datetime import date
from enum import Enum
class ServiceState(str, Enum):
ACCEPTED = "accepted" # carrier took custody
IN_TRANSIT = "in_transit"
DELIVERED = "delivered" # handed to recipient/agent
REFUSED = "refused" # recipient declined the article
UNCLAIMED = "unclaimed" # hold period lapsed, returned
RETURNED = "returned" # undeliverable (bad address)
# Which states establish legal service, and how the served date is derived.
# This mapping is ordinance-driven; refused/unclaimed rules vary by jurisdiction.
SERVED_BY_STATE: dict[ServiceState, str] = {
ServiceState.DELIVERED: "delivery_date",
ServiceState.REFUSED: "event_date", # refusal = constructive service
ServiceState.UNCLAIMED: "return_date", # only where constructive service allowed
}
@dataclass(frozen=True, slots=True)
class Article:
case_number: str
tracking_number: str # carrier article/barcode id
green_card_id: str | None # return-receipt id, if a physical card is used
state: ServiceState
served_date: date | None # None until a serving state resolves
Keeping served_date None until a serving state resolves is the safety property that matters: no cure clock can start, and no fine can accrue, against an article that is still IN_TRANSIT.
Permalink to this section Step 2 — Ingest and Normalize Carrier Delivery Events
Carrier feeds deliver scan events as coded strings ("Delivered", "Refused", "Unclaimed", "Return to Sender") with timestamps and locations. Normalize them into your ServiceState vocabulary at the boundary so the rest of the system never touches carrier-specific text, and make ingestion idempotent because carriers replay events.
from datetime import datetime
@dataclass(frozen=True, slots=True)
class ScanEvent:
tracking_number: str
raw_code: str
occurred_at: datetime # carrier-supplied, tz-aware
location: str
event_id: str # stable id from the carrier for dedup
# Map noisy carrier codes to our controlled vocabulary. Unknown codes must NOT
# silently map to a serving state — they route to manual review.
CODE_MAP: dict[str, ServiceState] = {
"ACCEPT": ServiceState.ACCEPTED, "ARRIVAL": ServiceState.IN_TRANSIT,
"DEPART": ServiceState.IN_TRANSIT, "DELIVERED": ServiceState.DELIVERED,
"REFUSED": ServiceState.REFUSED, "UNCLAIMED": ServiceState.UNCLAIMED,
"NO_SUCH_NUMBER": ServiceState.RETURNED, "RETURN_TO_SENDER": ServiceState.RETURNED,
}
def normalize(event: ScanEvent) -> ServiceState | None:
return CODE_MAP.get(event.raw_code.strip().upper()) # None -> manual review
An unmapped code returning None is deliberate fail-closed behavior: a carrier that introduces a new scan code must never accidentally be interpreted as delivery. Route None results to a clerk queue rather than guessing.
Permalink to this section Step 3 — Apply Events with a Guarded Transition Function
Not every event is a legal state change. A late IN_TRANSIT scan must not overwrite a DELIVERED state, and duplicate events must be no-ops. Encode the allowed transitions and dedupe on event_id.
# Terminal states never transition again; ordering guards against late scans.
TERMINAL = {ServiceState.DELIVERED, ServiceState.REFUSED,
ServiceState.UNCLAIMED, ServiceState.RETURNED}
def apply_event(article: Article, event: ScanEvent,
seen_event_ids: set[str]) -> Article:
if event.event_id in seen_event_ids:
return article # idempotent: replayed event is a no-op
seen_event_ids.add(event.event_id)
new_state = normalize(event)
if new_state is None:
return article # unknown code -> unchanged, flag upstream
if article.state in TERMINAL:
return article # never leave a terminal state
served: date | None = article.served_date
if new_state in SERVED_BY_STATE:
served = event.occurred_at.date() # the scan date is the served date
# Replace only the mutated fields; the dataclass is frozen (immutable record).
return Article(article.case_number, article.tracking_number,
article.green_card_id, new_state, served)
Guarding against re-entry of a terminal state protects the served date from being silently moved by a stray later scan — once delivery is proven on a date, that date is fixed evidence.
Permalink to this section Step 4 — Toll the Cure Clock From the Served Date
The whole point of tracking is to feed one date back into the enforcement timeline: the served date tolls (starts) the cure clock. Compute the deadline from the served date using the jurisdiction’s business-day calendar, and expose whether fines may accrue.
from datetime import timedelta
@dataclass(frozen=True, slots=True)
class CureClock:
served_date: date | None
deadline: date | None
is_running: bool # False until served; no accrual while False
def toll_cure_clock(article: Article, cure_days: int,
holidays: frozenset[date]) -> CureClock:
if article.served_date is None:
return CureClock(None, None, is_running=False) # unserved: clock never started
cursor, remaining = article.served_date, cure_days
while remaining > 0: # count business days only
cursor += timedelta(days=1)
if cursor.weekday() < 5 and cursor not in holidays:
remaining -= 1
return CureClock(article.served_date, cursor, is_running=True)
Because is_running is False for any unserved article, the fine-accrual job upstream can filter on it and never charge a respondent whose notice has not been proven served — the same tolling contract the parent pipeline relies on, and the input the daily-accruing fine schedules downstream consume.
Permalink to this section Step 5 — Persist an Append-Only Evidentiary Record
Every scan event and every state transition is evidence. Persist them append-only so the chain from acceptance to service is reconstructable at a hearing, and stamp each with the source so a green-card scan and a carrier API event are distinguishable.
import hashlib
import json
def evidentiary_entry(article: Article, event: ScanEvent,
new_state: ServiceState, prev_hash: str) -> dict:
entry = {
"case_number": article.case_number,
"tracking_number": article.tracking_number,
"green_card_id": article.green_card_id,
"raw_code": event.raw_code,
"resolved_state": new_state.value,
"occurred_at": event.occurred_at.isoformat(),
"location": event.location,
"source": "carrier_api", # or "green_card_scan"
"prev_hash": prev_hash,
}
body = json.dumps(entry, sort_keys=True, separators=(",", ":")).encode()
entry["hash"] = hashlib.sha256(body).hexdigest() # hash-chained, tamper-evident
return entry
Store the physical green card too: scan it to an image, attach it to the case, and record its green_card_id so the digital timeline and the paper receipt reconcile. This record is exactly what a hearing officer inspects when service is contested.
Permalink to this section Parameter and Flag Reference
| Parameter | Type | Default | Notes for proof-of-service |
|---|---|---|---|
serving_states |
set | {delivered, refused, unclaimed} |
Which outcomes establish service; refused/unclaimed rules are ordinance-specific. |
unclaimed_is_service |
bool |
False |
Whether constructive service on an unclaimed article is lawful in your jurisdiction. |
hold_period_days |
int |
15 |
Carrier hold before an unclaimed article returns; sets the earliest unclaimed date. |
served_date_source |
str |
"scan_date" |
Derive served date from the carrier scan timestamp, in the notice’s local tz. |
dedup_key |
str |
"event_id" |
Carrier event id; ingestion must be idempotent against replays. |
poll_interval_hours |
int |
6 |
How often to pull carrier tracking; balance freshness against rate limits. |
stale_after_days |
int |
45 |
An article with no terminal scan by now is escalated to manual review. |
retention_years |
int |
7 |
Retain events + green-card image for the state-mandated period. |
Permalink to this section Common Failure Patterns and Fixes
Permalink to this section Cure clock started from the mailing date
The most damaging error: computing the deadline from when the notice was mailed rather than when it was served. Keep served_date None until a serving state resolves and derive the deadline only from it, so an in-transit article cannot accrue fines. Assert in a test that an IN_TRANSIT article yields is_running=False.
Permalink to this section Duplicate carrier events double-processing
Carrier feeds replay events, and without dedup a redelivered DELIVERED scan can re-fire downstream side effects (a second fine timer, a duplicate notice). Dedupe on the carrier event_id before applying, and make apply_event a pure function of state so replays are no-ops.
Permalink to this section Late scan overwriting a terminal state
An out-of-order IN_TRANSIT scan arriving after DELIVERED must not reopen the article or move the served date. Treat delivered/refused/unclaimed/returned as terminal and reject further transitions; a genuine correction should be a new, explicitly-flagged evidentiary event, not a silent overwrite.
Permalink to this section Unknown scan code silently ignored or misclassified
A new carrier status string that is not in the map must fail closed to manual review, never default to a serving state. Return None from normalization for unmapped codes and alert on the rate of unmapped codes so a carrier feed change is caught in hours.
Permalink to this section Timezone drift moving the served date by a day
A UTC delivery timestamp converted naively can land the served date on the wrong calendar day, shifting the deadline. Convert the carrier timestamp to the notice’s local jurisdiction timezone before taking .date(), and store both the raw instant and the derived local date.
Permalink to this section Audit and Logging Guidance
Log every ingested scan event and every state transition with the tracking number, the raw carrier code, the resolved state, the source (carrier API versus green-card scan), and the derived served date. Write them to append-only (WORM) storage as hash-chained entries so the service timeline cannot be edited after a fine is contested, and retain them, together with the scanned green-card image, for the full state-mandated period. Record the served date’s derivation explicitly — which scan, in which timezone — because that single date is what a hearing turns on. Reconcile the digital timeline against the physical return receipt on a schedule, and alert when an article passes stale_after_days with no terminal scan so a lost article surfaces before its deadline lapses. These records join the same evidentiary trail as the rendered document from generating compliance notice PDFs with ReportLab, so the served hash and the archived hash can be shown to match.
Permalink to this section Frequently Asked Questions
Permalink to this section Does a refused or unclaimed article count as served?
It depends on the jurisdiction. Many ordinances treat refusal as constructive service on the refusal date, and some allow constructive service on an unclaimed article once the carrier hold period lapses — but others require actual delivery or a fallback method such as posting. Encode the rule as data (the serving_states and unclaimed_is_service settings) so the legal policy is explicit and auditable rather than buried in code.
Permalink to this section What date exactly starts the cure clock?
The proven served date — the carrier’s delivery scan date for a delivered article, or the refusal/return date where constructive service applies — converted to the notice’s local timezone. Never the generation or mailing date. Until a serving state resolves, the clock is not running and no fine may accrue.
Permalink to this section How do I reconcile the physical green card with the digital tracking feed?
Treat both as evidentiary sources feeding the same state machine: scan the returned green card to an image, record its green_card_id, and match it to the article’s tracking number. If the card’s signature date and the carrier’s delivery scan disagree, flag the case for manual review; the physical receipt is often the stronger evidence at a hearing, so preserve both and record which one established the served date.
Permalink to this section Related
- Automating violation notice generation and delivery — the parent pipeline whose cure clock this served date tolls.
- Generating compliance notice PDFs with ReportLab — the archived document whose hash this record matches.
- Modeling code violation lifecycles in Python — where a proven served date advances the case and arms escalation.
- Error handling and retry logic for ingestion pipelines — durable ingestion patterns for the carrier event feed.