Extension API¶
Extensions interact with Kiket via the REST API using API keys scoped to the project and permissions declared in the manifest.
Authentication¶
- Runtime tokens (preferred): The platform injects a short-lived runtime token into each webhook payload. Send it via
X-Kiket-Runtime-Token: <token>โthe official SDKs do this automatically. - Workspace tokens: For server-to-server calls outside webhook context (e.g., extensions receiving external webhooks), use
Authorization: Bearer <workspace_token>. - Both runtime tokens and workspace tokens carry scopes (e.g.,
read:issues,write:comments) and requests outside the scope return403.
Base URL¶
Endpoints¶
| Method | Path | Description | Required Scope |
|---|---|---|---|
| GET | /issues |
List issues visible to the project | read:issues |
| GET | /issues/:key |
Retrieve an issue by key | read:issues |
| POST | /issues |
Create an issue (titles, hierarchy, custom fields) | write:issues |
| POST | /issues/:key/comments |
Add a comment | write:comments |
| POST | /issues/:key/transitions |
Trigger a workflow transition | write:issues |
| PATCH | /issues/:key |
Update fields (labels, custom fields, assignee, parent) | write:issues |
| GET | /projects/:slug/workflows |
Fetch workflow metadata | read:workflows |
| GET | /projects/:slug/boards |
Fetch board definitions and provenance | read:boards |
| POST | /analytics/events |
Inject custom analytics events | write:analytics |
| GET | /custom_data/:module/:table |
List custom data records (project_id required) |
custom_data.read |
| GET | /custom_data/:module/:table/:id |
Retrieve a single custom data record | custom_data.read |
| POST | /custom_data/:module/:table |
Create a custom data record | custom_data.write |
| PATCH | /custom_data/:module/:table/:id |
Update a custom data record | custom_data.write |
| DELETE | /custom_data/:module/:table/:id |
Delete (soft delete) a record | custom_data.write |
| GET | /sla/events |
List SLA alerts for the installation (project_id, issue_id, state, limit) |
read:issues |
See the Custom Data guide for end-to-end examples (manifest, API, CLI, and SDK usage).
Command Palette Bridge¶
For in-app integrations (Stimulus/React controllers) the platform exposes session-authenticated endpoints that mirror the manifest-defined command palette contributions:
| Method | Path | Description |
|---|---|---|
| GET | /projects/:id/command_palette |
List commands available to the current user/project. |
| POST | /projects/:id/command_palette_run |
Execute a command and forward the payload to the extension runtime. |
Use these endpoints when building UI that launches extension commands directly from the browser. Calls inherit the current user's permissions, so commands respect role-based access automatically.
Pagination & Filtering¶
- Use
pageandper_pagequery params for pagination (per_pagemax 100). - Filter issues with
state,label,assignee, hierarchy (parent_id,root_id), or custom field (e.g.,custom_fields[customer]=acme).
Rate Limits¶
- Default: 600 requests per minute per installation.
- Responses include
X-RateLimit-RemainingandRetry-Afterheaders.
External Webhook Routing¶
Extensions can receive webhooks from external providers (Mailjet, Twilio, Slack, etc.) via per-installation webhook URLs. This eliminates the need for static API keysโKiket issues a runtime token for each incoming webhook.
How It Works¶
-
Each installation gets a unique webhook URL:
-
Configure the external provider to send webhooks to this URL (e.g., Mailjet Parse API, Twilio webhook settings).
-
Kiket forwards the webhook to your extension with a runtime token, triggering an
external.webhook.*event. -
Your extension processes the payload using the authenticated context.
Retrieving the Webhook URL¶
Extensions can retrieve their webhook URL via the API:
Response:
{
"webhook_url": "https://kiket.dev/webhooks/ext/wh_abc123xyz",
"webhook_base_url": "https://kiket.dev/webhooks/ext/wh_abc123xyz",
"actions": {
"inbound_email": "https://kiket.dev/webhooks/ext/wh_abc123xyz/inbound_email"
}
}
Rotating the Webhook Token¶
If the webhook token is compromised, rotate it:
Handling External Webhooks in SDK¶
Register a handler for the external.webhook.* event:
sdk.register('external.webhook.inbound_email', version: 'v1') do |payload, context|
# payload contains the raw external provider data
provider_data = payload['body']
headers = payload['headers']
# Process and call Kiket API with authenticated context
context[:client].post('/api/v1/ext/inbound_emails', {
inbound_email: normalize_payload(provider_data)
})
{ ok: true }
end
External Webhook Payload¶
When Kiket forwards an external webhook, the payload includes:
{
"event": "external.webhook.inbound_email",
"authentication": {
"token_type": "runtime",
"runtime_token": "rt_xxxxx",
"expires_at": "2026-01-17T12:34:56Z",
"scopes": ["ext.api.read", "ext.api.write"]
},
"api": {
"base_url": "https://kiket.dev",
"runtime_header": "X-Kiket-Runtime-Token"
},
"external_webhook": {
"action": "inbound_email",
"content_type": "application/json",
"body": { /* raw payload from external provider */ },
"headers": { /* headers from external provider */ }
}
}
Webhook Payloads¶
Webhook bodies include:
{
"event": "issue.transitioned",
"installation": {
"project": "core-platform",
"extension_key": "deployment-bot"
},
"authentication": {
"token_type": "runtime",
"runtime_token": "rt_xxxxx",
"expires_at": "2025-11-10T12:34:56Z",
"scopes": ["ext.api.read", "ext.api.write"]
},
"api": {
"base_url": "https://kiket.dev",
"runtime_header": "X-Kiket-Runtime-Token"
},
"issue": {
"key": "CORE-42",
"title": "Deploy new release",
"state": "in_progress",
"url": "https://kiket.dev/projects/core-platform/issues/CORE-42"
},
"transition": {
"from": "review",
"to": "in_progress",
"performed_by": "sasha@example.com"
}
}
SLA Alerts¶
- Use
GET /sla/eventsto list alert history; filter byproject_id,issue_id,state, orlimit. - Real-time updates arrive via the
workflow.sla_statuswebhook.stateis one ofimminent,breached, orrecovered.
{
"event": "workflow.sla_status",
"state": "imminent",
"issue": {
"id": 42,
"status": "in_progress",
"title": "Customer onboarding playbook"
},
"sla": {
"definition_id": 7,
"status": "in_progress",
"max_duration_minutes": 120,
"warning_buffer_minutes": 30
},
"metrics": {
"duration_minutes": 105,
"overdue_minutes": null
}
}
Guard Responses¶
When responding to workflow.before_transition with required: true, return JSON with a status field (allow, deny, or pending). Optional message is shown to the actor, and metadata is persisted alongside approval records. Responses default to allow when omitted; network errors are interpreted as deny. See the building guide for sample payloads.
SDKs¶
Official SDKs provide batteries-included toolkits for building Kiket extensions. All SDKs include:
- ๐ Webhook handlers with version-aware routing
- ๐ HMAC signature verification for inbound payloads
- ๐ Secret manager for extension secrets
- ๐ Built-in web server (Express, Sinatra, Spring Boot, ASP.NET Core)
- ๐ฆ Manifest loader with automatic secret hydration from environment
- ๐ Telemetry & feedback hooks for metrics collection
- ๐งช Testing utilities for webhook handler tests
Installation
Example
from kiket_sdk import KiketSDK
sdk = KiketSDK(
webhook_secret="sh_123",
workspace_token="wk_test",
extension_id="com.example.marketing",
extension_version="1.0.0",
)
@sdk.webhook("issue.created", version="v1")
async def handle_issue(payload, context):
summary = payload["issue"]["title"]
await context.endpoints.log_event("issue.created", summary=summary)
await context.secrets.set("WEBHOOK_TOKEN", "abc123")
return {"ok": True}
if __name__ == "__main__":
sdk.run(host="0.0.0.0", port=8080)
Installation
Example
import { KiketSDK } from '@kiket/sdk';
const sdk = new KiketSDK({
webhookSecret: 'sh_123',
workspaceToken: 'wk_test',
extensionId: 'com.example.marketing',
extensionVersion: '1.0.0',
});
sdk.webhook('issue.created', 'v1')(async (payload, context) => {
const summary = payload.issue.title;
await context.endpoints.logEvent('issue.created', { summary });
await context.secrets.set('WEBHOOK_TOKEN', 'abc123');
return { ok: true };
});
sdk.run('0.0.0.0', 8080);
Installation (Maven)
<dependency>
<groupId>dev.kiket</groupId>
<artifactId>kiket-sdk</artifactId>
<version>0.1.0</version>
</dependency>
Example
import dev.kiket.sdk.KiketSDK;
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();
sdk.register("issue.created", "v1", (payload, context) -> {
String summary = (String) ((Map) payload.get("issue")).get("title");
context.getEndpoints().logEvent("issue.created", Map.of("summary", summary));
context.getSecrets().set("WEBHOOK_TOKEN", "abc123");
return Map.of("ok", true);
});
sdk.run("0.0.0.0", 8080);
}
}
Installation
Example
require 'kiket_sdk'
sdk = KiketSDK.new(
webhook_secret: 'sh_123',
workspace_token: 'wk_test',
extension_id: 'com.example.marketing',
extension_version: '1.0.0'
)
sdk.register('issue.created', version: 'v1') do |payload, context|
summary = payload['issue']['title']
context[:endpoints].log_event('issue.created', summary: summary)
context[:secrets].set('WEBHOOK_TOKEN', 'abc123')
{ ok: true }
end
sdk.run!(host: '0.0.0.0', port: 8080)
Installation
Example
using Kiket.SDK;
var sdk = new KiketSDK(new SDKConfig
{
WebhookSecret = "sh_123",
WorkspaceToken = "wk_test",
ExtensionId = "com.example.marketing",
ExtensionVersion = "1.0.0"
});
sdk.Register("issue.created", "v1", async (payload, context) =>
{
var summary = payload["issue"]["title"].ToString();
await context.Endpoints.LogEventAsync("issue.created", new Dictionary<string, object>
{
["summary"] = summary
});
await context.Secrets.SetAsync("WEBHOOK_TOKEN", "abc123");
return new { ok = true };
});
sdk.Run("0.0.0.0", 8080);
Configuration¶
All SDKs support automatic configuration from:
- Environment variables: KIKET_WEBHOOK_SECRET, KIKET_WORKSPACE_TOKEN, KIKET_BASE_URL, KIKET_SECRET_*
- Manifest files: extension.yaml or manifest.yaml in project root
- Programmatic configuration: passed to SDK constructor
Example manifest:
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
Publishing to GitHub Packages¶
All SDKs are configured to publish to GitHub Packages with automated CI/CD:
- Python: Tag with
python-v*(e.g.,python-v0.1.0) - Node.js: Tag with
nodejs-v*(e.g.,nodejs-v0.1.0) - Java: Tag with
java-v*(e.g.,java-v0.1.0) - Ruby: Tag with
ruby-v*(e.g.,ruby-v0.1.0) - .NET: Tag with
dotnet-v*(e.g.,dotnet-v0.1.0)
See API Reference for platform-wide endpoints available to first-party clients.