Documentação para desenvolvedores
Início rápido
Crie uma chave da API, envie sua primeira requisição e receba webhooks com segurança em quatro etapas.
1. Criar uma chave de API
Crie uma integração e emita uma chave no console da conta. A chave completa é exibida apenas uma vez — guarde-a em um gerenciador de segredos.
- Conta → API & Webhooks — crie uma integração para o seu workspace pessoal ou para um espaço de equipe do qual você é proprietário.
- Escolha as permissões necessárias:
notes:read,transcripts:read,summaries:read,webhooks:manage. - A chave tem o formato
alt_live_{key_id}.{secret}e é exibida uma única vez. Guarde-a em um gerenciador de segredos.
Exporte-a no seu shell para que todos os comandos abaixo funcionem como estão:
export ALT_API_KEY="alt_live_...paste-your-key-here..."2. Buscar notas existentes
Envie o cursor retornado pela API na próxima requisição para percorrer toda a lista. Depois, busque a transcrição e o resumo de cada nota.
curl 'https://public-api.altalt.io/v1/notes?limit=100' \
-H "Authorization: Bearer $ALT_API_KEY"
# Follow next_cursor until has_more is false
curl 'https://public-api.altalt.io/v1/notes?limit=100&cursor=NEXT_CURSOR' \
-H "Authorization: Bearer $ALT_API_KEY"
# Fetch content per note (scopes: transcripts:read / summaries:read)
curl 'https://public-api.altalt.io/v1/notes/NOTE_ID/transcript' \
-H "Authorization: Bearer $ALT_API_KEY"
curl 'https://public-api.altalt.io/v1/notes/NOTE_ID/summary' \
-H "Authorization: Bearer $ALT_API_KEY"Para a sincronização incremental posterior, faça polling com ?updated_after=<last sync time> ou use webhooks.
3. Registrar um endpoint de webhook
Registre um endpoint HTTPS público para ser notificado sobre notas novas e alteradas em vez de fazer polling.
curl -X POST 'https://public-api.altalt.io/v1/webhook-endpoints' \
-H "Authorization: Bearer $ALT_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"url": "https://example.com/webhooks/alt",
"events": ["note.ended", "note.summary.generated", "note.updated", "note.deleted"]
}'A resposta inclui o signing_secret (whsec_...) — exibido uma única vez. O endpoint começa como pending_verification e é ativado depois que o seu receptor responde ao evento de verificação com um 2xx. Você também pode fazer isso sem código no console.
4. Verificar as assinaturas dos webhooks
Verifique a assinatura Standard Webhooks de cada requisição para confirmar que ela veio do Alt. Ignore duplicatas com event_id, responda primeiro e depois busque o conteúdo atualizado na API REST.
Node.js
import { createHmac, timingSafeEqual } from "node:crypto";
import http from "node:http";
// whsec_... secret from endpoint creation (shown once). Keep it server-side.
const SECRET = process.env.ALT_WEBHOOK_SECRET;
const secretBytes = Buffer.from(SECRET.slice("whsec_".length), "base64url");
const TOLERANCE_SECONDS = 300;
function isValidSignature(headers, rawBody) {
const id = headers["webhook-id"];
const timestamp = headers["webhook-timestamp"];
const signatureHeader = headers["webhook-signature"];
if (!id || !timestamp || !signatureHeader) return false;
// Reject stale timestamps (replay protection)
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > TOLERANCE_SECONDS) return false;
const expected = createHmac("sha256", secretBytes)
.update(`${id}.${timestamp}.${rawBody}`)
.digest("base64");
// Header may contain multiple space-delimited signatures: "v1,abc v1,def"
return String(signatureHeader)
.split(" ")
.some((part) => {
const [version, signature] = part.split(",");
if (version !== "v1" || !signature) return false;
const a = Buffer.from(signature);
const b = Buffer.from(expected);
return a.length === b.length && timingSafeEqual(a, b);
});
}
http
.createServer((req, res) => {
if (req.method !== "POST" || req.url !== "/webhooks/alt") {
res.writeHead(404).end();
return;
}
let rawBody = "";
req.on("data", (chunk) => (rawBody += chunk));
req.on("end", () => {
if (!isValidSignature(req.headers, rawBody)) {
res.writeHead(401).end();
return;
}
const event = JSON.parse(rawBody);
// 1. Dedupe on event.event_id (deliveries are at-least-once).
// 2. Enqueue for async processing, then ack fast.
// 3. Fetch the note from the REST API; apply only if revision is newer.
console.log(event.event_type, event.data.note_id, event.data.revision);
res.writeHead(204).end();
});
})
.listen(3000);Python
import base64, hashlib, hmac, json, os, time
from http.server import BaseHTTPRequestHandler, HTTPServer
# whsec_... secret from endpoint creation (shown once). Keep it server-side.
raw_secret = os.environ["ALT_WEBHOOK_SECRET"].removeprefix("whsec_")
SECRET = base64.urlsafe_b64decode(raw_secret + "=" * (-len(raw_secret) % 4))
TOLERANCE_SECONDS = 300
def is_valid_signature(headers, raw_body: bytes) -> bool:
msg_id = headers.get("webhook-id", "")
timestamp = headers.get("webhook-timestamp", "")
signature_header = headers.get("webhook-signature", "")
if not msg_id or not timestamp or not signature_header:
return False
# Reject stale timestamps (replay protection)
if abs(time.time() - float(timestamp)) > TOLERANCE_SECONDS:
return False
signed_content = f"{msg_id}.{timestamp}.".encode() + raw_body
digest = hmac.new(SECRET, signed_content, hashlib.sha256).digest()
expected = base64.b64encode(digest).decode()
# Header may contain multiple space-delimited signatures: "v1,abc v1,def"
for part in signature_header.split(" "):
version, _, signature = part.partition(",")
if version == "v1" and signature and hmac.compare_digest(signature, expected):
return True
return False
class Handler(BaseHTTPRequestHandler):
def do_POST(self):
if self.path != "/webhooks/alt":
self.send_response(404); self.end_headers(); return
raw_body = self.rfile.read(int(self.headers.get("Content-Length", 0)))
if not is_valid_signature(self.headers, raw_body):
self.send_response(401); self.end_headers(); return
event = json.loads(raw_body)
# 1. Dedupe on event["event_id"] (deliveries are at-least-once).
# 2. Enqueue for async processing, then ack fast.
# 3. Fetch the note from the REST API; apply only if revision is newer.
print(event["event_type"], event["data"]["note_id"], event["data"]["revision"])
self.send_response(204); self.end_headers()
HTTPServer(("", 3000), Handler).serve_forever()As assinaturas seguem a especificação Standard Webhooks, então você pode usar as bibliotecas oficiais standardwebhooks (npm / PyPI). Veja Webhooks para lidar com duplicatas, ordem de entrega e mudanças perdidas.