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, so it has its own base URL.

Where it lives

On the public API, https://api.codyhill.dev, which serves the MemoryStore routes alongside every other service. The platformctl memorystore commands read the same variable and fall back to that address, so you can leave it unset for them.

export CAI_MEMORYSTORE_API=https://api.codyhill.dev

Authentication

Every route except GET /healthz and GET /livez requires a bearer credential:

Authorization: Bearer <token-or-api-key>

The credential is either a session token (from POST /v1/auth/login on the core API, 12-hour life) or an API key (prefix cai_). 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 workload-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": "internet",
"memory_limit": "1Gi",
"maxmemory": "768Mi",
"ready_replicas": 1,
"replicas": 1,
"connection": {
"host": "<private-hostname>",
"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, deleting, or unknown (nothing has reported on it yet). Lowercase, like every other resource's state
readyboolThe authoritative "you can connect now" signal, and the field to branch on
messagestringWhy it is not ready, when it is not. Omitted once it is
external_exposureboolWhether TLS external access was requested on the instance itself. A cache reached through a published endpoint instead reports false here and true for externally_reachable — that is not a contradiction, it says which of the two paths put it on the internet
externally_reachableboolWhether the cache is actually reachable from outside, by either path
external_endpointstringrediss:// address; present only once there is one — the instance opted in and its route is programmed, or an endpoint was published for it
reachable_scopestringinternet. It reported vpc until private-by-default landed; that was wrong, and a client that trusted it treated an internet-facing address as VPC-only
memory_limitstringContainer memory limit
maxmemorystringThe server's dataset cap (75% of the container limit)
ready_replicas, replicasintAlways a single server in the
connection.host, connection.portstring, intThe instance's private address, reachable from your workloads on the platform (redis://, plaintext, port 6379)
connection.usernamestringAlways default
connection.credential_secretstringName of the stored credential that holds the password (ms-<name>-credential) — the name only, never the value
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 until it reports 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, reachable from the internet; the password is the only boundary
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_namestringnoStorage class for the data volume. Leave it blank unless your administrator told you which one to use

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>@<private-hostname>: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 it reports ready before connecting."
}
Copy it now

The password is shown once here and once per rotation. Every later read returns only the name of the stored credential 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", "ready": false,
"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": {
"orchestration": {"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
502The platform's own provisioning system failed while handling your request
503Health degraded; session signing key not loaded (this service cannot verify credentials: the shared session key is not loaded); stats unreachable