Skip to content

Herald Quick Start

The echo channel exercises every part of Herald — webhook, routing, Nexus run, outbox, dispatch — without a provider account. It performs no authentication at all, which is why it only registers behind --enable-echo and must never reach an internet-facing deployment.

Brings up PostgreSQL 17 and Herald with development credentials. The admin token, encryption key and tenant-1 send key are committed defaults — override them through the environment anywhere real.

Terminal window
git clone https://github.com/monaccode/astromesh-herald.git
cd astromesh-herald
docker compose -f deploy/docker-compose.yaml up --build
curl localhost:8081/healthz

The compose stack has no Nexus. Point NEXUS_URL at a reachable control plane — http://host.docker.internal:8080 is the default for a Nexus running on the host.

  1. Create an echo channel account.

    Terminal window
    curl -X POST localhost:8081/admin/accounts \
    -H "X-Admin-Token: $HERALD_ADMIN_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"tenant_id":"tenant-1","channel":"echo",
    "external_id":"echo-1","credentials":{}}'
  2. Bind it to an entry agent. The admin plane validates the entry agent against Nexus and rejects an unknown one with 422 — a typo fails here, not in front of a customer.

    Terminal window
    curl -X POST localhost:8081/admin/bindings \
    -H "X-Admin-Token: $HERALD_ADMIN_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"tenant_id":"tenant-1","channel":"echo","account_id":"<account id>",
    "entry_agent":"router","fallback_message":"Out of service",
    "config":{"nexus_api_key":"<tenant nexus key>"}}'
  3. Send an inbound message. This is what a provider’s webhook would deliver.

    Terminal window
    curl -X POST localhost:8081/webhooks/echo \
    -H "Content-Type: application/json" \
    -d '{"message_id":"m-1","account_id":"echo-1",
    "sender":"+549110000001","text":"hola"}'

    The entry agent router runs, its data.route decides whether a conversation is persisted, and the reply is queued in the outbox. See inbound routing.

  4. Send a proactive message, unprompted by anything the person did.

    Terminal window
    curl -X POST localhost:8081/api/v1/messages/send \
    -H "X-Herald-Key: <tenant-1 key>" \
    -H "Content-Type: application/json" \
    -d '{"channel":"echo","recipient":"+549110000001",
    "text":"recordatorio de pago"}'
    # → 202 {"message_id":"...","status":"pending"}
  5. Check whether it actually went out. 202 meant queued.

    Terminal window
    curl localhost:8081/api/v1/messages/<message_id> \
    -H "X-Herald-Key: <tenant-1 key>"
http://localhost:8081/ui/

Sign in by pasting the admin token. The outbox screen shows each entry’s retry budget, which is where a delivery that is quietly failing becomes visible.