How a newsletter gathers the week’s updates
The composer can only write about what it’s been given. So the first job is making sure the item pool actually reflects what happened this week. There are three ways an item gets in: somebody types a row in the Drive sheet, your blog publishes a post the feed lane picks up, or somebody forwards an update to a dedicated address. The first one is obvious. The other two exist because in real life nobody types a row in a sheet for the post that went live three minutes ago.
Key takeaways
- Three intake lanes feed one item pool: the Drive sheet, a blog feed lane, and an inbox-forwarding lane.
- The feed lane watches your blog’s RSS and proposes a row for each new post.
- Forwarded updates are tidied by Bedrock Haiku 4.5 into a clean title and a one-line note.
- Every proposed row goes to the team’s Slack for one-tap approval before it lands in the pool.
- The Drive sheet stays the canonical store. The other lanes are conveniences that write into it.
Three lanes into one item pool
Lane 1: the Drive sheet itself
The simplest lane. Open the item-pool sheet in Drive, add a row, save. The columns are short: a title, a one-line note, a category (blog post, product note, win, event, other), a link, the date it happened, and who added it. A small Lambda — drive-sync — runs every fifteen minutes, exports the sheet as plain CSV via the Drive API, and writes it to s3://nc-items-source/items.csv if the sheet has changed since the last sync. The composer reads from S3, not Drive directly. That keeps Drive API calls predictable and gives you S3 versioning for free, so a bad bulk-edit can be rolled back in one click.
This lane covers the things that don’t show up anywhere else: the client win, the trade-show booth that went well, the kind note a customer sent. Anyone on the team can drop those in throughout the week without learning a new tool.
Lane 2: the blog feed (the lane that fills itself)
Most of what belongs in a newsletter is already published somewhere — on your blog. A small feed-sync Lambda runs hourly, fetches your blog’s RSS or Atom feed, and compares the entries against the ones it has already seen (tracked by a stable id per entry in DynamoDB). For each genuinely new post, it pulls the title, the link, and the short summary the feed provides, and creates a proposed row. The proposal goes to the team’s Slack with approve, edit, and discard buttons. On approve, the row is written to the Drive sheet via the Sheets API.
The reason a new post is a proposal and not an automatic add is simple: not every post belongs in every issue. A small internal note, a re-publish, or a post that’s off-topic for the list shouldn’t pad the issue. One tap keeps the good ones and drops the rest, and the team stays in the loop on what the newsletter will cover.
Lane 3: inbox forwarding
Set up a dedicated inbound address — something like updates@your-company.com — via Amazon SES. Anyone on the team forwards an update to that address: a Slack message they copied, a quick “we shipped X” note, a screenshot caption, a paragraph about a customer story. SES writes the raw message to s3://nc-raw-mime/. The S3 PUT triggers a parser Lambda. The Lambda reads the body and calls Bedrock Haiku 4.5 to tidy the raw text into a clean title, a one-line note, a category, and any link it found. The model prompt is short: “Turn this forwarded update into one newsletter item. Return JSON only. Keep the facts; do not add any that aren’t in the text.”
The tidied item goes to the same Slack proposal flow as Lane 2 — the proposed row, and three buttons: approve, edit, discard. On approve, a Lambda writes the row to the Drive sheet. On edit, the person gets a fillable modal pre-populated with the proposal. On discard, the message is logged and the raw forward moved to a discarded prefix in S3 for audit. Forwarding is the lane that catches the wins that live in someone’s head or a chat thread and would otherwise never reach the list.
Why the pool stays the source of truth
Three lanes in, but only one place the composer actually reads. That’s a deliberate constraint. If the feed lane and the inbox lane both wrote straight into the draft, every “why is this in the issue?” question would mean checking three places. Funneling everything through the Drive sheet means there is exactly one row per item, anyone can read or edit any of it, and the composer’s input is a single, reviewable list. The convenience lanes are first-class for getting items in, but they always pass through the sheet on the way.
Next post: how the composer reads the pool, decides whether there’s enough to send, and drafts the issue grounded only in those items.
All posts