Reference
SDK Cheat Sheet
One-page reference for common Onyx SDK calls. Select a language to see the syntax.
Onyx Environment Variables
Set these to control how the SDK scopes requests and where it fetches credentials.
| Env Var | Description | Default |
|---|---|---|
| ONYX_DATABASE_ID | Database UUID used to scope requests; required. | none |
| ONYX_DATABASE_BASE_URL | HTTP base for DB API. | https://api.onyx.dev |
| ONYX_DATABASE_API_KEY | API key for the database; required. | none |
| ONYX_DATABASE_API_SECRET | API secret for the database; required. | none |
| ONYX_AI_BASE_URL | Base URL for AI/chat endpoints. | https://ai.onyx.dev |
| ONYX_DEFAULT_MODEL | Model used by db.chat() shorthand. | onyx |
| ONYX_CONFIG_PATH | Path to JSON credentials file; checked after env vars. | unset |
| ONYX_DEBUG | When 'true', enables request/response logging and config debug output. | false |
| ONYX_STREAM_DEBUG | When 'true'/'1', logs streaming connection details. | false |
Initialization Patterns
Initialize the SDK with configuration.
Auto resolution
Resolution order: env vars → ONYX_CONFIG_PATH file → ./onyx-database.json (default locations).
import com.onyx.cloud.api.onyx
val db = onyx.init<Any>()Initialize with config object
Pass credentials directly when you can’t rely on env/file resolution.
import com.onyx.cloud.api.onyx
val db = onyx.init<Any>(
OnyxConfig(
apiKey = ONYX_DATABASE_API_KEY,
apiSecret = ONYX_DATABASE_API_SECRET,
)
)Optional config fields
| Attribute | Description | Default |
|---|---|---|
| baseUrl | REST base URL for database operations. | https://api.onyx.dev |
| databaseId | Database ID (inferred for DB-scoped keys). | resolved from API key |
| aiBaseUrl | Base URL for AI endpoints. | https://ai.onyx.dev |
| defaultModel | Fallback AI model for shorthand chat. | onyx |
| partition | Default partition for queries/find/delete. | entity partition |
| requestLoggingEnabled | Log HTTP requests and bodies. | false |
| responseLoggingEnabled | Log HTTP responses and bodies. | false |
| ttl | Milliseconds to cache resolved credentials. | 300000 (5 minutes) |
| retry | Retry config for idempotent GET (honors Retry-After). | enabled; 3 retries; 300/600/1200ms |
Core CRUD
Upsert, fetch, update, and delete entities by primary key.
import com.onyx.cloud.api.onyx
val db = onyx.init<Any>()
val account = Account(id = "acct_1", name = "Checking", balance = 1250.0)
db.save(account)
val fetched: Account? = db.findById("acct_1")
db.save(account.copy(balance = 1400.0))
db.delete("Account", "acct_1")Atomic Saving
Persist a parent plus related records in one call—no explicit transactions needed.
Cascade save
Save Account with related Transactions (all-or-none commit).
val account = Account(
id = "acct_cascade",
name = "Primary",
balance = 0.0,
transactions = listOf(
Transaction(id = "txn_1", accountId = "acct_cascade", amount = 250.0, currency = "USD"),
Transaction(id = "txn_2", accountId = "acct_cascade", amount = 125.0, currency = "USD"),
)
)
val saved = db
.cascade("transactions:Transaction(accountId, id)")
.save(Account::class, account)Cascade builder
Compose cascade strings fluently.
val rel = db
.cascadeBuilder()
.graph("transactions")
.graphType("Transaction")
.targetField("accountId")
.sourceField("id")
println(rel.toString()) // transactions:Transaction(accountId, id)Cascade string syntax
<field>:<RelatedTable>(<targetField>, <sourceField>) — field: property on the parent holding related rows; RelatedTable: table to upsert; targetField: FK on the related table; sourceField: parent field to copy.
Resolvers
Hydrate schema-defined relations inline to avoid extra round trips.
Fetch by id with resolver
Load a transaction and its account in one call.
val tx: Transaction? = db
.findById<Transaction>("txn_123", resolvers = listOf("account"))Query with resolver
Include related data while filtering the base table.
import com.onyx.cloud.api.eq
val rows = db.from<Transaction>()
.where("status" eq "posted")
.resolve("account")
.list<Transaction>()Filter by resolver field
Filter using fields exposed by the resolver.
import com.onyx.cloud.api.eq
val rows = db.from<Transaction>()
.where("account.name" eq "Primary")
.resolve("account")
.list<Transaction>()Bulk Save
Insert many records efficiently by passing a list to save.
val txns = listOf(
Transaction(id = "txn_batch_1", accountId = "acct_batch", amount = 1.99, currency = "USD"),
Transaction(id = "txn_batch_2", accountId = "acct_batch", amount = 12.5, currency = "USD"),
Transaction(id = "txn_batch_3", accountId = "acct_batch", amount = 45.0, currency = "USD"),
Transaction(id = "txn_batch_4", accountId = "acct_batch", amount = 7.25, currency = "USD"),
)
db.save(Transaction::class, txns)Query & Filter
Boolean filters, ranges, text contains, ordering, and limits.
import com.onyx.cloud.api.*
val results = db.from<Transaction>()
.where("status" eq "posted")
.and("amount" gt 100.0)
.and("createdAt" gte "2025-01-01")
.and(contains("merchant", "aws"))
.orderBy(desc("createdAt"))
.limit(25)
.list<Transaction>()Defaults: if you omit limit() and pageSize, the API uses its server-side default page size (capped at 1000); responses include nextPage when more rows remain.
Query clauses
| Clause | Signature | What it does |
|---|---|---|
| where | where(condition) | Sets the primary filter. |
| and | and(condition) | Adds an AND condition. |
| or | or(condition) | Adds an OR condition. |
| orderBy | orderBy(asc/desc(...)) | Sorts results. |
| groupBy | groupBy(...fields) | Groups for aggregates. |
| distinct | distinct() | Return unique rows. |
| limit | limit(n) | Cap result size. |
| pageSize | pageSize(n) | Set page size for streaming/paged queries. |
| inPartition | inPartition(partition) | Scope query to a partition. |
| nextPage | nextPage(token) | Continue a paged query. |
Filters
| Helper | Signature | What it does |
|---|---|---|
| eq | "field" eq value | Equals. |
| ne | "field" ne value | Not equals. |
| gt | "field" gt value | Greater than. |
| gte | "field" gte value | Greater than or equal. |
| lt | "field" lt value | Less than. |
| lte | "field" lte value | Less than or equal. |
| between | between("field", low, high) | Inclusive range. |
| contains | contains("field", text) | Case-sensitive substring. |
| startsWith | startsWith("field", prefix) | Prefix match. |
| endsWith | endsWith("field", suffix) | Suffix match. |
| inOp | inOp("field", list) | Field in list. |
| notIn | notIn("field", list) | Field not in list. |
| isNull | isNull("field") | Field is null. |
| notNull | notNull("field") | Field is not null. |
| within | within("field", subquery) | Field in subquery results. |
| search | search(lucene, minScore) | Lucene predicate. |
Select Query
Pick specific columns to return from a table.
val rows = db.select("id", "accountId", "amount", "createdAt")
.from<Transaction>()
.list<Map<String, Any>>()First or Null
When you expect a single row, use firstOrNull() or its alias one().
import com.onyx.cloud.api.eq
val tx: Transaction? = db.from<Transaction>()
.where("id" eq "txn_123")
.firstOrNull()
val txOne: Transaction? = db.from<Transaction>()
.where("id" eq "txn_123")
.one()Inner Queries
Filter a table using sub-selects returned from another query.
import com.onyx.cloud.api.*
val accounts = db.from<Account>()
.where(
within(
"id",
db.select("accountId")
.from<Transaction>()
.where(gt("amount", 1000.0))
)
)
.list<Account>()Update Query
Set partial updates for all rows matching a condition.
import com.onyx.cloud.api.eq
val updatedCount = db.from<Transaction>()
.where("status" eq "pending")
.and("amount" gt 100.0)
.setUpdates("status" to "posted")
.update()Delete Query
Delete all rows that match a filter.
import com.onyx.cloud.api.eq
val deleted = db.from<Transaction>()
.where("status" eq "archived")
.delete()Group By
Group results by one or more fields to segment metrics.
import com.onyx.cloud.api.eq
val grouped = db.select("merchant", "currency")
.from<Transaction>()
.where("status" eq "posted")
.groupBy("merchant", "currency")
.list<Map<String, Any>>()Aggregations
Server-side rollups for dashboards and billing summaries.
import com.onyx.cloud.api.*
val totals = db.select(sum("amount"), "merchant")
.from<Transaction>()
.where("status" eq "posted")
.list<Map<String, Any>>()Server-side aggregate helpers
| Function | Signature | What it does |
|---|---|---|
| sum | sum("field") | Numeric sum of a column. |
| count | count("field"|"*") | Row count. |
| avg | avg("field") | Arithmetic mean. |
| min | min("field") | Smallest value. |
| max | max("field") | Largest value. |
| median | median("field") | 50th percentile. |
| percentile | percentile("field", p) | p-th percentile (0–100). |
| std | std("field") | Sample standard deviation. |
| variance | variance("field") | Sample variance. |
| upper | upper("field") | Uppercase before grouping/aggregation. |
| lower | lower("field") | Lowercase before grouping/aggregation. |
| format | format("field", "pattern") | Apply Java-style format (e.g., yyyy-MM). |
| substring | substring("field", from, length) | Substring prior to grouping. |
| replace | replace("field", pattern, repl) | Regex/substring replacement. |
Streaming Queries
Consume large result sets incrementally and react to live query responses.
import com.onyx.cloud.api.eq
val events = mutableListOf<Transaction>()
val subscription = db.from<Transaction>()
.where("status" eq "posted")
.onItem<Transaction> { events.add(it) }
.onItemAdded<Transaction> { events.add(it) }
.onItemUpdated<Transaction> { events.add(it) }
.onItemDeleted<Transaction> { events.add(it) }
.stream<Transaction>(includeQueryResults = true, keepAlive = true)
// later
subscription.cancelAndJoin()Full-Text Search
Lucene-style search across tables marked SEARCHABLE in your schema.
All searchable tables
Runs across every table marked SEARCHABLE (optionally set a minimum score).
val hits = db.search("error AND status:active", minScore = 4.0)
.limit(50)
.list<Map<String, Any>>()Table-scoped search with minScore
Scopes search to a single table and applies a score threshold.
val hits = db.from<Transaction>()
.search("status:posted AND merchant:"AWS"", minScore = 4.4)
.list<Transaction>()Pagination
Page through ordered results with stable cursors (offset-free).
import com.onyx.cloud.api.asc
val page1 = db.from<Transaction>()
.orderBy(asc("createdAt"))
.limit(50)
.page<Transaction>()
val next = page1.nextPage
if (next != null) {
val page2 = db.from<Transaction>()
.orderBy(asc("createdAt"))
.page<Transaction>(nextPage = next)
}AI Chat
Use the AI endpoint to answer questions that reference your data.
Chat completions
Full request with model and messages.
val response = db.chat(
model = "onyx",
messages = listOf(
AIChatMessage(role = "system", content = "You are a finance assistant."),
AIChatMessage(role = "assistant", content = "I summarize trends each Friday."),
AIChatMessage(role = "user", content = "Draft a status update for finance stakeholders."),
)
)
println(response)Streaming chat
Stream tokens as they arrive.
val stream = db.chatStream(
model = "onyx",
messages = listOf(AIChatMessage(role = "user", content = "List the top 3 spend categories this week."))
)
for (chunk in stream) {
val delta = chunk.choices.firstOrNull()?.delta?.content
if (delta != null) print(delta)
}Shorthand chat (string in, string out)
Quick one-liner for common prompts.
val summary = db.chat("Summarize yesterday's transactions")List available models
Discover model IDs before picking one.
val models = db.getModels()
models.data.forEach { println(it.id) }Get model details
Inspect a specific model's capabilities and limits.
val model = db.getModel("onyx")
println(model)Request script approval
Validate a mutation script before execution.
val approval = db.requestScriptApproval(
script = "db.save({ id: 'acct_1', name: 'Checking' })"
)
println(approval)Documents
Upload binary files with metadata, fetch with optional resizing, or delete stored documents.
Save a document
Stores metadata in the DB and writes bytes to the database’s _documents path.
val payload = "hello".toByteArray()
val doc = OnyxDocument(
documentId = "hello.txt",
path = "/docs/hello.txt",
mimeType = "text/plain",
content = payload
)
db.saveDocument(doc)Get a document (optional resize for images)
Fetches by documentId; width/height resize images on the fly.
val doc = db.getDocument("hello.txt")Delete a document
Removes the file and its metadata.
db.deleteDocument("hello.txt")Storage & search
Files are written to your database’s filesystem under _documents/; only metadata (path, mimeType, timestamps) is stored in the Document record. If you provide plaintext content, it is chunked into Lucene-indexed parts so full-text search can find the document.
Secrets
Manage encrypted secrets stored per database; db calls never persist plaintext.
List secrets (metadata only)
Returns keys, purposes, and timestamps—no values.
val secrets = db.listSecrets()
println(secrets)Fetch a secret value
Decrypts and returns the plaintext for a single key.
val secret = db.getSecret("stripe_api_key")Create or rotate a secret
Stores the value encrypted at rest; re-encrypts on each update.
db.putSecret(
key = "stripe_api_key",
value = "sk_live_...",
purpose = "Stripe server key"
)Delete a secret
Removes the record from the database.
db.deleteSecret("stripe_api_key")How Onyx secures secrets
- Each secret is encrypted with a random AES-256-GCM key; that key is wrapped using the database’s 4096-bit RSA public key. Only ciphertext and wrapped keys are stored.
- Per-database RSA private keys are themselves encrypted with a master key.
- API routes require valid database credentials; plaintext values are only returned on authenticated
GET /secret/{key}and are never persisted decrypted. - Back up the keystore directory (
secret-keystore) together with the master key to preserve decryption ability.
Schema
Fetch, diff, validate, and publish schema revisions.
val current = db.getSchema() ?: emptyMap<String, Any>()
val next = current.toMutableMap().apply {
// modify entities/attributes here
put("revisionDescription", "Describe your change")
}
val diff = db.diffSchema(next)
val validation = db.validateSchema(next)
if (validation.valid == true) {
db.updateSchema(next, publish = true)
}Need the JSON shape for entities, attributes, and resolvers? See Schema Example.