Java Sdk

Java 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's
    initSipCall({ phoneNumber, intentId, clientSecret }) (readme.io: Calls) — for a custom call UI you build yourself.
  • kasookoo-click-to-call-widget
    (readme.io docs)'s
    <kasookoo-dialer> — a drop-in call button/dial pad. It doesn't call initSipCall()
    directly itself; instead, it fetches an intent on its own from a route you
    give it (intentEndpoint, passed to its init()), 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/intent_id and clientSecret/client_secret. Nothing else you
put in that response matters to either integration.

Install

Maven:

<dependency>
    <groupId>ai.kasookoo</groupId>
    <artifactId>kasookoo-server-sdk</artifactId>
    <version>0.1.0-beta.0</version>
</dependency>

Gradle:

implementation("ai.kasookoo:kasookoo-server-sdk:0.1.0-beta.0")

Usage

import ai.kasookoo.sdk.Kasookoo;
import ai.kasookoo.sdk.CallIntent;

Kasookoo kasookoo = Kasookoo.builder()
        .secretKey(System.getenv("KASOOKOO_SECRET_KEY"))
        .build();

CallIntent intent = kasookoo.callIntents.create(
        "Website visitor", // subject — must match the `subject` your frontend passes to init() — see below
        "+18005551234",    // phoneNumber
        300L                // maxDurationSeconds — optional; use the two-arg overload to omit it
);

callIntents.create() sends your secret key to Kasookoo and returns a
CallIntent:

public record CallIntent(
        String intentId,        // unique id for this call intent
        String clientSecret,    // short-lived secret for the frontend — hand this off, never the org secret key
        String subject,
        String phoneNumber,
        long expiresIn,         // seconds until clientSecret expires if unused
        long maxDurationSeconds,
        String status           // "requires_dial", or any other status string the API returns
) {}

Respond from your route with at least intentId/intent_id and
clientSecret/client_secret — 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 KasookooApiException — see
Error handling 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

Set via Kasookoo.builder():

MethodTypeDefaultDescription
.secretKey(...)StringrequiredYour organization's secret key.
.baseUrl(...)Stringhttps://sdk-test.kasookoo.aiAPI base URL.
.timeout(...)java.time.Duration10 secondsRequest timeout.
.httpClient(...)java.net.http.HttpClienta default client using .timeout(...)Override for testing, or to reuse your app's own client.

Error handling

import ai.kasookoo.sdk.KasookooApiException;
import ai.kasookoo.sdk.KasookooConfigException;

try {
    kasookoo.callIntents.create("Visitor", "+18005551234");
} catch (KasookooApiException err) {
    // err.getStatus()  — HTTP status code (0 for network errors/timeouts)
    // err.getCode()    — API-provided error code, if any
    // err.getDetails() — full raw response body, if any
} catch (KasookooConfigException err) {
    // invalid SDK usage — missing/empty field, bad config — thrown before any network call
}

Both extend RuntimeException — unchecked, so they propagate without a
throws declaration.

Example

import ai.kasookoo.sdk.CallIntent;
import ai.kasookoo.sdk.Kasookoo;
import ai.kasookoo.sdk.KasookooApiException;

public class Main {
    public static void main(String[] args) {
        Kasookoo kasookoo = Kasookoo.builder()
                .secretKey(System.getenv("KASOOKOO_SECRET_KEY"))
                .build();

        try {
            CallIntent intent = kasookoo.callIntents.create(
                    "Website visitor",
                    "+18005551234",
                    300L
            );
            System.out.println("Call intent created: " + intent);
            // Hand intent.clientSecret() (and intent.intentId()) to the frontend —
            // never the org's secret key itself.
        } catch (KasookooApiException err) {
            System.out.printf(
                    "Kasookoo API error [%d]%s: %s%n",
                    err.getStatus(),
                    err.getCode() != null ? " " + err.getCode() : "",
                    err.getMessage()
            );
        }
    }
}

Requirements

  • Java 17+. No runtime dependencies — the same design as the Node.js SDK
    (native fetch) and the Python SDK (stdlib urllib).

Did this page help you?