PricingHelpDocsContactLogin
Technical documentation

Documentation

Technical guides for crawling, indexing, domain settings, API access, usage metering, and ZENIA Assistant integration.

API Access

ZENIA provides a customer-facing REST API under /api/v1 for server-to-server integrations where API Access is enabled for the workspace. Use it to read workspace data, inspect domains, fetch usage, and send chat messages without relying on the browser dashboard.

API access is protected by scoped API keys created inside Account > API Access. Each key is tied to a workspace and to the access rules available for the current plan.

Plain-language overview

What this means if you are not technical

API Access lets your own systems talk to ZENIA without opening the dashboard manually. In simple terms, you create a private key, give it only the permissions it needs, and let your website, CRM, internal app, or automation tool request information from ZENIA.

A typical setup looks like this: create a key, store it on your server, choose an indexed domain that already belongs to your workspace, and let your backend call the API to read workspace information, inspect domains, check usage, or send chat questions.

If you are working with a developer, your part is usually to create the key, copy it once, share the indexed domain name, and confirm which actions the integration should be allowed to perform.

Availability

Who can use API Access

API Access is available on plans that include the API Access feature. ZENIA checks plan availability and workspace eligibility on every API request.

If the workspace is on a plan without API Access, or if API Access is not currently available for that workspace, API calls are rejected even if a key still exists.

Key management

Create, store, and revoke keys safely

Create keys from Account > API Access. You choose a name and one or more scopes for each key.

The raw secret is shown exactly once. ZENIA stores only a secure hash, so you cannot reveal an existing key later. If a secret is lost or exposed, revoke the key and create a new one.

Use separate API keys for separate integrations or environments. The API Usage section can help you see which keys are active and whether they are producing errors or rate-limit responses.

Viewing your API usage

Viewing your API usage

You can monitor your workspace API activity from Account > API Access > API Usage, where this feature is available.

This view helps you confirm whether your integrations are active and healthy. It can show aggregate request counts, successful requests, error requests, rate-limited requests, chat requests, endpoint usage, top error codes, and per-key usage.

The API Usage view is read-only and scoped to your own workspace. It does not show raw API keys, Authorization headers, request bodies, chat messages, assistant replies, IP addresses, or data from other workspaces.

Use it to check:

  • whether an integration is still sending requests
  • which API key is being used
  • whether requests are failing
  • whether requests are being rate-limited
  • whether chat API usage is increasing
Scopes

Available scopes

Scopes restrict what each key can do. Use the smallest set of scopes required by your integration.

Scopes are enforced by the API key, while the current plan still decides whether API Access and analytics are available.

ScopeWhat it allowsNeeded for
chat:writeSend messages to the chat APIPOST /api/v1/chat/messages
workspace:readRead workspace-level informationGET /api/v1/workspace
domains:readRead domains owned by the workspaceGET /api/v1/domains, GET /api/v1/domains/:hostname
usage:readRead current usage summariesGET /api/v1/usage/current, GET /api/v1/domains/:hostname/usage/current
analytics:readRead usage history endpoints when analytics is enabled by the live planGET /api/v1/usage/history, GET /api/v1/domains/:hostname/usage/history
Before you test

Prepare the three values you need

Before making requests, prepare these values:

  • ApiBaseUrl: the full API base URL, for example https://buildbot.ro/api/v1.
  • ZENIA_API_KEY: the raw API key shown once when you create it.
  • your-indexed-domain.com: a hostname that already exists in the same workspace as the key.

The examples below assume ApiBaseUrl already includes /api/v1, so each request only appends the endpoint path such as /workspace or /chat/messages.

Domain-specific routes require the hostname to exist in your workspace. Chat requests work best after the domain has been indexed, because answers depend on the available knowledge base.

In PowerShell, prefer building JSON with a hashtable and ConvertTo-Json -Compress instead of manually escaping JSON strings.

Authentication

Authenticate with a Bearer token

Send the raw API key in the Authorization header as Bearer YOUR_KEY.

Use API keys only from your backend or other trusted server environments. Do not use them in browser code. Authentication failures may be rate-limited before request processing continues.

Authenticate with a Bearer token
PowerShell
$ApiBaseUrl = "https://buildbot.ro/api/v1"
$ApiKey = "ZENIA_API_KEY"

$Headers = @{
  Authorization = "Bearer $ApiKey"
  "Content-Type" = "application/json"
}

Invoke-RestMethod -Method Get -Uri "$ApiBaseUrl/workspace" -Headers $Headers
Endpoints

Available endpoints

All endpoints are mounted under /api/v1. Domain endpoints only return domains owned by the authenticated workspace.

Read and chat endpoints may be rate-limited to protect service quality. If you exceed the limit, the API returns RATE_LIMITED and may include a Retry-After header.

EndpointRequired scopePurpose
GET /Any valid keyAPI status and capability check
GET /workspaceworkspace:readWorkspace plan and access context
GET /domainsdomains:readList workspace domains
GET /domains/:hostnamedomains:readRead one owned domain
GET /usage/currentusage:readCurrent workspace usage
GET /usage/historyanalytics:readWorkspace usage history
GET /domains/:hostname/usage/currentusage:readCurrent usage for one domain
GET /domains/:hostname/usage/historyanalytics:readHistorical usage for one domain
POST /chat/messageschat:writeSend a chat message and receive the grounded answer payload
Chat requests

Send messages from your backend

The chat endpoint accepts a domain hostname and the user message. ZENIA applies the domain's server-side retrieval and assistant settings automatically.

Domain-specific chat works best after the domain has been indexed, because the answer depends on the available knowledge base. The first chat request can omit sessionId. When supported, ZENIA generates one automatically and returns it in the response.

Successful response

What a successful chat response looks like

A successful chat request returns ok: true and a message object.

citations may be empty, confidence may be null, and fallback may be omitted when it does not apply.

JSON
{"ok":true,"message":{"reply":"Answer text...","sessionId":"generated-or-existing-session-id","citations":[],"confidence":"medium","fallback":false}}
Follow-up messages

Reuse sessionId to continue the same conversation

Your first chat request can omit sessionId. When conversation sessions are supported for the request, the API returns a session identifier in the response.

Store that value and send it again on the next message if you want the follow-up to stay in the same conversation thread.

Code examples

Code examples

Run these examples only from your backend or a trusted server environment. Do not expose API keys in browser code, mobile apps, public repositories, screenshots, logs, support chats, or AI assistants.

Send the first chat message
PowerShell
$ApiBaseUrl = "https://buildbot.ro/api/v1"
$ApiKey = "ZENIA_API_KEY"
$Domain = "your-indexed-domain.com"

$Headers = @{
  Authorization = "Bearer $ApiKey"
  "Content-Type" = "application/json"
}

$Body = @{
  domain = $Domain
  message = "What services do you offer?"
} | ConvertTo-Json -Compress

Invoke-RestMethod `
  -Method Post `
  -Uri "$ApiBaseUrl/chat/messages" `
  -Headers $Headers `
  -Body $Body
Continue with sessionId
PowerShell
$Body = @{
  domain = $Domain
  sessionId = "SESSION_ID_FROM_PREVIOUS_RESPONSE"
  message = "And what are the pricing options?"
} | ConvertTo-Json -Compress

Invoke-RestMethod `
  -Method Post `
  -Uri "$ApiBaseUrl/chat/messages" `
  -Headers $Headers `
  -Body $Body
Read current usage
PowerShell
Invoke-RestMethod `
  -Method Get `
  -Uri "$ApiBaseUrl/usage/current" `
  -Headers $Headers
Testing and rollout

How to test the integration explicitly

Use this rollout order:

  1. Test the root and workspace endpoints first.
  2. Test the domain and usage endpoints next.
  3. Test chat without sessionId so you can confirm whether the server generates one.
  4. Test chat with an explicit sessionId if your app needs to keep a conversation thread stable.
  5. Run negative tests for missing auth, invalid key, validation failures, oversized payloads, rate limits, and a foreign or missing domain.

After sending a few requests, open Account > API Access > API Usage and confirm whether the counters update.

Once the quick-start request works, run the full smoke test below to check the wider endpoint surface.

Advanced

Advanced: full endpoint smoke test

Use this script after the quick-start request works. It checks the main read routes, chat routes, and common error cases.

Advanced: full endpoint smoke test
PowerShell
$ApiBaseUrl = "https://buildbot.ro/api/v1"
$ApiKey = "ZENIA_API_KEY"
$Domain = "your-indexed-domain.com"

$Headers = @{
  Authorization = "Bearer $ApiKey"
  "Content-Type" = "application/json"
}

function Test-Route {
  param(
    [string]$Name,
    [string]$Method,
    [string]$Uri,
    [object]$Body = $null,
    [int[]]$ExpectedStatus = @(200),
    [hashtable]$RequestHeaders = $Headers
  )

  Write-Host ""
  Write-Host "=== $Name ===" -ForegroundColor Cyan
  Write-Host "$Method $Uri"

  try {
    $params = @{
      Method = $Method
      Uri = $Uri
    }

    if ($null -ne $RequestHeaders) {
      $params.Headers = $RequestHeaders
    }

    if ($null -ne $Body) {
      if ($Body -is [string]) {
        $params.Body = $Body
      } else {
        $params.Body = ($Body | ConvertTo-Json -Compress)
      }
    }

    $response = Invoke-RestMethod @params
    Write-Host "PASS 200" -ForegroundColor Green
    $response | ConvertTo-Json -Depth 10
  }
  catch {
    $status = $null
    $text = $_.Exception.Message

    if ($_.Exception.Response) {
      $status = [int]$_.Exception.Response.StatusCode
      $reader = New-Object System.IO.StreamReader($_.Exception.Response.GetResponseStream())
      $text = $reader.ReadToEnd()
    }

    if ($ExpectedStatus -contains $status) {
      Write-Host "PASS $status" -ForegroundColor Green
    } else {
      Write-Host "FAIL status=$status expected=$($ExpectedStatus -join ",")" -ForegroundColor Red
    }

    Write-Host $text
  }
}

Test-Route -Name "GET /api/v1" -Method "GET" -Uri "$ApiBaseUrl"
Test-Route -Name "GET /api/v1/workspace" -Method "GET" -Uri "$ApiBaseUrl/workspace"
Test-Route -Name "GET /api/v1/domains" -Method "GET" -Uri "$ApiBaseUrl/domains"
Test-Route -Name "GET /api/v1/domains/:hostname" -Method "GET" -Uri "$ApiBaseUrl/domains/$Domain"
Test-Route -Name "GET /api/v1/usage/current" -Method "GET" -Uri "$ApiBaseUrl/usage/current"
Test-Route -Name "GET /api/v1/usage/history" -Method "GET" -Uri "$ApiBaseUrl/usage/history?months=12"
Test-Route -Name "GET /api/v1/domains/:hostname/usage/current" -Method "GET" -Uri "$ApiBaseUrl/domains/$Domain/usage/current"
Test-Route -Name "GET /api/v1/domains/:hostname/usage/history" -Method "GET" -Uri "$ApiBaseUrl/domains/$Domain/usage/history?months=12"

$chat1 = @{
  domain = $Domain
  message = "What services do you offer?"
}

Test-Route -Name "POST /api/v1/chat/messages without sessionId" -Method "POST" -Uri "$ApiBaseUrl/chat/messages" -Body $chat1

$chat2 = @{
  domain = $Domain
  sessionId = "manual-test-session-001"
  message = "And what are the pricing options?"
}

Test-Route -Name "POST /api/v1/chat/messages with sessionId" -Method "POST" -Uri "$ApiBaseUrl/chat/messages" -Body $chat2

$BadHeaders = @{
  Authorization = "Bearer invalid_key"
  "Content-Type" = "application/json"
}

Test-Route -Name "GET /api/v1/workspace without auth" -Method "GET" -Uri "$ApiBaseUrl/workspace" -RequestHeaders $null -ExpectedStatus @(401)
Test-Route -Name "GET /api/v1/workspace with invalid key" -Method "GET" -Uri "$ApiBaseUrl/workspace" -RequestHeaders $BadHeaders -ExpectedStatus @(401)
Test-Route -Name "POST /api/v1/chat/messages with empty body" -Method "POST" -Uri "$ApiBaseUrl/chat/messages" -Body "{}" -ExpectedStatus @(400)
Test-Route -Name "POST /api/v1/chat/messages with malformed JSON" -Method "POST" -Uri "$ApiBaseUrl/chat/messages" -Body '{' -ExpectedStatus @(400)

$largeBody = @{
  domain = $Domain
  message = ("x" * 40000)
} | ConvertTo-Json -Compress

Test-Route -Name "POST /api/v1/chat/messages with oversized body" -Method "POST" -Uri "$ApiBaseUrl/chat/messages" -Body $largeBody -ExpectedStatus @(413)
Test-Route -Name "POST /api/v1/chat/messages with foreign domain" -Method "POST" -Uri "$ApiBaseUrl/chat/messages" -Body @{ domain = "not-owned-example.test"; message = "Hello" } -ExpectedStatus @(404)
Errors

Common error responses

Error bodies include a machine-readable code field. Use that field in your integration. Do not rely on the exact wording of the human-readable error message.

When the API returns 429 RATE_LIMITED, respect the Retry-After header when it is present.

CodeMeaningWhat to do
API_AUTH_REQUIREDMissing or malformed Bearer tokenSend Authorization: Bearer <key>
API_SCOPE_REQUIREDKey does not have the required scopeCreate a key with the required scope
API_ACCESS_NOT_AVAILABLECurrent plan does not include API Access, or API Access is not available for the workspaceUse a plan and workspace setup that includes API Access
DOMAIN_NOT_FOUNDDomain is missing, deleted, or not owned by the workspaceCheck the domain in the dashboard
API_KEY_INVALIDKey is wrong, revoked, expired, or malformedCheck the key or create a new one
ANALYTICS_NOT_AVAILABLECurrent plan does not include analytics, or analytics is not available for the workspaceUse current usage endpoints or change to a plan that includes analytics
VALIDATION_ERRORRequired body fields are missing or invalidCheck required fields such as domain and message
INVALID_JSONRequest body is not valid JSONFix JSON formatting or use ConvertTo-Json
PAYLOAD_TOO_LARGERequest body is too largeReduce the request body size
RATE_LIMITEDToo many requestsWait and retry after Retry-After if it is present
CHAT_RELAY_FAILEDChat service could not process the messageRetry later; if the problem persists, check service status
Security

Security best practices

Treat API keys like passwords. Do not paste them into AI assistants, support chats, screenshots, logs, browser code, mobile apps, or public repositories. If a key is exposed, revoke it immediately and create a new one.

Use separate keys for separate integrations or environments. Give each key only the scopes it needs. The dashboard may show when a key was last used, but it does not expose IP addresses.

API Usage is not a request log. It shows aggregate counters only. It does not reveal request bodies, chat messages, assistant replies, Authorization headers, raw API keys, or IP addresses.