Skip to content

About

Norbix sdk for Kotlin

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

norbix-kotlin

CI Kotlin License

Official Kotlin SDK for Norbix. The SDK ships two top-level clients, one per plane:

  • NorbixApi (package ai.norbix.sdk.api) — project-scoped data. Default base URL: https://api.norbix.ai.
  • NorbixHub (package ai.norbix.sdk.hub) — project / account configuration. Default base URL: https://hub.norbix.ai.

Modules are exposed as flat fields on each client, so call sites read like api.database.find(...) or hub.files.getFilesIntegrations(...).

Client Modules
NorbixApi accessToken, ai, apikeys, auth, database, echo, files, membership, payments, publicProjects
NorbixHub accessToken, account, ai, apikeys, auth, code, database, echo, email, environments, files, system, logs, membership, notifications, payments, regions, scheduler, webhooks

Install

Published to Maven Central as ai.norbix:norbix-kotlin. Gradle:

repositories {
  mavenCentral()
}

dependencies {
  implementation("ai.norbix:norbix-kotlin:1.10.0") // latest: https://central.sonatype.com/artifact/ai.norbix/norbix-kotlin
}

Requires Java 17+; compiled for Kotlin 2.0+ consumers.

Quickstart — API

import ai.norbix.sdk.api.NorbixApi

val api = NorbixApi(apiKey = "sk_live_xxx", projectId = "proj_123")
val response = api.database.find(mapOf("collectionName" to "orders", "take" to 20))
println(response)

Login flow (no API key, just user credentials):

import ai.norbix.sdk.api.NorbixApi
import ai.norbix.sdk.core.LoginCredentials

val api = NorbixApi(projectId = "proj_123")
val auth = api.login(LoginCredentials("alice@team.io", "secret"))
println(auth["bearerToken"])

Quickstart — Hub

import ai.norbix.sdk.hub.NorbixHub

val hub = NorbixHub(
    apiKey = "sk_live_xxx",
    projectId = "proj_123",
)
val schemas = hub.database.getDatabaseSchemas()
val profile = hub.account.getAccountProfile()
println(profile)

Preview a notification with its signed link (no sign-in)

The push, email and SMS preview routes open with the signed link (hash) alone — no API key or bearer token needed. When the client does have a token it is still sent (scope OPTIONAL).

val hub = NorbixHub(projectId = "proj_123") // no apiKey, no bearerToken
val preview = hub.notifications.previewEmailNotification(mapOf("hash" to signedLink))
// also: previewPushNotification(...), previewSmsNotification(...)

A signed-in member can pass projectId + notificationId instead of hash. A bad or expired link comes back as a NorbixError with status 401.

SMS campaigns to account users

An SMS campaign can go to the account's own people — the owner and team members (deliveryType = "AccountUsers"). A member without a phone number is skipped; each member saves their own number with updateMyAccountUserPhone. The SMS template content has a body only (no subject — the sender is the integration), and getSmsCampaigns takes an optional campaignId filter.

val hub = NorbixHub(bearerToken = token, projectId = "proj_123") // no accountId needed
hub.notifications.createSmsCampaign(mapOf(
    "templateId" to "tmpl_1",
    "integrationId" to "nbin_1",
    "deliveryType" to "AccountUsers",
    "accountUsers" to mapOf(
        "recipientsSourceType" to "AccountUsers",
        "recipients" to listOf("usr_owner", "usr_member"),
        "campaignTime" to 1767225600, // unix seconds, UTC
    ),
))

// Your own team-member record (not the organisation profile) and your own phone.
val me = hub.account.getMyAccountUserProfile()                          // GET /{version}/account/me
hub.account.updateMyAccountUserPhone(mapOf("phone" to "+37060000000"))  // PUT /{version}/account/me/phone; "" clears it

The one-message calls getSmsCampaignMessage, getEmailCampaignMessage and getPushCampaignMessage (GET …/campaigns/{campaignId}/messages/{id}) are gone: the gateway no longer serves that route. Use the list calls (get*CampaignMessages) or the batch notification calls.

Triggers: order and break on failure

When several triggers fire for the same event they run as a queue. Two optional fields on trigger (every save: schema, membership, files and payments triggers) shape it: order — a whole number, 0 or more; lower runs earlier, a trigger without an order runs after the numbered ones, equal places run by name — and breakOnError — true stops the later triggers of the same event when this trigger's action fails (default false). Both come back on the get and list calls; a negative order fails with CM-ERRORS-TRIGGERS-008.

hub.membership.saveMembershipTrigger(mapOf(
    "trigger" to mapOf(
        "type" to "Membership",
        "name" to "welcome first",
        "when" to "OnRegistered",
        "isEnabled" to true,
        "order" to 1,
        "breakOnError" to true,
        "action" to mapOf("type" to "WebhookCall"),
    ),
))

Code — cloud functions

Norbix does not host code: a trigger's Code action, a scheduler task or a manual call runs a function in your cloud (AWS Lambda, Azure Functions, Google Cloud Functions) through a Code integration. hub.code lists the functions an integration may call, their aliases and versions, invokes one by hand, gives a sample envelope for a local run and the last runs of a trigger or task — getCodeFunctions, getCodeFunctionQualifiers, invokeCodeFunction, getCodeFunctionEventSample, getCodeFunctionRuns.

val functions = hub.code.getCodeFunctions(mapOf("integrationId" to "int_1", "prefix" to "orders-"))
val result = hub.code.invokeCodeFunction(
    mapOf(
        "functionRef" to mapOf("integrationId" to "int_1", "functionId" to "orders-on-update", "qualifier" to "prod"),
        "payload" to mapOf("orderId" to "6ac54a59212c29912eaddd81", "dryRun" to true),
        "invocationType" to "Sync",
    ),
)

Details and the envelope the function receives: docs/hub/code.md.

Configuration

Every constructor argument is optional and resolved in this order:

  1. Explicit constructor argument.
  2. Matching NORBIX_* environment variable.
  3. Built-in default.
Constructor arg Env variable Default
projectId NORBIX_PROJECT_ID — (required)
apiKey NORBIX_API_KEY null
bearerToken NORBIX_BEARER_TOKEN null
accountId NORBIX_ACCOUNT_ID null (no call needs it: account calls take the account from the token; hub.account.verifyAccount takes accountId in its request)
region NORBIX_REGION null (no region — see Regions)
language NORBIX_LANGUAGE null (no Accept-Language — see Language)
baseUrl (API) NORBIX_API_URL https://api.norbix.ai
baseUrl (Hub) NORBIX_HUB_URL https://hub.norbix.ai
version (API) NORBIX_API_VERSION v3
version (Hub) NORBIX_HUB_VERSION v3

This means a script that exports NORBIX_PROJECT_ID and NORBIX_API_KEY can call NorbixApi() with no arguments.

Self-hosted / custom domain

If you deploy Norbix to your own infrastructure, point the SDK at it through baseUrl or the matching env var:

val api = NorbixApi(
    projectId = "proj_123",
    apiKey = "sk_live_xxx",
    baseUrl = "https://api.norbix.isidos.lt",
)

val hubLocal = NorbixHub(
    projectId = "proj_123",
    apiKey = "sk_live_xxx",
    baseUrl = "http://localhost:5001",
)

Or, without code changes:

export NORBIX_API_URL=https://api.norbix.isidos.lt
export NORBIX_HUB_URL=https://hub.norbix.isidos.lt

Regions

Norbix can serve a project from multiple regions. The SDK targets a region by sending the nb-region header on every request. Unlike other options there is no default region: when no region is configured, no header is sent and the backend picks its own default.

The region resolves in the usual configuration order: explicit constructor argument → NORBIX_REGION environment variable → unset.

val hub = NorbixHub(projectId = "proj_123", apiKey = "sk_live_xxx", region = "nb-eu-germany")
val api = NorbixApi(projectId = "proj_123", apiKey = "sk_live_xxx", region = "nb-eu-germany")

Or, without code changes:

export NORBIX_REGION=nb-eu-germany

Switching regions at runtime

Both clients expose setRegion / getRegion:

hub.setRegion("nb-us-east")  // subsequent requests send nb-region: nb-us-east
hub.getRegion()              // "nb-us-east"
hub.setRegion(null)          // unset: header no longer sent, default base URL restored

getRegion() returns null when no region is set.

Per-call override

Methods on hub.regions (and Transport.send itself) accept an optional region argument that wins over the client's region for that single request. The override only changes the nb-region header — the base URL is not touched:

hub.regions.list(region = "nb-us-east")

Regional base URLs

When the client points at the SDK default base URL, configuring a region (constructor, NORBIX_REGION, or setRegion) also composes a regional variant of that URL:

Plane Default With region = "nb-eu-germany"
Hub https://hub.norbix.ai https://nb-eu-germany.hub.norbix.ai
API https://api.norbix.ai https://nb-eu-germany.api.norbix.ai

setRegion(null) restores the plain default URL.

A custom base URL — passed to the constructor or set via NORBIX_HUB_URL / NORBIX_API_URL — is never rewritten, so self-hosted deployments are unaffected: the client keeps your URL and only sends the nb-region header.

Managing regions — hub.regions

hub.regions.list() (GET /{version}/account/regions, token only) lists the regions available to the account. Each item carries id (the region code), continent, and name:

{ "items": [ { "id": "nb-eu-germany", "continent": "Europe", "name": "Germany" } ] }

hub.regions.updateProjectRegions(...) (PATCH /{version}/account/projects/{projectId}/settings/regions, token only) updates the project's regions. primaryRegion and additionalRegions are region code strings; pass null to leave one unchanged. Empty response on success:

hub.regions.updateProjectRegions(
    projectId = "proj_123",
    primaryRegion = "nb-eu-germany",
    additionalRegions = listOf("nb-us-east"),
)

Regions at project creation

The SDK is untyped on the wire, so hub.account.createProject takes a Map and passes the region keys straight through to the gateway:

hub.account.createProject(mapOf(
    "name" to "My project",
    "primaryRegion" to "nb-eu-germany",
    "additionalRegions" to listOf("nb-us-east"),
))

Language

The gateway picks the answer's language from the standard Accept-Language header only (q-values are honoured; when nothing matches, the project's default language is used). The answer names the language it used in Content-Language. The old cultureCode request field and the CultureCode / norbix-language headers are gone; the SDK does not send them.

Set language on the client to send Accept-Language on every request. Like region there is no default: when unset, no header is sent. It resolves in the usual order: constructor argument → NORBIX_LANGUAGE → unset.

val api = NorbixApi(projectId = "proj_123", apiKey = "sk_live_xxx", language = "lt")
val hub = NorbixHub(projectId = "proj_123", apiKey = "sk_live_xxx", language = "lt-LT, en;q=0.8")

hub.setLanguage("en")            // subsequent requests send Accept-Language: en
hub.getLanguage()                // "en"
hub.setLanguage(null)            // unset: header no longer sent

hub.regions.list(language = "de") // per-call override, same places as `region`
hub.transport.send(path = "/{version}/echo", method = "GET", language = "de")

The language never changes the base URL. A client language replaces an Accept-Language entry in defaultHeaders.

Errors

try {
    api.files.getFileInfo(mapOf("filesIntegrationId" to id, "path" to "a/b.txt"))
} catch (e: NorbixError) {
    // httpStatus / errorCode are the names every Norbix SDK uses.
    // status / code are the same values, kept for older code.
    println("${e.httpStatus} ${e.errorCode}: ${e.message}")
    e.errors.forEach { println("${it.errorCode} ${it.fieldName}: ${it.message}") }
    println(e.body) // the answer exactly as it arrived
}

message and errorCode are the gateway's own. The gateway puts them inside responseStatus.errors[], so the SDK reads that list first, takes the first entry for the message and the code, and keeps every entry in errors. Only when the body has no responseStatus are the top-level message and errorCode read. Request failed (HTTP N) with the code HTTP_<status> is the last fallback, used when the body says nothing — a 500 page that is not JSON, say.

Breaking change — a refused call now throws

The gateway answers a business refusal (an unknown id, a rule that says no) with HTTP 200 and responseStatus.isSuccess = false. The SDK used to hand that answer back as a normal value, so code carried on as if the call had worked. It now throws a NorbixError with httpStatus 200 and the gateway's message and error code.

If your code checked the responseStatus of the answer itself, move that check into a try / catch. Endpoints that answer with raw bytes rather than a document (sendBytes — file download, the public file link) are not JSON and are unchanged.

Resource cleanup

Both clients implement AutoCloseable, so they work with use { }:

NorbixApi(projectId = "proj_123", apiKey = "sk_live_xxx").use { api ->
    api.echo.echo()
}

SDK maintenance

API/Hub modules, tests, and per-module docs are generated by the SDK maintenance workflow.

Versioning

The major version is frozen at v1 until the public launch.

  • A breaking change is released as a minor (for example v1.2.0 → v1.3.0), never as a new major.
  • Write it as feat(<scope>): <what> and add a line Breaking: <what changed and what callers must do> in plain words, in the pull-request body and in the commit message.
  • Never mark it the conventional-commits way: no ! in the title (feat!:), no BREAKING CHANGE footer. The PR title check fails a pull request that does.
  • As a safety net, the release config (.releaserc.json → releaseRules) maps breaking commits to a minor, so one that slips through still does not bump the major.

Development

./gradlew test
./gradlew build

End-user AI chat and project AI settings

api.ai is the end-user AI chat for a signed-in project user: availability, sessions, entries, feedback, attachments, memory and startEndUserChatTurn, which answers at once with a turnId. The answer streams over the gateway's SSE endpoint on the user's own channel ai-chat:{projectId}:{authId} (events ai.chat.turn.*, ai.chat.session.*); a subscription to another user's channel is refused with 403 and responseStatus.errorCode = "AiChatChannelRefused" before the stream starts — do not retry it. This SDK has no SSE client. Path tokens are read from the request map.

val turn = api.ai.startEndUserChatTurn(mapOf("sessionId" to sessionId, "message" to "What can you do?"))
val entries = api.ai.getEndUserChatEntries(mapOf("SessionId" to sessionId))

Project owners configure the assistant on the Hub: hub.account (getProjectAiSettings, updateProjectAiSettings, createProjectAiAssistant, updateProjectAiAssistant, deleteProjectAiAssistant, getProjectAiUsage, setAdminPortalEnabled) and hub.ai (getEmbeddingIntegrations, saveEmbeddingIntegration, getEmbeddingIntegration, deleteEmbeddingIntegration, testEmbeddingIntegration, setLlmIntegrationAsDefault). Tables: docs/api/ai.md, docs/hub/ai.md, docs/hub/account.md.

Project settings, public config, MCP endpoint and AI service users

  • hub.account: updateProjectAdminUrl, updateProjectLegalDocuments, updateProjectExposeLegal, updateProjectExposeBrand, updateProjectExposeAuth, getAdminPortalStructure, assignAdminPortalServiceUser.
  • api.publicProjects: getPublicProjectConfig, getPublicProjectLegal — no sign-in; sent with no Authorization header.
  • hub.account, AI service users: createAiServiceUser, listAiServiceUsers, rotateAiServiceUserKey, revokeAiServiceUserKey, deleteAiServiceUser.
  • hub.account, developer MCP endpoint: sendMcpMessage, openMcpStream, endMcpSession. They return an McpResponse that carries the session id from the Mcp-Session-Id answer header.
hub.account.updateProjectLegalDocuments(mapOf("projectId" to projectId, "termsMarkdown" to "# Terms"))
hub.account.updateProjectExposeLegal(mapOf("projectId" to projectId, "exposed" to true))
hub.account.updateProjectExposeBrand(mapOf("projectId" to projectId, "exposed" to true))
hub.account.updateProjectExposeAuth(mapOf("projectId" to projectId, "exposed" to true))
val terms = api.publicProjects.getPublicProjectLegal(projectId = projectId, kind = "terms")

Tables and the MCP example: docs/hub/account.md, docs/api/public_projects.md.

About

Norbix sdk for Kotlin

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages