YAML Source is the Network Storage authoring standard.
Use the s&box Library Manager install for auto-updates. GitHub is best for agents, source review, contributions, and manual installs.

Collections HTTP API

Use the Collections HTTP API when a dedicated server or backend service needs to read, insert, update, or delete collection data directly. For normal player-fac...

# Collections HTTP API

Use the Collections HTTP API when a dedicated server or backend service needs to read, insert, update, or delete collection data directly. For normal player-facing gameplay, prefer server-authoritative endpoints. Direct collection writes are powerful and should be kept server-side.

## Authentication

Every request includes the project ID in the URL and a Network Storage secret key. Public keys cannot call direct collection APIs; game clients should use endpoints or queries.

Dedicated-server or backend access uses a secret key with **Collections: Execute** permission:

```http
x-api-key: sbox_sk_YOUR_SECRET_KEY
```

Secret keys with **Collections: Execute** can access collections through this API. Read/write key permissions are for the Sync Tool and Management API; execute is the runtime permission for hosted servers and backend systems.

Secret-key collection API calls are treated as trusted dedicated-server/backend authentication and s&box auth tokens are not required.

## Per-player collection records

Base URL:

```text
https://api.sboxcool.com/v3/storage/{projectId}/{collectionId}/{key}
```

`collectionId` may be the collection id or collection name from the dashboard/YAML Source. `key` is usually a Steam ID, a `playerKey`, or a save-slot key such as `{steamId}_{recordId}`.

### Read a record

```http
GET /v3/storage/{projectId}/{collectionId}/{key}
x-api-key: sbox_sk_...
```

Response is the saved JSON document.

### Insert or replace a record

```http
POST /v3/storage/{projectId}/{collectionId}/{key}
content-type: application/json
x-api-key: sbox_sk_...
```

Body shape, shown as YAML here for documentation (send the equivalent JSON object at runtime):

```yml
playerName: Ada
xp: 1200
gold: 45
```

This creates the row when it does not exist, or replaces the full document when it already exists. The final document validates against the collection schema, applies save-version protection when enabled, updates unique indexes, writes ledger entries for ledger-tracked fields, and returns the saved document.

### Patch with operations

Send an operations body instead of a full document:

```http
POST /v3/storage/{projectId}/{collectionId}/{key}
content-type: application/json
x-api-key: sbox_sk_...
```

Body shape, shown as YAML here for documentation (send the equivalent JSON object at runtime):

```yml
ops:
- op: inc
path: xp
value: 50
source: dedicated-server
reason: Match reward
- op: set
path: lastMatchId
value: match_123
```

Operations are applied to the existing document, then the final document is schema-validated and saved. Use this for edits/patches when you do not want to send the entire document.

### Delete a record document

```http
DELETE /v3/storage/{projectId}/{collectionId}/{key}
x-api-key: sbox_sk_...
```

For public-key/client calls, the collection must have record deletion enabled. Secret keys with **Collections: Execute** can delete rows/documents for dedicated-server diagnostics and backoffice cleanup even when public record deletion is disabled. This removes the saved JSON document, ledger file, unique-index claims, and the save-slot index entry when the key matches a save-slot record.

This deletes a row/record only. Collection definitions themselves can only be deleted from the website dashboard, where two-step verification is enforced.

## Save-slot helper routes

For collections with multiple records per player:

```http
GET /v3/storage/{projectId}/{collectionId}/{steamId}/records
POST /v3/storage/{projectId}/{collectionId}/{steamId}/records
PATCH /v3/storage/{projectId}/{collectionId}/{steamId}/records/{recordId}
DELETE /v3/storage/{projectId}/{collectionId}/{steamId}/records/{recordId}
```

These list, create, rename, and delete save-slot records. `DeleteRecord` deletes an individual save slot and its document data; it does not delete the collection definition.

## Global collections

Global collections use append/list/get routes:

```http
POST /v3/storage/{projectId}/{collectionId}/append
GET /v3/storage/{projectId}/{collectionId}/list?limit=50
GET /v3/storage/{projectId}/{collectionId}/record/{recordId}
```

`append` validates and stores a new global record with server-managed `_id`, `_timestamp`, and `_writerId` fields.

## Official s&box library helpers

The official library wraps the HTTP routes above:

```csharp
// Read a document. If documentId is omitted, the current player's Steam ID is used.
var player = await NetworkStorage.GetDocument( "players", "76561198000000001" );

// Create or replace a full document.
await NetworkStorage.SaveDocument( "players", "76561198000000001", new
{
playerName = "Ada",
xp = 1200,
gold = 45
} );

// Edit an existing document with server-side operations.
await NetworkStorage.UpdateDocument( "players", "76561198000000001",
NetworkStorageOperation.Increment( "xp", 50, source: "dedicated-server", reason: "Match reward" ),
NetworkStorageOperation.Set( "lastMatchId", "match_123" ) );

// Delete one row/document.
await NetworkStorage.DeleteDocument( "players", "76561198000000001" );
```

Save-slot helper methods:

```csharp
var records = await NetworkStorage.ListRecords( "saves", steamId );
var created = await NetworkStorage.CreateRecord( "saves", steamId, "Slot 1" );
await NetworkStorage.RenameRecord( "saves", recordId, "Renamed Slot", steamId );
await NetworkStorage.DeleteRecord( "saves", recordId, steamId );
```

Dedicated servers load the runtime secret from `+network_storage_secret_key sbox_sk_...`; see [Dedicated Server Runtime](/wiki/network-storage-v3/dedicated-server-runtime).

## Errors

Common errors:

| Code | Meaning |
|------|---------|
| `UNAUTHORIZED` | Missing or invalid key. |
| `FORBIDDEN` | Secret key does not have Collections: Execute permission. |
| `ENDPOINT_ONLY` | Public key tried to access an endpoint-controlled collection directly. Use a secret key with Collections: Execute or route through an endpoint. |
| `SCHEMA_VALIDATION_FAILED` | The final document does not match the collection schema. |
| `RECORD_DELETE_DISABLED` | Public/client delete attempted on a collection that does not allow record deletion. Dedicated secret keys with `collections: x` can delete rows for server/backoffice cleanup. |
| `UNIQUE_CONFLICT` | A `_unique` field value is already claimed by another record. |
| `STALE_SAVE` | Save-version protection rejected an outdated save. |