Webhook Integration

This guide shows how to replace a hardcoded LLM call in your app with an ArkEvent webhook. Your app POSTs to a URL and gets a structured response back directly — no polling, no code changes to your LLM logic, just YAML.


Prerequisites

  • Docker Desktop with Kubernetes enabled
  • kubectl (bundled with Docker Desktop)
  • helm
  • An Anthropic API key

Enable Kubernetes in Docker Desktop: Settings → Kubernetes → Enable Kubernetes → Apply & Restart

Verify:

kubectl get nodes
# NAME             STATUS   ROLES           AGE
# docker-desktop   Ready    control-plane   1m

1. Deploy Redis

ark-operator uses Redis Streams as the task queue between the operator and agent pods.

kubectl apply -f https://raw.githubusercontent.com/arkonis-dev/ark-operator/main/config/prereqs/redis.yaml

Wait for it to be ready:

kubectl rollout status statefulset/redis -n ark-system

2. Install the operator

helm repo add arkonis https://charts.arkonis.dev
helm repo update

helm install ark-operator arkonis/ark-operator \
  --namespace ark-system \
  --create-namespace \
  --set taskQueueURL=redis.ark-system.svc.cluster.local:6379 \
  --set triggerWebhook.url=http://ark-operator.ark-system.svc.cluster.local:8092 \
  --set apiKeys.anthropicApiKey=sk-ant-...

Wait for the operator to be ready:

kubectl rollout status deployment/ark-operator -n ark-system

3. Store your prompt in a ConfigMap

For long prompts, keep them out of the YAML spec and in a ConfigMap instead. The operator reads it at reconcile time and restarts agent pods automatically when it changes.

Create a file prompt.txt with your full system prompt:

You are a data extraction assistant. Your job is to read the provided
text carefully and extract structured information from it.

Return only a valid JSON object — no explanation, no markdown, no code
fences. The JSON must match the schema the caller expects exactly.

If a field cannot be determined from the text, set it to null.
Never guess or invent values.
Do not use any tools. Respond directly with the JSON object.

Create the ConfigMap:

kubectl create configmap extractor-prompt \
  --from-file=system.txt=./prompt.txt \
  --namespace ark-system

4. Define your agent and flow

Create a file my-flow.yaml. The agent references the prompt from the ConfigMap via systemPromptRef.

apiVersion: arkonis.dev/v1alpha1
kind: ArkAgent
metadata:
  name: extractor-agent
  namespace: ark-system
spec:
  model: claude-sonnet-4-20250514
  systemPromptRef:
    configMapKeyRef:
      name: extractor-prompt
      key: system.txt
  limits:
    maxTokensPerCall: 4000
    timeoutSeconds: 30
---
apiVersion: arkonis.dev/v1alpha1
kind: ArkFlow
metadata:
  name: extract-flow
  namespace: ark-system
spec:
  steps:
    - name: extract
      arkAgent: extractor-agent
      inputs:
        prompt: "{{ .pipeline.input.text }}"
  output: "{{ .steps.extract.output }}"
---
apiVersion: arkonis.dev/v1alpha1
kind: ArkEvent
metadata:
  name: extract-trigger
  namespace: ark-system
spec:
  source:
    type: webhook
  targets:
    - pipeline: extract-flow
      input:
        text: "{{ .trigger.body.text }}"

Apply it:

kubectl apply -f my-flow.yaml

Check the webhook URL was assigned:

kubectl get arkevent extract-trigger -n ark-system

5. Expose the webhook to localhost

kubectl port-forward svc/ark-operator 8092:8092 -n ark-system

Leave this running in a terminal. Your app can now reach the webhook at http://localhost:8092.


6. Call it from your app

Replace your hardcoded LLM call with a POST to the webhook. Add ?mode=sync so the request waits and returns the result directly — no polling needed.

First get your webhook token:

kubectl get secret extract-trigger-webhook-token -n ark-system -o jsonpath='{.data.token}' | base64 -d

Then call it from your app:

const token = process.env.ARK_WEBHOOK_TOKEN

const response = await fetch(
  'http://localhost:8092/triggers/ark-system/extract-trigger/fire?mode=sync&timeout=30s',
  {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${token}`,
    },
    body: JSON.stringify({ text: "Your input text here..." })
  }
)

const result = await response.json()
// result.status    → "succeeded" or "failed"
// result.output    → the agent's response string
// result.durationMs → how long it took
console.log(result.output)

The flow runs synchronously — the HTTP connection stays open until Claude responds, then the result is returned in the response body. Default timeout is 60s; override with ?timeout=30s.


7. Iterate on the prompt

Edit prompt.txt and reapply the ConfigMap — no YAML changes needed:

kubectl create configmap extractor-prompt \
  --from-file=system.txt=./prompt.txt \
  --namespace ark-system \
  --dry-run=client -o yaml | kubectl apply -f -

The operator detects the ConfigMap change, restarts the agent pods, and the next webhook call uses the updated prompt. No app restart required.


Teardown

helm uninstall ark-operator -n ark-system
kubectl delete namespace ark-system
kubectl delete namespace ark-system

Apache 2.0 · ARKONIS