Write & Stage
Layer stamps every row-producing write with _hevlayer_upserted_at for supported stores. The stamp is what holds the
read watermark; the full set of reserved attributes Layer
manages on a row lives in the document model.
To branch or copy a namespace, see Branching.
Upsert and delete
POST /v2/namespaces/{namespace} accepts a JSON body. The first schema write
creates the namespace. With the common client:
await client.write_namespace("preview_docs", {
"distance_metric": "cosine_distance",
"schema": {
"vector": {"type": "[3]f32"},
"body": {"type": "string", "full_text_search": True},
"category": {"type": "string"},
"year": {"type": "int"},
},
"upsert_rows": [{"id": "atlas", "vector": [1, 0, 0],
"body": "moon geology", "category": "space", "year": 2026}],
})_, err := client.WriteNamespace(ctx, "preview_docs", hevlayer.TurbopufferWriteRequest{
"distance_metric": "cosine_distance",
"schema": map[string]any{
"vector": map[string]any{"type": "[3]f32"},
"body": map[string]any{"type": "string", "full_text_search": true},
"category": map[string]any{"type": "string"},
"year": map[string]any{"type": "int"},
},
"upsert_rows": []map[string]any{{"id": "atlas", "vector": []float64{1, 0, 0},
"body": "moon geology", "category": "space", "year": 2026}},
})await client.writeNamespace("preview_docs", {
distance_metric: "cosine_distance",
schema: {
vector: { type: "[3]f32" },
body: { type: "string", full_text_search: true },
category: { type: "string" }, year: { type: "int" },
},
upsert_rows: [{ id: "atlas", vector: [1, 0, 0],
body: "moon geology", category: "space", year: 2026 }],
});curl --fail-with-body "$LAYER_GATEWAY_URL/v2/namespaces/preview_docs" \
-H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"distance_metric":"cosine_distance","schema":{"vector":{"type":"[3]f32"},"body":{"type":"string","full_text_search":true},"category":{"type":"string"},"year":{"type":"int"}},"upsert_rows":[{"id":"atlas","vector":[1,0,0],"body":"moon geology","category":"space","year":2026}]}' Delete by string ID using the same route:
await client.write_namespace("preview_docs", {"deletes": ["atlas"]})await client.writeNamespace("preview_docs", { deletes: ["atlas"] });curl --fail-with-body "$LAYER_GATEWAY_URL/v2/namespaces/preview_docs" \
-H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" \
-H 'Content-Type: application/json' -d '{"deletes":["atlas"]}' Patches, conditions and column writes
On other stores, write-shape support follows the backend notes above. Use
patch_rows to modify named fields without replacing a complete document only
when the selected store supports patches.
Status
Layer validates the body before forwarding and can fail independently of the backing store, so the write path carries a few statuses a plain proxy wouldn’t:
- 200 OK — applied to the selected store. Gateway bookkeeping depends on the backend.
- 422 Unprocessable Entity — Layer rejected the body before forwarding:
no recognized native write operation, a reserved
_hevlayer_*attribute name, a removed custom-write key, or a schema type the configured store cannot represent. The body is a Layer error ({ "error": "validation_error", … }or{ "error": "UnsupportedByStore", … }), not a store one. - Upstream passthrough — any non-2xx the backing store returns is relayed verbatim.
- 502 Bad Gateway — Layer could not reach the backing store
(
{ "error": "upstream_error", … }); the write did not apply.
The passthrough case includes a failed conditional write (upsert_condition,
patch_condition, delete_condition) — turbopuffer’s error body comes back
untouched.
Stage
Stage caches a document before it’s upserted into your vector store. That O(1) read/write is especially useful for queuing chunks in a two-stage pipeline, where a CPU worker stages chunks and a GPU worker reads them back to write vectors. Staged documents are ephemeral until they’re upserted, though — a Layer document cache outage loses anything still staged.
await client.put_pipeline_document_chunks("product-images", "asin-B08N5WRWNW", {
"chunks": [
{"id": "asin-B08N5WRWNW-0", "text": "Wireless noise-cancelling headphones"},
{"id": "asin-B08N5WRWNW-1", "text": "40-hour battery life", "metadata": {"page": 2}},
],
})client.PutPipelineDocumentChunks(ctx, "product-images", "asin-B08N5WRWNW", &hevlayer.PutChunksRequest{
Chunks: []hevlayer.Chunk{
{ID: "asin-B08N5WRWNW-0", Text: "Wireless noise-cancelling headphones"},
{ID: "asin-B08N5WRWNW-1", Text: "40-hour battery life", Metadata: map[string]interface{}{"page": 2}},
},
})await client.putPipelineDocumentChunks("product-images", "asin-B08N5WRWNW", {
chunks: [
{ id: "asin-B08N5WRWNW-0", text: "Wireless noise-cancelling headphones" },
{ id: "asin-B08N5WRWNW-1", text: "40-hour battery life", metadata: { page: 2 } },
],
});curl -X PUT "$LAYER_GATEWAY_URL/v2/pipelines/product-images/documents/asin-B08N5WRWNW" \
-H "Authorization: Bearer $LAYER_GATEWAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"chunks": [
{"id": "asin-B08N5WRWNW-0", "text": "Wireless noise-cancelling headphones"},
{"id": "asin-B08N5WRWNW-1", "text": "40-hour battery life", "metadata": {"page": 2}}
]
}' Staging stores chunks in the Aerospike document cache and marks the document pending.
Re-staging the same document ID replaces the chunks and resets state to
pending. The full pipeline API is documented under
Pipelines.