Skip to main content

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.

No public API hostname yet

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>'.

RoleCan call
memberlist, get, stats, create
admineverything 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) and page_token, with next_page_token in 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

MethodPathAuthPurpose
GET/v1/projects/{projectID}/memorystoresmemberList instances
GET/v1/projects/{projectID}/memorystores/{name}memberGet one instance
GET/v1/projects/{projectID}/memorystores/{name}/statsmemberLive server statistics
POST/v1/projects/{projectID}/memorystoresmemberCreate an instance
DELETE/v1/projects/{projectID}/memorystores/{name}adminDelete an instance
POST/v1/projects/{projectID}/memorystores/{name}/rotate-credentialadminMint a new password
GET/healthznoneReadiness check
GET/liveznoneLiveness 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"
}
FieldTypeMeaning
namestringThe instance name you chose. Immutable
resource_pathstringStable path: projects/<short>/memorystores/<name>
project_idstringThe owning project's UUID
size_classstringsmall, medium, or large
versionstringServer version; omitted when the platform default runs
maxmemory_policystringEviction policy (see create)
persistenceobjectenabled and volume size
statestringProvisioning, Ready, Degraded, or Unknown (not yet reconciled)
readyboolThe authoritative "you can connect now" signal
external_exposureboolWhether TLS external access was requested
externally_reachableboolWhether the external route is actually live
external_endpointstringrediss:// address; present only once the route is programmed
reachable_scopestringvpc — the external endpoint is reachable from your Crusoe VPC, never the public internet
memory_limitstringContainer memory limit
maxmemorystringThe server's dataset cap (75% of the container limit)
ready_replicas, replicasintAlways single-node in the alpha
connection.host, connection.portstring, intIn-cluster address (redis://, plaintext, port 6379)
connection.usernamestringAlways default
connection.credential_secretstringThe Kubernetes Secret holding the password (ms-<name>-credential)
conditionsarrayCredentialReady, ServiceReady, WorkloadReady, Ready, plus ExternallyReachable for exposed instances
created_atstringRFC 3339 timestamp

List instances

GET /v1/projects/{projectID}/memorystores

Auth: member.

NameTypeRequiredDefaultNotes
page_sizeintno501–200
page_tokenstringnoOpaque 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 INFO output are omitted, never zeroed.
  • hit_rate is a 0–1 ratio and appears only after at least one lookup has happened.
  • used_memory_pct appears only when maxmemory is greater than 0.
  • This is a point-in-time snapshot. There is no time-series history.

Errors:

StatusMessage
409this memorystore is not provisioned yet; wait for it to report Ready, then read its stats
503could 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: ...
502could 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": ""}
}
FieldTypeRequiredDefaultRules
namestringyes1–40 chars, ^[a-z]([-a-z0-9]*[a-z0-9])?$ (lowercase letters, digits, hyphens, starting with a letter). Becomes the DNS name; immutable
size_classstringnosmallsmall, medium, or large
versionstringnoplatform defaultServer version; blank means the platform default
maxmemory_policystringnonoevictionOne of noeviction, allkeys-lru, allkeys-lfu, allkeys-random, volatile-lru, volatile-lfu, volatile-random, volatile-ttl
external_exposureboolnofalseOpt-in TLS rediss:// endpoint, VPC-reachable only
persistence.enabledboolnotrueTo turn persistence off you must send {"persistence":{"enabled":false}} explicitly — omitting the field means on
persistence.sizestringno2GiVolume size; fixed after create
persistence.storage_class_namestringnoKubernetes storage class override

Size classes (fixed; maxmemory is 75% of the container limit, by design, for OOM protection):

ClassContainer memoryUsable (maxmemory)
small256Mi192Mi
medium1Gi768Mi
large4Gi3Gi

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."
}
Copy it now

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:

StatusMessage
400invalid JSON body: ...
400name must be 1-40 characters, lower-case letters, digits and hyphens, starting with a letter
400sizeClass must be one of small, medium, large
400maxmemoryPolicy must be one of ...
409a memorystore named <name> already exists in this project
502could 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:

StatusMessage
409this memorystore has no credential yet; wait for it to finish provisioning
502could 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

LimitValue
Create body16 KiB
Request timeout30 s (REQUEST_TIMEOUT_SECONDS)
Stats read budget3 s
Instance name1–40 chars, starts with a letter
List page_sizedefault 50, max 200
Password appearancescreate and rotate responses only

See platform limits for cross-service limits.

Status codes

CodeWhen
400Invalid JSON; bad name, size class, or policy; malformed project id (projectID must be a UUID)
401No or invalid credential
403Member calling an admin route
404Not found, or not yours — identical on purpose
409Name taken; not provisioned yet (rotate, stats)
429Per-principal rate limit
502Upstream (Kubernetes API) failures
503Health degraded; session signing key not loaded (this service cannot verify credentials: the shared session key is not loaded); stats unreachable