Skip to content

About

Local visual editor and preview server for WhatsApp message templates stored as JSON in your repository.

Topics

Resources

Stars

8 stars

Watchers

0 watching

Forks

Repository files navigation

English | Português (Brasil)

whatsapp-template-manager

A local visual editor for WhatsApp message templates, stored as JSON files in your repository.

Introduction

whatsapp-template-manager is the local editor and server for any repository that versions WhatsApp templates. Point it at a folder, and it gives you a gallery, a live preview and a form-based editor on top of plain JSON files you can review in Git.

It needs no account, database, deployment or messaging credentials, and it never talks to Meta. You get Meta-ready JSON out; submitting it is up to you.

Why

Templates are copy that product, recruiting and engineering all touch, yet they usually live in a provider dashboard or in hand-edited JSON. That makes them hard to review and hard to preview. Keeping them as files next to your code gives you pull requests, history and diffs. The editor gives you what raw JSON can't: variable samples, WhatsApp formatting and a message preview as you type.

Getting started

Requires Node.js 22 or newer.

Run it without installing:

npx whatsapp-template-manager dev

Or add it to your project:

pnpm add -D whatsapp-template-manager
{
  "scripts": {
    "templates": "watm dev"
  }
}
pnpm templates

Open the printed address (default http://127.0.0.1:5173) and keep the terminal running while you edit. Use --dir to point at a different folder, for example watm dev --dir whatsapp/templates.

Folder convention

The folder passed to --dir (default ./templates, created if missing) holds one <id>.json file per template and is meant to be committed to your repository.

your-repo/
└── templates/
    ├── candidate-feedback-approved-v2.json
    └── order-update.json

The file name and the id inside the file must match, and both are derived from the template name. Only regular files are read; symlinks inside the folder are rejected.

Features

  • Gallery. Browse template cards, search by name, file name, category, language or message text, and filter by category and language. Each card can be duplicated (opens an unsaved copy) or copied as Meta JSON.
  • Editor with live preview. Edit header, body, footer and buttons while a WhatsApp-style preview updates with your variable samples. Inline markers (*bold*, _italic_, ~strike~, triple-backtick monospace) are rendered in the preview and kept in the JSON.
  • Free-text names. Type a name like Order update; the file is saved as order-update.json. Renaming the template renames the file.
  • Import Meta JSON. Paste or load a Meta template creation payload. Problems are listed with their JSON path and unsupported Meta features are reported, not silently dropped. A valid payload opens as an unsaved template; nothing is written until you save.
  • Export and copy Meta JSON. Get the exact creation payload, including edits you haven't saved yet.
  • Live folder sync. Changes to the folder (a git pull, a branch switch, another editor) show up automatically. If you have unsaved edits to a template that changed on disk, you choose between reloading and keeping yours, and saving over a newer file fails with a conflict instead of overwriting it.
  • Drafts can be saved while incomplete; export requirements are shown separately. Light and dark themes are supported.

An empty folder shows an empty gallery with From scratch and Import JSON actions. The package ships no starter templates.

CLI reference

watm dev [--dir <path>] [--port <n>] [--open]
Option Default Description
-d, --dir <path> ./templates Templates folder, resolved from the current directory. Created if missing.
-p, --port <n> 5173 Port to listen on. If it is busy, the next free port is used.
--open off Open the editor in your default browser.
-h, --help Show help.
-v, --version Show the version.

A symlinked folder path given to --dir is followed once at startup.

Template file format

Each file is the local source of truth. This example is adapted and shortened from the fixture in templates/candidate-feedback-approved.json, with the id matching the name (the file would be saved as candidate-feedback-approved-v2.json):

{
  "version": 1,
  "id": "candidate-feedback-approved-v2",
  "name": "candidate_feedback_approved_v2",
  "category": "UTILITY",
  "language": "pt_BR",
  "creationSource": "from-scratch",
  "definition": {
    "header": {
      "type": "text",
      "template": "Atualização sobre sua candidatura",
      "variables": []
    },
    "body": {
      "type": "body",
      "template": "Você avançou para a próxima etapa da vaga de {{0:variable}}.",
      "variables": [
        {
          "id": 0,
          "name": "jobTitle",
          "type": "variable",
          "props": {
            "variableType": "text",
            "sample": "Analista de Dados Pleno"
          }
        }
      ]
    },
    "footer": null,
    "buttons": [],
    "authenticationConfig": null
  }
}

Inside the text, variables are internal placeholders ({{0:variable}}) whose IDs stay stable while you edit. Unknown fields produce a visible load error.

Mapping to the Meta payload

Export produces a template creation payload, the shape used by POST /{WABA_ID}/message_templates:

{
  "name": "candidate_feedback_approved_v2",
  "language": "pt_BR",
  "category": "UTILITY",
  "components": [
    {
      "type": "HEADER",
      "format": "TEXT",
      "text": "Atualização sobre sua candidatura"
    },
    {
      "type": "BODY",
      "text": "Você avançou para a próxima etapa da vaga de {{1}}.",
      "example": { "body_text": [["Analista de Dados Pleno"]] }
    }
  ]
}
  • Variables are numbered from {{1}} in order of first appearance, repeated variables reuse their position, and numbering restarts for each component.
  • Variable samples become the example values Meta asks for.
  • Internal editor metadata (id, version, creationSource, variable names) is left out.
  • A name that is not already lowercase letters, digits and underscores is converted to that form for export.

This is not the /messages send payload, and exporting does not submit anything. Meta still evaluates category, wording and eligibility during approval.

Supported scope: UTILITY and MARKETING categories, text headers, text bodies, optional text footers, quick-reply buttons, phone buttons and HTTP(S) URL buttons (dynamic URLs use one trailing {{1}} and a full sample URL). Authentication templates, media headers, catalogs, flows and other specialized buttons are intentionally unsupported.

Reference: Meta WhatsApp Business Management API: Message Templates.

Security and local-only use

  • The server binds to 127.0.0.1 only. It is a local tool; static hosting does not support saving.
  • API requests are accepted only when the Host header is 127.0.0.1 or localhost with a port. Cross-origin and cross-site requests are rejected, and saving requires a same-origin application/json request of at most 256 KB.
  • Nothing is sent to Meta or any other service. The server never contacts Meta or sends messages.
  • "Saved locally" means a file was written, not that Meta approved the template.

Templates may contain recipient-facing copy and sample values. Use fictitious samples, not real personal data, since these files are committed to your repository.

Development

pnpm install
pnpm dev                                   # Vite dev server on http://127.0.0.1:5173, uses ./templates
WATM_DIR=../other-repo/templates pnpm dev  # target another templates folder
pnpm build                                 # dist/client (UI) + dist/cli (watm)

Run the checks one after the other, not in parallel:

pnpm format:check
pnpm lint
pnpm typecheck
pnpm test

The UI is built with React, Tailwind CSS v4 and shadcn/ui components (generated into src/components/ui). Local file state and discard confirmation live in src/hooks/use-templates.ts; the server is in server/.

Releasing

Versions are managed with Changesets.

  1. In a feature PR, run pnpm changeset and commit the generated file.
  2. After merging to main, a bot opens (or updates) a PR titled release: version packages.
  3. Merge that PR. The release workflow publishes to npm through trusted publishing and creates the GitHub Release.

The publish only runs when the package.json version is not on npm yet, so any merge method works and re-running the workflow is safe.

Contributing

Issues and pull requests are welcome in the GitHub repository. Please run the checks above before opening a pull request, and add or update tests for behavior changes. There is no separate contributing guide yet.

License

Released under the MIT License.

About

Local visual editor and preview server for WhatsApp message templates stored as JSON in your repository.

Topics

Resources

Stars

8 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Contributors

Languages