Inovacc Desarrolladores

Referencia de la API

Datos

Bases de datos, colecciones y registros con esquemas y reglas, por una sola puerta REST/JSON.

URL base https://data.inovacc.dev

Descripción general

Datos es la base de datos de tu aplicación, servida por una sola puerta REST/JSON en https://data.inovacc.dev. Creas bases de datos, les das colecciones, y lees y escribes registros con simples llamadas HTTP. Una colección es tipada, con campos declarados por un esquema, o libre, y contiene documentos JSON sin esquema. Quién puede hacer qué se decide en cada solicitud, primero por los permisos de la credencial y luego, registro por registro, por las reglas que declara la colección.

El problema que resuelve es operar datos persistentes sin operar una base de datos: sin servidor, sin pool de conexiones, sin herramienta de migraciones, sin respaldos que programar. Tu aplicación conserva su propio código y almacena aquí sus datos; dónde viven los datos lo decide Inovacc y nunca se muestra. La misma API atiende a tus servidores, a tus páginas web (con una clave de navegador) y a las personas con sesión iniciada en tu app, y un oyente en tiempo real envía los cambios a medida que ocurren.

Usa Datos para el estado de la aplicación: notas de usuarios, pedidos, configuraciones, catálogos, cualquier cosa con forma de registros. Almacena contenido binario grande con Archivos, consulta lo que sucedió con el Registro de actividad, y notifica a otros sistemas con Eventos.

Conceptos

Bases de datos. Una base de datos es una clave que elige tu aplicación, como shop o crm/v2 (una / se escribe %2F en una ruta). Una base de datos pertenece a la aplicación cuya credencial la creó; la base de datos de otra aplicación responde 404, como si no existiera. Una credencial a nivel de organización ve todas las bases de datos de la organización.

Colecciones: con esquema o libres. Una colección con esquema declara hasta 64 campos, cada uno con un tipo: text, integer, number, bool, datetime, json, relation (un id de registro en una colección target) o blob (el SHA-256 de un archivo en la misma base de datos). Un campo puede ser required, unique, indexed o fulltext (en text). Una colección libre no declara nada: cada registro es un documento JSON, consultado por ruta con puntos (profile.city), y se le puede dar un esquema más adelante cuando todos los documentos encajen. Escribir en una colección que no existe crea una libre, cuando tu credencial puede cambiar esquemas.

Registros y versiones. Un registro es {id, version, created_at, updated_at, ...fields}. Su id lo genera el servicio (rec_ más 26 caracteres ordenables) o lo eliges tú. Cada cambio sube version en uno; las actualizaciones y eliminaciones deben enviar If-Match: <version>, de modo que dos escritores nunca se sobrescriban en silencio.

Reglas. Una colección declara cinco reglas, list, view, create, update y delete. Cada una es null (nadie), "" (cualquier credencial de servidor de tu organización), public (cualquiera, incluidas las claves de navegador), users (las personas con sesión iniciada de la aplicación propietaria, y los servidores), o una expresión como owner = @auth.id. Las reglas se aplican a toda credencial, incluida la de un administrador.

Credenciales. Una clave secreta es para servidores. Una clave publicable (apb_...) es para páginas web: funciona solo desde los orígenes que lista su aplicación y solo donde una regla la admite. El token de acceso de una persona con sesión iniciada lleva solo permisos de registros y archivos.

Cómo funciona

Toda llamada lleva Authorization: Bearer <credential>. No hay encabezado de organización: tu organización, y la aplicación, se obtienen de la credencial, y nada en una ruta, una consulta o un encabezado puede nombrar a otra. Una solicitud sin una credencial válida, o a una ruta que no es una ruta del servicio, recibe un único 401 idéntico.

Para cada llamada el servicio primero verifica el permiso de la credencial para la acción (leer, escribir, crear, actualizar, eliminar o cambios de esquema) sobre la base de datos. Cualquier cosa distinta de "permitir" es 403 forbidden sin detalle. Luego la regla de la colección decide por registro: una lista devuelve solo los registros que admite la regla list; leer un registro que la regla view oculta es 404, igual que un registro inexistente; una creación verifica los datos nuevos; una actualización verifica tanto el registro almacenado como los datos nuevos.

Las escrituras llevan versión. POST /records crea y responde 201 con ETag: "1". PATCH cambia los campos nombrados (en una colección libre es un parche de combinación JSON), PUT reemplaza el registro, y PUT con If-None-Match: * lo crea en el id que elijas. Un If-Match incorrecto es 409 version_conflict; uno ausente es 428.

Las listas toman filter (field op value unidos por && y ||), sort, limit (hasta 200) y un cursor opaco, y devuelven next_cursor hasta la última página; q busca en los campos fulltext. Un lote aplica hasta 100 creaciones, actualizaciones y eliminaciones en una sola transacción: todas tienen éxito, o ninguna.

Un oyente en tiempo real es un WebSocket en GET .../collections/{c}/listen: envía ready, luego eventos added, modified y removed para los registros que tu regla te permite listar. Cada llamada se registra en el Registro de actividad.

Primeros pasos

Necesitas una clave secreta con permiso para crear bases de datos y colecciones (Autenticación).

  1. Crea una base de datos. PUT /v1/databases/notes sin cuerpo. La respuesta es 201 con {key, created_at}; repetirlo responde 200.
  2. Declara una colección. PUT /v1/databases/notes/collections/items con los campos owner y body y reglas (consulta los ejemplos). La respuesta es la colección con schema_version 1.
  3. Escribe un registro. POST .../collections/items/records con los campos. La respuesta es 201 con el registro, su id y version 1.
  4. Actualízalo. PATCH .../records/{id} con If-Match: 1. La respuesta tiene version 2; enviar If-Match: 1 de nuevo es 409 version_conflict.
  5. Lista. GET .../records?filter=owner%20%3D%20%22me%22&limit=10 devuelve tu registro y next_cursor: null.

Casos de uso

Datos por usuario en una aplicación web. Una aplicación de notas declara una colección cuyas cinco reglas dicen owner = @auth.id. La página llama a Datos directamente con el token de la persona con sesión iniciada; cada persona lista y edita solo sus propias notas, y la nota de otra persona es un 404. Ningún código de servidor lo hace cumplir: lo hacen las reglas.

Un catálogo público. Una tienda en línea mantiene products como una colección libre con list y view en public y las escrituras cerradas. El escaparate la lee desde el navegador con una clave publicable; la administración la escribe desde un servidor con una clave secreta.

Paneles en vivo. Una pantalla de operaciones abre un oyente sobre orders filtrado por los pedidos abiertos de hoy. Ejecuta la consulta después de ready, y luego aplica los eventos added, modified y removed a medida que llegan, sin sondeo.

Límites y precios

LímiteValor
Cuerpo de solicitud JSON1 MiB
Un registro900 KiB
Un campo json64 KiB
Campos por colección; colecciones por base de datos64; 64
Registros por página200 (por defecto 50)
Operaciones por lote100, en una sola transacción
Filtro2,048 caracteres, 40 valores, profundidad de anidamiento 8
Cadena de consulta16 KiB
Búsqueda de texto completo q16 términos, 256 caracteres
Oyentes por colección; un evento de oyente1,000; 512 KiB
Tasa600 solicitudes por minuto por titular de credencial, por ubicación (429 rate_limited)

Precios: a consultar.

Errores

EstadoCódigoQué significa y qué hacer
400invalid_body, invalid_name, unknown_field, missing_field, invalid_valueEl cuerpo o un nombre incumple las reglas; corrígelo.
400record_too_large, field_too_large, reserved_fieldReduce el registro; no envíes id, version, created_at, updated_at en un cuerpo.
400invalid_schema, invalid_rule, schema_change_unsupportedLa definición de la colección no es válida, o se cambió un campo en el mismo lugar.
400invalid_filter, invalid_sort, invalid_cursor, invalid_limit, invalid_searchCorrige la consulta de la lista; empieza de nuevo sin el cursor.
400invalid_if_match, batch_too_large, cross_shard_batchRevisa el encabezado o divide el lote.
401invalid_credentialsEnvía una credencial válida.
403forbiddenLos permisos de la credencial o la regla de la colección lo rechazan.
403origin_not_allowed, secret_key_in_browserUna clave de navegador desde un origen no listado, o una clave secreta en un navegador.
404database_not_found, collection_not_found, record_not_foundNo existe, o no tienes permiso para verlo.
409version_conflictAlguien cambió el registro; léelo de nuevo y reintenta.
409already_exists, unique_violation, not_empty, schema_violationEl id o el valor ya está en uso; vacíala antes de eliminar; los documentos no encajan en el esquema.
412, 428precondition_failed, precondition_requiredEl registro ya existe; envía If-Match.
413payload_too_largeEl cuerpo supera 1 MiB.
429rate_limited, too_many_listenersReduce el ritmo; cierra los oyentes que no necesites.
503service_unavailableReintenta más tarde; no se escribió nada.

Buenas prácticas

  • Envía siempre If-Match con la versión que leíste, y ante 409 version_conflict vuelve a leer antes de reintentar.
  • Cierra toda colección por defecto y ábrela regla por regla; una colección nacida de una escritura admite cualquier credencial de servidor hasta que la restrinjas.
  • Nunca pongas una clave secreta en una página web: usa una clave publicable y reglas public o @auth.
  • Elige tus propios ids de registro para las importaciones, de modo que una importación reintentada no cree nada dos veces (409 already_exists).
  • Usa un lote cuando varias escrituras deban tener éxito juntas.
  • Indexa los campos por los que filtras y ordenas, y sigue next_cursor en lugar de usar páginas grandes.
  • Con un oyente, espera a ready antes de consultar, y luego descarta los eventos que no sean más recientes que lo que devolvió la consulta.

Autenticación

Toda llamada lleva tu clave; tu organización viene de ella. Consulta la guía de autenticación.

EncabezadoAuthorizationBearer <clave 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

Ejemplo

Lenguaje

⋮
GET /v1/databasesEjemplo

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))
Detalles de la fuente

Entrada del catálogo

Id
identity/component-taxonomy/capabilities#data
Repositorio
identity
Ruta
contracts/component-taxonomy/catalog/capabilities.json
Commit
08ef3cd75d82

Ejemplo

Id
identity/component-taxonomy/quickstart#data
Repositorio
identity
Ruta
contracts/component-taxonomy/catalog/capabilities.json
Commit
08ef3cd75d82
Actualizado el 2026-10-10.