If you ship an AI product, your terms of service are part of your security architecture. They are the contractual basis for suspending an account that is scraping outputs to train a competitor, for refusing a use you consider dangerous, for limiting liability when a model is confidently wrong and for telling users what happens to their prompts. Terms that nobody can enforce, that users never validly accepted, or that promise things your systems do not do, are worse than useless: they create liability and give a false sense of control.
This page takes the provider's side: you are writing, versioning and enforcing the terms for your own AI product or API. (Reading the terms of the model vendors you buy from is the mirror-image problem, covered in AI use agreements.) It covers the document set, the clauses that are specific to AI, how to collect acceptance that will hold up, how to map each clause to a technical control, an enforcement ladder in code, and a worked extraction case. This is engineering guidance, not legal advice; your counsel writes the words, and you make sure the systems match them.
The document set and terms as code
A provider usually publishes several documents that work together. The terms of service are the contract: who may use the service, payment, ownership, warranties, liability and termination. The acceptable use policy, often called a usage policy, lists what users may not do with the service and is usually incorporated into the terms by reference so that it can be updated more often. The privacy notice explains personal data handling. Business customers typically get commercial terms and a data processing agreement that override the consumer defaults on points such as training on customer data.
Keep these in one repository as text files with stable clause identifiers, the way you keep code. Every published version gets a content hash and an effective date, and each clause has an ID such as AUP-4.2 that the enforcement systems can cite. That one habit makes everything later in this page possible.
The clauses that are different for AI
Most of a ToS is standard commercial drafting. The clauses below are the ones that are different for AI, and each one implies a system requirement.
| Clause | What it says, in substance | What engineering must provide |
|---|---|---|
| Inputs and outputs | Who owns prompts and generated output; output may not be unique | Per-account data separation; no promise of exclusivity in the UI |
| Data use and training | Whether inputs are used to improve models, and how to opt out | A real opt-out flag honoured by every data pipeline |
| Accuracy disclaimer | Output may be wrong; users must review before relying on it | Visible disclosure in the product, not only in the terms |
| Acceptable use | Prohibited content and activities, high-risk uses | Moderation and abuse detection mapped to AUP clause IDs |
| Model protection | No extraction, reverse engineering, or using outputs to build competing models | Rate limits, extraction analytics, account linking |
| Automated access | Only documented APIs; no scraping of the consumer app | Bot detection, API keys, quotas |
| Eligibility | Minimum age, sanctioned regions, business-only tiers | Age gate, geo and sanctions screening |
| Suspension and termination | When and how you may restrict access, notice and appeal | Enforcement ladder, notices, appeal workflow |
| Changes to terms | How users are told about updates and when they take effect | Versioning, notification, re-acceptance |
Many large providers' terms restrict using outputs to develop competing models and prohibit attempts to extract model weights or system prompts. Whether such a clause is enforceable against a particular user is a legal question, but it only matters at all if you can detect the behaviour and prove the user agreed to the clause. For the detection side, see model extraction through APIs.
Acceptance that holds up
A contract needs assent. United States courts have repeatedly declined to enforce terms that users were never clearly asked to accept. In Specht v. Netscape (2002) and Nguyen v. Barnes and Noble (2014), terms reachable only through a link that users were not required to view (browsewrap) did not bind them. In Berman v. Freedom Financial Network (2022) the Ninth Circuit held that a sign-up flow must give conspicuous notice of the terms and an unambiguous act that signals assent. The safe pattern is clickwrap: the user takes an explicit action, such as ticking a box or pressing a button labelled with the agreement, next to a clear link to the exact terms.
Then record it. If a dispute arrives in two years, you must be able to show which text this account accepted, when, and through which interface.
import hashlib, json, time
TERMS = {"tos": "tos-2026-09-01.md", "aup": "aup-2026-09-01.md"}
def terms_hash(path):
with open(path, "rb") as f:
return hashlib.sha256(f.read()).hexdigest()
def record_acceptance(db, account_id, ui_variant, client_ip):
row = {
"account_id": account_id,
"accepted_at": int(time.time()),
"documents": {k: {"file": v, "sha256": terms_hash(v)} for k, v in TERMS.items()},
"ui_variant": ui_variant, # screenshot of this variant is archived
"client_ip": client_ip,
"method": "clickwrap_checkbox",
}
db.append("terms_acceptance", json.dumps(row)) # append-only table
return row
def require_current_terms(db, account_id, material_versions):
last = db.latest("terms_acceptance", account_id)
if last is None:
return "block: no acceptance on record"
accepted = {k: d["sha256"] for k, d in json.loads(last)["documents"].items()}
stale = [k for k, h in material_versions.items() if accepted.get(k) != h]
return "ok" if not stale else "reaccept: " + ",".join(stale)Archive a screenshot or rendered copy of each sign-up variant, because the court will ask what the user saw. Treat the acceptance table as an append-only audit log with the same protection as billing data. Store hashes of only the material versions in material_versions; typo fixes should not force every user through a new click.
Mapping every clause to a control
A clause that no system enforces is a promise you cannot keep, and a control that no clause authorises is a dispute waiting to happen. Maintain a mapping from clause IDs to controls and review it whenever either side changes. Each control emits events tagged with the clause ID, so an enforcement record can say which rule was broken. The moderation layer, described in LLM abuse detection, should classify against your AUP categories, not a generic taxonomy, so that a flag translates directly into a clause.
Run the mapping in both directions as a check in CI. Every clause marked enforceable must have at least one control with an owner and a test; every control that can restrict an account must cite at least one clause. Data-use clauses deserve particular care: if the terms say opted-out customers' data is not used for training, the training data loader must filter on that flag, and you should be able to show the filter in code and the counts it removed.
An enforcement ladder
Enforcement should be graduated, consistent and explainable. A ladder lets you respond proportionately, and recording the clause and evidence at each step protects both the user and you.
LADDER = ["warn", "throttle", "restrict_feature", "suspend", "terminate"]
SEVERE = {"AUP-1.1", "AUP-1.2"} # e.g. child safety, credible violence: skip the ladder
def next_action(history, clause_id, confidence):
if clause_id in SEVERE and confidence >= 0.9:
return "suspend", "severe clause; human review within 24h"
if confidence < 0.6:
return "none", "log only; below action threshold"
prior = [h for h in history if h["clause"] == clause_id and h["age_days"] < 90]
step = min(len(prior), len(LADDER) - 1)
action = LADDER[step]
if action in {"suspend", "terminate"}:
return action, "requires human approval before execution"
return action, "automatic"
def enforcement_record(account_id, clause_id, action, evidence_ids, terms_sha):
return {"account": account_id, "clause": clause_id, "action": action,
"evidence": evidence_ids, "terms_version": terms_sha,
"notice_sent": True, "appeal_url": "/appeals/new"}Thresholds and the severe list are policy decisions; the point of the code is that they are written down, reviewed and applied the same way to every account. Notify users of actions with the clause cited and an appeal route, unless doing so would tip off a serious abuser, and give appeals to people who did not make the original decision.
Worked example: output harvesting for distillation
An API customer on a self-serve plan sends 1.4 million requests in a week, compared with a median of a few thousand. The prompts are templated, cover a systematic grid of topics, ask for long step-by-step answers and arrive through three accounts that share a payment card and an IP range. The pattern matches the distillation signals your extraction analytics watch for.
The response follows the pipeline. Analytics raise an event tagged TOS-7.3, the clause prohibiting use of outputs to develop competing models, with confidence 0.8. The acceptance ledger shows all three accounts accepted the version containing that clause through the clickwrap flow, with archived screenshots. There is no prior history, so the ladder's first step applies, but because the accounts are linked the team escalates to throttling all three and sends a notice citing the clause and asking about the use case. Usage continues through a fourth account; a reviewer approves suspension. Every step is recorded with evidence IDs and the terms hash, so if the customer disputes the action, the file already exists.
Changing terms and regulatory floors
Terms change: new features, new laws, new abuse patterns. Treat a change like a release. Diff the old and new text in review, mark which clauses are material, tell users in advance in the way your current terms promise, and require re-acceptance for material changes through the require_current_terms gate. Some changes cannot be imposed retroactively on data already collected under older promises, so record which terms version governed each dataset.
Regulation sets floors that terms cannot contract out of. Consumer protection law in many places limits unfair terms and disclaimers. GDPR requires a lawful basis for processing personal data, which a clause in the terms does not supply by itself. The EU Digital Services Act requires services in scope to explain content moderation restrictions and procedures in their terms. The EU AI Act adds transparency duties, such as telling people they are interacting with an AI system; check current application dates with counsel. Your internal policy for employees is a separate document, covered in employee AI usage policies.
Failure modes
- Browsewrap acceptance. A footer link and no explicit action; the terms may not bind anyone.
- Promises the pipeline breaks. The terms say opted-out data is not trained on, but a new data export ignores the flag.
- Unversioned terms. You cannot show which text a user accepted, so the clause you rely on may not apply to them.
- Clause-free enforcement. Accounts suspended by a classifier with no cited rule, no notice and no appeal, inviting disputes and regulatory complaints.
- Copied terms. A competitor's or vendor's text pasted in, promising features or protections you do not have.
- Over-broad disclaimers. Clauses that consumer law will not enforce, giving false comfort.
Trade-offs
Strict, detailed AUPs give enforcement clear footing but raise false-positive costs and deter legitimate users; vague ones are flexible but hard to apply consistently. Frequent re-acceptance keeps the record clean but annoys users, so reserve it for material changes. Automatic enforcement scales but must stay below the threshold where wrong decisions harm paying customers; human review is accurate but slow and expensive. Publishing detailed abuse signals helps honest users comply and helps abusers evade, so describe prohibited behaviour, not detection methods.
What to do next
- Move your ToS and AUP into a versioned repository with clause IDs, content hashes and effective dates.
- Replace any browsewrap flow with clickwrap and start an append-only acceptance ledger with archived screenshots of each sign-up variant.
- Build the clause-to-control map and a CI check that every enforceable clause has a control and every restricting control cites a clause.
- Verify each data-use promise in code: find the filter that honours the opt-out and record its counts.
- Implement a written enforcement ladder with notices, human approval for suspension and an appeal path.
- Treat terms changes as releases with legal review, notice, and re-acceptance for material changes.