---
id: w6/m2/t002
title: REST GET /v1/owners + GET /v1/owners/{ownerId} (filters, user-ID→default-workspace)
worker: worker6
status: done
estimate: 30m
depends_on: [w6/m2/t001, w6/m1]
---

## Objective

The two core read endpoints, shaped exactly per t001's pinned schema: `GET /v1/owners` lists the workspaces the caller's key/session can access (optional `name` / `email` filters, Render's cursor envelope); `GET /v1/owners/{ownerId}` retrieves one — and, matching Render's documented quirk, a user ID (`own-…`) returns that user's **default workspace** rather than an error.

## Context

- REST adapter: `lego/backend/internal/api/` over core verbs (docs/ADR006-bex-api.md); m1/t006 built the core list path — add a get-by-id.
- "Default workspace" for bex = the m9-minted workspace (first membership); encode that mapping in core.
- ID rendering: tenants already use `tea-` ids (OpenFGA `workspace:tea-<id>`); mint/expose `own-` ids for identities where needed.
- API keys are user-scoped (finding 10) — the key's identity, not the key, determines visible workspaces.

## Steps

1. Core: `GetWorkspace(id)` with the own-→default-workspace resolution; list filters.
2. REST handlers + routes; Render envelope + error shapes (401/404 consistent with existing endpoints).
3. Register in the OpenAPI/route table the backend maintains, if any.

## Files

- `lego/backend/internal/api/`
- `lego/backend/internal/core/`

## Acceptance criteria

- [x] Both endpoints return t001's pinned shapes byte-compatible modulo values
- [x] `own-…` id on the retrieve endpoint returns the caller's default workspace
- [x] Cross-tenant get (a workspace the caller isn't a member of) is 403/404 per Render's behavior

## Out of scope

- Members endpoint (t003); mutations (never — finding 9).
