# 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

```sh
export INOVACC_API_KEY="<your API key>"
```

**cURL**

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

**TypeScript**

```ts
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**

```py
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**

```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**

```rs
// 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**

```mjs
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
<?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**

```rb
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**

```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#**

```cs
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**

```kt
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**

```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))
```

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:

```sh
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.

1. **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.
2. **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çalho `Origin`) é recusada com `403 secret_key_in_browser`.
3. **Rotação.** Crie uma segunda chave, implante-a em todos os lugares onde a primeira é usada, confirme no [Registro de atividade](/pt-br/products/activity/) que o id da chave antiga não aparece mais como `actor_id` e então desative a chave antiga. Uma chave desativada responde `403 key_disabled`; revogar a credencial de uma aplicação tem efeito em até um minuto.
4. **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 `.env` fora do controle de versão.
- **Nunca registre em log o cabeçalho `Authorization`** e 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](/pt-br/guides/errors/): o envelope de erro e os códigos que você pode tratar.
- [Limites](/pt-br/guides/limits/): tamanhos de requisição, tempos limite e taxas.
- [Webhooks](/pt-br/guides/webhooks/): verificação de entregas com um segredo de assinatura.
- [Referência da API](/pt-br/reference/): cada produto e seus endpoints.
