Agent Wranglers

Recommended

Weigh every legitimate interest in writing

Before a purpose relies on legitimate interests, write an assessment with the purpose, necessity and balancing tests. Stop the processing for anyone who objects, unless the assessment shows compelling grounds.

Applies when

The project it has users in the EU.

How to do it

GDPR Article 6(1)(f) allows processing that is necessary for a legitimate interest, unless the person's rights override it. Fraud checks, security logs and product analytics often rely on it. The purpose test names the interest and who benefits. The necessity test asks whether less data, or none, would do. The balancing test weighs the effect on people, what they would expect, and whether children are involved. Article 21 lets people object to this processing at any time. An objection to direct marketing always stops it. Public authorities cannot use this basis for their public tasks. When the balancing finds high risk, run the DPIA screening. Stop marketing mail at once with one-click unsubscribe.

Examples

Using these examples: they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

YAML example

- purpose: fraud-checks
  legal_basis: legitimate_interests
  assessment:
    purpose_test: Stop stolen cards from paying for orders, which protects the shop and card holders
    necessity_test: Uses order amount, card country and device signals, no browsing history
    balancing_test: Customers expect fraud checks at checkout, and no children use the service
    result: proceed
    reviewed: 2026-10-01

JavaScript example

const TESTS = ['purpose_test', 'necessity_test', 'balancing_test'];
const MAX_AGE_DAYS = 365;
const DAY_MS = 86_400_000;

export const assessmentProblems = (entry, now = Date.now()) => {
  if (entry.legal_basis !== 'legitimate_interests') return [];
  const assessment = entry.assessment ?? {};
  const missing = TESTS.filter(test => !assessment[test]).map(test => `${entry.purpose}: no ${test}`);
  const isStale = !assessment.reviewed || now - Date.parse(assessment.reviewed) > MAX_AGE_DAYS * DAY_MS;
  return isStale ? [...missing, `${entry.purpose}: review older than ${MAX_AGE_DAYS} days`] : missing;
};

Python example

from datetime import date, timedelta

TESTS = ["purpose_test", "necessity_test", "balancing_test"]
MAX_AGE = timedelta(days=365)

def assessment_problems(entry: dict, today: date | None = None) -> list[str]:
    if entry.get("legal_basis") != "legitimate_interests":
        return []
    today = today or date.today()
    assessment = entry.get("assessment") or {}
    problems = [f"{entry['purpose']}: no {test}" for test in TESTS if not assessment.get(test)]
    reviewed = assessment.get("reviewed")
    if not reviewed or today - reviewed > MAX_AGE:
        problems.append(f"{entry['purpose']}: review older than {MAX_AGE.days} days")
    return problems

How to check

Every record of processing entry with the basis legitimate_interests links an assessment with the three tests filled in and a review date within 12 months. An end-to-end test sends an objection to direct marketing, and the next marketing job skips that person.

Sources

For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/compliance/records-and-data-map/legitimate-interests-assessment">Agent Wranglers compliance playbook</a>. In code you write, add a comment next to it with the same link.