Follow one message from an accepted request through delivery, and inspect a simulated failure.
Before you start
API_BASE: Setexport API_BASE=http://127.0.0.1:8787/v1in your command terminal.- From the Shoalrun repository root, run
node framework/example-configs/notifications/demo.mjsin a separate terminal and keep it running. If it is already running, reuse it. - Use a scratch directory for queued.json and delivery.json. These commands replace those files.
- Have cURL and Node.js available in your command terminal.
Queue the welcome email
- Use
API_BASEfrom Before you start: The demo API base URL set in your terminal.
Send the welcome template to the demo recipient. The demo stores a simulated message and sends no email.
curl --fail-with-body --silent --show-error \
--request POST "$API_BASE/messages/email" \
--header 'Content-Type: application/json' \
--data '{"to":"reader@example.com","template":"welcome","variables":{"firstName":"Avery"}}' \
--output queued.json --write-out 'HTTP %{http_code}\n'
cat queued.json
MESSAGE_ID=$(node -p "JSON.parse(require('fs').readFileSync('queued.json', 'utf8')).id")
printf '\nMessage ID: %s\n' "$MESSAGE_ID"
A successful response looks like {"id":"msg_…","status":"queued"}. If the POST fails, inspect its HTTP status and response before continuing. Do not reuse a message ID from an earlier attempt.
Save MESSAGE_ID: The id returned in queued.json; use this exact value for the delivery check.
Continue when: The POST returns HTTP 202, status is queued, and MESSAGE_ID is nonempty. This confirms acceptance only.
Check delivery for that message
- Use
API_BASEfrom Before you start: Use the same environment as the queue request. - Use
MESSAGE_IDfrom Queue the welcome email: Put the returned ID into the status URL; do not substitute the example ID.
Look up the message using the ID saved in the previous step:
curl --fail-with-body --silent --show-error \
"$API_BASE/messages/$MESSAGE_ID" \
--output delivery.json --write-out 'HTTP %{http_code}\n'
cat delivery.json
The local demo returns queued on the first lookup. Run the lookup once more to see its terminal status. This deterministic transition is part of the simulation; a real provider needs its own polling interval and timeout.
| Status or error | What to do next |
|---|---|
queued |
The request was accepted; delivery is still pending. In this demo, repeat the lookup once. |
delivered |
Check that id matches MESSAGE_ID. The delivery check is complete. |
failed |
Inspect failureReason. Correct the cause before creating another message; do not keep polling a terminal failure. |
| HTTP 404 | Check the saved ID and API base URL. Restarting the demo clears its messages. |
| Connection refused | Start the demo and check that it is listening on port 8787. |
To exercise failure handling, repeat the queue step with blocked@example.com, save its new message ID, and check that ID twice. The demo returns failed with failureReason: recipient_rejected.
Save deliveryStatus: The status returned for MESSAGE_ID in delivery.json.
Continue when: The response id equals MESSAGE_ID and status is delivered. Queued and failed do not satisfy this recipe's goal.
Check the outcome
The final response contains the same message ID you saved and status: delivered. You have verified the simulated delivery flow. The local demo sends no email; a real provider reporting delivery still does not prove the recipient read it.