Two of the four rights California attaches to automated decisions on 1 January 2027 are, from an engineering point of view, features with states and edge cases: a notice that appears before the decision, and a way for the person to refuse. The legal summaries stop at describing them. This is what they look like once you have to build them.
For scope, thresholds and the rest of the calendar, start with what the ADMT rules require you to build. This piece assumes you already know you are covered and wants the implementation.
The notice is a component, not a page
The requirement is that a person is told, before the technology is used on them, what it is for, how it reaches decisions, which categories of personal information affect the output, what the output looks like, and how that output feeds the decision. Plus how to exercise their rights.
That shape rules out the obvious shortcut. A paragraph in the privacy policy cannot satisfy it, because a privacy policy is not delivered before a specific decision and cannot describe a specific tool. What you need is a component that takes a decision type and renders the notice for that tool.
So the data model comes first, and it is smaller than people fear:
- a decision type, with a stable identifier
- the tool behind it, with a version
- the plain-language purpose, the input categories, the output type, and how the output is used
- an effective date, because notices change and you will need to know which version someone saw
Then the component reads that record. The reason to store it rather than hardcode it in the template is not elegance, it is that the notice text is the thing your counsel will edit, repeatedly, without wanting to file a pull request.
Record that it was shown
An unlogged notice is indistinguishable from no notice. When someone asks what they were told, "our application form displays a notice" is a claim about today's code, not evidence about their case.
So the render writes a row: who, which decision type, which notice version, when. Cheap, and it converts an argument into a lookup. This is the same discipline as the wider audit trail an AI decision needs, and in practice both should write to the same place.
The opt-out has two shapes, and you pick per tool
The rules let you avoid building a true opt-out in two different ways, and the two are not interchangeable.
For hiring, work assignment and compensation, you may decline opt-outs if you have verified the technology works as intended and does not discriminate. That verification is an artefact you keep, not an assurance you give, which makes this route an evaluation-and-evidence project rather than a UI one.
For the other significant decisions, the opt-out does not apply if you offer a meaningful human appeal to a reviewer with real authority to reconsider.
Choosing between them early matters because they produce completely different backlogs:
| Verified-tool route | Human appeal route | |
|---|---|---|
| Applies to | Hiring, assignment, compensation | The other significant decisions |
| Main build | Evaluation harness plus stored results | Appeal queue, reviewer role, reversal path |
| Ongoing cost | Re-run and re-document the evaluation | Staff the queue inside an SLA |
| Fails when | The evaluation goes stale | Reviewers rubber-stamp |
Most teams underestimate the second column's last row. An appeal process where nobody has ever overturned anything is not an appeal process, and an override rate of zero across thousands of decisions is the number that gives it away.
Reversal is harder than the decision
Here is the part that surprises engineers. Deciding is easy; undeciding is not. A rejection has already propagated: an email went out, a record changed state, a downstream system synced, maybe a webhook fired at a third party.
So an appeal path needs to answer, per decision type, what actually gets undone and what cannot be. Write that list before you build the queue. Some things are trivially reversible, some need a compensating action rather than a rollback, and a few are genuinely one-way, which is worth knowing before you promise someone a review.
The edge cases that will find you
- Repeat decisions. If the same person is evaluated monthly, do they see the notice every time? Deciding once and documenting the reasoning is fine. Not deciding is not.
- Notice changes mid-process. Someone applies under version 3 and gets decided under version 4. This is why the shown-notice log stores a version.
- Third-party tools. If the model belongs to a vendor, you still owe the explanation. Ask what they will tell you about the logic before you sign, not after a request arrives.
- Partial automation. A tool that ranks but does not decide may sit outside the rules, which makes the human step load-bearing. If that human follows the ranking every time, you are relying on a distinction your own data contradicts. Our piece on keeping a human in the loop is about exactly this gap.
What this means for your team
- Build the notice as data plus a component, not as copy in a template. Counsel will edit it more than you expect.
- Log that the notice was shown, with its version. One row, and it is the only evidence you will have.
- Pick the opt-out shape per decision type now. The two routes have almost nothing in common, and picking late means building the wrong one.
- Write the reversal inventory before the appeal queue. What can actually be undone shapes what you can offer.
- Measure your override rate. If it is zero, your human review is decorative, and you would rather learn that from your own dashboard.
None of this is a large project by the standard of the compliance conversation around it. It is a table, a component, a log and a queue, and the sequencing matters more than the volume. If you have a decision flow in scope and want a straight read on which of the two routes fits it, tell us what the decision looks like.