CLI command
The email Echo sends: a sending domain's health, read from DNS when asked, the revenue a campaign's emails led to, and A/B variants of a campaign's first message.
Invocation#
echo-cli email domains --domain <domain> --selector <dkim selector>
echo-cli email check --domain <domain> --selector <dkim selector> --by <name>
echo-cli email checks --domain <domain>
echo-cli email revenue --campaign <campaign-id> --window-days <n>
echo-cli email ab list --campaign <campaign-id>
echo-cli email ab add --campaign <campaign-id> --name <text> --file <template.txt> --by <name>
echo-cli email ab remove --campaign <campaign-id> --variant <variant-id>
echo-cli email ab test --campaign <campaign-id> --window-days <n> --sigmas <n>Inputs and output#
domains writes domain, checked_at, spf, dkim { selector, reading }, dmarc and blocklists. spf, dkim.reading and dmarc are each { found: [records] }, the TXT records that start with v=spf1, v=DKIM1 or v=DMARC1, or { none: <why> } when DNS has none at that name or none of that kind. blocklists has one entry per declared zone: zone, listed (true when <domain>.<zone> answers an A record, false when it does not exist) and answer, what DNS said. revenue writes campaign_id, window_days, total, by_step (each with step, null for the first message), by_variant (each with variant_id, null for none) and purchases; total and each step and variant hold clicks, clickers (distinct recipients who clicked), purchases and revenue, a sum per currency code. purchases lists every attributed purchase: event_id, recipient_id, step, variant_id, revenue, currency, purchased_at and clicked_at. ab list writes variants, oldest first (id, campaign_id, name, message_template, created_by, created_at), and report, one entry per variant with variant_id, name, recipients and by_status, its recipients counted by their stored status; ab add and remove write the variant.
State effect#
domains calls Echo Web's authenticated /api/analytics/email/domains with ECHO_INTERNAL_BEARER at ECHO_WEB_BASE_URL; the Sending domains section of the tracking screen and of Echo Desktop's Contact frequency screen call the same operation, and echo-mcp reads it as echo_email_domain_health. Echo Web reads the TXT records of the domain, of <selector>._domainkey.<domain> and of _dmarc.<domain>, and the A record of <domain>.<zone> for each domain blocklist zone the deployment declares in ECHO_DOMAIN_BLOCKLISTS (comma-separated), from DNS on every call; nothing is stored. Every http(s) link in a campaign email is sent as a tracked link, <origin>/api/public/clicks?r=<recipient>&s=<step>&u=<link>&t=<signature under ECHO_EMAIL_LINK_SECRET>; opening it stores the click in crm_email_clicks with the recipient, campaign and step, and redirects to the link, adding utm_source=email, utm_medium=email and utm_campaign=<campaign id> unless the link already carries its own utm_source, so the visit it starts is an email touch: analytics attribution and cohorts read it as the channel utm_email with the campaign as its detail. The message stored as sent keeps the links as written, and the preference link is not tracked. revenue calls /api/crm/campaigns/<id>/revenue, as the /campaigns/variants screen and Echo Desktop's Campaign A/B variants section do, and echo-mcp reads it as echo_campaign_revenue. A purchase is a stored web or mobile event with a positive revenue property and a currency, as the billing webhooks record them; its buyer is the email in its email or user_email property, else — for a purchase recorded by user id or RevenueCat app user id alone — the email Echo's sign-in holds for that user, or for a signed-in user the identity edges link to that id. It counts under the campaign when a recipient with that email clicked one of its tracked links at most --window-days days before it, and goes to that recipient's latest such click and their variant. Revenue is summed per currency, never converted, and read from stored rows on every call; a sign-in lookup that fails stops the report with "the signed-in user <id> could not be read: <cause>". ab calls /api/crm/campaigns/<id>/variants, as the /campaigns/variants screen does, and echo-mcp reads it as echo_campaign_variants. A variant is a named template written as a campaign's is, its first line the subject. When a campaign with variants sends a recipient its first message, the recipient gets one variant, chosen evenly by its id so a repeated tick chooses the same, kept on the recipient; a sequence step's message is the step's own. Without variants every recipient gets the campaign's template. A variant some recipient got cannot be removed, so its results stay.
Refusals#
- A wrong invocation is refused before anything is sent: "email takes <every usage, separated by semicolons>; got <word>", "email ab takes <every ab usage>; got <word>", "email <verb> does not take <flag>; it takes …", "<flag> needs a value", "email <verb> needs <flag>" and "--file <path> could not be read: <cause>".
- Echo Web answers a bad request with "domain must be a domain name such as example.com; got <value>" or "selector is required: the DKIM selector the domain signs with, as the s= tag of its DKIM-Signature names it; got <value>". A deployment without ECHO_DOMAIN_BLOCKLISTS or ECHO_IP_BLOCKLISTS answers "<setting> is required", and one naming no zone "ECHO_DOMAIN_BLOCKLISTS names no blocklist zone; it is a comma-separated list of domain blocklist zones" (or "ECHO_IP_BLOCKLISTS names no blocklist zone; it is a comma-separated list of IP blocklist zones"). A DNS failure other than a name or record that does not exist stops the check with "the <TXT|A> records of <name> could not be read from DNS" and the resolver's cause. The command prints Echo Web's sentence and exits 1.
- domains also writes ip_blocklists { checked, unchecked }: every single IPv4 address the domain's SPF records name with ip4: is looked up in each IP blocklist zone the deployment declares in ECHO_IP_BLOCKLISTS (comma-separated) as <address octets reversed>.<zone>, listed when that name answers an A record, each entry with ip, zone, listed and answer; unchecked names every other address mechanism of the SPF records — an ip4: range, an ip6: address, include:, a and mx — which are not expanded or guessed at. The Sending domains sections of the tracking screen and of Echo Desktop show both.
- check reads the domain as domains does and stores the reading in email_domain_checks through POST /api/analytics/email/domains/checks, writing the check: id, domain, selector, health (the whole reading), failures (each SPF, DKIM or DMARC record DNS has none of, and each domain or IP blocklist listing, in words), checked_by and checked_at. From then on the daily /api/cron/email-domain-checks (04:23 UTC) checks every stored domain again with the selector of its latest check, recorded as the daily domain checks, so the history grows on its own. checks writes { checks, canary }: the domain's stored checks, newest first, and beside them every run of the deliverability canary (/api/cron/crm-deliverability-canary) whose sender — CRM_OUTREACH_FROM when it ran — sent from the domain, newest first, read from the canary's own automation audit rows: ran_at, from, outcome (done or failed), error (why a failed run stopped, else null), judged (its verdict on the previous probe: verdict pass or fail, recipient, received_at and checks — received, SPF, DKIM, DMARC and the spam and virus verdicts, each with pass and detail — or null for a first probe or a failed run) and failed_checks (the names of the checks that did not pass, null when nothing was judged). The Sending domains sections of the tracking screen and of Echo Desktop store a check and list both the same way. check without --by is a usage error, and Echo Web refuses a check as domains is refused.
- A variant is refused with a bad request: "name is required: what the variant is called in the report", "message_template is required: the message the variant sends, its first line the subject, as a campaign template is written", "created_by is required: an internal caller has no signed-in user, so it names who is acting" or "variant_id is required: the variant to remove"; not found with "no campaign has id <id>; echo-cli outreach lists the campaigns" or "no campaign variant has id <id>; echo-cli email ab list --campaign <id> lists them"; and a conflict with "campaign <id> already has a variant named <name>, <id>" or "variant <name> (<id>) was already sent to <n> recipients; its results stay with it, so it cannot be removed". The command prints Echo Web's sentence and exits 1.
- revenue is refused with a bad request "window_days must be a positive whole number: the days after a click within which a purchase counts" and not found "no campaign has id <id>; echo-cli outreach lists the campaigns"; the command first refuses "--window-days must be a whole number of days; got <value>" with the usage status. A tracked link Echo did not sign answers "This link could not be opened: this link was not made by Echo for this recipient; open it from the email it came in" (400), one missing a part "…: this link is not a whole tracked link: it needs r, s, u and t as the email carried them" (400), and one whose recipient was deleted "…: the campaign recipient <id> this link was sent to no longer exists" (404). A campaign email whose links cannot be made, as without ECHO_EMAIL_LINK_SECRET or NEXT_PUBLIC_SITE_URL, is not sent and records why.
- ab test calls /api/crm/campaigns/<id>/variants/test (echo-mcp: echo_campaign_variant_test; the Campaign A/B variants sections of the campaigns screen and of Echo Desktop show it) and writes { campaign_id, window_days, sigmas, variants, clicks, purchases }: each variant given to a recipient with recipients, clickers (its recipients with a tracked click), buyers (its recipients with a purchase within --window-days after their click, as revenue attributes it), click_rate and purchase_rate over its recipients; clicks and purchases each name the leader on that rate, its comparisons with every other variant (difference of rates, low and high = difference ± --sigmas standard errors of it, the errors from each rate's binomial variance) and winner, the leader only when every low lies above zero, else null. Nothing is stored. It is refused with "sigmas must be a positive number: how many standard errors each interval spans", revenue's window_days refusal, and not found with "campaign <id> has given only variant <id> to a recipient, so there is nothing to compare; …" (or no variant); the command first refuses "--sigmas must be a number of standard errors; got <value>" with the usage status.