# Collections

A collection is a named set of documents with a schema. You search one collection at a time
(with SQL you name it in `FROM`), and you create, size and delete data per collection. Collections
are isolated: no query can reach another organization's data or even another collection you have
no role on.

## Names

A collection name starts with a lowercase letter and then uses only lowercase letters, digits and
underscores: `^[a-z][a-z0-9_]*$`.

| Valid | Invalid |
|---|---|
| `books` | `Books` (uppercase) |
| `beir_scifact` | `beir-scifact` (hyphen) |
| `logs2026` | `2026_logs` (starts with a digit) |

Names are unique within an organization. A name that breaks the rule is rejected with
`400 invalid_collection_name`; creating one that already exists answers `409 already_exists`.

## Create a collection

Creating a collection needs the `admin` role. How many collections you can have depends on your
plan: past it you get `403 limit_exceeded`. See [Pricing](pricing.md#free-tier) and
[Limits and errors](limits-and-errors.md).

```bash
curl -X PUT https://api.warpsearch.io/v1/collections/books \
  -H "Authorization: Bearer $WARPSEARCH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "description": "Book catalog",
    "class": "search",
    "labels": {"team": "catalog", "env": "prod"},
    "metadata": {"team": "catalog"},
    "mappings": {
      "properties": {
        "title":     {"type": "text"},
        "genre":     {"type": "keyword"},
        "year":      {"type": "integer"},
        "embedding": {"type": "dense_vector", "dims": 384, "similarity": "cosine"}
      }
    }
  }'
```

The body is optional, and so is every key in it:

| Key | Meaning |
|---|---|
| `description` | Free text, up to 2000 characters. |
| `class` | What the collection is for: `search` (the default), `knowledge`, `stream` or `archive`. See [Collection classes](#collection-classes). Fixed at creation. |
| `labels` | Up to 64 string key/value pairs, such as `{"env": "prod"}`. Keys are up to 63 letters, digits, `-`, `_` or `.`, optionally with a DNS-style prefix and `/`. Keys starting `warpsearch.io/` are reserved. |
| `metadata` | A JSON object of your own, up to 16 KB. Use it for owner, environment or anything you want to find later. |
| `mappings` | Field definitions in Elasticsearch mapping format. A bare `{"properties": ...}` also works. See [Schemas and field types](schemas-and-fields.md). |
| `settings` | Elasticsearch-style index settings. Only `analysis` (custom analyzers and normalizers) is used; the rest is ignored with a warning. |

You can also create collections in the [management console](/console/) under **Collections**,
with the MCP `create_collection` tool, or implicitly through the Elasticsearch-compatible
`_bulk` API when your key may create collections. Plain `POST` to `/v1/collections/{c}/documents`
does not create a collection; it answers `404` for one that does not exist.

### Collection classes

A class tells WarpSearch what the collection is for, so it can pick storage and lifecycle defaults,
most visibly how long the collection stays warm. It does not change the price per request or per
GB. An unknown class answers `400 invalid_class`.

| Class | For | Warmth |
|---|---|---|
| `search` | Catalog, site and app search, records | Released after 15 minutes idle; keep-warm offered |
| `knowledge` | RAG chunks, embeddings, hybrid search | Released after 15 minutes idle; keep-warm offered |
| `stream` | Logs, events, SIEM, metrics | Only recent partitions stay on NVMe; older ones load on demand when a query's time range reaches them |
| `archive` | Compliance, legal hold, rarely queried history | Never warm; a cold launch is slow |

A new collection can be written to straight away. Define the fields you care about up front: you
can add fields later, but changing an existing field does not rewrite documents already stored. See
[Schemas and field types](schemas-and-fields.md#changing-a-schema).

## List and describe

```bash
curl https://api.warpsearch.io/v1/collections \
  -H "Authorization: Bearer $WARPSEARCH_API_KEY"

curl https://api.warpsearch.io/v1/collections/books \
  -H "Authorization: Bearer $WARPSEARCH_API_KEY"
```

The first call lists the collections your key can see. A collection you hold no role on is not in
the list, and asking for it answers `404`, exactly as for a name that does not exist. The second
returns one collection:

| Field | Meaning |
|---|---|
| `name`, `class`, `description`, `labels`, `metadata` | What you set at creation (labels and metadata can change). |
| `engine_version` | Present when WarpSearch pins the collection to an engine version. |
| `fields` | Each field's `name`, `type` and whether it is `multi`-valued. |
| `doc_count`, `size_bytes`, `stats_at` | Size figures. They are refreshed about once a minute, so they can lag a recent write. |
| `created_at`, `created_by` | When and by whom (a user or an API key). |
| `time_field`, `message_field`, `default_fields`, `id_field` | Defaults the query languages use, for example which fields a bare Lucene term searches. |

The console shows the same figures on each collection's page, and `GET /v1/usage` shows
organization totals against your plan's limits.

## Change description, metadata and fields

`PATCH` changes the description, metadata and labels. Metadata and labels merge key by key, and
setting a key to `null` removes it:

```bash
curl -X PATCH https://api.warpsearch.io/v1/collections/books \
  -H "Authorization: Bearer $WARPSEARCH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"metadata": {"team": null, "owner": "search"}}'
```

To add fields, `PUT /v1/collections/{c}/mapping` takes a mapping with the new properties.
Both calls need the `admin` role.

## Delete a collection

```bash
curl -X DELETE https://api.warpsearch.io/v1/collections/books \
  -H "Authorization: Bearer $WARPSEARCH_API_KEY"
```

This deletes the collection and every document in it, and there is no undo. You can then create a
collection with the same name from scratch. Deleting needs the `admin` role.

> **Coming soon:** opening and closing a collection at runtime, to pause a collection without
> deleting it.

## Idle collections and cold starts

WarpSearch does not keep compute attached to a collection nobody is using. After a period without
requests the collection is unloaded to durable storage, and the next request loads it again.

While it loads, the request can answer `503` with code `warming_up` and a `Retry-After` header.
Nothing is wrong and nothing is billed for the wait. Retry after the number of seconds in the
header:

```bash
curl -i https://api.warpsearch.io/v1/collections/books/search \
  -H "Authorization: Bearer $WARPSEARCH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"language": "lucene", "query": "title:dune"}'
# HTTP/1.1 503 Service Unavailable
# Retry-After: 5
# {"code":"warming_up","detail":"the collection is loading; retry shortly", ...}
```

Small collections are ready within moments; large ones take longer. Build retry-with-backoff into
any client that talks to a collection that may be idle, and see
[Pricing](pricing.md#cold-starts-and-keep-warm) for keep-warm, which holds a collection ready.
Free accounts do not include keep-warm.

A free collection that is not queried or written for 30 days is deleted, and its mapping is kept
so you can create it again; see [Pricing](pricing.md#free-tier).

## Roles on collections

Your API key's role decides what you can do with a collection:

| Action | Role needed |
|---|---|
| List and describe | `viewer` |
| Search, count, translate, get by id | `viewer` |
| Index, replace and delete documents | `editor` |
| Create, change mapping or delete | `admin` |

A role that is too low answers `403 forbidden` with a message naming the role you need. More in
[API keys, roles and rate limits](access-control.md).

## Gotchas

- Sizes in `doc_count` and `size_bytes` are cached, not live.
- Collection names cannot be changed. To rename, create a new collection and reload your data.
- Collections created in your organization are yours alone; there is no shared or public
  collection.
- The same name can exist in different organizations without conflict.
