Documentation
HTTP data plane
Advanced reference. The normal integration — for every Redis client — is the one-line Redis connection string. This page documents the JSON-over-HTTPS API that sits underneath it: the layer the gateway translates every Redis command into, and the endpoint list exposed through openapi.json. Reach for it only when you are building non-Redis tooling (HTTP probes, control scripts, restricted-egress callers).
Base path
Every data call lives under one base path:
https://cacheify.bot-keep.xyz/api/data/{dbId}All requests take Authorization: Bearer <rk_…> — the database's single active API key. Redis array replies come back as JSON arrays; Redis nil is null; a missing or expired key is HTTP 404.
Endpoints
| Method | Path | Purpose |
|---|---|---|
| POST | /api/data/{dbId}/command | Run one Redis command {"command","args"} |
| POST | /api/data/{dbId}/pipeline | Atomic batch of 1-50 commands [{"command","args"}] |
| GET | /api/data/{dbId}/keys?pattern=&cursor= | Browse keys (cursor paging) |
| GET | /api/data/{dbId}/keys/{key} | Key detail: value + ttlMs (404 = absent or expired) |
| PUT | /api/data/{dbId}/strings/{key} | Save a string {"value","expiresInMs"?} |
| DELETE | /api/data/{dbId}/keys/{key} | Delete a key |
| POST | /api/data/{dbId}/keys/{key}/expire | Re-arm a TTL {"expiresInMs"} |
| POST | /api/data/{dbId}/rate-limit | Rate-limit probe for client feedback |
Full machine-readable contract: GET /api/openapi.json.
Cookbook: store, then read
The two calls most auth flows need — a 5-minute TTL value and its read-back:
curl -X PUT "https://cacheify.bot-keep.xyz/api/data/$DB_ID/strings/session:9942" \
-H "Authorization: Bearer $RK_TOKEN" \
-H "Content-Type: application/json" \
-d '{"value":"active","expiresInMs":300000}'import os, requests
base = os.environ["CACHEIFY_BASE_URL"]
auth = {"Authorization": "Bearer " + os.environ["CACHEIFY_API_KEY"]}
# save with a 5-minute TTL
requests.put(f"{base}/api/data/{os.environ['CACHEIFY_DB_ID']}/strings/session:9942",
headers=auth, json={"value": "active", "expiresInMs": 300000})
# read back: value + remaining TTL in one call
detail = requests.get(f"{base}/api/data/{os.environ['CACHEIFY_DB_ID']}/keys/session:9942",
headers=auth).json()
value, ttl_ms = detail["data"]["key"]["value"], detail["data"]["key"]["ttlMs"]const base = process.env.CACHEIFY_BASE_URL;
const auth = { Authorization: `Bearer ${process.env.CACHEIFY_API_KEY}` };
// save with a 5-minute TTL
await fetch(`${base}/api/data/${process.env.CACHEIFY_DB_ID}/strings/session:9942`, {
method: 'PUT',
headers: { ...auth, 'Content-Type': 'application/json' },
body: JSON.stringify({ value: 'active', expiresInMs: 300000 }),
});
// read back: 404 means the key expired or never existed
const { key } = await (await fetch(
`${base}/api/data/${process.env.CACHEIFY_DB_ID}/keys/session:9942`,
{ headers: auth },
)).json(); // key.value, key.ttlMsPipeline instead of round-trips
Send a batch of 1-50 commands that execute in order with one HTTP round-trip; each reply comes back in its own element:
curl -X POST https://cacheify.bot-keep.xyz/api/data/$DB_ID/pipeline \
-H "Authorization: Bearer $RK_TOKEN" \
-H "Content-Type: application/json" \
-d '{"commands":[
{"command":"HSET","args":["cart:7719","sku-1","2"]},
{"command":"HSET","args":["cart:7719","sku-4","1"]},
{"command":"HGETALL","args":["cart:7719"]}]}'Per-database concurrency is capped server-side (MAX_INFLIGHT_PER_DB): a saturated database answers 429 + Retry-After immediately instead of holding your socket.
Spring Boot without Redis deps
Spring's RedisTemplate speaks RESP over TCP — for that path use the gateway connection string. For serverless or restricted-egress deployments, the HTTP bridge below keeps the same interface so your service layer never learns the difference:
# application.properties (values come from the environment, never hardcoded)
cacheify.base-url=${CACHEIFY_BASE_URL:https://cacheify.bot-keep.xyz}
cacheify.database-id=${CACHEIFY_DB_ID:}
cacheify.api-key=${CACHEIFY_API_KEY:}// Spring HTTP bridge: same OtpStore interface, Cacheify over REST instead
// of the Redis protocol. For serverless or locked-down-egress callers.
// storeOtpHash -> PUT /api/data/{dbId}/strings/{key} (expiresInMs)
// getOtpHash -> GET /api/data/{dbId}/keys/{key} (value + ttlMs)
// deleteOtpHash -> DELETE /api/data/{dbId}/keys/{key}
@Repository
public class HttpOtpStore implements OtpStore {
private static final Duration TTL = Duration.ofMinutes(5);
private static final String PREFIX = "myapp:otp:";
private final RestTemplate http;
private final String baseUrl, dbId, apiKey;
public HttpOtpStore(RestTemplate cacheifyRestTemplate,
@Value("${cacheify.base-url}") String baseUrl,
@Value("${cacheify.database-id}") String dbId,
@Value("${cacheify.api-key}") String apiKey) {
this.http = cacheifyRestTemplate; this.baseUrl = baseUrl;
this.dbId = dbId; this.apiKey = apiKey;
}
public void storeOtpHash(String email, String otpHash) {
http.exchange(url("strings", email), HttpMethod.PUT,
json(Map.of("value", otpHash, "expiresInMs", TTL.toMillis())), String.class);
}
public Optional<String> getOtpHash(String email) {
try {
Map<?, ?> env = http.getForObject(url("keys", email), Map.class);
Map<?, ?> key = (Map<?, ?>) ((Map<?, ?>) env.get("data")).get("key");
return Optional.ofNullable((String) key.get("value"));
} catch (HttpStatusCodeException gone) {
return Optional.empty(); // 404: never issued, consumed, or expired
}
}
public void deleteOtpHash(String email) {
http.exchange(url("keys", email), HttpMethod.DELETE, json(null), String.class);
}
private String url(String resource, String email) {
return baseUrl + "/api/data/" + dbId + "/" + resource + "/"
+ URLEncoder.encode(PREFIX + email.toLowerCase(), StandardCharsets.UTF_8);
}
private static HttpEntity<Map<String, Object>> json(Map<String, Object> body) {
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + apiKey);
headers.set("Content-Type", "application/json");
return new HttpEntity<>(body, headers);
}
}404maps toOptional.empty()— identical to a Redis nil read.- URL-encode key names (
URLEncodercovers:and@in e-mails). - Wire the key through properties sourced from env vars; never hardcode it.