Quebec Law 25 Data Portability: Building the Export API Everyone Deferred

The right to data portability under Quebec’s Law 25 has been in force since 22 September 2024, and satisfying it is not a policy exercise. It is an engineering one, because it asks your systems to emit something most of them were never designed to produce: a structured, machine-readable copy of a person’s data, on demand.

Most of the writing on this obligation treats it as a legal event to be logged and diarised. It is not. The subject-access right that preceded it could be met, badly but defensibly, by exporting a screen or printing a PDF. Portability closes that escape hatch. The reader on the other end is another system, and the format has to be one that system can ingest. That single change turns a manual back-office task into a design problem, and it is the design problem that firms operating in Quebec deferred because the deadline arrived before the endpoint did.

What the right actually demands of a system

The portability right sits in section 27 of the Act respecting the protection of personal information in the private sector — the private-sector statute that Law 25 amended. Read as an engineer rather than a lawyer, it says four things that matter to architecture.

Free · 4 minutes

When two of your systems disagree, do you know which one to believe?

Fourteen questions on ownership, lineage, and quality — the difference between a number on a dashboard and a number you could defend. Banded finding on screen, full sheet by email.

  1. The scope is computerised personal information. Paper records are out. The right is explicitly about the data your systems hold.
  2. It covers information collected from the individual, and excludes information your organisation created or inferred from it. The profile your model built is not portable; the fields the person gave you are.
  3. The output must be in a structured, commonly used technological format. The Quebec guidance names CSV, XML and JSON; it rules out images and PDFs precisely because they are not reusable.
  4. On request, the data must be transmittable to a third party the individual authorises, not only back to the individual.

The response clock is the same 30 days that governs access requests generally under the Act. There is a relief valve — an enterprise can decline where compliance raises serious practical difficulties — but the burden sits with you to show it, and “our schema cannot express this” is not the difficulty the legislator had in mind. It is the difficulty they expect you to engineer away.

Why your existing schema cannot answer this

The reason portability is hard is not the API surface. It is that the two distinctions the law draws — collected versus inferred, and about-this-person versus about-someone-else — are distinctions your data model almost certainly does not carry. Operational databases are built to run the business, not to trace where each value came from. A customer row holds the email the person typed, the risk score a model produced, and the segment a marketing job assigned, side by side, with nothing marking which is which.

To emit a compliant export you have to reconstruct provenance after the fact, and provenance you did not capture at write time is expensive to recover. This is the same lineage problem that sits underneath most modern reporting obligations — the discipline of knowing which field came from where is exactly what makes a reporting backbone trustworthy in any regulated domain. Portability just makes the individual, rather than a supervisor, the party who can demand it.

So the first deliverable is not code. It is a classification of every personal-data field your systems hold into three buckets: collected from the individual (portable), created or inferred (not portable), and about a third party (excluded on other grounds). That inventory is the specification the export service is built against, and building it is where the real work lives. It is close kin to the field-level provenance mapping that the documentation obligations arriving elsewhere also force, and doing it once serves both.

The shape of the export service

Once the classification exists, the service itself is disciplined and small. It authenticates the requester to the standard of your account-recovery flow, not lower — an export endpoint that hands over a full personal-data copy on weak proof of identity is a breach waiting to be filed. It assembles the portable fields from each source system, tags each with its origin so the classification is auditable, and serialises the result into one of the named formats behind a versioned schema envelope. It records who requested what and when, because the 30-day clock and any refusal both have to be evidenced.

A minimal endpoint that returns the collected-from-subject data and deliberately omits the inferred fields looks like this.

# Portable-export endpoint (FastAPI). Returns only data collected from the
# individual, in a structured, commonly used format, per Quebec Law 25 s.27.

from datetime import datetime, timezone
from fastapi import FastAPI, Depends, HTTPException

app = FastAPI()

# Field registry: which personal-data fields are 'collected' (portable)
# versus 'inferred'/'created' (excluded from portability).
PORTABLE_FIELDS = {"full_name", "email", "phone", "postal_address",
                   "date_of_birth", "transaction_history"}
EXCLUDED_FIELDS = {"risk_score", "marketing_segment", "churn_probability"}

def verify_subject(subject_id: str, token: str) -> str:
    # Authenticate to account-recovery strength, not lower. A weak check here
    # turns the portability right into a data-exfiltration vector.
    if not identity_proven(subject_id, token):
        raise HTTPException(status_code=403, detail="identity not verified")
    return subject_id

@app.get("/portability/export")
def export(subject: str = Depends(verify_subject)):
    record = load_personal_data(subject)          # from source systems
    portable = {k: v for k, v in record.items() if k in PORTABLE_FIELDS}

    log_portability_request(subject, released=list(portable))  # 30-day clock

    return {
        "schema": "sixteenpillars.portability/v1",
        "format": "application/json",
        "subject_id": subject,
        "generated_at": datetime.now(timezone.utc).isoformat(),
        "data": portable,          # collected from the individual only
        "excluded": sorted(EXCLUDED_FIELDS),  # named, so the omission is auditable
    }

The instructive line is the last one. Naming what you excluded, rather than silently dropping it, is what lets you defend the boundary later — to the individual, to the Commission d’accès à l’information, or to your own auditors. An export that quietly omits the inferred profile looks identical to one that forgot a source system. The envelope removes that ambiguity.

Build the service, not the workaround

The tempting response to a low request volume is a runbook: when a portability request lands, an engineer runs some queries, assembles a file by hand, and emails it. That works for the first three requests and fails on the fourth, because it is not repeatable, not evidenced, and not consistent. The moment two exports of the same person’s data disagree, you have manufactured the dispute the right was meant to prevent. On-demand portability at any scale is an engineered endpoint or it is a liability, and treating it as a proper part of the estate is exactly the kind of decision a maturing in-house technology function is there to make.

The firms that deferred this did so because the portability right looked like a compliance line item and turned out to be a data-lineage project. The endpoint is a fortnight of work. The classification behind it is the part you should have started a year ago.

Free interactive tool

Website compliance checklist

What your site has to do, based on what it actually does

Answer as much or as little as you like — the list builds as you go. Nothing is stored against your name and no email is required.

Can you trust the architecture you have?

Architecture diagrams rarely show the reality of how systems actually operate. An independent review establishes what is really there.