---
title: "Delete Projects Batch"
method: DELETE
path: "/projects"
---

# Delete Projects Batch

`DELETE /projects`

## Request

- Base URL: `https://usemeridian.us` (the document declares no server; this is the origin it was published from)
- URL: `https://usemeridian.us/projects`
- Auth: none declared

0e4980d4 — batch delete: accepts multiple project ids in one call.

Repeated query param: ``DELETE /projects?project_id=id1&project_id=id2``.
Query params (not a DELETE body) are used deliberately — ``httpx``/many
HTTP clients and proxies don't reliably support a request body on DELETE,
while repeated query params are universally supported and match FastAPI's
native ``list[str] = Query(...)`` binding.

Complements the single-project ``DELETE /projects/{project_id}`` above by
driving ``db_module.delete_project`` with a list, which runs each
child-table DELETE once with a single ``WHERE project_id IN (...)``
across the whole batch instead of looping the full per-table statement
set once per project (see the db function's docstring for the batching
rationale).

All-or-nothing like the single-project path: every id is validated to
exist (404, naming the unknown ids) and the in-progress-task guard is
checked across the whole batch (409) before anything is deleted — a
guard violation or an unknown id aborts the entire batch, not just the
offending project.

3f4ba195 — explicit PERM_SETTINGS guard (authorization-bypass fix).
``_required_perm_for_request`` (meridian/_deps.py) maps whole-project
deletion to PERM_SETTINGS only via its ``path.startswith("/projects/")``
branch, which the bare ``/projects`` path this batch route answers on
never satisfies — it silently fell through to the PERM_WRITE default,
so a cross-workspace 'member' (PERM_WRITE but not PERM_SETTINGS) could
delete an owner's project through this batch form even though the
identical action via the singular ``DELETE /projects/{project_id}``
route is correctly owner/admin-gated by that same middleware mapping.
Rather than change the generic path-prefix mapping in ``_deps.py``
(out of scope for this fix), enforce the same requirement explicitly
here — independent of the middleware's coarser default — mirroring the
inline ``_require_workspace_perm`` guard ``routes/export.py`` uses for
``POST /account/delete``. A no-op for self-hosted / same-workspace /
non-member callers, exactly like the middleware itself (``ctx is None``).

## Query parameters

- `project_id` string[], required

## Response `200`

Successful Response

- object

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/usemeridian/apis/meridian.md) · [All operations](https://skmtc.dev/usemeridian/apis/meridian/llms.txt) · [OpenAPI document](https://skmtc.dev/usemeridian/apis/meridian/revisions/f37a894bd374?raw)
