> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mareaalcalina.com/llms.txt
> Use this file to discover all available pages before exploring further.

# ToS, jurisdiction, and the 451 response

> Terms of Service acceptance, Mexican-law jurisdiction, the 90-day no-ToS cleanup, and the 451 tos_not_accepted error shape.

# Terms of Service and jurisdiction

Marea Alcalina is incorporated under **Mexican law** (LFPDPPP — *Ley Federal de Protección de Datos Personales en Posesión de los Particulares*). All accounts agree to the ToS at sign-up; agent-bootstrapped accounts inherit a deferred-acceptance flow described below.

This page documents:

1. How ToS acceptance is tracked.
2. The `451 tos_not_accepted` error envelope.
3. The 90-day no-ToS cleanup (involuntary deletion).
4. Jurisdiction.

For the related Mexican data-rights flow (Acceso, Rectificación, Cancelación, Oposición), see [ARCO procedures](/concepts/arco-procedures).

## How acceptance is tracked

Each user document has a `tosAcceptedAt` timestamp:

| State               | `tosAcceptedAt` value     | Surface in `GET /v1/me`                 |
| ------------------- | ------------------------- | --------------------------------------- |
| Not yet accepted    | `null`                    | `tosAcceptedAt: null`                   |
| Accepted (any path) | ISO-8601 timestamp string | `tosAcceptedAt: "2026-04-15T10:00:00Z"` |

Dashboard sign-ups accept ToS at the same step that creates the account. **Agent-bootstrapped accounts** (`POST /v1/users`) are created with `tosAcceptedAt: null` and `agentBootstrapped: true` — the user is expected to accept ToS later via the dashboard modal during their first interactive visit.

## The 90-day cleanup

A scheduled job runs daily at 03:00 America/Mexico\_City and applies four sweeps for agent-bootstrapped accounts:

| Sweep                       | Trigger                                                  | Action                                 |
| --------------------------- | -------------------------------------------------------- | -------------------------------------- |
| Unverified 30d              | `verificationStatus: pending` + `createdAt < now-30d`    | Hard-delete (`reason: 30d_unverified`) |
| Day 7 reminder              | Verified, `tosAcceptedAt: null`, verified 7+ days ago    | Email reminder #1                      |
| Day 14 reminder             | Same, 14+ days, reminder count = 1                       | Email reminder #2                      |
| Day 60 pre-deletion warning | Same, 60+ days, no warning sent yet (feature-flag gated) | Email warning #3                       |
| No-ToS 90d                  | Verified, `tosAcceptedAt: null`, verified 90+ days ago   | Hard-delete (`reason: 90d_no_tos`)     |

The 90d cleanup is governed by Mexican counsel guidance — an agent that creates an account on behalf of a user does not have authority to bind that user to the ToS, so the user must accept (or implicitly reject by not visiting) within a bounded window.

## The 451 error envelope

`451 tos_not_accepted` is the error type that ToS-gated mutations return when the calling user has not accepted the ToS. The `POST /v1/storefronts/:storefrontId/publish` endpoint enforces this gate today (right after the plan paywall, before ownership / empty-storefront checks — see [Publishing](/concepts/publishing)). Other public-facing mutations will adopt the same gate as they ship.

```json theme={null}
{
  "error": {
    "type": "tos_not_accepted",
    "code": "tos_required",
    "message": "User must accept the Terms of Service before this operation.",
    "doc": "https://docs.mareaalcalina.com/concepts/tos-jurisdiction",
    "param": null,
    "recoverable": true,
    "retryAfterMs": null,
    "nextActions": [
      {
        "label": "Open the Marea dashboard and accept the Terms of Service.",
        "method": "GET",
        "url": "https://mareaalcalina.com/dashboard"
      }
    ],
    "upgrade": null
  }
}
```

Agent contract for `451 tos_not_accepted`:

* **Surface `nextActions[0].url` verbatim.** The user must accept the ToS through the dashboard modal — the agent **cannot** accept on the user's behalf.
* Treat it as `recoverable: true`. After the user accepts, the next call (same `Idempotency-Key`) succeeds.
* Do **not** retry without surfacing the action. This is the canonical agent-failure-mode for this error.

The enum value `tos_not_accepted` is locked in the error envelope (`src/api/contracts/error.zod.ts`). The publish endpoint enforces the gate today via `assertTosAccepted(ctx)` in `src/api/v1/storefronts.publish.ts`; a `users/{uid}.tosAcceptedAt` of `null` or absent fires the 451.

## Jurisdiction

| Property           | Value                                                                                    |
| ------------------ | ---------------------------------------------------------------------------------------- |
| Governing law      | Mexican federal law                                                                      |
| Privacy framework  | LFPDPPP (Articles 16–18 → user rights; see [ARCO procedures](/concepts/arco-procedures)) |
| Brazilian residual | LGPD disclosures apply to Brazilian residents                                            |
| Disputes           | Courts of Mexico City                                                                    |

The current production ToS is available at the dashboard sign-up flow. Modal copy is counsel-reviewed; do not paraphrase it here — the dashboard URL is the source of truth.

## What the agent surfaces to the user

| Situation                              | Surface                                                                                                  |
| -------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| Bootstrap completed                    | "Your account is created. Open the dashboard within 90 days to accept the Terms of Service."             |
| `451 tos_not_accepted`                 | Verbatim `nextActions[0].label` + click-through to `nextActions[0].url`                                  |
| Day-7 / Day-14 / Day-60 reminder email | The user receives these directly from Marea — agent doesn't surface anything                             |
| 90d cleanup fired                      | Agent receives a `user.cancelled` webhook with `reason: 90d_no_tos` (see [Webhooks](/concepts/webhooks)) |

## Verification in code

* `src/api/contracts/error.zod.ts` — locked `tos_not_accepted` enum value.
* `src/api/scheduled/CleanupUnverifiedAccounts.ts` — 30d/60d/90d sweeps.
* `src/api/constants/cleanup.constants.ts` — `TOS_DEADLINE_DAYS`, `TOS_REMINDER_DAYS`, `TOS_PREDELETION_WARNING_DAY`.
* `src/api/services/account-delete.service.ts` — `hardDeleteUserAccount` (reason: `90d_no_tos`).
