emit MCP

Programmable RSS MCP — any source to email, Slack, or webhook. Pay per send, no subscriber tax.

Documentación

$1.60 per 1,000 emails

$0 per-subscriber fees

$10 minimum top-up · no card to start

3 calls to wire up the pipe

Stop checking. Start subscribing.

Every pipe below is real and buildable in the next five minutes — one API call or a couple of clicks. Each one ends in an RSS feed URL, an email, a Slack message, or a JSON POST to your own code.

blog→email

Publish once. We handle the sending.

Every new post becomes a clean, readable email to your list, from your own domain. Around a thousand sends for $1.60.

Start this pipe →

page→slack

Stop refreshing. Get pinged.

Watch any URL — pricing pages, competitors, notices. Your team hears about changes in Slack, even if the page never had a feed.

Start this pipe →

changelog→webhook

Releases into your pipeline.

New versions of your dependencies fire a webhook — open an issue, kick off CI, tell the channel. Clean JSON, no poller to write.

Start this pipe →

A sending pipe. Not a CMS.

Emit does the four boring things between "you published a post" and "someone opened the email," and nothing else.

$

No subscriber tax

10,000 readers you email twice a month shouldn't cost the same as a daily blast. You're billed per email sent — full stop.

1 credit · 1 email · prepaid

@

Your domain, real deliverability

Send from a verified domain over Resend or Amazon SES with DKIM, bounce & complaint handling, list-unsubscribe headers, and one-click opt-out baked in.

posts@yourdomain.dev · SPF · DMARC

{ }

Everything is an API

A typed OpenAPI 3.1 spec, bearer keys, idempotent writes, and signed webhooks for key events. No dashboard you're forced through — wire it in and forget it.

compact REST API · < 200 LOC client

Three calls to a live 200 OK.

01 Create an account POST /v1/accounts

# returns an emit_live_… key, shown once
curl -X POST api.rssemit.com/v1/accounts \
  -d '{"name":"My Blog",
       "email":"me@blog.dev"}'

02 Connect a feed POST /v1/feeds

# polled every 5m · conditional GET
curl -X POST api.rssemit.com/v1/feeds \
  -H "Authorization: Bearer $KEY" \
  -d '{"url":"…/rss.xml",
       "from_email":"posts@blog.dev"}'

03 Top up & go POST /v1/billing/topup

# $20 ≈ 12,500 emails. autopilot.
curl -X POST api.rssemit.com/v1/billing/topup \
  -H "Authorization: Bearer $KEY" \
  -d '{"amount_usd":20}'
# one-time: verify your email + sending
# domain (DNS), then new posts mail
# themselves

Built so your coding agent can wire it up alone.

We ship a typed OpenAPI spec, a one-shot MCP server, and a Claude Code skill that handle domain verification, DNS records, subscriber import, and the first broadcast — in a single conversation.

Claude Code skill Codex CLI Cursor MCP server

Paste it into Claude Code, Cursor, Codex, or any agent with API access — it asks which use case you want first, then only sets up what that needs (Slack/Discord setups skip the domain and funding steps entirely).

~/blog · claude code

● Reading CLAUDE.md, found feed at /rss.xml.

● Verifying domain blog.dev…

emit.create_account → emit_live_••• issued

emit.verify_domain → 3 DNS records added to vercel.app

emit.connect_feed → posts@blog.dev · polling 5m

emit.import_subscribers → 1,284 from buttondown.csv

● Done. Your next post will email 1,284 subscribers. Credits for 3.9k emails on file.

>

One endpoint. Whatever language.

No SDKs to chase across versions. Bearer auth, JSON in, JSON out.

# Send a one-off broadcast to all confirmed subscribers
curl -X POST https://api.rssemit.com/v1/broadcasts \
  -H "Authorization: Bearer $EMIT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "subject":   "Shipping Emit v1.4",
        "body_html": "<h1>We shipped.</h1>",
        "feed_id":   "8Hk2q9f1-…"
      }'

# → { "id": "8Hk2q9f1…", "status": "queued", "recipient_count": 4218 }
// node 20 · zero deps
const r = await fetch("https://api.rssemit.com/v1/broadcasts", {
  method: "POST",
  headers: {
    "Authorization": \`Bearer ${process.env.EMIT_KEY}\`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    subject:   "Shipping Emit v1.4",
    body_html: "<h1>We shipped.</h1>",
    feed_id:   "8Hk2q9f1-…",
  }),
});

const { id, recipient_count } = await r.json();
console.log(\`queued ${recipient_count} to ${id}\`);
# python 3.11 · stdlib only
import os, json, urllib.request as u

req = u.Request(
    "https://api.rssemit.com/v1/broadcasts",
    method="POST",
    headers={
        "Authorization": f"Bearer {os.environ['EMIT_KEY']}",
        "Content-Type": "application/json",
    },
    data=json.dumps({
        "subject":   "Shipping Emit v1.4",
        "body_html": "<h1>We shipped.</h1>",
        "feed_id":   "8Hk2q9f1-…",
    }).encode(),
)

res = json.loads(u.urlopen(req).read())
print(f"queued {res['recipient_count']} to {res['id']}")
// go 1.22
body, _ := json.Marshal(map[string]any{
    "subject":   "Shipping Emit v1.4",
    "body_html": "<h1>We shipped.</h1>",
    "feed_id":   "8Hk2q9f1-…",
})

req, _ := http.NewRequest("POST",
    "https://api.rssemit.com/v1/broadcasts",
    bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer " + os.Getenv("EMIT_KEY"))
req.Header.Set("Content-Type", "application/json")

res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
# ruby 3.3
require "net/http"; require "json"

uri = URI("https://api.rssemit.com/v1/broadcasts")
req = Net::HTTP::Post.new(uri, {
  "Authorization" => "Bearer #{ENV['EMIT_KEY']}",
  "Content-Type"  => "application/json",
})
req.body = {
  subject:   "Shipping Emit v1.4",
  body_html: "<h1>We shipped.</h1>",
  feed_id:   "8Hk2q9f1-…",
}.to_json

res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts JSON.parse(res.body)["id"]

$1.60 per 1,000 credits — an email costs one. Making feeds is free; you pay only when work runs. Full pricing →

A readable email, not a marketing artifact.

Default template is plaintext-first, HTML-friendly, dark-mode safe, and respects your post's typography. Override it per feed with your own raw HTML via the feed's template_html.

preview posts@blog.devreader@hey.com

html · 41 kb

blog.dev · issue #042 · may 26

How conditional GET keeps polling cheap

We poll 14,000 RSS feeds every five minutes. Here's the ETag trick that drops 92% of those requests to a 304 — and why it matters for your bill.

The naive answer is "cache the feed body and diff it." That works until you're hitting 14k publishers per cycle, your egress bill stops making sense, and half of those publishers are running WordPress instances that hate you personally.

Continue reading →

you're receiving this because you subscribed at blog.dev · unsubscribe

<!-- raw HTML, rendered with Jinja. Set
     per feed via template_html. Variables:
     title, body, url, unsubscribe_url -->

<h1>{{ title }}</h1>

<div>{{ body }}</div>

<a href="{{ url }}">
  Continue reading →
</a>

<hr>
<p>
  <a href="{{ unsubscribe_url }}">
    Unsubscribe
  </a>
</p>

Questions you'd ask before paying.

Pricing questions — credits, the free tier, bounces — are answered on the pricing page. If yours isn't either place, will@heltonlabs.com — a human reads it.

Can I get a feed as JSON instead of email?

Yes — that's a JSON broadcast. Point any feed URL, including one you don't own, at your endpoint and each new item arrives as application/json with an X-Emit-Event: sink.item header; the body is {"event","sink_id","name","item"}. Set the schedule to daily or weekly and emit batches them into a single sink.digest POST with an items array. No poller, no XML parsing, no cron — see RSS to webhook.

How is the RSS feed polled?

Every five minutes by default, with conditional GET (If-None-Match + If-Modified-Since). You can switch to on_publish mode and POST to /v1/feeds/{id}/ping from a webhook for instant delivery.

Can my coding agent set everything up?

That's the design goal. Emit ships a single-file OpenAPI 3.1 spec, an MCP server, and a Claude Code skill, so add a newsletter to my blog using emit in Claude Code or Codex can do DNS records, domain verification, subscriber import, and the first broadcast without you leaving the terminal.

What domains do you send from?

Yours. You verify a domain (we generate the DKIM, SPF, and DMARC records), and every send goes from: posts@yourdomain.dev. No <@rssemit.com> ever appears in your subscribers' inboxes.

Where does my data live?

Postgres in us-east-1. We never sell subscriber data — there's nothing in it for us, and it would tank the only thing we're selling.