---
id: w2/m4/t003
title: REST DELETE /v1/services/{id} (204) + GraphQL deleteService + MCP delete_service
worker: worker2
status: done
estimate: 25m
depends_on: [w2/m4/t002]
---

## Objective

Expose the t002 verb on all three surfaces: REST `DELETE /v1/services/{id}` returning 204 (shape verified against Render's OpenAPI; also under the `/v1/apps` alias), GraphQL `deleteService(id)` mutation for the dashboard, and MCP `delete_service` (`{serviceId}`) — a bex extension named after the REST-verb pattern (like `restart_service`), since Render's official MCP server ships no delete tool.

## Context

- The create-side fragments already exist in `lego/backend/internal/apps/{rest,graphql,mcp}.go` (from m2/t001) — the delete fragments sit beside them, delegating to the same `Service`.
- Error mapping: `ErrNotFound` → 404, unauthorized → 403, success → 204 empty body (match the Render spec's delete semantics exactly).

## Steps

1. REST route + error mapping in `rest.go`; verify status/shape against `render-public-api-1.json`.
2. GraphQL `deleteService` mutation matching the existing mutation style; MCP `delete_service` registration.
3. Update docs/ADR006-bex-api.md REST table + MCP tool table (marked as bex extension).

## Files

- `lego/backend/internal/apps/rest.go`, `lego/backend/internal/apps/graphql.go`, `lego/backend/internal/apps/mcp.go`
- `docs/ADR006-bex-api.md`

## Acceptance criteria

- [ ] All three surfaces delete through the identical `Service.Delete` (no second implementation).
- [ ] `DELETE /v1/services/{id}` → 204; unknown id → 404; docs tables updated.

## Out of scope

- Dashboard delete UI (w5); acceptance (t004).
