API keys
An API key is the credential pair (keyId + secret) you sign Ripllo requests with. Each workspace can have multiple keys with different names and scopes — one for production, one for staging, one for a one-off back-office script. This page covers ripllo.apiKeys on the Node SDK. For the HTTP surface, see API → API keys; for how a key is plumbed into the SDK constructor, see Authentication.
The Node SDK's types for this namespace are stale.
apiKeys.createis typed{ description?, scope? }andapiKeys.revokeis typed to resolve{ revoked: boolean }; the API actually takes{ name, scopes }and returns{ apiKey }. The bodies are passed through verbatim, so the calls below work at runtime — in TypeScript you may need a cast until the types are regenerated. Sending the shape the types suggest gets you a400 VALIDATION.
Namespace
ripllo.apiKeys.list()
ripllo.apiKeys.create(input?)
ripllo.apiKeys.revoke(id)
No update — key metadata is immutable. To "rename" a key, revoke and re-create. Any principal that can write to the workspace can mint keys: a dashboard session, or an API key holding the write scope. There is no separate key-management permission.
Methods
apiKeys.list
Signature. ripllo.apiKeys.list(): Promise<{ apiKeys: ApiKey[] }>
Returns every key in the workspace newest-first, including revoked ones (filter on revokedAt). The secret is never returned on list — you get secretPreview (fulksk_…ab12) instead.
const { apiKeys } = await ripllo.apiKeys.list();
for (const k of apiKeys) {
const state = k.revokedAt ? `revoked ${k.revokedAt}` : 'active';
console.log(`${k.keyId} — ${k.name} — [${k.scopes.join(',')}] — ${state}`);
}
apiKeys.create
Signature. ripllo.apiKeys.create(input): Promise<{ apiKey: ApiKey; secret: string }>
Mints a new key. name is required (1–120 chars); scopes is an array defaulting to ['read', 'write']. The SDK auto-generates an Idempotency-Key so retrying a transient failure won't mint duplicates. The response is the only time the secret is returned — and it comes back as a sibling of apiKey, never as a field on it.
const { apiKey, secret } = await ripllo.apiKeys.create({
name: 'Production server (jakarta)',
scopes: ['read', 'write'],
});
console.log(apiKey.keyId); // → 'AKIAFULK…' — the public access key
console.log(secret); // → 'fulksk_…' — STORE NOW
The secret appears once. Stash it in your secret manager before the call returns. If you lose it, revoke and mint a new one.
Scope values are read, write and admin, and they are enforced on every signed request: read covers GET/HEAD, write covers every mutation. Mint at least the one you need — a key created with ['admin'] alone satisfies neither gate and gets 403 INSUFFICIENT_SCOPE on everything. (The partner superset ripllo:platform:admin, which unlocks X-Ripllo-On-Behalf-Of, is issued by Ripllo out-of-band and cannot be minted here.)
Key ids and secrets currently carry AKIAFULK / fulksk_ prefixes — inherited from the Fulkruma implementation this was copied from. They are valid Ripllo credentials; don't pattern-match on a RIPLLO prefix.
apiKeys.revoke
Signature. ripllo.apiKeys.revoke(id): Promise<{ apiKey: { id: string; revokedAt: string } }>
Revokes a key. Subsequent requests signed with that key fail with REVOKED_KEY. There's no un-revoke.
await ripllo.apiKeys.revoke('clx8f2k9r0000...');
The argument is the record ID (a cuid), not the keyId (AKIAFULK…). Two different identifiers — the record ID is the management primary key; the key ID is the public access key you sign with.
Types
interface ApiKey {
id: string; // cuid — record id, used by revoke()
name: string;
keyId: string; // 'AKIAFULK…' — the public access key
secretPreview: string; // 'fulksk_…ab12' — on list
scopes: string[]; // ['read', 'write'] by default
createdAt: string;
lastUsedAt: string | null;
revokedAt: string | null;
createdBy: string | null;
}
// create resolves to { apiKey: ApiKey; secret: string } — the plaintext
// secret is NOT a field on apiKey. accountId is implicit (your workspace)
// and is not projected.
For how scopes are checked on the wire, see API → API keys and API → Authentication.
Common patterns
Mint a per-service key and stash it
async function mintServerKey(serviceName: string) {
const { apiKey, secret } = await ripllo.apiKeys.create({
name: `Auto-provisioned: ${serviceName} (${new Date().toISOString().slice(0, 10)})`,
scopes: ['read', 'write'],
});
await secretManager.put(`${serviceName}/RIPLLO_KEY_ID`, apiKey.keyId);
await secretManager.put(`${serviceName}/RIPLLO_KEY_SECRET`, secret);
return apiKey.keyId;
}
Audit active keys
async function auditActiveKeys() {
const { apiKeys } = await ripllo.apiKeys.list();
const active = apiKeys.filter((k) => !k.revokedAt);
for (const k of active) {
const ageDays = Math.floor((Date.now() - Date.parse(k.createdAt)) / 86_400_000);
const lastUsed = k.lastUsedAt ?? 'never used';
console.log(` ${k.keyId} — ${k.name} — ${ageDays}d old — ${lastUsed}`);
}
}
Mint-then-revoke rotation
Mint first, deploy the new credentials, then revoke the old — this gives you an overlap window where both keys work, so a botched deploy doesn't lock you out:
async function rotateKey(oldRecordId: string, name: string) {
const { apiKey: next, secret } = await ripllo.apiKeys.create({
name: `${name} (rotation ${new Date().toISOString().slice(0, 10)})`,
scopes: ['read', 'write'],
});
await deployNewKey(next.keyId, secret);
// After confirming services use the new key:
await ripllo.apiKeys.revoke(oldRecordId);
}
Translate a keyId to a record ID
revoke needs the record ID (a cuid); you usually have the public AKIAFULK… key ID. Translate via list:
async function revokeByKeyId(publicKeyId: string) {
const { apiKeys } = await ripllo.apiKeys.list();
const found = apiKeys.find((k) => k.keyId === publicKeyId);
if (!found) throw new Error(`No key with keyId ${publicKeyId}`);
await ripllo.apiKeys.revoke(found.id);
}
Auth header recap
The SDK signs every request for you, but if you're debugging on the wire:
Authorization: Ripllo-HMAC-SHA256 keyId=<keyId>, scope=*, signature=<hex>
X-Ripllo-Timestamp: <unix_seconds>
The string-to-sign is <METHOD>\n<PATH-WITHOUT-QUERY>\n<TIMESTAMP>\n<BODY_SHA256>, optionally with \n<IdempotencyKey> appended. See API → Authentication for the full recipe.
Errors
| Code | Status | Cause |
|---|---|---|
VALIDATION |
400 | name missing or over 120 chars, or a scopes entry outside read/write/admin. |
NO_ACCOUNT |
403 | The principal carries no accountId. |
INSUFFICIENT_SCOPE |
403 | Calling key lacks read (list) or write (create/revoke). |
NOT_FOUND |
404 | Record ID doesn't exist in this workspace (on revoke). |
ALREADY_REVOKED |
409 | Revoking an already-revoked key. |
Next
- Authentication — how to use a minted key with the SDK constructor.
- API → API keys — HTTP reference.
- API → Authentication — the underlying signing recipe.