Autenticação
Toda chamada a uma API da Inovacc é autenticada por um único cabeçalho, Authorization: Bearer <credential>. A credencial identifica a sua organização e, no caso da credencial de uma aplicação, a aplicação também, então você nunca as nomeia na requisição. As chamadas que alteram algo na API de IA também levam um X-Operation-Id, que as torna seguras para repetir. Este guia explica as credenciais, os cabeçalhos que cada API lê, como funciona a idempotência e como manter as chaves seguras durante toda a sua vida.
Os cabeçalhos
| Cabeçalho | Onde | Valor |
|---|---|---|
Authorization | toda chamada, toda API | Bearer <credential> |
X-Operation-Id | API de IA (ai.inovacc.dev): todo POST, PUT e DELETE; opcional em GET | um id único que você escolhe: de 8 a 128 letras, dígitos, _ ou - |
Dados (data.inovacc.dev) e Eventos (events.inovacc.dev) não leem nenhum cabeçalho de organização ou de operação: o tenant vem apenas da credencial, e qualquer cabeçalho que tente nomear outro é ignorado. Dados usa versões HTTP (If-Match, If-None-Match) e Eventos usa uma idempotency_key no corpo para repetições seguras; veja as páginas dos produtos.
Uma requisição sem credencial, com uma credencial malformada ou com uma que não é reconhecida é recusada antes de qualquer coisa ser executada: 401 com missing_credentials ou invalid_credentials. Em todas as APIs essa resposta é a mesma, quer o caminho chamado exista ou não, de modo que nada sobre a API pode ser descoberto sem uma credencial.
Obter uma chave
As chaves secretas são criadas no console da Inovacc por uma pessoa da sua organização e mostradas uma única vez: a Inovacc guarda apenas um hash, então uma chave perdida não pode ser exibida de novo, só substituída. O acesso é por convite atualmente: peça ao seu contato na Inovacc que convide você e depois crie uma chave na sua organização.
Credenciais
| Credencial | Aparência | Para | Aceita por |
|---|---|---|---|
| Chave de API secreta | aik_ seguido de 64 caracteres hexadecimais | os seus servidores | IA, Dados, Eventos |
| Token de acesso de cliente OAuth | um token assinado emitido para um cliente de uma aplicação | os seus servidores | IA, Dados, Eventos |
| Chave publicável | apb_... | páginas web, a partir das origens que a sua aplicação lista | Dados (registros e arquivos, onde as regras permitem), Eventos (apenas transmissões) |
| Token de acesso de usuário final | emitido quando uma pessoa se autentica na sua aplicação | o navegador ou aplicativo dessa pessoa | Dados (registros e arquivos, conforme as regras da coleção) |
Uma chave pode ser vinculada a uma aplicação. Essa chave alcança apenas o que a aplicação habilitou: na API de IA cada rota exige uma capacidade (ai.chat, ai.embeddings, knowledge, decision), recusada com 403 capability_not_enabled caso contrário; em Dados ela enxerga apenas os bancos de dados que a sua aplicação criou e a pasta de arquivos da sua aplicação; em Eventos ela só pode nomear o seu próprio espaço de trabalho.
As chaves publicáveis não são segredos. Elas foram feitas para ir dentro de uma página web. O que protege os seus dados é a lista de origens permitidas e, acima de tudo, as regras de cada coleção: uma chave publicável não pode fazer nada que uma regra não admita.
Idempotência
Na API de IA, X-Operation-Id é uma chave de idempotência. A primeira resposta bem-sucedida a um id de operação é armazenada por 24 horas. Envie o mesmo id de novo e você recebe essa mesma resposta, marcada com x-idempotent-replay: true, sem que o trabalho seja executado duas vezes e sem uma segunda cobrança. É isso que torna segura uma nova tentativa após um tempo esgotado ou uma conexão interrompida.
- Um erro nunca é armazenado: repetir uma chamada que falhou com o mesmo id a executa de novo.
- Enquanto a primeira chamada ainda está em execução, uma segunda chamada com o mesmo id retorna 409
operation_in_progress; aguarde e tente novamente. - Em chat, embeddings e run, um id reutilizado devolve a resposta armazenada, qualquer que seja o novo corpo. Em itens de coleção, o mesmo id com um arquivo diferente retorna 409
operation_id_reused. - Gere um id novo para cada operação nova e reutilize um id apenas para repetir a chamada para a qual ele foi criado.
Toda resposta de IA repete o id sob o qual foi executada em x-operation-id; em um GET sem um id, o serviço cria um. Uma resposta de chat transmitida nunca é armazenada, então não pode ser repetida.
Exemplo
Linguagem
⋮
Shell
export INOVACC_API_KEY="<your API key>"export INOVACC_API_KEY="<your API key>"
Linguagem
⋮
cURL
curl "https://ai.inovacc.dev/v1/models" \
-H "Authorization: Bearer $INOVACC_API_KEY"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());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())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))
}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(())
}// 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());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);<?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}"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());
}
}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()}");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()}")
}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))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))
A resposta lista as rotas configuradas para a sua organização. As rotas são nomeadas por organização, então a lista que você vê é a sua. Uma chamada que altera algo acrescenta o seu id de operação:
Linguagem
⋮
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"}]}'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"}]}'
Ciclo de vida da chave
Uma chave passa por quatro etapas, e cada uma tem os seus próprios cuidados.
- Criação. Crie uma chave por aplicação e por ambiente (produção, homologação, o notebook de um desenvolvedor), nunca uma chave compartilhada por tudo. Copie a chave direto do console para o seu repositório de segredos.
- Uso. Carregue a chave a partir do ambiente ou de um gerenciador de segredos na inicialização. Envie-a apenas no cabeçalho
Authorization, apenas por HTTPS e apenas a partir de um servidor: uma chave secreta que chega à API de Dados ou de Eventos a partir de um navegador (com um cabeçalhoOrigin) é recusada com 403secret_key_in_browser. - Rotação. Crie uma segunda chave, implante-a em todos os lugares onde a primeira é usada, confirme no Registro de atividade que o id da chave antiga não aparece mais como
actor_ide então desative a chave antiga. Uma chave desativada responde 403key_disabled; revogar a credencial de uma aplicação tem efeito em até um minuto. - Aposentadoria. Remova a chave de todo repositório de segredos e pipeline quando uma aplicação ou ambiente deixar de existir.
Um comportamento para planejar: no Banco de Dados Vetorial, o escopo local pertence à chave que o escreveu. Uma chave nova enxerga um escopo local novo e vazio; use o escopo shared para o conhecimento que precisa sobreviver a uma rotação.
Os tokens de acesso OAuth expiram. Quando uma chamada responde 401 invalid_credentials com um token que antes funcionava, obtenha um novo token e tente novamente uma vez; não repita um 401 em um laço.
Higiene das chaves
- Nunca faça commit de uma chave, nem mesmo em um repositório privado ou em um arquivo de teste. Mantenha os arquivos
.envfora do controle de versão. - Nunca registre em log o cabeçalho
Authorizatione oculte-o dos relatórios de erro e dos traces. - Dê a cada integração a sua própria chave, para que o tráfego dela seja visível no Registro de atividade e ela possa ser rotacionada isoladamente.
- Prefira uma chave vinculada a uma aplicação com apenas as capacidades de que precisa a uma chave de toda a organização.
- Rotacione em um calendário e imediatamente quando alguém que tinha acesso sair ou uma chave puder ter vazado.
- Em um navegador, use uma chave publicável ou o token de uma pessoa autenticada e feche toda coleção com regras.
Veja também
- Erros: o envelope de erro e os códigos que você pode tratar.
- Limites: tamanhos de requisição, tempos limite e taxas.
- Webhooks: verificação de entregas com um segredo de assinatura.
- Referência da API: cada produto e seus endpoints.