kasookoo-server-sdk
v0.1.0-beta.4
Published
Official Node.js/TypeScript server-side SDK for Kasookoo — create call intents and other backend operations.
Maintainers
Readme
kasookoo-server-sdk
Node.js / TypeScript SDK for Kasookoo's server-side API. Server-only — it holds your organization's secret key, which must never reach a browser.
This SDK only creates call intents — it doesn't place calls itself. That half happens on the frontend, in either of two places:
kasookoo-sdk'sinitSipCall({ phoneNumber, intentId, clientSecret })(readme.io: Calls) — for a custom call UI you build yourself.kasookoo-click-to-call-widget's (readme.io docs)<kasookoo-dialer>— a drop-in call button/dial pad. It doesn't callinitSipCall()directly itself; instead, it fetches an intent on its own from a route you give it (intentEndpoint, passed to itsinit()), then dials with what that route returns.
Either way, the shape is the same: your frontend asks your backend for a call
intent, your backend creates it with this SDK, and your route must respond
with the two fields the frontend needs to actually place the call:
intentId and clientSecret. Nothing else you put in that response matters
to either integration.
Install
npm install kasookoo-server-sdkUsage
import { Kasookoo } from "kasookoo-server-sdk"
const kasookoo = new Kasookoo({
secretKey: process.env.KASOOKOO_SECRET_KEY!,
})
const intent = await kasookoo.callIntents.create({
subject: "Website visitor", // must match the `subject` your frontend passes to init() — see below
phoneNumber: "+18005551234",
maxDurationSeconds: 300, // optional
})callIntents.create() sends your secret key to Kasookoo and returns:
interface CallIntent {
intentId: string // unique id for this call intent
clientSecret: string // short-lived secret for the frontend — hand this off, never the org secret key
subject: string
phoneNumber: string
expiresIn: number // seconds until clientSecret expires if unused
maxDurationSeconds: number
status: "requires_dial" | string
}Respond from your route with at least intentId and clientSecret — that's
the whole contract, whichever frontend integration is consuming it (see
above): kasookoo-sdk's initSipCall(), or kasookoo-click-to-call-widget's
intentEndpoint. Your org's secret key never needs to leave the server.
Keep subject in sync with the frontend's init()
The subject you pass here must be the exact same value the frontend
passed to init() — kasookoo-sdk's KasookooClient.init({ subject }) or
kasookoo-click-to-call-widget's KasookooClickToCall.init({ subject }).
Kasookoo checks the two against each other and rejects the call intent if
they don't match (surfaced as a KasookooApiError — see
Errors below), so always pass subject here
explicitly, kept identical to whatever your frontend's init() call used —
don't assume it's implied or optional just because the frontend already sent
one.
Configuration
| Option | Type | Default | Description |
|---|---|---|---|
| secretKey | string | required | Your organization's secret key. |
| baseUrl | string | https://sdk-test.kasookoo.ai | API base URL. |
| timeoutMs | number | 10000 | Request timeout in milliseconds. |
| fetch | typeof fetch | global fetch | Override for testing, or on Node < 18. |
Example: a mini Express app
import express from "express"
import { Kasookoo } from "kasookoo-server-sdk"
const kasookoo = new Kasookoo({ secretKey: process.env.KASOOKOO_SECRET_KEY })
const app = express().use(express.json())
app.post("/my-api/call-intent", async (req, res) => {
const { phoneNumber, subject } = req.body
try {
const { intentId, clientSecret } = await kasookoo.callIntents.create({ subject, phoneNumber })
res.json({ intentId, clientSecret })
} catch {
res.sendStatus(502)
}
})
app.listen(3000)Point your frontend's intentEndpoint at /my-api/call-intent. Protect this
route with authentication or rate limiting before going live.
Errors
The SDK throws two error types, both exported from the package:
import { KasookooApiError, KasookooConfigError } from "kasookoo-server-sdk"KasookooApiError
Kasookoo rejected the request, or couldn't be reached.
| Property | Description |
|---|---|
| message | Human-readable description (the API's own message when it sent one). |
| status | HTTP status code. 0 if no response was received (network error or timeout). |
| code | Error code from the API, if any. The SDK sets "timeout" and "network_error" itself when status is 0. |
| details | The full parsed error response body, if any. |
KasookooConfigError
Invalid SDK usage, thrown before any network call: a missing secretKey,
no fetch available (Node < 18 without the fetch option), or an empty
subject / phoneNumber passed to callIntents.create().
Handling them
try {
await kasookoo.callIntents.create({ subject, phoneNumber })
} catch (err) {
if (err instanceof KasookooApiError) {
console.error(err.status, err.code, err.message)
} else if (err instanceof KasookooConfigError) {
console.error(err.message)
} else {
throw err
}
}A subject that doesn't match the one your frontend passed to init() is
rejected by Kasookoo and surfaces as a KasookooApiError.
Requirements
- Node.js 18+ (for global
fetch; pass your own via thefetchoption on older runtimes).
