Skip to content

Quick Start

The echo channel runs every part of Herald without a provider account: the webhook, routing, the Nexus run, the outbox and dispatch. It does no authentication at all, so it only exists behind --enable-echo and must never reach an internet-facing deployment.

You need a running Nexus with a published agent and a tenant API key. In the examples the agent is router.

PostgreSQL 17 plus Herald, with development credentials. The admin token, the encryption key and the tenant-1 send key have committed defaults; override them anywhere real.

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

The compose stack has no Nexus. NEXUS_URL defaults to http://host.docker.internal:8080, a Nexus running on the host.

DefaultValue
Admin token0123456789abcdef repeated to 64 characters
Send keytenant-1:dev-send-key
  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":{}}'
    # → 201 {"id":"<account id>", …}
  2. Bind it to an entry agent. Herald checks the agent against Nexus with the key in config and rejects an unknown one with 422, so a typo fails here and 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, try again later",
    "config":{"nexus_api_key":"<tenant Nexus API key>"}}'
  3. Deliver an inbound message, as a provider’s webhook would.

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

    The 200 comes back before the agent runs. A worker then resolves the account and tenant, runs router through Nexus, opens the conversation (unless the agent’s data.route declines) and queues the answer.

  4. See the conversation and the reply.

    Terminal window
    curl "localhost:8081/admin/conversations?tenant=tenant-1" -H "X-Admin-Token: $HERALD_ADMIN_TOKEN"
    curl "localhost:8081/admin/outbox?tenant=tenant-1" -H "X-Admin-Token: $HERALD_ADMIN_TOKEN"

    The reply’s row is sent within a second. Echo delivers into memory, so nothing leaves the process.

  5. Send a proactive message.

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

    Terminal window
    curl localhost:8081/api/v1/messages/<message_id> -H "X-Herald-Key: <tenant-1 send key>"
    # → {"message_id":"…","status":"sent","attempts":0,"last_error":""}

If the agent fails, step 4 shows the fallback message queued instead of an answer, and Herald’s log says why.

http://localhost:8081/ui/

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