Alphakdeps.io is experimental and evolving — the registry and packages may change or reset.
Templatekdeps asset

kdeps-templates-whatsapp-bot

v1.0.1

kdeps template: whatsapp-bot

set templates / shelf workflow

PurposeProject scaffolds for kdeps new, and the knowledge vault's files (routing table, schema, graph page).

LLM context: none - kdeps uses it without sending it to the model

Where it runs

Part of the templates set, used in workflow and agent mode. Project scaffolds for kdeps new, and the knowledge vault's files (routing table, schema, graph page).

  1. kdeps new, or the knowledge vault's first use
  2. the template is renderedtemplates/whatsapp-bot
  3. the project or vault files are written

Loaded by: kdeps (agent loop), kdeps new, kdeps run, kdeps knowledge build

In the code: kdeps newtemplates.(*Generator).GenerateProject

Install

A kdeps asset: installed with the first-run profile that includes its set, or the first time a command needs it, and updated without a new release.

kdeps install templates/whatsapp-bot

Pin a version with kdeps install templates/whatsapp-bot@<version>.

Contents

README.md.j2

# {{ name }}

{{ description }}

A WhatsApp bot as a plain kdeps workflow: Meta's webhook calls `/webhook`, the
`llm` resource answers the message, `reply` sends the answer with the WhatsApp
Cloud API, and `respond` acknowledges the webhook.

```text
WhatsApp -> POST /webhook -> llm -> reply (Graph API /messages) -> 200 ok
Meta     -> GET  /webhook -> respond (hub.challenge when the verify token matches)
```

## Quick Start

1. Set the credentials from your Meta app (WhatsApp > API Setup):
   ```bash
   export WHATSAPP_PHONE_NUMBER_ID=1234567890     # the sending number's ID
   export WHATSAPP_ACCESS_TOKEN=EAAG...           # system user or temporary token
   export WHATSAPP_VERIFY_TOKEN=pick-any-string   # also entered in Meta's webhook form
   export WHATSAPP_APP_SECRET=abc123...           # App settings > Basic; signs each POST
   export KDEPS_API_AUTH_TOKEN=dev-token          # /webhook is public; other routes need it
   ```

2. Run it and expose port {{ port }} over HTTPS (Meta only calls HTTPS URLs):
   ```bash
   kdeps run workflow.yaml
   # e.g. cloudflared tunnel --url http://localhost:{{ port }}
   ```

3. In Meta's webhook settings, set the callback URL to `https://<host>/webhook`
   and the verify token to `WHATSAPP_VERIFY_TOKEN`, then subscribe to `messages`.

## How it works

- `GET /webhook` returns `hub.challenge` as plain text only when
  `hub.verify_token` equals `WHATSAPP_VERIFY_TOKEN` (403 otherwise).
- `POST /webhook` is checked against `X-Hub-Signature-256`
  (`hmacSHA256(WHATSAPP_APP_SECRET, request.rawBody)`); a forged POST gets 403
  before any model call. Leave `WHATSAPP_APP_SECRET` unset only for local tests.
- Status callbacks (sent, delivered, read) have no text message: `llm` and
  `reply` are skipped and the webhook gets `ok`.
- The first text message of each notification is answered.

Change the model or persona in `resources/llm.yaml`. `WHATSAPP_API_URL`
overrides the Graph API base (default `https://graph.facebook.com/v19.0`).

resources/llm.yaml.j2

actionId: llm
name: Generate Reply

validations:
  methods: [POST]
  routes: [/webhook]
  # Status callbacks (sent, delivered, read) carry no text message: nothing to answer.
  skip:
    - "{% raw %}(request.body.entry?.[0]?.changes?.[0]?.value?.messages?.[0]?.text?.body ?? '') == ''{% endraw %}"
  # Reject a forged POST before spending a model call: Meta signs the body with the app secret.
  check:
    - "{% raw %}env('WHATSAPP_APP_SECRET', '') == '' || request.headers['X-Hub-Signature-256'] == 'sha256=' + hmacSHA256(env('WHATSAPP_APP_SECRET'), request.rawBody){% endraw %}"
  error:
    code: 403
    message: "invalid signature"

chat:
  model: llama3.2:1b  # llamafile alias - auto-downloaded to ~/.kdeps/models (~1.1 GB, first run only)
  scenario:
    - role: assistant
      prompt: Answer in a few short sentences, in the language of the message.
  prompt: "{% raw %}{{ request.body.entry[0].changes[0].value.messages[0].text.body }}{% endraw %}"
  timeout: 60s

resources/reply.yaml.j2

actionId: reply
name: Send WhatsApp Reply
requires:
  - llm

validations:
  methods: [POST]
  routes: [/webhook]
  skip:
    - "{% raw %}(request.body.entry?.[0]?.changes?.[0]?.value?.messages?.[0]?.text?.body ?? '') == ''{% endraw %}"

# WhatsApp Cloud API: send the answer back to the sender's number.
component:
  name: http_request
  with:
    method: POST
    url: "{% raw %}{{ env('WHATSAPP_API_URL', 'https://graph.facebook.com/v19.0') }}/{{ env('WHATSAPP_PHONE_NUMBER_ID') }}/messages{% endraw %}"
    headers:
      Authorization: "{% raw %}Bearer {{ env('WHATSAPP_ACCESS_TOKEN') }}{% endraw %}"
      Content-Type: application/json
    data:
      messaging_product: whatsapp
      to: "{% raw %}{{ request.body.entry[0].changes[0].value.messages[0].from }}{% endraw %}"
      type: text
      text:
        body: "{% raw %}{{ get('llm').message.content }}{% endraw %}"
    failOnError: true
    timeout: 30s

resources/respond.yaml.j2

actionId: respond
name: Webhook Response
requires:
  - reply

validations:
  routes: [/webhook]
  # GET is Meta's verification handshake: echo hub.challenge (digits only)
  # when hub.verify_token matches.
  check:
    - "{% raw %}request.method != 'GET' || (request.query['hub.mode'] == 'subscribe' && request.query['hub.verify_token'] == env('WHATSAPP_VERIFY_TOKEN', '') && env('WHATSAPP_VERIFY_TOKEN', '') != '' && request.query['hub.challenge'] matches '^[0-9]+$'){% endraw %}"
  error:
    code: 403
    message: "forbidden"

apiResponse:
  success: true
  headers:
    Content-Type: text/plain; charset=utf-8
  response: "{% raw %}{{ request.method == 'GET' ? request.query['hub.challenge'] : 'ok' }}{% endraw %}"

workflow.yaml.j2

apiVersion: kdeps.io/v1
kind: Workflow

metadata:
  name: {{ name }}
  description: {{ description }}
  version: "{{ version }}"
  targetActionId: respond       # its result is the webhook's HTTP response

settings:
  apiServer:
    hostIp: "0.0.0.0"
    portNum: {{ port }}
    routes:
      - path: /webhook
        methods: [GET, POST]    # GET: Meta's verification handshake; POST: messages
        public: true            # Meta sends no bearer token; the POST signature is checked instead

Versions

VersionPublishedStatus
1.0.110/21/2026active
1.0.010/14/2026active

Details

Author
kdeps
License
Apache-2.0
Latest Version
1.0.1
Published
10/14/2026

Tags

kdepstemplate