Inovacc Developer

Guides

Authentication

Every call to an Inovacc API is authenticated by one header, Authorization: Bearer <credential>. The credential identifies your organization, and for an application's credential the application too, so you never name them in the request. Calls that change something on the AI API also carry an X-Operation-Id, which makes them safe to retry. This guide explains the credentials, the headers each API reads, how idempotency works, and how to keep keys safe over their whole life.

The headers

HeaderWhereValue
Authorizationevery call, every APIBearer <credential>
X-Operation-IdAI API (ai.inovacc.dev): every POST, PUT and DELETE; optional on GETa unique id you choose: 8 to 128 letters, digits, _ or -

Data (data.inovacc.dev) and Events (events.inovacc.dev) read no organization or operation header at all: the tenant comes only from the credential, and any header that tries to name another is ignored. Data uses HTTP versions (If-Match, If-None-Match) and Events uses an idempotency_key in the body for safe retries; see their product pages.

A request without a credential, with a malformed one, or with one that is not recognised is refused before anything runs: 401 with missing_credentials or invalid_credentials. On every API this answer is the same whether the path you called exists or not, so nothing about the API can be learned without a credential.

Get a key

Secret keys are created in the Inovacc console by a person of your organization and shown once: Inovacc stores only a hash, so a lost key cannot be shown again, only replaced. Access is by invitation today: ask your Inovacc contact to invite you, then create a key under your organization.

Credentials

CredentialLooks likeForAccepted by
Secret API keyaik_ followed by 64 hex charactersyour serversAI, Data, Events
OAuth client access tokena signed token issued to an application's clientyour serversAI, Data, Events
Publishable keyapb_...web pages, from the origins its application listsData (records and files, where rules allow), Events (streams only)
End user access tokenissued when a person signs in to your applicationthat person's browser or appData (records and files, by the collection's rules)

A key may be bound to an application. Such a key reaches only what that application has enabled: on the AI API each route needs a capability (ai.chat, ai.embeddings, knowledge, decision), refused with 403 capability_not_enabled otherwise; on Data it sees only the databases its application created and its application's file folder; on Events it may name only its own workspace.

Publishable keys are not secrets. They are meant to ship inside a web page. What protects your data is the list of allowed origins and, above all, the rules of each collection: a publishable key can do nothing a rule does not admit.

Idempotency

On the AI API, X-Operation-Id is an idempotency key. The first successful answer to an operation id is stored for 24 hours. Send the same id again and you receive that same answer, marked x-idempotent-replay: true, without the work running twice and without a second charge. That is what makes a retry after a timeout or a dropped connection safe.

  • An error is never stored: retrying a failed call with the same id runs it again.
  • While the first call is still running, a second call with the same id is 409 operation_in_progress; wait and retry.
  • On chat, embeddings and run, a reused id returns the stored answer whatever the new body says. On collection items, the same id with a different file is 409 operation_id_reused.
  • Generate a new id for every new operation, and reuse an id only to retry the call it was created for.

Every AI answer echoes the id it ran under in x-operation-id; on a GET without one, the service makes one up. A streamed chat reply is never stored, so it cannot be replayed.

Example

Language

⋮
    Command LineExample

    Shell

    export INOVACC_API_KEY="<your API key>"

    Language

    ⋮
    cURL RequestExample

    cURL

    curl "https://ai.inovacc.dev/v1/models" \
      -H "Authorization: Bearer $INOVACC_API_KEY"

    TypeScript

    const url = "https://ai.inovacc.dev/v1/models";
    
    const response = await fetch(url, {
      method: "GET",
      headers: {
        Authorization: `Bearer ${process.env.INOVACC_API_KEY}`,
      },
    });
    
    console.log(response.status, await response.text());

    Python

    import os
    import urllib.request
    
    request = urllib.request.Request(
        "https://ai.inovacc.dev/v1/models",
        method="GET",
        headers={
            "User-Agent": "inovacc-python-sample",
            "Authorization": "Bearer " + os.environ["INOVACC_API_KEY"],
        },
    )
    
    with urllib.request.urlopen(request) as response:
        print(response.status, response.read().decode())

    Go

    package main
    
    import (
    	"fmt"
    	"io"
    	"net/http"
    	"os"
    )
    
    const url = "https://ai.inovacc.dev/v1/models"
    
    func main() {
    	req, err := http.NewRequest("GET", url, nil)
    	if err != nil {
    		panic(err)
    	}
    	req.Header.Set("Authorization", "Bearer "+os.Getenv("INOVACC_API_KEY"))
    
    	res, err := http.DefaultClient.Do(req)
    	if err != nil {
    		panic(err)
    	}
    	defer res.Body.Close()
    
    	out, err := io.ReadAll(res.Body)
    	if err != nil {
    		panic(err)
    	}
    	fmt.Println(res.Status, string(out))
    }

    Rust

    // Cargo.toml: reqwest = { version = "0.12", features = ["blocking", "json"] }
    
    fn main() -> Result<(), Box<dyn std::error::Error>> {
        let api_key = std::env::var("INOVACC_API_KEY")?;
        let response = reqwest::blocking::Client::new()
            .get("https://ai.inovacc.dev/v1/models")
            .bearer_auth(api_key)
            .send()?;
        println!("{} {}", response.status(), response.text()?);
        Ok(())
    }

    JavaScript

    const url = "https://ai.inovacc.dev/v1/models";
    
    const response = await fetch(url, {
      method: "GET",
      headers: {
        Authorization: `Bearer ${process.env.INOVACC_API_KEY}`,
      },
    });
    
    console.log(response.status, await response.text());

    PHP

    <?php
    
    $curl = curl_init('https://ai.inovacc.dev/v1/models');
    curl_setopt_array($curl, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . getenv('INOVACC_API_KEY'),
        ],
    ]);
    
    $response = curl_exec($curl);
    echo curl_getinfo($curl, CURLINFO_HTTP_CODE), ' ', $response, "\n";
    curl_close($curl);

    Ruby

    require "net/http"
    require "uri"
    
    uri = URI('https://ai.inovacc.dev/v1/models')
    
    request = Net::HTTP::Get.new(uri)
    request["Authorization"] = "Bearer #{ENV.fetch('INOVACC_API_KEY')}"
    
    response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |http|
      http.request(request)
    end
    
    puts "#{response.code} #{response.body}"

    Java

    import java.net.URI;
    import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse;
    
    public class Main {
        public static void main(String[] args) throws Exception {
            String apiKey = System.getenv("INOVACC_API_KEY");
    
            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create("https://ai.inovacc.dev/v1/models"))
                    .header("Authorization", "Bearer " + apiKey)
                    .GET()
                    .build();
    
            HttpResponse<String> response = HttpClient.newHttpClient()
                    .send(request, HttpResponse.BodyHandlers.ofString());
            System.out.println(response.statusCode() + " " + response.body());
        }
    }

    C#

    var url = "https://ai.inovacc.dev/v1/models";
    var apiKey = Environment.GetEnvironmentVariable("INOVACC_API_KEY");
    
    using var client = new HttpClient();
    using var request = new HttpRequestMessage(HttpMethod.Get, url);
    request.Headers.Add("Authorization", $"Bearer {apiKey}");
    
    using var response = await client.SendAsync(request);
    Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");

    Kotlin

    import java.net.URI
    import java.net.http.HttpClient
    import java.net.http.HttpRequest
    import java.net.http.HttpResponse
    
    fun main() {
        val apiKey = System.getenv("INOVACC_API_KEY")
    
        val request = HttpRequest.newBuilder()
            .uri(URI.create("https://ai.inovacc.dev/v1/models"))
            .header("Authorization", "Bearer " + apiKey)
            .GET()
            .build()
    
        val response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString())
        println("${response.statusCode()} ${response.body()}")
    }

    Swift

    import Foundation
    #if canImport(FoundationNetworking)
    import FoundationNetworking
    #endif
    
    let apiKey = ProcessInfo.processInfo.environment["INOVACC_API_KEY"] ?? ""
    
    let url = URL(string: "https://ai.inovacc.dev/v1/models")!
    var request = URLRequest(url: url)
    request.httpMethod = "GET"
    request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
    
    let (data, response) = try await URLSession.shared.data(for: request)
    let status = (response as? HTTPURLResponse)?.statusCode ?? 0
    print(status, String(decoding: data, as: UTF8.self))

    The answer lists the routes configured for your organization. Routes are named per organization, so the list you see is yours. A call that changes something adds its operation id:

    Language

    ⋮
      Command LineExample

      Shell

      curl -X POST "https://ai.inovacc.dev/v1/chat/completions" \
        -H "Authorization: Bearer $INOVACC_API_KEY" \
        -H "Content-Type: application/json" \
        -H "X-Operation-Id: $(uuidgen)" \
        -d '{"model":"auto","messages":[{"role":"user","content":"Hello"}]}'

      Key lifecycle

      A key moves through four stages, and each has its own care.

      1. Creation. Create one key per application and per environment (production, staging, a developer's laptop), never one key shared by everything. Copy the key straight from the console into your secret store.
      2. Use. Load the key from the environment or a secret manager at start-up. Send it only in the Authorization header, only over HTTPS, and only from a server: a secret key that reaches the Data or Events API from a browser (with an Origin header) is refused with 403 secret_key_in_browser.
      3. Rotation. Create a second key, deploy it everywhere the first is used, confirm in the Activity log that the old key's id no longer appears as actor_id, then have the old key disabled. A disabled key answers 403 key_disabled; revoking an application's credential takes effect within a minute.
      4. Retirement. Remove the key from every secret store and pipeline when an application or environment goes away.

      One behaviour to plan for: in the Vector Database, the local scope belongs to the key that wrote it. A new key sees a new, empty local scope; use the shared scope for knowledge that must survive a rotation.

      OAuth access tokens expire. When a call answers 401 invalid_credentials with a token that used to work, obtain a new token and retry once; do not retry a 401 in a loop.

      Key hygiene

      • Never commit a key, not even to a private repository or a test file. Keep .env files out of version control.
      • Never log the Authorization header, and redact it from error reports and traces.
      • Give each integration its own key, so its traffic is visible in the Activity log and it can be rotated alone.
      • Prefer an application-bound key with only the capabilities it needs over an organization-wide key.
      • Rotate on a schedule, and at once when someone who had access leaves or a key may have leaked.
      • In a browser, use a publishable key or a signed-in person's token, and close every collection with rules.

      See also

      • Errors: the error envelope and the codes you can handle.
      • Limits: request sizes, timeouts and rates.
      • Webhooks: verifying deliveries with a signing secret.
      • API reference: every product and its endpoints.
      Updated 2026-10-10.