DEVELOPERS

SDKs & clients

Tesska doesn't ship an official SDK yet — the API is plain REST + JSON, so any HTTP client works out of the box. This page gives you a minimal runnable example, plus the WebCrypto capabilities you need for mode B end-to-end decryption.

Official SDKsPlanned

Official SDKs haven't been released yet; Node and Python will come first. No need to wait: everything is exposed over REST, and the examples below are copy-paste ready.

  • The API is plain REST + JSON — no proprietary protocol, no signature scheme; curl is enough
  • Authentication is a single HTTP Bearer header: a project-scoped key issued in the console's Token Center (prefix tsk_)
  • The API won't change when SDKs ship — fetch code you write today migrates cleanly

Endpoint cheat sheet

The full public surface is below. A Bearer key only works for its bound project and scopes; callers receive short-lived tokens, never the original secret.

POST /v1/login exchange a grant for a short-lived token GET /v1/grants list grants available to this key GET /v1/platforms list available platforms GET /v1/approvals/:id poll an approval (mode B) GET /oauth/authorize Connect Tesska (authorization code + PKCE S256) POST /oauth/token code + code_verifier -> user-scoped access_token

Minimal example: Node + fetch

Node 18+ ships fetch natively — zero dependencies. Issue a key for your project in the console's Token Center and export it as TESSKA_API_KEY:

// Node 18+ — fetch is built in, no dependencies const KEY = process.env.TESSKA_API_KEY; // tsk_... issued in Token Center const res = await fetch("https://tesska.com/v1/login", { method: "POST", headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ platform: "github", account: "deploy-bot", scope: ["repo:read"], }), }); const data = await res.json(); if (data.kind === "pending_approval") { // Mode B: owner approval required — poll GET /v1/approvals/:id console.log("waiting for approval:", data.requestId); } else { // Mode A: short-lived access token — never the raw secret console.log("token:", data.token, "expires:", data.expiresAt); }

The returned token is a short-lived access token, not the raw credential.Mode A returns a token directly; raw plaintext delivery requires an explicit per-asset setting plus a global switch, both off by default. Mode B returns pending_approval and pushes the owner's phone for approval.

Mode B: end-to-end decryption

Once an approval-required credential is approved, resultCt from GET /v1/approvals/:id is ciphertext sealed to your ephemeral public key — only your private key can open it. Generate an ECDH P-256 ephemeral key pair with WebCrypto and send the public key as clientPubKey with /v1/login:

  • Your runtime needs WebCrypto with ECDH (P-256), HKDF-SHA256 and AES-256-GCM — every modern browser and Node 18+ qualifies
  • The server never sees plaintext: the credential is decrypted in the owner's browser and sealed directly to your public key
  • See the Approvals page for the full polling and decryption walkthrough
// WebCrypto — built into browsers and Node 18+ (globalThis.crypto) const kp = await crypto.subtle.generateKey( { name: "ECDH", namedCurve: "P-256" }, true, ["deriveBits"], ); const raw = await crypto.subtle.exportKey("raw", kp.publicKey); const clientPubKey = Buffer.from(raw).toString("base64"); // 1. POST /v1/login with { ...body, clientPubKey } // 2. poll GET /v1/approvals/:id until status === "approved" // 3. decrypt resultCt: ECDH -> HKDF-SHA256 -> AES-256-GCM

WebhooksPlanned

Outbound webhooks aren't live yet. Until then, poll GET /v1/approvals/:id for mode B results (responses include expiresAt; the request expires after that). This page will be updated when webhooks ship.