Java
Kiket Java SDK.
Build and run Kiket extensions with a batteries-included, strongly-typed Java toolkit.
Features
- ๐ Webhook handlers โ register handlers for events with
sdk.register("case.created", "v1", handler). - ๐ Transparent authentication โ HMAC verification for inbound payloads, workspace-token client for outbound calls.
- ๐ Secret manager โ list, fetch, rotate, and delete extension secrets stored in Google Secret Manager.
- ๐ Built-in Spring Boot app โ serve extension webhooks locally or in production without extra wiring.
- ๐ Version-aware routing โ register multiple handlers per event and propagate version headers on outbound calls.
- ๐ฆ Manifest-aware defaults โ automatically loads
extension.yaml/manifest.yaml, applies configuration defaults, and hydrates secrets fromKIKET_SECRET_*environment variables. - ๐งฑ Typed & documented โ designed for Java 17+ with full type safety and rich Javadoc comments.
- ๐ Telemetry & feedback hooks โ capture handler duration/success metrics automatically.
- ๐ Custom data client โ call
/api/v1/ext/custom_data/...withcontext.getEndpoints().customData(projectId)using the runtime token. - ๐ Rate-limit helper โ inspect
/api/v1/ext/rate_limitviacontext.getEndpoints().rateLimit()before fanning out jobs.
Quickstart
Maven
<dependency>
<groupId>dev.kiket</groupId>
<artifactId>kiket-sdk</artifactId>
<version>0.1.0</version>
</dependency>Example
import dev.kiket.sdk.KiketSDK;
import dev.kiket.sdk.handler.WebhookHandler;
public class Main {
public static void main(String[] args) {
KiketSDK sdk = KiketSDK.builder()
.webhookSecret("sh_123")
.workspaceToken("wk_test")
.extensionId("com.example.marketing")
.extensionVersion("1.0.0")
.build();
// Register webhook handler (v1)
sdk.register("case.created", "v1", (payload, context) -> {
String summary = (String) ((Map) payload.get("case")).get("title");
System.out.println("Event version: " + context.getEventVersion());
context.getEndpoints().logEvent("case.created", Map.of("summary", summary));
context.getSecrets().set("WEBHOOK_TOKEN", "abc123");
return Map.of("ok", true);
});
// Register webhook handler (v2)
sdk.register("case.created", "v2", (payload, context) -> {
String summary = (String) ((Map) payload.get("case")).get("title");
context.getEndpoints().logEvent("case.created", Map.of(
"summary", summary,
"schema", "v2"
));
return Map.of("ok", true, "version", context.getEventVersion());
});
sdk.run("0.0.0.0", 8080);
}
}Custom Data Client
When your manifest defines custom_data.permissions, the SDK automatically uses the runtime token provided in the webhook payload for API calls via context.getClient():
sdk.register("case.created", "v1", (payload, context) -> {
String projectId = String.valueOf(((Map<?, ?>) payload.get("case")).get("project_id"));
var customData = context.getEndpoints().customData(projectId);
var options = new CustomDataClient.CustomDataListOptions();
options.setLimit(10);
options.setFilters(Map.of("status", "active"));
customData.list("com.example.crm.contacts", "automation_records", options);
customData.create("com.example.crm.contacts", "automation_records", Map.of(
"email", "lead@example.com",
"metadata", Map.of("source", "webhook")
));
return Map.of("ok", true);
});SLA Alert Stream
SLA monitors raise workflow.sla_status events. Use the helper to inspect current alerts:
sdk.register("workflow.sla_status", "v1", (payload, context) -> {
String projectId = String.valueOf(((Map<?, ?>) payload.get("case")).get("project_id"));
var slaClient = context.getEndpoints().slaEvents(projectId);
var options = new SlaEventsClient.SlaEventsListOptions();
options.setState("imminent");
options.setLimit(5);
var events = slaClient.list(options);
if (events.getData().isEmpty()) {
return Map.of("ok", true);
}
var first = events.getData().get(0);
context.getEndpoints().logEvent("sla.warning", Map.of(
"issue_id", first.get("issue_id"),
"state", first.get("state")
));
return Map.of("acknowledged", true);
});Configuration
Environment Variables
KIKET_WEBHOOK_SECRETโ Webhook HMAC secret for signature verificationKIKET_WORKSPACE_TOKENโ Workspace token for API authenticationKIKET_BASE_URLโ Kiket API base URL (defaults tohttps://kiket.dev)KIKET_SDK_TELEMETRY_URLโ Telemetry reporting endpoint (optional)KIKET_SDK_TELEMETRY_OPTOUTโ Set to1to disable telemetryKIKET_SECRET_*โ Secret overrides (e.g.,KIKET_SECRET_API_KEY)
Manifest File
Create an extension.yaml or manifest.yaml file:
id: com.example.marketing
version: 1.0.0
delivery_secret: sh_production_secret
settings:
- key: API_KEY
secret: true
- key: MAX_RETRIES
default: 3
- key: TIMEOUT_MS
default: 5000API Reference
KiketSDK
Main SDK class for building extensions.
KiketSDK sdk = KiketSDK.builder()
.webhookSecret(String)
.workspaceToken(String)
.baseUrl(String)
.settings(Map<String, Object>)
.extensionId(String)
.extensionVersion(String)
.manifestPath(String)
.autoEnvSecrets(boolean)
.telemetryEnabled(boolean)
.feedbackHook(FeedbackHook)
.telemetryUrl(String)
.build();Methods:
sdk.register(String event, String version, WebhookHandler handler)โ Register a webhook handlersdk.run(String host, int port)โ Start the Spring Boot serversdk.stop()โ Stop the server
HandlerContext
Context passed to webhook handlers:
public interface HandlerContext {
String getEvent();
String getEventVersion();
Map<String, String> getHeaders();
KiketClient getClient();
ExtensionEndpoints getEndpoints();
Map<String, Object> getSettings();
String getExtensionId();
String getExtensionVersion();
ExtensionSecretManager getSecrets();
String secret(String key); // Secret helper with payload-first fallback
}Response Helpers
Use the ExtensionResponse class to build properly formatted responses:
import dev.kiket.sdk.responses.ExtensionResponse;
// Simple allow
return ExtensionResponse.allow().build();
// Allow with message and data
return ExtensionResponse.allow()
.message("Successfully configured")
.data("routeId", 123)
.build();
// Allow with output fields (displayed in configuration UI)
return ExtensionResponse.allow()
.message("Mailjet configured successfully")
.data("routeId", route.getId())
.outputField("inbound_email", route.getEmail())
.build();
// Deny with error details
return ExtensionResponse.deny("Invalid credentials")
.data("errorCode", "AUTH_FAILED")
.build();
// Pending for async operations
return ExtensionResponse.pending("Awaiting approval")
.data("jobId", "abc123")
.build();Output fields are displayed in the extension configuration UI after setup, allowing extensions to expose generated data like email addresses, webhook URLs, or status information.
Secret Helper
The secret() method provides a simple way to retrieve secrets with automatic fallback:
// Checks payload secrets first (per-org config), falls back to ENV
String slackToken = context.secret("SLACK_BOT_TOKEN");
// Example usage
sdk.register("case.created", "v1", (payload, context) -> {
String apiKey = context.secret("API_KEY");
if (apiKey == null) {
throw new IllegalStateException("API_KEY not configured");
}
// Use apiKey...
return Map.of("ok", true);
});The lookup order is:
- Payload secrets (per-org configuration from
payload["secrets"]) - Environment variables (extension defaults via
System.getenv())
This allows organizations to override extension defaults with their own credentials.
Publishing to GitHub Packages
When you are ready to cut a release:
- Update the version in
pom.xml. - Run the test suite (
mvn test). - Build distributables:
mvn clean package - Commit and tag the release:
git add pom.xml git commit -m "Bump Java SDK to v0.x.y" git tag java-v0.x.y git push --tags - GitHub Actions will automatically publish to GitHub Packages.
License
MIT
Rate-Limit Helper
Throttle expensive webhooks by checking the remaining window:
sdk.register("automation.dispatch", "v1", (payload, context) -> {
RateLimitInfo limits = context.getEndpoints().rateLimit();
if (limits != null && limits.getRemaining() < 5) {
context.getEndpoints().logEvent("rate_limited", Map.of(
"remaining", limits.getRemaining(),
"reset_in", limits.getResetIn()
));
return Map.of("deferred", true);
}
// Continue with heavy work
return Map.of("ok", true);
});