Documentation développeur

Démarrage rapide

Créez une clé API, envoyez votre première requête et recevez des webhooks en toute sécurité en quatre étapes.

1. Créer une clé API

Créez une intégration et émettez une clé depuis la console de votre compte. La clé complète n'est affichée qu'une seule fois — conservez-la dans un gestionnaire de secrets.

  • Compte → API & Webhooks — créez une intégration pour votre espace de travail personnel ou pour un espace d'équipe dont vous êtes propriétaire.
  • Choisissez les scopes : notes:read, transcripts:read, summaries:read, webhooks:manage.
  • La clé se présente sous la forme alt_live_{key_id}.{secret} et n'est affichée qu'une seule fois. Conservez-la dans un gestionnaire de secrets.

Exportez-la dans votre shell pour que toutes les commandes ci-dessous fonctionnent telles quelles :

shell
export ALT_API_KEY="alt_live_...paste-your-key-here..."

2. Récupérer les notes existantes

Transmettez le cursor renvoyé par l’API à la requête suivante pour parcourir toute la liste. Récupérez ensuite la transcription et le résumé de chaque note.

curl
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"

Pour la synchronisation incrémentale ensuite, interrogez l'API avec ?updated_after=<last sync time> ou appuyez-vous sur les webhooks.

3. Enregistrer un endpoint de webhook

Enregistrez un endpoint HTTPS public pour être notifié des notes créées et modifiées, au lieu d'interroger l'API en boucle.

curl
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"]
  }'

La réponse contient le signing_secret (whsec_...), affiché une seule fois. L'endpoint démarre à l'état pending_verification ; il devient actif dès que votre récepteur répond à l'événement de vérification par un 2xx. Vous pouvez aussi le faire sans code depuis la console.

4. Vérifier les signatures des webhooks

Vérifiez la signature Standard Webhooks de chaque requête pour confirmer qu’elle provient d’Alt. Ignorez les doublons avec event_id, répondez d’abord, puis récupérez le contenu à jour via la REST API.

Node.js

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

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

Les signatures suivent la spécification Standard Webhooks : les bibliothèques officielles standardwebhooks (npm / PyPI) fonctionnent donc également. Consultez Webhooks pour les doublons, l'ordre de réception et la réconciliation.