API Keys
API keys are created and managed only through the Layerum platform UI. API and SDK integrations do not create keys directly.
- Sign in to Layerum and open workspace
API Keys. - Create key, assign capabilities and scopes, then copy one-time secret.
- Use secret as
Authorization: Bearer lyr_wk_.... - Rotate/revoke is also done in the same UI page.
bash
# .env
LAYERUM_API_KEY=lyr_wk_xxxxxxxxxxxxxxxxxCapabilities, Scopes, and Limits
- Capabilities:
query:read,data:ingest,config:manage. - Scopes:
allowed_entity_ids,allowed_model_config_ids. - Limits:
rpm_limit,daily_token_limit, optionalexpires_at. - Rotate keeps same key record but issues a new secret.
- Revoke disables key immediately.
SDK Overview
Official package @layerum-team/rag-sdk is a typed client for public API /v1/*.
npm: https://www.npmjs.com/package/@layerum-team/rag-sdk
bash
npm i @layerum-team/rag-sdkSDK namespaces: entities, entityTypes, documents, ingestion, query, models.
SDK Client Options
ts
import { LayerumClient } from '@layerum-team/rag-sdk';
const client = new LayerumClient({
apiKey: process.env.LAYERUM_API_KEY!,
baseUrl: 'https://api.layerum.com',
timeoutMs: 30000,
maxRetries: 2
});apiKeyrequired (lyr_wk_...).baseUrloptional override, defaulthttps://api.layerum.com.timeoutMsdefault30000.maxRetriesdefault2(GET on 429/5xx only).
SDK Error Handling
SDK throws LayerumApiError for API failures.
ts
import { LayerumApiError } from '@layerum-team/rag-sdk';
try {
await client.query.run({
entityId: '<entity_uuid>',
modelConfigId: '<model_config_uuid>',
query: 'Hello'
});
} catch (e) {
if (e instanceof LayerumApiError) {
console.error(e.status, e.message, e.path, e.method, e.details);
}
}SDK: entities
list(parentId?: UUID)->GET /v1/entities; optional client-side filter by parent.tree()->GET /v1/entities/tree.create(input)->POST /v1/entities.remove(entityId)->DELETE /v1/entities/:id.
ts
await client.entities.create({
entityTypeId: '<entity_type_uuid>',
name: 'Invoices',
description: 'Accounting docs',
parentId: '<optional_parent_uuid>',
metadata: { team: 'finance' }
});SDK: entityTypes
list()->GET /v1/entity-types.get(entityTypeId)->GET /v1/entity-types/:id.create(input)->POST /v1/entity-types.remove(entityTypeId)->DELETE /v1/entity-types/:id.
ts
await client.entityTypes.create({
name: 'project',
label: 'Project'
});SDK: documents
list({ entityId, allInWorkspace? })->GET /v1/entities/:entityId/documentsorGET /v1/documents.get(entityId, sourceId)->GET /v1/entities/:entityId/documents/:id.createText(input)->POST /v1/entities/:entityId/documentswithsource_type: "text".createUrl(input)->POST /v1/entities/:entityId/documentswithsource_type: "url".upload(input)->POST /v1/entities/:entityId/documents/uploadmultipart.uploadInit(input)->POST /v1/entities/:entityId/documents/upload/init.uploadComplete(input)->POST /v1/entities/:entityId/documents/upload/complete.uploadDirect(input)-> init + S3 POST + complete.remove({ entityId, sourceId })->DELETE /v1/entities/:entityId/documents/:id.
ts
const source = await client.documents.uploadDirect({
entityId: '<entity_uuid>',
file: fileBlob,
fileName: 'large.pdf',
contentType: 'application/pdf'
});
await client.ingestion.waitUntilReady({
entityId: '<entity_uuid>',
sourceId: source.id
});Important: uploadInit.fileSizeBytes must match uploaded object size exactly.
SDK: ingestion
retry({ entityId, sourceId })->POST /v1/entities/:entityId/documents/:id/retry.error({ entityId, sourceId })->GET /v1/entities/:entityId/documents/:id/error.waitUntilReady({ entityId, sourceId, timeoutMs?, intervalMs? })-> polls document status until completed or failed.
Defaults: timeoutMs = 120000, intervalMs = 2000.
SDK: query
run(input)->POST /v1/query.runDebug(input)->POST /v1/query/debug.
ts
const result = await client.query.run({
entityId: '<entity_uuid>',
modelConfigId: '<model_config_uuid>',
query: 'Summarize key points',
topK: 8,
includeParentScopes: true
});
console.log(result.answer);
console.log(result.citations);SDK: models
listConfigs()->GET /v1/models.getConfig(id)->GET /v1/models/:id.createConfig(input)->POST /v1/models.updateConfig(id, input)->PATCH /v1/models/:id.removeConfig(id)->DELETE /v1/models/:id.listDefinitions({ type? })->GET /v1/model-definitions?type=....
ts
const configs = await client.models.listConfigs();
const created = await client.models.createConfig({
name: 'SDK Config',
llmModelId: '<llm_model_uuid>',
embeddingModelId: '<embedding_model_uuid>',
});
const embeddings = await client.models.listDefinitions({ type: 'embedding' });SDK: providers
list()->GET /v1/providers.get(id)->GET /v1/providers/:id.create(input)->POST /v1/providers.test(id)->POST /v1/providers/:id/test.remove(id)->DELETE /v1/providers/:id.
Public API (/v1)
Authentication for all /v1 endpoints:
http
Authorization: Bearer lyr_wk_xxxxxxxxxxxxxxxxx- Key must be active, not revoked, and not expired.
- Capability checks are enforced (
query:read,data:ingest,config:manage). - Entity/model scopes and limits are enforced.
API Error Format
json
{
"statusCode": 400,
"message": "Validation failed",
"timestamp": "2026-03-24T12:00:00.000Z",
"path": "/v1/query",
"method": "POST",
"details": {}
}Public API: Entities
GET
/v1/entitiesGET
/v1/entities/treePOST
/v1/entitiesBody
entity_type_id (uuid, required)name (string, required, max 200)description (string, optional)parent_id (uuid, optional)metadata (object, optional)
Notes
- Scoped API keys cannot create root entities unless scope allows it.
DELETE
/v1/entities/:idPublic API: Entity Types
GET
/v1/entity-typesGET
/v1/entity-types/:idPOST
/v1/entity-typesBody
name (string, required, max 50)label (string, required, max 100)
DELETE
/v1/entity-types/:idPublic API: Documents
GET
/v1/entities/:entityId/documentsGET
/v1/documentsGET
/v1/entities/:entityId/documents/:idPOST
/v1/entities/:entityId/documentsBody
source_type ("file" | "url" | "text", required)display_name (string, required)source_url (string, optional)raw_text (string, optional)visibility ("self" | "self_and_descendants", optional)model_config_id (uuid, optional)
POST
/v1/entities/:entityId/documents/uploadBody
multipart/form-datafile (required)visibility (optional)model_config_id (optional)
POST
/v1/entities/:entityId/documents/upload/initBody
file_name (string, required)file_size_bytes (int >= 1, required)content_type (string, optional)visibility ("self" | "self_and_descendants", optional)model_config_id (uuid, optional)
POST
/v1/entities/:entityId/documents/upload/completeBody
upload_id (uuid, required)display_name (string, optional)visibility ("self" | "self_and_descendants", optional)model_config_id (uuid, optional)checksum (string, optional)
Notes
- Uploaded object size must exactly match file_size_bytes declared at upload/init.
DELETE
/v1/entities/:entityId/documents/:idPOST
/v1/entities/:entityId/documents/:id/retryGET
/v1/entities/:entityId/documents/:id/errorPublic API: Models
GET
/v1/modelsGET
/v1/models/:idPOST
/v1/modelsPATCH
/v1/models/:idDELETE
/v1/models/:idGET
/v1/model-definitionsQuery
type (string, optional)
Public API: Providers
GET
/v1/providersGET
/v1/providers/:idPOST
/v1/providersPOST
/v1/providers/:id/testDELETE
/v1/providers/:idPublic API: Query
POST
/v1/queryBody
entityId (uuid, required)modelConfigId (uuid, required)query (string, required)topK (number 1..20, optional)includeParentScopes (boolean, optional)
POST
/v1/query/debugBody
entityId (uuid, required)modelConfigId (uuid, required)query (string, required)topK (number 1..20, optional)includeParentScopes (boolean, optional)
Notes
- Returns additional debug metadata when backend provides it.
json
{
"entityId": "<entity_uuid>",
"modelConfigId": "<model_config_uuid>",
"query": "Summarize latest updates",
"topK": 8,
"includeParentScopes": true
}Capacity and Upload Model
Capacity is account-level and can be distributed dynamically across workspaces.
- Counted usage: entities, documents/data source bytes, chunks, S3 files tied to documents, pending upload reservations.
- Not counted: audit records, access records, API key metadata, model/provider metadata.
- Direct upload hardening: capacity reserved at
upload/init, object validated atupload/complete, stale sessions are cleaned up.
bash
curl -X POST "https://api.layerum.com/v1/entities/<entityId>/documents/upload/init" \
-H "Authorization: Bearer lyr_wk_..." \
-H "Content-Type: application/json" \
-d '{
"file_name":"manual.pdf",
"file_size_bytes":123456,
"content_type":"application/pdf"
}'