Skip to content
Open
295 changes: 295 additions & 0 deletions docs/content/console/api-reference.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,295 @@
---
title: API Reference
description: The Walrus Console external API surface for spaces, buckets, files, and Seal sponsorship, including authentication, conventions, and error codes.
keywords: [walrus console, api reference, spaces, buckets, files, seal, api keys]
---

This reference covers the Walrus Console external API: the endpoints third-party developers use to manage spaces, buckets, and files, plus the [Seal](/docs/data-security) sponsorship endpoints that back private-bucket access control.

:::info

This documents the current Testnet surface. The API is in alpha and currently only available on Testnet at `https://api.testnet.harbor.walrus.xyz`. The endpoint structure might change before Mainnet GA. For a guided walkthrough of the full encrypted flow, start with the [Quick Start](./quickstart). For the product model behind these endpoints, see the [concepts and overview](./overview).

:::

## Authentication

Every request carries an API key as a bearer token:

```http
Authorization: Bearer hbr_…
```

Mint keys through the Console web interface. Each key has one of two roles:

- `read_write` keys can change state: create and delete buckets, upload and delete files, and finalize private buckets.
- `read_only` keys can list, check status, and download only. Any write with a read-only key returns `403` with code `read_only_api_key`.

A request with no valid key returns `401`. A key without access to the target resource returns `403`.

## Base URL and conventions

All paths are relative to the base URL and prefixed with `/api/v1`:

```
https://api.testnet.harbor.walrus.xyz
```

Request and response bodies are JSON unless noted (file upload uses `multipart/form-data`, and download returns a raw byte stream). Resource identifiers are UUIDs.

### Pagination

List endpoints return an opaque cursor. Pass `limit` to set page size and `cursor` to fetch the next page. When there are no further results, the cursor field returns `null`. Bucket lists return the cursor as `next_cursor`; file lists return it under `pagination.nextCursor`.

### Asynchronous operations

Upload and delete operations are asynchronous. Upload returns `202` with a file ID, then you poll the file status endpoint until the state is `completed`. Delete returns `204` immediately and releases storage after a background worker confirms the removal.

## Errors

Error responses use the shape:

```json
{ "error": "Human-readable message", "code": "machine_readable_code" }
```

The `code` field is present when a machine-readable value applies. Common error codes include:

| Code | Meaning |
| --- | --- |
| `unauthorized` | Missing or invalid API key. |
| `read_only_api_key` | A write was attempted with a read-only key. |
| `api_key_registering` / `api_key_revoking` / `api_key_revoked` | The key is not currently usable. |
| `bucket_not_in_scope` | The key cannot access the target bucket. |
| `bucket_not_finalized` | The bucket has not completed the finalize step. |
| `mirror_missing_grant` | The onchain access grant has not yet mirrored into the access index. Retry. |
| `quota_exceeded` | The operation exceeds the plan or storage quota. |
| `payload_too_large` | The upload exceeds the size limit. |
| `bad_request` | The request failed validation. |
| `USED_NONCE` / `EXPIRED_TIMESTAMP` / `ADDRESS_MISMATCH` / `INVALID_CHALLENGE` | Wallet challenge errors, returned by the signature-authentication flow. |

Create-bucket requests that exceed a plan limit return `422` with a distinct shape:

```json
{ "code": "PLAN_LIMIT_EXCEEDED", "limit": "storage", "currentTier": "free" }
```

The `limit` value is one of `storage`, `users`, or `buckets`.

## Spaces

A space is the top-level container for your data. Console creates a Personal Space at sign-up.

### List spaces

`GET /api/v1/spaces`

Lists all spaces accessible to the authenticated user.

| Parameter | Location | Required | Description |
| --- | --- | --- | --- |
| `type` | query | No | Filter by space type: `personal` or `team`. |

Returns `200` with a `data` array of spaces. Each space includes `id`, `type`, `name`, `storage_used`, `storage_cap`, `bucket_count`, `role`, and `created_at`. Team Spaces also include `member_count`.

### List buckets in a space

`GET /api/v1/spaces/{id}/buckets`

| Parameter | Location | Required | Description |
| --- | --- | --- | --- |
| `id` | path | Yes | Space UUID. |
| `limit` | query | No | Page size, 1 to 1000. Default 100. |
| `cursor` | query | No | Cursor from a previous page. |
| `q` | query | No | Case-insensitive substring match on bucket name. |
| `visibility` | query | No | Filter by `public` or `private`. |
| `sortField` | query | No | `name`, `date`, or `storage`. Default `date`. |
| `sortOrder` | query | No | `asc` or `desc`. Default `desc`. |

Returns `200` with `buckets` and `next_cursor`.

### Create a bucket

`POST /api/v1/spaces/{id}/buckets`

Reserves a private bucket. This is the first step of the reserve, sign, and finalize handshake. In the current alpha, `scope` accepts `private` only.

Body:

```json
{ "name": "secrets", "scope": "private" }
```

Returns `201` with the reservation:

```json
{
"bucket_id": "…",
"bytes": "<base64 sponsored Sui transaction>",
"digest": "…",
"state": "pending_policy"
}
```

Sign `bytes` locally with your service key, then call the finalize endpoint. The bucket stays in `pending_policy` and accepts no uploads until finalize succeeds. A name conflict returns `409`. A plan-limit breach returns `422`. See the [Quick Start](./quickstart) for the full signing flow.

### List files in a space

`GET /api/v1/spaces/{id}/files`

Searches files across every bucket in the space.

| Parameter | Location | Required | Description |
| --- | --- | --- | --- |
| `id` | path | Yes | Space UUID. |
| `limit` | query | No | Page size, 1 to 100. Default 20. |
| `cursor` | query | No | Cursor from a previous page. |
| `q` | query | No | Case-insensitive substring match on file name. |
| `sortField` | query | No | `name`, `date`, `size`, or `type`. Default `date`. |
| `sortOrder` | query | No | `asc` or `desc`. Default `desc`. |

Returns `200` with `data` and a `pagination` object (`limit`, `hasMore`, `nextCursor`).

## Buckets

A bucket holds files inside a space. Private buckets store [Seal](/docs/data-security) ciphertext only.

### Get a bucket

`GET /api/v1/buckets/{id}`

Returns `200` with the bucket under `data`: `id`, `space_id`, `name`, `oyster_bucket_name`, `visibility`, `seal_policy_id`, `storage_used`, `created_at`, and `updated_at`.

### Update a bucket

`PUT /api/v1/buckets/{id}`

| Field | Required | Description |
| --- | --- | --- |
| `name` | Yes | New bucket name, 1 to 100 characters in length. |
| `sealPolicyId` | No | Seal policy ID. Pass `null` to clear. |
| `visibility` | No | Immutable in v1. Changing it returns `403`. |

Returns `200` with the updated bucket (`id`, `name`, `visibility`, `updated_at`). A name conflict returns `409`.

### Delete a bucket

`DELETE /api/v1/buckets/{id}?confirm=true`

Requires the `confirm=true` query parameter, and the bucket must be empty. Returns `204` with an `X-Deleted-Id` header. A missing confirmation, a validation error, or a non-empty bucket returns `400`.

### Finalize a private bucket

`POST /api/v1/buckets/{id}/finalize`

Submits the signature over the reserved transaction bytes to activate a pending private bucket.

Body:

```json
{ "signature": "<base64 signature>" }
```

Returns `200`:

```json
{ "bucket_id": "…", "seal_policy_id": "…", "state": "active" }
```

`seal_policy_id` is the onchain bucket-policy object ID used by Seal for access checks. The bucket is now ready for uploads.

### List files in a bucket

`GET /api/v1/buckets/{id}/files`

Takes the same `limit`, `cursor`, `q`, `sortField`, and `sortOrder` parameters as the space file listing. Returns `200` with `data` and `pagination`.

### Upload a file

`POST /api/v1/buckets/{id}/files`

Uploads a file asynchronously using `multipart/form-data`.

| Field | Required | Description |
| --- | --- | --- |
| `file` | Yes | The file bytes. For private buckets, upload the Seal-encrypted object. |
| `name` | No | File name, up to 255 characters in length. |
| `metadata` | No | JSON object string persisted with the file. Maximum 8 KiB. |

Returns `202` with the file summary under `data`. Poll the status endpoint until the state is `completed`.

:::info

After finalize, the onchain access grant takes a few seconds to propagate to the access index. During this window, uploads fail with a `403` and the error code `mirror_missing_grant`. Retry every few seconds until the grant appears.

:::

Other responses include `409` (bucket not finalized or duplicate file name), `413` (payload too large), `422` (quota exceeded), and `429` (rate limited).

## Files

### Get file metadata

`GET /api/v1/buckets/{id}/files/{fileId}`

Returns `200` with the file summary under `data`: `id`, `bucket_id`, `name`, `blob_id`, `oyster_object_id`, `size`, `mime_type`, `status`, `is_private`, `metadata`, `created_at`, `updated_at`, and `deleted_at`.

### Delete a file

`DELETE /api/v1/buckets/{id}/files/{fileId}`

Marks the file as `deleting` and enqueues a background job to remove the underlying blob. Returns `204` immediately. Storage is released after the worker confirms the delete. Repeated calls for the same file ID are deduplicated.

### Get upload status

`GET /api/v1/buckets/{id}/files/{fileId}/status`

Polls the state of an in-flight upload after the upload endpoint returns `202`.

Returns `200` with `data.state`, one of `queued`, `active`, `completed`, or `failed`. When the worker reports it, `data.progress` gives fractional progress from 0 to 1. When the state is `failed`, `data.error` includes a `code` and `message`. Once the job leaves the queue this endpoint returns `404`, which does not mean the upload failed. If you get a `404` after uploading, check the file metadata endpoint to confirm the final status before treating it as an error.

### Download a file

`GET /api/v1/buckets/{id}/files/{fileId}/download`

Streams the raw file content with `Content-Type` and `Content-Disposition` headers. For a private bucket, this returns the Seal ciphertext, which you decrypt client-side. See the [Quick Start](./quickstart) for the decrypt flow.

A file in `deleting` or `deleted` status returns `423`. An unavailable blob stream returns `500`.

## Seal sponsorship

These endpoints build and sponsor the onchain bucket-policy transactions that manage private access. Console sponsors the gas through [Enoki](https://docs.enoki.mystenlabs.com/), so your service key signs but does not need a token balance. They are typically driven through the SDK patterns shown in the [Quick Start](./quickstart).

### Sponsor a transaction

`POST /api/v1/seal/sponsor`

Builds a sponsored bucket-policy transaction and returns the bytes to sign plus a digest to echo to the execute endpoint. The body's `kind` selects the operation:

| `kind` | Purpose | Additional fields |
| --- | --- | --- |
| `bucket_group_create` | Create a bucket access group. | `bucketId` |
| `grant_bucket_access` | Grant access to recipients. | `groupIds`, `recipientAddress`, `scope` (`read` or `readwrite`) |
| `share_admin` | Add an admin to a group. | `groupId`, `member` |
| `unshare` | Remove a member from a group. | `groupId`, `member` |
| `unshare_bucket_access` | Revoke a service signer's access. | `groupId`, `serviceSignerAddress` |

Returns `200` with `bytes` and `digest`.

### Execute a sponsored transaction

`POST /api/v1/seal/sponsor/{digest}/execute`

Submits the signature over the sponsored bytes. Console broadcasts the transaction and returns the onchain digest.

| Parameter | Location | Required | Description |
| --- | --- | --- | --- |
| `digest` | path | Yes | The sponsor digest from the sponsor endpoint. |

Body:

```json
{ "signature": "<base64 signature>" }
```

Returns `200` with the onchain `digest`. An expired or unknown digest returns `404`.
43 changes: 43 additions & 0 deletions docs/content/console/auth.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
title: Authentication and Accounts
description: How Walrus Console sign-in works with Google and Apple through Sui zkLogin, how your Pearl wallet is provisioned, and how accounts stay separate per identity.
keywords: [walrus console, sign in, zklogin, pearl wallet, apple, google, accounts]
---

Walrus Console uses the same kind of login you already know using Google or Apple; no wallet, no seed phrase, no crypto setup. Just sign in and start storing data.

:::info

Walrus Console is available on Mainnet through a closed, invite-only beta. It currently supports Google sign-in, and plans to support Apple soon. For the product model, see the [concepts and overview](./overview).

:::

## How sign-in works

Console uses Sui [zkLogin](https://docs.sui.io/concepts/cryptography/zklogin). You authenticate with an identity provider you already have, and Console derives a [Sui address](https://docs.sui.io/concepts/cryptography/transaction-auth/keys-addresses) from that identity without exposing your provider account onchain and without asking you to manage a private key.

## Sign in with Google

1. Visit the [Walrus Console app](https://testnet.harbor.walrus.xyz/).
2. Choose **Continue with Google** and complete the Google sign-in.
3. Console provisions your account and a [Personal Space](./overview#core-concepts-spaces-buckets-and-files), then takes you to the dashboard.

Apple sign-in joins soon and works the same way, deriving your Sui address from your Apple identity. It accepts Apple's private email relay, so you can use the **Hide My Email** option.

## Your Pearl wallet

On first sign-in, Console silently provisions a Pearl wallet, the embedded wallet Console manages on your behalf, for your derived Sui address. This wallet holds the storage resources your data uses. Console manages the wallet and paying for transactions for you, so you do not need to fund it or sign transactions to get started. More advanced workflows require explicit signing steps.

## Accounts stay separate per identity

:::warning

Each identity provider maps to a separate Console account and a separate Sui address. If you sign in with Google and later sign in with Apple, you get two independent accounts, not one merged account. Data stored under one is not visible under the other.

:::

Console shows an account-separation notice the first time you sign in so this is clear before you store anything. Choose one provider and use it consistently. Linking multiple providers to a single account is planned for a later release.

## Wallet sign-in

Signing in with an existing Sui wallet is planned for a later release. For now, sign in with Google or Apple through zkLogin. If you hold assets in a personal Sui wallet, note that data you store through Console lives under your zkLogin-derived address, which is separate from a wallet you connect later.
Loading
Loading