Kiket docs
API & SDKsSDKs

.NET

Kiket .NET SDK.

Build and run Kiket extensions with a batteries-included, strongly-typed .NET 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 ASP.NET Core 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 from KIKET_SECRET_* environment variables.
  • ๐Ÿ“‡ Custom data client โ€“ call /api/v1/ext/custom_data/... with context.Endpoints.CustomData(projectId) using the runtime token.
  • ๐Ÿ“‰ Rate-limit helper โ€“ call context.Endpoints.GetRateLimitAsync() to inspect /api/v1/ext/rate_limit before launching heavy jobs.
  • ๐Ÿงฑ Typed & documented โ€“ designed for .NET 8.0 with full type safety and rich XML documentation.
  • ๐Ÿ“Š Telemetry & feedback hooks โ€“ capture handler duration/success metrics automatically.

Quickstart

dotnet add package Kiket.SDK
using Kiket.SDK;

var sdk = new KiketSDK(new SDKConfig
{
    WorkspaceToken = "wk_test",
    ExtensionId = "com.example.marketing",
    ExtensionVersion = "1.0.0"
});

// Register webhook handler (v1)
sdk.Register("case.created", "v1", async (payload, context) =>
{
    var summary = payload["case"]["title"].ToString();
    Console.WriteLine($"Event version: {context.EventVersion}");

    await context.Endpoints.LogEventAsync("case.created", new Dictionary<string, object>
    {
        ["summary"] = summary
    });
    await context.Secrets.SetAsync("WEBHOOK_TOKEN", "abc123");

    return new { ok = true };
});

// Register webhook handler (v2)
sdk.Register("case.created", "v2", async (payload, context) =>
{
    var summary = payload["case"]["title"].ToString();

    await context.Endpoints.LogEventAsync("case.created", new Dictionary<string, object>
    {
        ["summary"] = summary,
        ["schema"] = "v2"
    });

    return new { ok = true, version = context.EventVersion };
});

sdk.Run("0.0.0.0", 8080);

Custom Data Client

When your manifest declares custom_data.permissions, the SDK automatically uses the runtime token provided in the webhook payload for API calls via context.Client. Use the helper to work with module data:

sdk.Register("case.created", "v1", async (payload, context) =>
{
    var projectId = payload["case"]["project_id"].ToString();
    var customData = context.Endpoints.CustomData(projectId!);

    var list = await customData.ListAsync("com.example.crm.contacts", "automation_records", new CustomDataListOptions
    {
        Limit = 10,
        Filters = new Dictionary<string, object> { ["status"] = "active" }
    });

    await customData.CreateAsync("com.example.crm.contacts", "automation_records", new Dictionary<string, object>
    {
        ["email"] = "lead@example.com",
        ["metadata"] = new Dictionary<string, object> { ["source"] = "webhook" }
    });

    return new { synced = list?.Data.Count ?? 0 };
});

SLA Alert Stream

Inspect the SLA alert feed for an installation:

sdk.Register("workflow.sla_status", "v1", async (payload, context) =>
{
    var projectId = payload["case"]["project_id"].ToString();
    var slaClient = context.Endpoints.SlaEvents(projectId!);

    var events = await slaClient.ListAsync(new SlaEventsListOptions
    {
        State = "imminent",
        Limit = 5
    });

    if (events?.Data?.Count == 0)
    {
        return new { ok = true };
    }

    var first = events!.Data![0];
    await context.Endpoints.LogEventAsync("sla.warning", new Dictionary<string, object>
    {
        ["issue_id"] = first["issue_id"],
        ["state"] = first["state"]
    });

    return new { acknowledged = true };
});

Configuration

Environment Variables

  • KIKET_WEBHOOK_SECRET โ€“ Webhook HMAC secret for signature verification
  • KIKET_WORKSPACE_TOKEN โ€“ Workspace token for API authentication
  • KIKET_BASE_URL โ€“ Kiket API base URL (defaults to https://kiket.dev)
  • KIKET_SDK_TELEMETRY_URL โ€“ Telemetry reporting endpoint (optional)
  • KIKET_SDK_TELEMETRY_OPTOUT โ€“ Set to 1 to disable telemetry
  • KIKET_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: 5000

API Reference

KiketSDK

Main SDK class for building extensions.

var sdk = new KiketSDK(new SDKConfig
{
    WebhookSecret = "...",
    WorkspaceToken = "...",
    BaseUrl = "...",
    Settings = new Dictionary<string, object>(),
    ExtensionId = "...",
    ExtensionVersion = "...",
    ManifestPath = "...",
    AutoEnvSecrets = true,
    TelemetryEnabled = true,
    FeedbackHook = record => { /* ... */ },
    TelemetryUrl = "..."
});

Methods:

  • sdk.Register(string event, string version, WebhookHandler handler) โ€“ Register a webhook handler
  • sdk.Run(string host = "127.0.0.1", int port = 8000) โ€“ Start the ASP.NET Core server
  • await sdk.StopAsync() โ€“ Stop the server

HandlerContext

Context passed to webhook handlers:

public class HandlerContext
{
    public string Event { get; }
    public string EventVersion { get; }
    public Dictionary<string, string> Headers { get; }
    public KiketClient Client { get; }
    public ExtensionEndpoints Endpoints { get; }
    public Dictionary<string, object> Settings { get; }
    public string? ExtensionId { get; }
    public string? ExtensionVersion { get; }
    public ExtensionSecretManager Secrets { get; }
    public string? Secret(string key);  // Secret helper with payload-first fallback
}

Response Helpers

Use the ExtensionResponse class to build properly formatted responses:

using Kiket.SDK.Responses;

// Simple allow
return ExtensionResponse.Allow().Build();

// Allow with message and data
return ExtensionResponse.Allow()
    .WithMessage("Successfully configured")
    .WithData("routeId", 123)
    .Build();

// Allow with output fields (displayed in configuration UI)
return ExtensionResponse.Allow()
    .WithMessage("Mailjet configured successfully")
    .WithData("routeId", route.Id)
    .WithOutputField("inbound_email", route.Email)
    .Build();

// Deny with error details
return ExtensionResponse.Deny("Invalid credentials")
    .WithData("errorCode", "AUTH_FAILED")
    .Build();

// Pending for async operations
return ExtensionResponse.Pending("Awaiting approval")
    .WithData("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
var slackToken = context.Secret("SLACK_BOT_TOKEN");

// Example usage
sdk.Register("case.created", "v1", async (payload, context) =>
{
    var apiKey = context.Secret("API_KEY");
    if (apiKey is null)
    {
        throw new InvalidOperationException("API_KEY not configured");
    }
    // Use apiKey...
    return new { ok = true };
});

The lookup order is:

  1. Payload secrets (per-org configuration from payload["secrets"])
  2. Environment variables (extension defaults via Environment.GetEnvironmentVariable())

This allows organizations to override extension defaults with their own credentials.

ExtensionEndpoints

High-level extension endpoints:

await context.Endpoints.LogEventAsync("event.name", data);
var metadata = await context.Endpoints.GetMetadataAsync();

ExtensionSecretManager

Secret manager for CRUD operations:

var value = await context.Secrets.GetAsync("API_KEY");
await context.Secrets.SetAsync("API_KEY", "new-value");
await context.Secrets.DeleteAsync("API_KEY");
var keys = await context.Secrets.ListAsync();
await context.Secrets.RotateAsync("API_KEY", "new-value");

Publishing to GitHub Packages

When you are ready to cut a release:

  1. Update the version in Kiket.SDK.csproj.
  2. Run the test suite (dotnet test).
  3. Build and pack:
    dotnet build --configuration Release
    dotnet pack --configuration Release
  4. Commit and tag the release:
    git add Kiket.SDK/Kiket.SDK.csproj
    git commit -m "Bump .NET SDK to v0.x.y"
    git tag dotnet-v0.x.y
    git push --tags
  5. GitHub Actions will automatically publish to GitHub Packages.

License

MIT

Rate-Limit Helper

Gate expensive automation against the current window:

sdk.Register("automation.dispatch", "v1", async (_payload, context) =>
{
    var limits = await context.Endpoints.GetRateLimitAsync();
    if (limits is not null && limits.Remaining < 5)
    {
        await context.Endpoints.LogEventAsync("rate_limited", new()
        {
            ["remaining"] = limits.Remaining,
            ["reset_in"] = limits.ResetIn
        });
        return new { deferred = true };
    }

    // Continue with the heavy work
    return new { ok = true };
});

On this page