Inovacc Desenvolvedores

Referência da API

Dados

Bancos, coleções e registros com esquemas e regras, por uma única porta REST/JSON.

URL base https://data.inovacc.dev

Visão geral

Dados é o banco de dados da sua aplicação, servido por uma única porta REST/JSON em https://data.inovacc.dev. Você cria bancos de dados, dá a eles coleções e lê e grava registros com chamadas HTTP simples. Uma coleção é tipada, com campos declarados por um esquema, ou livre, contendo documentos JSON sem esquema. Quem pode fazer o quê é decidido em cada requisição, primeiro pelas permissões da credencial e depois, registro a registro, pelas regras que a coleção declara.

O problema que ele resolve é manter dados persistentes sem operar um banco de dados: sem servidor, sem pool de conexões, sem ferramenta de migração, sem backups para agendar. Sua aplicação mantém o próprio código e armazena seus dados aqui; onde os dados vivem é decidido pela Inovacc e nunca é exibido. A mesma API atende os seus servidores, as suas páginas web (com uma chave de navegador) e as pessoas autenticadas no seu aplicativo, e um ouvinte em tempo real envia as mudanças conforme elas acontecem.

Use Dados para o estado da aplicação: notas de usuários, pedidos, configurações, catálogos, qualquer coisa com formato de registro. Armazene conteúdo binário grande com Arquivos, consulte o que aconteceu com o Registro de atividade e avise outros sistemas com Eventos.

Conceitos

Bancos de dados. Um banco de dados é uma chave que a sua aplicação escolhe, como shop ou crm/v2 (uma / é escrita %2F em um caminho). Um banco de dados pertence à aplicação cuja credencial o criou; o banco de dados de outra aplicação responde 404, como se não existisse. Uma credencial no nível da organização enxerga todos os bancos de dados da organização.

Coleções: com esquema ou livres. Uma coleção com esquema declara até 64 campos, cada um com um tipo: text, integer, number, bool, datetime, json, relation (um id de registro em uma coleção target) ou blob (o SHA-256 de um arquivo no mesmo banco de dados). Um campo pode ser required, unique, indexed ou fulltext (em text). Uma coleção livre não declara nada: cada registro é um documento JSON, consultado por caminho com pontos (profile.city), e pode receber um esquema mais tarde, quando todos os documentos se encaixarem nele. Gravar em uma coleção que não existe cria uma coleção livre, quando a sua credencial pode alterar esquemas.

Registros e versões. Um registro é {id, version, created_at, updated_at, ...fields}. Seu id é gerado pelo serviço (rec_ mais 26 caracteres ordenáveis) ou escolhido por você. Toda alteração aumenta version em um; atualizações e exclusões devem enviar If-Match: <version>, para que dois escritores nunca se sobrescrevam silenciosamente.

Regras. Uma coleção declara cinco regras, list, view, create, update e delete. Cada uma é null (ninguém), "" (qualquer credencial de servidor da sua organização), public (qualquer pessoa, chaves de navegador incluídas), users (as pessoas autenticadas da aplicação proprietária, e os servidores) ou uma expressão como owner = @auth.id. As regras valem para todas as credenciais, inclusive a de um administrador.

Credenciais. Uma chave secreta é para servidores. Uma chave publicável (apb_...) é para páginas web: funciona apenas a partir das origens que a sua aplicação lista e apenas onde uma regra a admite. O token de acesso de uma pessoa autenticada carrega apenas permissões de registros e arquivos.

Como funciona

Toda chamada leva Authorization: Bearer <credential>. Não há cabeçalho de organização: a sua organização, e a aplicação, vêm da credencial, e nada em um caminho, consulta ou cabeçalho pode nomear outra. Uma requisição sem credencial válida, ou para um caminho que não é uma rota, recebe um 401 idêntico.

Em cada chamada, o serviço verifica primeiro a permissão da credencial para a ação (leitura, escrita, criação, atualização, exclusão ou alterações de esquema) no banco de dados. Qualquer coisa diferente de "permitir" é 403 forbidden sem detalhes. Depois a regra da coleção decide por registro: uma listagem devolve apenas os registros que a regra list admite; ler um registro que a regra view oculta é 404, igual a um registro inexistente; uma criação verifica os dados novos; uma atualização verifica tanto o registro armazenado quanto os dados novos.

As escritas são versionadas. POST /records cria e responde 201 com ETag: "1". PATCH altera os campos informados (em uma coleção livre é um JSON merge patch), PUT substitui o registro, e PUT com If-None-Match: * o cria no id que você escolheu. Um If-Match errado é 409 version_conflict; um ausente é 428.

As listagens aceitam filter (field op value unidos por && e ||), sort, limit (até 200) e um cursor opaco, e devolvem next_cursor até a última página; q pesquisa os campos fulltext. Um lote aplica até 100 criações, atualizações e exclusões em uma única transação: todas têm sucesso, ou nenhuma.

Um ouvinte em tempo real é um WebSocket em GET .../collections/{c}/listen: ele envia ready e depois eventos added, modified e removed para os registros que a sua regra permite listar. Toda chamada é registrada no Registro de atividade.

Primeiros passos

Você precisa de uma chave secreta com permissão para criar bancos de dados e coleções (Autenticação).

  1. Crie um banco de dados. PUT /v1/databases/notes sem corpo. A resposta é 201 com {key, created_at}; repetir a chamada responde 200.
  2. Declare uma coleção. PUT /v1/databases/notes/collections/items com os campos owner e body e as regras (veja os exemplos). A resposta é a coleção com schema_version 1.
  3. Grave um registro. POST .../collections/items/records com os campos. A resposta é 201 com o registro, seu id e version 1.
  4. Atualize-o. PATCH .../records/{id} com If-Match: 1. A resposta tem version 2; enviar If-Match: 1 de novo resulta em 409 version_conflict.
  5. Liste. GET .../records?filter=owner%20%3D%20%22me%22&limit=10 devolve o seu registro e next_cursor: null.

Casos de uso

Dados por usuário em um aplicativo web. Um aplicativo de notas declara uma coleção cujas cinco regras dizem owner = @auth.id. A página chama Dados diretamente com o token da pessoa autenticada; cada pessoa lista e edita apenas as próprias notas, e a nota de outra pessoa resulta em 404. Nenhum código de servidor garante isso: as regras garantem.

Um catálogo público. Uma loja online mantém products como uma coleção livre com list e view definidos como public e as escritas fechadas. A vitrine a lê do navegador com uma chave publicável; o back office a escreve a partir de um servidor com uma chave secreta.

Painéis em tempo real. Uma tela de operações abre um ouvinte em orders filtrado pelos pedidos abertos de hoje. Ela executa a consulta após ready, depois aplica os eventos added, modified e removed conforme chegam, sem fazer polling.

Limites e preços

LimiteValor
Corpo da requisição JSON1 MiB
Um registro900 KiB
Um campo json64 KiB
Campos por coleção; coleções por banco de dados64; 64
Registros por página200 (padrão 50)
Operações por lote100, em uma única transação
Filtro2.048 caracteres, 40 valores, profundidade de aninhamento 8
Query string16 KiB
Busca de texto completo q16 termos, 256 caracteres
Ouvintes por coleção; um evento de ouvinte1.000; 512 KiB
Taxa600 requisições por minuto por titular de credencial, por localização (429 rate_limited)

Preços: sob consulta.

Erros

StatusCódigoO que significa e o que fazer
400invalid_body, invalid_name, unknown_field, missing_field, invalid_valueO corpo ou um nome viola as regras; corrija-o.
400record_too_large, field_too_large, reserved_fieldReduza o registro; não envie id, version, created_at, updated_at em um corpo.
400invalid_schema, invalid_rule, schema_change_unsupportedA definição da coleção não é válida, ou um campo foi alterado no lugar.
400invalid_filter, invalid_sort, invalid_cursor, invalid_limit, invalid_searchCorrija a consulta da listagem; recomece sem o cursor.
400invalid_if_match, batch_too_large, cross_shard_batchConfira o cabeçalho ou divida o lote.
401invalid_credentialsEnvie uma credencial válida.
403forbiddenAs permissões da credencial ou a regra da coleção a recusam.
403origin_not_allowed, secret_key_in_browserUma chave de navegador vinda de uma origem não listada, ou uma chave secreta em um navegador.
404database_not_found, collection_not_found, record_not_foundNão existe, ou você não pode vê-lo.
409version_conflictAlguém alterou o registro; leia-o novamente e tente de novo.
409already_exists, unique_violation, not_empty, schema_violationO id ou valor já está em uso; esvazie antes de excluir; os documentos não se encaixam no esquema.
412, 428precondition_failed, precondition_requiredO registro já existe; envie If-Match.
413payload_too_largeO corpo excede 1 MiB.
429rate_limited, too_many_listenersDiminua o ritmo; feche os ouvintes de que não precisa.
503service_unavailableTente novamente mais tarde; nada foi gravado.

Boas práticas

  • Envie sempre If-Match com a versão que você leu e, em 409 version_conflict, leia de novo antes de tentar novamente.
  • Feche toda coleção por padrão e abra-a regra a regra; uma coleção nascida de uma escrita admite qualquer credencial de servidor até que você a restrinja.
  • Nunca coloque uma chave secreta em uma página web: use uma chave publicável e regras public ou @auth.
  • Escolha os seus próprios ids de registro nas importações, para que uma importação repetida não crie nada duas vezes (409 already_exists).
  • Use um lote quando várias escritas precisarem ter sucesso juntas.
  • Indexe os campos em que você filtra e ordena e siga next_cursor em vez de usar páginas grandes.
  • Com um ouvinte, espere por ready antes de consultar, e descarte depois os eventos que não sejam mais novos do que o que a consulta devolveu.

Autenticação

Toda chamada leva a sua chave; a sua organização vem dela. Veja o guia de autenticação.

CabeçalhoAuthorizationBearer <chave de API>

Endpoints

GET /v1/databases

PUT /v1/databases/{database}

PUT /v1/databases/{database}/collections/{collection}

POST /v1/databases/{database}/collections/{collection}/records

POST /v1/databases/{database}/batch

Exemplo

Linguagem

⋮
GET /v1/databasesExemplo

cURL

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

TypeScript

const url = "https://data.inovacc.dev/v1/databases";

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://data.inovacc.dev/v1/databases",
    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://data.inovacc.dev/v1/databases"

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://data.inovacc.dev/v1/databases")
        .bearer_auth(api_key)
        .send()?;
    println!("{} {}", response.status(), response.text()?);
    Ok(())
}

JavaScript

const url = "https://data.inovacc.dev/v1/databases";

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://data.inovacc.dev/v1/databases');
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://data.inovacc.dev/v1/databases')

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://data.inovacc.dev/v1/databases"))
                .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://data.inovacc.dev/v1/databases";
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://data.inovacc.dev/v1/databases"))
        .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://data.inovacc.dev/v1/databases")!
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))
Detalhes da fonte

Entrada do catálogo

Id
identity/component-taxonomy/capabilities#data
Repositório
identity
Caminho
contracts/component-taxonomy/catalog/capabilities.json
Commit
08ef3cd75d82

Exemplo

Id
identity/component-taxonomy/quickstart#data
Repositório
identity
Caminho
contracts/component-taxonomy/catalog/capabilities.json
Commit
08ef3cd75d82
Atualizado em 2026-10-10.