---
title: "List the workspace's managed repos"
description: "Every repo in the workspace registry: display name, purpose (base, site, app, or other), the template it was created from (template_slug, null for..."
api_method: GET
api_path: "/v1/workspaces/{workspaceId}/repos"
canonical_url: https://wiblo.app/docs/developers/api/repos/list-workspace-repos
last_updated: 2026-10-11T19:02:53+02:00
md_url: https://wiblo.app/docs/developers/api/repos/list-workspace-repos.md
---

# List the workspace's managed repos

`GET /v1/workspaces/{workspaceId}/repos`

Every repo in the workspace registry: display name, purpose (`base`, `site`, `app`, or `other`), the template it was created from (`template_slug`, null for an empty repo), status, default branch, and the REAL clone URL once the repo is ready — the discovery surface behind `wiblo repos list`, `wiblo repos clone`, and native `git clone` inside a sandbox. Every active member may call it; the sandbox's per-session workspace key (`wbl_ephem_ws_*`) is an expected bearer. Per-user rate-limited at 60/60s.

## Path parameters

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `workspaceId` | `string` | Yes |  |

## Request

**curl**

```bash
curl https://api.wiblo.app/v1/workspaces/{workspaceId}/repos \
  -H "Authorization: Bearer $WIBLO_TOKEN"
```

**TypeScript**

```ts
import { createSdk, listWorkspaceRepos } from "@workspace/sdk"

const sdk = createSdk({ baseUrl: "https://api.wiblo.app" })

const { data, error } = await listWorkspaceRepos({
  client: sdk,
  path: { workspaceId: "..." },
})
```

## Responses

**`200`** — The workspace repo registry. Returns `WorkspaceRepoListResponse`.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `repos` | `array<WorkspaceRepoSummary>` | Yes |  |

**`401`** — No valid session, `wbl_*` bearer, or run token was present. Returns `ApiErrorEnvelope`.

**`404`** — Workspace not visible to the caller (`WORKSPACE_NOT_FOUND`). Returns `ApiErrorEnvelope`.

**`422`** — Path param failed UUID validation (`INVALID_PARAMS`). Returns `ApiErrorEnvelope`.

**`429`** — Rate limit exceeded. The body's `error.code` is `RATE_LIMITED` and `error.details.retry_after` is the same number of seconds as the `Retry-After` header. Returns `ApiErrorEnvelope`.

### The WorkspaceRepoSummary object

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `string` | Yes |  |
| `display_name` | `string` | Yes |  |
| `purpose` | `"base" \| "site" \| "app" \| "other"` | Yes |  |
| `template_slug` | `string \| null` | Yes |  |
| `status` | `"provisioning" \| "ready" \| "failed" \| "archived"` | Yes |  |
| `default_branch` | `string` | Yes |  |
| `clone_url` | `string \| null` | Yes |  |
| `site` | `object \| null` | Yes |  |
| `site.id` | `string` | Yes |  |
| `site.slug` | `string` | Yes |  |
| `site.region` | `"iad1" \| "sfo1" \| "cpt1" \| "syd1" \| "lhr1"` | Yes |  |
| `site.status` | `"provisioning" \| "ready" \| "failed" \| "archived"` | Yes |  |
| `site.failure` | `"address_taken" \| "provider" \| null` | Yes | Why the site is `failed`, and null for every other status. `address_taken`: the deploy provider already holds a project named by the slug that no site owns, so the same create refuses again; rename the site with `PATCH .../sites/{siteId}` (`wiblo sites rename`). `provider`: Vercel refused or did not answer; the same create again, or the next build, retries. |
| `site.live_url` | `string \| null` | Yes |  |
| `site.latest_version` | `object \| null` | Yes | The newest version that is `queued`, `building`, `ready`, or `error`, with its `number` and its `status`. The `number` is null until that version is first ready. An `error` version whose deployment is gone keeps the number it had. On the create answer, this is version one as the create left it. It is null there when version one could not be asked for or started, or Vercel refused the project. On the list answer, it is null when the site has no such version. |
| `created_at` | `string` | Yes |  |
| `updated_at` | `string` | Yes |  |
