# NextStorage Object Storage

> Buckets, API keys, uploads, object URLs, and usage for NextStorage on NexHost.

Source: https://nexthomelabs.com/docs/storage/nextstorage
Markdown: https://nexthomelabs.com/docs-md/storage/nextstorage
Slug: storage/nextstorage
Section: Create
Last updated: 2026-09-14
Reading time: 6 min read

---

# NextStorage Object Storage

NextStorage is Nexthomelabs' fourth product alongside NexHost, ProperInbox, and Nextsend: S3-style object storage for applications. Create buckets in the dashboard, generate a NextStorage API key, upload files through the NextStorage API, and serve them from your `nexthomelabs.net` object URLs. Every workspace starts with a 10 GB free quota.

## What is NextStorage

NextStorage gives every workspace:

- **Buckets** — logical containers similar to R2 or S3 buckets.
- **Objects** — files addressed by a path-like object key and a unique object UID.
- **API keys** — `nhs_live_…` credentials for programmatic uploads.
- **Object URLs** — `https://storage.nexthomelabs.net/o/:objectUid` delivery addresses.
- **Usage** — stored bytes, bandwidth, requests, and object counts.

## Creating a Bucket

1. Open **NextStorage** in the dashboard.
2. Choose **Create Bucket**.
3. Enter a bucket name and choose **Private** (default) or **Public**.
4. Open the bucket to upload objects, browse by prefix, or change settings.

Buckets are isolated by workspace. Another workspace cannot list, read, or write your buckets.

Deleting a non-empty bucket requires explicit confirmation.

## Endpoint

The canonical Storage API lives on the dedicated storage host with no backend prefix:

```text
https://storage.nexthomelabs.net/v1
```

Object delivery uses the same host (`/o/:objectUid` and `/b/:bucketUid/:objectKey`). The older
`nexthomelabs.net/api/storage/v1` and `nexthomelabs.net/storage/...` paths keep working for
backward compatibility.

## API Authentication

Every programmatic request uses a workspace API key:

```http
Authorization: Bearer nhs_live_xxxxxxxxxxxxxxxxxxxxx
```

## Generating an API Key

1. Open **NextStorage**, then **API Access**.
2. Enter a key name and choose permissions.
3. Choose **Generate key**.
4. Copy the full key immediately — it is shown only once.

Available permissions:

- `storage:read`
- `storage:write`
- `storage:delete`
- `buckets:read`
- `buckets:write`

Revoke a key at any time. Revoked keys stop working immediately.

## Uploading Objects

```bash
curl -X POST "https://storage.nexthomelabs.net/v1/buckets/my-assets/objects" \
  -H "Authorization: Bearer nhs_live_CUSTOMER_API_KEY" \
  -F "file=@./photo.jpg" \
  -F "key=images/photo.jpg" \
  -F "visibility=public"
```

JavaScript:

```js
const form = new FormData();
form.append('file', file);
form.append('key', 'products/gown.jpg');
form.append('visibility', 'public');

const response = await fetch('https://storage.nexthomelabs.net/v1/buckets/my-assets/objects', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer nhs_live_CUSTOMER_API_KEY'
  },
  body: form
});

const object = await response.json();
console.log(object.url);
```

A successful upload returns:

```json
{
  "id": "obj_01K5DV7AXY0G6PPDX9M66ACZXQ",
  "bucket": "my-assets",
  "key": "products/gown.jpg",
  "name": "gown.jpg",
  "contentType": "image/jpeg",
  "size": 428193,
  "visibility": "public",
  "status": "available",
  "url": "https://storage.nexthomelabs.net/o/obj_01K5DV7AXY0G6PPDX9M66ACZXQ"
}
```

## Object Keys

Object keys are path-like names:

- `photo.jpg`
- `avatars/user-48/avatar.png`
- `products/gowns/gown-001.jpg`
- `videos/promos/september.mp4`
- `documents/contracts/agreement.pdf`

Keys support nested prefixes, unicode filenames, and folder-style listing with `?prefix=images/&search=photo`. Path traversal (`..`, empty segments) is rejected. The object UID is independent from the object key.

## Listing Objects

```bash
curl "https://storage.nexthomelabs.net/v1/buckets/my-assets/objects?prefix=images/&limit=100" \
  -H "Authorization: Bearer nhs_live_CUSTOMER_API_KEY"
```

## Retrieving Objects

Every object has a stable UID URL:

```text
https://storage.nexthomelabs.net/o/:objectUid
```

Bucket-style URLs use the globally unique bucket ID returned by the API:

```text
https://storage.nexthomelabs.net/b/:bucketUid/:objectKey
```

Clients stay on `nexthomelabs.net`. Delivery streams the object with the correct `Content-Type`, supports video range requests, and records bandwidth. Public images carry `ETag`/`Last-Modified` with browser and edge caching, so repeat `<img>` loads resolve to a fast `304` instead of a full download.

## Public Buckets

A public bucket is a bucket-wide grant: every current and future object can be opened through its
NextStorage URL without authentication. Use this for a dedicated website-assets bucket where all
files are intended for public delivery.

If the bucket is private, individual objects can still be made public from the **Objects** tab or
through the API. This is the safer choice when only selected images, videos, or documents should be
public.

```bash
curl -X PATCH "https://storage.nexthomelabs.net/v1/buckets/my-assets/objects/images/photo.jpg" \
  -H "Authorization: Bearer nhs_live_CUSTOMER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"visibility":"public"}'
```

Public URLs are delivery URLs, not API-only URLs. They return the real file with its stored
`Content-Type`, so a public image can be used directly in frontend markup:

```html
<img
  src="https://storage.nexthomelabs.net/o/obj_01K5DV7AXY0G6PPDX9M66ACZXQ"
  alt="Product photo"
>
```

Do not place a NextStorage API key in browser code. Public delivery requires no key and includes
cross-origin headers for websites and apps on other domains.

## Private Buckets

Private buckets are the default. A private object requires one of:

1. **A logged-in workspace session** — open the same-origin URL (`/api/storage/o/:uid`) in a
   browser signed into the owning workspace. The dashboard preview does exactly this, so a
   private image in a private bucket renders for members with bucket access and for nobody else.
2. **A signed URL** — mint `GET /storage/objects/:uid/signed-url` while logged in, then embed
   the returned `?expires&sig` URL in an `<img>` on any site. It is bound to that team, object,
   and expiry (default 15 minutes, max 24 hours) and needs no session or API key to render.
3. **A NextStorage API key** with `storage:read` scoped to the team/bucket (`Authorization: Bearer nhs_live_…`).

```bash
curl "https://nexthomelabs.com/api/storage/objects/obj_01K5DV7AXY0G6PPDX9M66ACZXQ/signed-url" \
  -H "Cookie: nexhost_session=YOUR_SESSION"
# {"url":"https://storage.nexthomelabs.net/o/obj_…?expires=…&sig=…","expiresAt":"…"}
```

```html
<img src="https://storage.nexthomelabs.net/o/obj_01K5DV7AXY0G6PPDX9M66ACZXQ?expires=1757865600&sig=abc123" alt="Private product photo">
```

Making a public bucket private locks every object in that bucket. Selected objects can then be
published again individually. Unauthenticated requests to private objects receive a branded private
file page during browser navigation and a machine-readable `FORBIDDEN` response for API requests.

## Videos and large files

Every video container is accepted (`mp4`, `mov`, `webm`, `mkv`, `avi`, and the rest) and single
files may use the full 10 GB quota. Uploads are quota-gated, not format-gated: a 5 GB video
succeeds while free quota remains and counts toward the 10 GB that unlocks paid storage. Keep the
browser tab open until progress reaches 100% — large uploads stream to disk on the server and
forward with an extended upstream timeout.

Two upload paths, different ceilings:

- **Dashboard browser upload** (same-origin `/api/storage/...`) crosses the CDN and tops out
  around 95 MB per file. Fine for images and short clips.
- **Storage-host upload** (`https://storage.nexthomelabs.net/v1/...` with a NextStorage API key)
  bypasses that CDN cap, so multi-GB videos succeed:

```bash
curl -X POST "https://storage.nexthomelabs.net/v1/buckets/my-assets/objects" \
  -H "Authorization: Bearer nhs_live_CUSTOMER_API_KEY" \
  -F "file=@./big-video.mkv" \
  -F "key=videos/big-video.mkv" -F "visibility=private"
```

Chunked/resumable browser uploads for multi-GB files are planned next; until then the API path
above is the supported route for anything over ~95 MB.

## Deleting Objects

```bash
curl -X DELETE "https://storage.nexthomelabs.net/v1/buckets/my-assets/objects/images%2Fphoto.jpg" \
  -H "Authorization: Bearer nhs_live_CUSTOMER_API_KEY"
```

Encode `/` in object keys as `%2F` when calling key-addressed routes. Deletion updates bucket usage immediately.

## Usage and Limits

The dashboard shows buckets, objects, storage used, bandwidth used, and request counts. Uploads check workspace quota before contacting storage, and oversized uploads are rejected with `STORAGE_QUOTA_EXCEEDED` or `UPLOAD_FAILED`. The free tier is 10 GB per workspace with up to 25 buckets; single objects may be up to 10 GB.

## Errors

NextStorage returns its own error codes:

| Code | Meaning |
| --- | --- |
| `INVALID_API_KEY` | The supplied NextStorage API key is invalid. |
| `BUCKET_NOT_FOUND` | The requested NextStorage bucket was not found. |
| `OBJECT_NOT_FOUND` | The requested NextStorage object was not found. |
| `STORAGE_QUOTA_EXCEEDED` | Your NextStorage storage quota has been exceeded. |
| `UPLOAD_FAILED` | NextStorage could not complete the upload. |
| `STORAGE_UNAVAILABLE` | NextStorage is temporarily unable to access this object. |

