Skip to content

Webhook API

Version: 1.0 Last Updated: 2026-02-14

Overview

Two separate API surfaces for webhook functionality: 1. Management API (/marketplace/webhooks/...) — JWT-authenticated, creator-only CRUD 2. Ingestion API (/webhooks/{webhook_id}) — HMAC-authenticated, public endpoint for external services

Authentication

  • Management endpoints: Bearer token via Authorization: Bearer <token> header (creator-only)
  • Ingestion endpoint: HMAC-SHA256 signature via X-Webhook-Signature header (no JWT)

Management Endpoints

Create Webhook

POST /marketplace/webhooks/{app_id}

Request Body:

{
  "action_id": "add_item",
  "description": "Stripe payment webhook",
  "payload_mapping": {
    "amount": "event.data.amount"
  }
}

Response (200):

{
  "webhook_id": "uuid",
  "secret": "64-char-hex-shown-once",
  "url": "https://api.ikiro.ai/webhooks/{webhook_id}",
  "action_id": "add_item",
  "description": "Stripe payment webhook"
}

Errors: 400 (invalid action, max limit, not creator)

List Webhooks

GET /marketplace/webhooks/{app_id}

Response (200):

{
  "webhooks": [
    {
      "webhook_id": "uuid",
      "action_id": "add_item",
      "description": "Stripe payment webhook",
      "is_active": true,
      "url": "https://api.ikiro.ai/webhooks/{webhook_id}",
      "rate_limit_per_minute": 10,
      "created_at": "2026-02-14T...",
      "updated_at": "2026-02-14T..."
    }
  ],
  "total": 1
}

Delete Webhook

DELETE /marketplace/webhooks/{app_id}/{webhook_id}

Response (200):

{"status": "ok", "message": "Webhook deleted"}

Rotate Secret

POST /marketplace/webhooks/{app_id}/{webhook_id}/rotate-secret

Response (200):

{
  "webhook_id": "uuid",
  "new_secret": "64-char-hex-shown-once"
}

Get Recent Events

GET /marketplace/webhooks/{app_id}/{webhook_id}/events?limit=20

Response (200):

{
  "events": [
    {
      "event_id": "uuid",
      "signature_valid": true,
      "action_triggered": "add_item",
      "result_status": "ok",
      "error_message": null,
      "ip_address": "1.2.3.4",
      "created_at": "2026-02-14T..."
    }
  ],
  "total": 42
}


Ingestion Endpoint

Invoke Webhook

POST /webhooks/{webhook_id}

Headers: - X-Webhook-Signature: HMAC-SHA256 hex digest of the request body using the webhook secret - Content-Type: application/json

Request Body: Any valid JSON (max 64KB)

Success Response (200):

{"status": "ok"}

Error Responses: - 400 — Invalid webhook ID or invalid JSON - 401 — Invalid or missing signature - 404 — Webhook not found or inactive - 413 — Payload too large (>64KB) - 429 — Rate limit exceeded - 500 — Action execution failed - 503 — Service unavailable

Signing Payloads

External services should sign the raw request body:

import hmac, hashlib

signature = hmac.new(
    secret.encode("utf-8"),
    request_body,
    hashlib.sha256
).hexdigest()

# Send as header: X-Webhook-Signature: {signature}