Inovacc Desenvolvedores

Referência da API

Arquivos

Armazenamento de arquivos por conteúdo, com envio em partes para arquivos grandes.

URL base https://data.inovacc.dev

Visão geral

Arquivos armazena o conteúdo binário da sua aplicação (imagens, documentos, exportações, gravações) em https://data.inovacc.dev, com envios em partes para tudo o que for grande. Ele oferece duas formas de endereçar um arquivo:

  • por caminho, na pasta da sua aplicação: PUT /v1/files/users/123/avatar.png, do jeito que um bucket de armazenamento na nuvem nomeia arquivos, com listagens por pasta, metadados do usuário e pastas compartilhadas com outras aplicações do seu espaço de trabalho;
  • por conteúdo, dentro de um banco de dados: PUT /v1/databases/{db}/blobs/{sha256}, em que o próprio SHA-256 do arquivo é o seu nome, de modo que um registro de Dados pode apontar para ele com um campo blob.

O problema que ele resolve é manter arquivos junto dos seus dados sem operar armazenamento: a Inovacc escolhe onde os bytes vivem, verifica-os contra o hash, remove duplicatas dentro da sua pasta e os devolve com cabeçalhos que impedem que um arquivo baixado seja executado em um navegador.

Use Arquivos para tudo o que for bytes em vez de campos. Mantenha a descrição estruturada de um arquivo (proprietário, título, status) como um registro em Dados. Para tornar um arquivo pesquisável por significado, adicione-o a uma coleção de Conhecimento e busca vetorial.

Conceitos

A pasta da sua aplicação. Cada aplicação tem a sua própria pasta. A pasta é escolhida a partir da credencial, nunca da requisição: uma credencial vinculada a uma aplicação usa a pasta dessa aplicação, e uma credencial não vinculada a nenhuma aplicação recebe 403 application_required em toda rota de caminho. Os caminhos são UTF-8, de 1 a 1.024 bytes, separados por /, diferenciam maiúsculas de minúsculas, sem segmentos vazios, . ou ..; um caminho que ainda não esteja nessa forma é recusado, nunca reescrito silenciosamente.

Metadados. Uma escrita pode levar um Content-Type e cabeçalhos X-File-Meta-<name> próprios (2 KiB no total). Ambos voltam em toda leitura. Uma escrita substitui por inteiro o conteúdo, o tipo e os metadados do arquivo.

Hashes. Todo arquivo é identificado pelo seu SHA-256. Envie X-File-Sha256 com uma escrita e os bytes são conferidos contra ele (400 hash_mismatch, nada é armazenado). Dentro da pasta de uma aplicação, dois caminhos com os mesmos bytes compartilham uma única cópia armazenada; a cópia é mantida até que o último caminho seja removido. A deduplicação nunca atravessa aplicações ou organizações.

Blobs de banco de dados. Um blob é endereçado pelo SHA-256 em seu caminho dentro de um banco de dados. Enviá-lo de novo responde 200 em vez de 201. Qualquer pessoa com permissão de leitura no banco de dados que conheça o hash pode lê-lo: as regras de registros não se aplicam a blobs.

Pastas compartilhadas. Uma aplicação pode criar uma pasta team que aparece para toda aplicação com acesso como shared/team/.... Quem a criou é o proprietário e concede a outras aplicações do mesmo espaço de trabalho read ou read_write. Uma aplicação sem acesso não consegue saber que a pasta existe: toda operação responde 404.

Arquivos grandes. Até 100 MiB vão em uma única requisição. Acima disso, até 5 GiB, você envia em partes e depois vincula o resultado a um caminho (ou ele se torna o blob).

Como funciona

Toda chamada leva Authorization: Bearer <credential>; não há cabeçalho de organização, porque a sua organização e a aplicação vêm da credencial. A permissão verificada é de leitura, escrita ou exclusão de blobs. Uma página web pode usar essas rotas com uma chave publicável a partir de uma origem que a sua aplicação lista.

Escrita por caminho. PUT /v1/files/{path} com os bytes e um Content-Length (obrigatório). A resposta é 201 para um caminho novo ou 200 para uma substituição: {"path","size","sha256","content_type","updated_at","metadata"} e ETag: "<sha256>".

Leitura. GET /v1/files/{path} devolve o arquivo inteiro (intervalos não são suportados) com Content-Type, ETag, X-File-Sha256, X-File-Updated-At e os seus cabeçalhos X-File-Meta-*. Todo download também traz X-Content-Type-Options: nosniff, um Content-Security-Policy que o isola e Cache-Control: no-store, de modo que uma página HTML enviada nunca possa ser executada como se fosse o seu site. HEAD devolve apenas os cabeçalhos.

Listagem. GET /v1/files?prefix=photos/&delimiter=/ lista os arquivos diretamente sob uma pasta como items e cada subpasta uma única vez em prefixes. Siga next_cursor até que seja null: uma página pode ser menor que limit e ainda assim ter uma próxima.

Em partes. POST /v1/files-uploads/{sha256} inicia um envio (ou responde 200 com exists: true quando a sua pasta já contém esses bytes). Envie as partes de 1 a 10.000 com PUT .../{upload_id}/parts/{n}, cada uma entre 5 MiB e 100 MiB, exceto a última, e depois POST .../complete com a lista de partes e seus etags. A Inovacc lê o objeto inteiro de volta e confere o seu SHA-256 e o tamanho. Por fim, PUT /v1/files/{path} com X-File-Sha256 e corpo vazio dá a ele um caminho. Os blobs de banco de dados usam as mesmas quatro etapas em /v1/databases/{db}/blobs/{sha256}/uploads.

Envios, downloads e exclusões são registrados no Registro de atividade com o hash e o tamanho do arquivo.

Primeiros passos

Você precisa de uma chave secreta vinculada a uma aplicação, com permissões de blobs (Autenticação).

  1. Envie um arquivo. PUT /v1/files/reports/2026-10.csv com o arquivo como corpo e Content-Type: text/csv (veja os exemplos). A resposta é 201 com o seu sha256.
  2. Leia-o de volta. GET /v1/files/reports/2026-10.csv. Os bytes chegam com ETag igual ao hash do passo 1.
  3. Liste a pasta. GET /v1/files?prefix=reports/&delimiter=/. O seu arquivo está em items.
  4. Substitua-o. Faça PUT no mesmo caminho com bytes novos. A resposta é 200, com um novo sha256.
  5. Exclua-o. DELETE /v1/files/reports/2026-10.csv responde 204; um segundo GET resulta em 404 file_not_found.

Casos de uso

Fotos de perfil a partir do navegador. Um aplicativo web envia cada avatar para users/<id>/avatar.png com uma chave publicável e guarda o sha256 devolvido no registro do usuário, para que uma página perceba quando a imagem mudou.

Exportações compartilhadas com um aplicativo parceiro. Uma aplicação de relatórios cria a pasta compartilhada exports, concede read à aplicação de faturamento e grava arquivos mensais em shared/exports/. A aplicação de faturamento os lista e baixa; revogar a concessão tem efeito já na requisição seguinte dela.

Mídia grande com integridade. Uma ferramenta de vídeo calcula o hash de uma gravação de 2 GiB no cliente, envia-a em partes de 100 MiB e a conclui. A Inovacc só a armazena se o SHA-256 coincidir, de modo que uma transferência corrompida nunca se torna um arquivo.

Limites e preços

LimiteValor
Arquivo ou blob em uma requisição100 MiB, Content-Length obrigatório
Arquivo ou blob enviado em partes5 GiB; partes de 5 MiB a 100 MiB (exceto a última), numeradas de 1 a 10.000
Caminho1.024 bytes
Prefixo de listagem256 bytes
Metadados por arquivo2 KiB
Página de listagem1 a 200 (padrão 50)
Nome da pasta compartilhada1 a 64 letras, dígitos, _ ou -
Taxa600 requisições por minuto por titular de credencial, por localização

Preços: sob consulta.

Erros

StatusCódigoO que significa e o que fazer
400invalid_path, invalid_prefixO caminho ou prefixo não está na forma canônica; corrija do seu lado.
400invalid_metadataContent-Type ou X-File-Meta-* viola as regras ou excede 2 KiB.
400hash_mismatchOs bytes não correspondem ao SHA-256 que você enviou; nada foi armazenado.
400invalid_part, invalid_limit, invalid_cursor, invalid_granteeCorrija as partes do envio, a listagem ou a concessão.
401invalid_credentialsEnvie uma credencial válida.
403application_requiredAs rotas de caminho exigem uma credencial vinculada a uma aplicação.
403forbiddenFalta permissão, ou um beneficiário com read tentou escrever.
404file_not_found, blob_not_found, share_not_found, upload_not_foundNão existe, ou você não tem acesso a ele.
409already_exists, not_emptyO nome da pasta compartilhada já está em uso; esvazie a pasta antes de excluí-la.
411length_requiredEnvie Content-Length.
413payload_too_largeMais de 100 MiB em uma requisição (use partes) ou mais de 5 GiB.
429rate_limitedDiminua o ritmo.
503service_unavailableTente novamente mais tarde.

Boas práticas

  • Envie X-File-Sha256 em toda escrita para que um envio danificado seja recusado em vez de armazenado.
  • Use partes acima de 100 MiB e comece com POST .../uploads: ele responde exists: true quando os bytes já estão lá.
  • Guarde o sha256, não apenas o caminho, nos seus registros; ele diz exatamente a que conteúdo um registro se refere.
  • Siga next_cursor até o fim ao listar; páginas curtas são normais.
  • Conceda read, a menos que um parceiro precise escrever, e revogue as concessões de que não precisa mais.
  • Não conte com blobs serem privados por regra: qualquer pessoa que possa ler o banco de dados e conheça o hash pode ler um blob.

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

PUT /v1/databases/{database}/blobs/{sha256}

GET /v1/databases/{database}/blobs/{sha256}

DELETE /v1/databases/{database}/blobs/{sha256}

POST /v1/databases/{database}/blobs/{sha256}/uploads

Exemplo

Linguagem

⋮
PUT /v1/databases/{database}/blobs/{sha256}Exemplo

cURL

# The body is {} here: see parameters.
curl -X PUT "https://data.inovacc.dev/v1/databases/{database}/blobs/{sha256}" \
  -H "Authorization: Bearer $INOVACC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

TypeScript

// The body is {} here: see parameters.
const body: Record<string, unknown> = {};

const url = "https://data.inovacc.dev/v1/databases/{database}/blobs/{sha256}";

const response = await fetch(url, {
  method: "PUT",
  headers: {
    Authorization: `Bearer ${process.env.INOVACC_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify(body),
});

console.log(response.status, await response.text());

Python

import json
import os
import urllib.request

# The body is {} here: see parameters.
body = {}

request = urllib.request.Request(
    "https://data.inovacc.dev/v1/databases/{database}/blobs/{sha256}",
    data=json.dumps(body).encode(),
    method="PUT",
    headers={
        "User-Agent": "inovacc-python-sample",
        "Authorization": "Bearer " + os.environ["INOVACC_API_KEY"],
        "Content-Type": "application/json",
    },
)

with urllib.request.urlopen(request) as response:
    print(response.status, response.read().decode())

Go

package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
)

const url = "https://data.inovacc.dev/v1/databases/{database}/blobs/{sha256}"

// The body is {} here: see parameters.
const body = `{}`

func main() {
	req, err := http.NewRequest("PUT", url, strings.NewReader(body))
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("INOVACC_API_KEY"))
	req.Header.Set("Content-Type", "application/json")

	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"] }
// Cargo.toml: serde_json = "1"

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api_key = std::env::var("INOVACC_API_KEY")?;
    // The body is {} here: see parameters.
    let body = serde_json::json!({});
    let response = reqwest::blocking::Client::new()
        .put("https://data.inovacc.dev/v1/databases/{database}/blobs/{sha256}")
        .bearer_auth(api_key)
        .json(&body)
        .send()?;
    println!("{} {}", response.status(), response.text()?);
    Ok(())
}

JavaScript

// The body is {} here: see parameters.
const body = {};

const url = "https://data.inovacc.dev/v1/databases/{database}/blobs/{sha256}";

const response = await fetch(url, {
  method: "PUT",
  headers: {
    Authorization: `Bearer ${process.env.INOVACC_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify(body),
});

console.log(response.status, await response.text());

PHP

<?php

// The body is {} here: see parameters.
$body = <<<'JSON'
{}
JSON;

$curl = curl_init('https://data.inovacc.dev/v1/databases/{database}/blobs/{sha256}');
curl_setopt_array($curl, [
    CURLOPT_CUSTOMREQUEST => 'PUT',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('INOVACC_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => $body,
]);

$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/{database}/blobs/{sha256}')
# The body is {} here: see parameters.
body = <<~'JSON'
{}
JSON

request = Net::HTTP::Put.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch('INOVACC_API_KEY')}"
request["Content-Type"] = "application/json"
request.body = body

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");
        // The body is {} here: see parameters.
        String body = "{}";

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://data.inovacc.dev/v1/databases/{database}/blobs/{sha256}"))
                .header("Authorization", "Bearer " + apiKey)
                .header("Content-Type", "application/json")
                .method("PUT", HttpRequest.BodyPublishers.ofString(body))
                .build();

        HttpResponse<String> response = HttpClient.newHttpClient()
                .send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.statusCode() + " " + response.body());
    }
}

C#

using System.Text;

// The body is {} here: see parameters.
var body = "{}";

var url = "https://data.inovacc.dev/v1/databases/{database}/blobs/{sha256}";
var apiKey = Environment.GetEnvironmentVariable("INOVACC_API_KEY");

using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Put, url);
request.Headers.Add("Authorization", $"Bearer {apiKey}");
request.Content = new StringContent(body, Encoding.UTF8, "application/json");

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")
    // The body is {} here: see parameters.
    val body = "{}"

    val request = HttpRequest.newBuilder()
        .uri(URI.create("https://data.inovacc.dev/v1/databases/{database}/blobs/{sha256}"))
        .header("Authorization", "Bearer " + apiKey)
        .header("Content-Type", "application/json")
        .method("PUT", HttpRequest.BodyPublishers.ofString(body))
        .build()

    val response = HttpClient.newHttpClient()
        .send(request, HttpResponse.BodyHandlers.ofString())
    println("${response.statusCode()} ${response.body()}")
}

Swift

import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

// The body is {} here: see parameters.
let body = #"""
{}
"""#

let apiKey = ProcessInfo.processInfo.environment["INOVACC_API_KEY"] ?? ""

let url = URL(string: "https://data.inovacc.dev/v1/databases/{database}/blobs/{sha256}")!
var request = URLRequest(url: url)
request.httpMethod = "PUT"
request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = Data(body.utf8)

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#files
Repositório
identity
Caminho
contracts/component-taxonomy/catalog/capabilities.json
Commit
08ef3cd75d82

Exemplo

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