Certificate of Analysis (COA)
A Certificate of Analysis is the lab record for a single production batch — the measured ABV, pH, brix, sensory notes, and overall pass/fail assessment. COAs are the QC evidence that a batch met its specs before it left your bonded premises. Customers ask for them; auditors verify against them; the TTB cross-checks label-claim ABV against them.
A COA covers a production batch, not a finished bottle. One batch = one COA. If a batch is split across multiple bottling runs, the COA values still describe the bulk liquid that fed every bottle.
When a COA is required
A COA is always recommended for alcoholic products and often required for:
- Federal label-claim ABV verification — the COA’s measured ABV must match the label claim within ±0.3% (TTB rule).
- Customer/co-packing relationships where the client requires test results before accepting a delivery.
- Any product entering interstate commerce where state-level reporting is required.
- Internal QC sign-off before closing a batch.
vestl does not hard-gate batch closure on COA presence — that’s a workflow choice. But for compliance_regime = ttb products, the COA’s ABV is what feeds the TTB Batch Record, so practically you can’t ship a TTB batch without one.
Pages
/compliance/coa — COA list
A flat list of every COA across all batches, sorted by test date. Filters: assessment (Pass / Fail / Conditional), date range. The page subtitle is “Lab analysis records by batch.”
/compliance/coa/[batchId] — Single COA
The COA form for one specific production batch. Like the TTB record form, the URL parameter is the batch ID; the form auto-creates a draft COA if none exists.
The form is structured into:
- Test metadata — test date, lab name (internal QC or external).
- Quantitative results — ABV %, pH, brix.
- Sensory — color description, aroma notes, taste notes.
- Overall assessment — Pass / Fail / Conditional, plus optional notes.
Multiple COAs per batch are allowed (e.g. retests after a fail). The “current” COA is the most recent by test_date.
Field reference
Production batch
The batch this COA covers. Set when the COA is first created and cannot be changed (a retest or follow-up COA is a new record on the same batch). Click through to /batches/[id] to see the full production context.
Test date
When the lab work was actually done. Often a few days after production completion — the COA can be backdated to the test date even when entered later. Drives FEFO ordering of multiple COAs for the same batch.
Lab name
Who ran the analysis. Internal QC labs use the QC user’s name or a generic “Internal QC” string; external labs use the lab’s business name (e.g. “Industrial Test Systems”, “Eurofins”). Optional but strongly recommended for provenance.
ABV % (alcohol by volume)
The lab-measured alcohol content as a percentage. The single most load-bearing field on a COA — it’s the value that:
- Feeds the TTB Batch Record’s
abv_pct(and therefore proof-gallon math) - Must match the label-claim ABV within ±0.3% per federal rule
- Is referenced by client acceptance specs
If you don’t have a measured ABV, the COA cannot drive TTB compliance — leave the COA in Conditional until the test result is in.
pH
The acidity/alkalinity of the liquid (0–14 scale, lower = more acidic). Important for fermented products (beer, kombucha, cider, wine) where pH drives microbial stability and shelf life. Optional for spirits.
Brix
Sugar content as a percentage by mass (1° Brix = 1g sucrose per 100g solution). Used in fermented products to track sugar consumption during fermentation, and in sweetened spirits/liqueurs to verify recipe adherence. Optional.
Color description
Free-text sensory note (“Pale gold”, “Deep amber”, “Cloudy white”). The qualitative half of color analysis; the quantitative half is on the SRM/Lovibond scale, which vestl doesn’t currently model.
Aroma notes
Free-text sensory note from the QC taster. Used to spot batch-to-batch drift over time (e.g. “Less juniper-forward than batch B-0042 — investigate dry-gin step timing”). Visible on the COA list when filtering for sensory regression.
Taste notes
Same as aroma notes, for palate. Often paired with aroma to triangulate a deviation (“Aroma normal, taste flat” → likely carbonation issue, not formulation).
Overall assessment
| Value | What it means |
|---|---|
| Pass | Batch meets all spec targets and sensory expectations. Cleared for closure and release. |
| Conditional | Batch passes some checks but at least one is incomplete (e.g. ABV not yet measured) or borderline. Hold the batch; expect a follow-up COA. |
| Fail | Batch is out of spec on at least one dimension. Hold the batch; either rework, reblend, or destroy. |
A Fail COA does not automatically reject the batch — that’s a workflow decision a production manager makes after reviewing the COA. But the COA’s Fail status is the documented evidence for whatever decision follows.
Notes
Free-text. Use this for: rationale on a Conditional or Fail call, retest schedule, instrument calibration concerns, references to deviation reports.
Multiple COAs per batch
A single batch can have many COAs:
- The first COA might be
Conditionalbecause the lab hasn’t returned ABV yet. - A follow-up COA when the ABV comes back marks the batch
Pass(orFail). - A second follow-up after a rework records the post-rework results.
vestl never deletes a COA — they’re all kept on the batch’s record. The most recent (by test_date) is treated as the “current” assessment for filtering and dashboard widgets, but the full history is visible on the batch detail page.
How COAs connect to TTB
When a batch has both a COA and a TTB Batch Record:
- The TTB record’s
abv_pctis populated from the most recent passing COA when the TTB record is first created. - If the COA’s ABV is later corrected (retest, instrument recalibration), the TTB record’s ABV is not auto-updated — TTB records are tamper-resistant by design. You must manually create a deviation note and update the TTB ABV before the record is submitted.
- Filing the monthly TTB report freezes both the TTB record and the COA values used to derive it.
Audit trail
Every COA insert/update is audit-logged in audit_log with the user, timestamp, IP address, and field-level diff. The audit trail is INSERT-ONLY. Available at Compliance → Audit Trail, filtered by table_name = coa_records.
For COAs that fed a Filed TTB record, the audit trail is the only place an after-the-fact change is visible — the displayed values are frozen, but the trail shows everything that ever happened.