MemoryStore API
This page lists every endpoint on the MemoryStore service (memorystore-api), with request fields, response shapes, and the exact error messages the server returns. For a guided introduction, start with the MemoryStore overview and quickstart.
Base URL
MemoryStore has its own API service, separate from the core platform API. In-cluster it is memorystore-api.cai-system.svc:8080.
The platform is in alpha and does not publish a public hostname for this API. Reach it through a port-forward (shown below), or through an endpoint your administrator provides. The platformctl CLI does this automatically when $CAI_MEMORYSTORE_API is not set.
kubectl -n cai-system port-forward svc/memorystore-api 18081:8080 &
export CAI_MEMORYSTORE_API=http://localhost:18081
Authentication
Every route except GET /healthz and GET /livez requires a bearer credential:
Authorization: Bearer <token-or-api-key>
The credential is a session token (from POST /v1/auth/login on the core API, 12-hour life), an API key (prefix cai_), or the platform automation token. Roles are re-read from the database on every request. See API authentication.
An unauthenticated call gets 401 with this exact message: authentication required: sign in via agent-engine-api (POST /v1/auth/login) and send 'Authorization: Bearer <token>'.
| Role | Can call |
|---|---|
| member | list, get, stats, create |
| admin | everything a member can, plus delete and rotate-credential |
Conventions
- Error envelope. Every non-2xx response is
{"error": "<message>", "request_id": "<id>"}. - The 404 rule. "Not found" and "not yours" return byte-identical 404s. 403 appears only when you are a member but the action needs admin.
- Pagination.
page_size(1–200, default 50) andpage_token, withnext_page_tokenin responses, same as every platform list. - Request timeout. 30 seconds per request by default (
REQUEST_TIMEOUT_SECONDS). - Passwords are structural secrets. The instance object cannot contain a password — it has no field for one. The password appears in exactly two responses: create and rotate-credential.
Endpoints at a glance
| Method | Path | Auth | Purpose |
|---|---|---|---|
| GET | /v1/projects/{projectID}/memorystores | member | List instances |
| GET | /v1/projects/{projectID}/memorystores/{name} | member | Get one instance |
| GET | /v1/projects/{projectID}/memorystores/{name}/stats | member | Live server statistics |
| POST | /v1/projects/{projectID}/memorystores | member | Create an instance |
| DELETE | /v1/projects/{projectID}/memorystores/{name} | admin | Delete an instance |
| POST | /v1/projects/{projectID}/memorystores/{name}/rotate-credential | admin | Mint a new password |
| GET | /healthz | none | Readiness check |
| GET | /livez | none | Liveness check |
The instance object
Every read returns this shape:
{
"name": "sessions",
"resource_path": "projects/<short>/memorystores/sessions",
"project_id": "<uuid>",
"size_class": "medium",
"version": "9.1.0-alpine3.23",
"maxmemory_policy": "noeviction",
"persistence": {"enabled": true, "size": "2Gi"},
"state": "Ready",
"ready": true,
"external_exposure": false,
"externally_reachable": false,
"external_endpoint": "rediss://sessions-<short>.<domain>:443",
"reachable_scope": "vpc",
"memory_limit": "1Gi",
"maxmemory": "768Mi",
"ready_replicas": 1,
"replicas": 1,
"connection": {
"host": "ms-sessions.cai-p-<short>.svc.cluster.local",
"port": 6379,
"username": "default",
"credential_secret": "ms-sessions-credential"
},
"conditions": [{"type": "Ready", "status": "True", "reason": "...", "message": "..."}],
"created_at": "2026-08-06T12:00:00Z"
}
| Field | Type | Meaning |
|---|---|---|
name | string | The instance name you chose. Immutable |
resource_path | string | Stable path: projects/<short>/memorystores/<name> |
project_id | string | The owning project's UUID |
size_class | string | small, medium, or large |
version | string | Server version; omitted when the platform default runs |
maxmemory_policy | string | Eviction policy (see create) |
persistence | object | enabled and volume size |
state | string | Provisioning, Ready, Degraded, or Unknown (not yet reconciled) |
ready | bool | The authoritative "you can connect now" signal |
external_exposure | bool | Whether TLS external access was requested |
externally_reachable | bool | Whether the external route is actually live |
external_endpoint | string | rediss:// address; present only once the route is programmed |
reachable_scope | string | vpc — the external endpoint is reachable from your Crusoe VPC, never the public internet |
memory_limit | string | Container memory limit |
maxmemory | string | The server's dataset cap (75% of the container limit) |
ready_replicas, replicas | int | Always single-node in the alpha |
connection.host, connection.port | string, int | In-cluster address (redis://, plaintext, port 6379) |
connection.username | string | Always default |
connection.credential_secret | string | The Kubernetes Secret holding the password (ms-<name>-credential) |
conditions | array | CredentialReady, ServiceReady, WorkloadReady, Ready, plus ExternallyReachable for exposed instances |
created_at | string | RFC 3339 timestamp |
List instances
GET /v1/projects/{projectID}/memorystores
Auth: member.
| Name | Type | Required | Default | Notes |
|---|---|---|---|---|
page_size | int | no | 50 | 1–200 |
page_token | string | no | — | Opaque cursor |
Response 200:
{"memorystores": [], "next_page_token": "..."}
Get an instance
GET /v1/projects/{projectID}/memorystores/{name}
Auth: member. Returns 200 with the instance object; 404 otherwise.
Get live statistics
GET /v1/projects/{projectID}/memorystores/{name}/stats
Auth: member. Reads a live INFO snapshot directly from the running server. The internal read budget is 3 seconds.
Response 200:
{
"memorystore": "projects/<short>/memorystores/sessions",
"name": "sessions",
"used_memory_bytes": 1048576, "used_memory_human": "1.00M",
"maxmemory_bytes": 805306368, "maxmemory_policy": "noeviction",
"used_memory_pct": 0.13,
"connected_clients": 1, "blocked_clients": 0,
"keyspace_hits": 10, "keyspace_misses": 2, "hit_rate": 0.8333,
"evicted_keys": 0, "expired_keys": 0,
"total_commands_processed": 42, "instantaneous_ops_per_sec": 0,
"uptime_in_seconds": 3600, "db_keys": 12,
"note": "These are live values read directly from the running instance right now - a point-in-time snapshot. ..."
}
Field behavior worth knowing:
- Fields absent from the server's
INFOoutput are omitted, never zeroed. hit_rateis a 0–1 ratio and appears only after at least one lookup has happened.used_memory_pctappears only whenmaxmemoryis greater than 0.- This is a point-in-time snapshot. There is no time-series history.
Errors:
| Status | Message |
|---|---|
| 409 | this memorystore is not provisioned yet; wait for it to report Ready, then read its stats |
| 503 | could not reach this memorystore to read its stats; it may be scaled down, still provisioning, restarting, or mid-credential-rotation - try again once it reports Ready: ... |
| 502 | could not read the memorystore stats: ... |
Create an instance
POST /v1/projects/{projectID}/memorystores
Auth: member. Returns 201. Body limit: 16 KiB.
Request:
{
"name": "sessions",
"size_class": "medium",
"version": "",
"maxmemory_policy": "allkeys-lru",
"external_exposure": false,
"persistence": {"enabled": true, "size": "2Gi", "storage_class_name": ""}
}
| Field | Type | Required | Default | Rules |
|---|---|---|---|---|
name | string | yes | — | 1–40 chars, ^[a-z]([-a-z0-9]*[a-z0-9])?$ (lowercase letters, digits, hyphens, starting with a letter). Becomes the DNS name; immutable |
size_class | string | no | small | small, medium, or large |
version | string | no | platform default | Server version; blank means the platform default |
maxmemory_policy | string | no | noeviction | One of noeviction, allkeys-lru, allkeys-lfu, allkeys-random, volatile-lru, volatile-lfu, volatile-random, volatile-ttl |
external_exposure | bool | no | false | Opt-in TLS rediss:// endpoint, VPC-reachable only |
persistence.enabled | bool | no | true | To turn persistence off you must send {"persistence":{"enabled":false}} explicitly — omitting the field means on |
persistence.size | string | no | 2Gi | Volume size; fixed after create |
persistence.storage_class_name | string | no | — | Kubernetes storage class override |
Size classes (fixed; maxmemory is 75% of the container limit, by design, for OOM protection):
| Class | Container memory | Usable (maxmemory) |
|---|---|---|
small | 256Mi | 192Mi |
medium | 1Gi | 768Mi |
large | 4Gi | 3Gi |
Response 201 — the only response that contains the password, besides rotate:
{
"memorystore": {"name": "sessions", "state": "Provisioning", "ready": false},
"credential": {
"username": "default",
"password": "<24-random-bytes base64url>",
"uri": "redis://:<password>@ms-sessions.cai-p-<short>.svc.cluster.local:6379",
"external_uri": "rediss://:<password>@sessions-<short>.<domain>:443"
},
"note": "This is the only response that contains the password. Later reads return the name of the Secret holding it, never the value. The instance is still provisioning; poll until state is Ready before connecting."
}
The password is shown once here and once per rotation. Every later read returns the name of the Kubernetes Secret that holds it (ms-<name>-credential), never the value.
external_uri appears only when external exposure is on and the endpoint is already published — rare at create time; it typically appears on a later rotate.
Errors:
| Status | Message |
|---|---|
| 400 | invalid JSON body: ... |
| 400 | name must be 1-40 characters, lower-case letters, digits and hyphens, starting with a letter |
| 400 | sizeClass must be one of small, medium, large |
| 400 | maxmemoryPolicy must be one of ... |
| 409 | a memorystore named <name> already exists in this project |
| 502 | could not create the memorystore: ... |
Delete an instance
DELETE /v1/projects/{projectID}/memorystores/{name}
Auth: admin. Returns 202:
{"name": "sessions", "resource_path": "projects/<short>/memorystores/sessions",
"state": "Deleting",
"note": "The instance and its data volume are being reclaimed. This is not reversible."}
Errors: 404 as usual; 502 could not delete the memorystore: ....
Rotate the credential
POST /v1/projects/{projectID}/memorystores/{name}/rotate-credential
Auth: admin. Empty body. Returns 200 with the same credential shape as create, and this note:
Rotation is not instantaneous. The server reads its password once, at start, so the new one takes effect when the instance restarts - which the platform triggers now. Until that rollout completes, the previous password is still the one in force.
Errors:
| Status | Message |
|---|---|
| 409 | this memorystore has no credential yet; wait for it to finish provisioning |
| 502 | could not rotate the credential: ... |
Health checks
GET /healthz
No auth. Readiness. Returns 200 or 503:
{"status": "ok", "checks": {
"kubernetes": {"status": "ok"},
"memorystore_crd": {"status": "ok"},
"database": {"status": "ok"},
"session_key": {"status": "ok"}}}
Failing checks report unreachable, missing, or unconfigured, plus a verbatim detail field.
GET /livez
No auth. Liveness. Always returns 200 {"status":"ok"} while the process is up.
Server limits
| Limit | Value |
|---|---|
| Create body | 16 KiB |
| Request timeout | 30 s (REQUEST_TIMEOUT_SECONDS) |
| Stats read budget | 3 s |
| Instance name | 1–40 chars, starts with a letter |
List page_size | default 50, max 200 |
| Password appearances | create and rotate responses only |
See platform limits for cross-service limits.
Status codes
| Code | When |
|---|---|
| 400 | Invalid JSON; bad name, size class, or policy; malformed project id (projectID must be a UUID) |
| 401 | No or invalid credential |
| 403 | Member calling an admin route |
| 404 | Not found, or not yours — identical on purpose |
| 409 | Name taken; not provisioned yet (rotate, stats) |
| 429 | Per-principal rate limit |
| 502 | Upstream (Kubernetes API) failures |
| 503 | Health degraded; session signing key not loaded (this service cannot verify credentials: the shared session key is not loaded); stats unreachable |
Related pages
- MemoryStore overview — what the service is and when to use it
- Connect from workloads — using the credential Secret instead of copying passwords
- CLI: data and messaging commands — the read-only
platformctl memorystorecommands - API overview — shared conventions across all platform APIs