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_tokenMinimal 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-GCMWebhooksPlanned
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.