<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ashley Armstrong</title>
    <description>The latest articles on DEV Community by Ashley Armstrong (@amastr92).</description>
    <link>https://dev.to/amastr92</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4025813%2Faf71b45d-eadb-40e8-ad59-4c6ca57ae648.png</url>
      <title>DEV Community: Ashley Armstrong</title>
      <link>https://dev.to/amastr92</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/amastr92"/>
    <language>en</language>
    <item>
      <title>Part 4: Going Fully Autonomous: EventBridge, DynamoDB, and Automated Publishing</title>
      <dc:creator>Ashley Armstrong</dc:creator>
      <pubDate>Sun, 12 Jul 2026 08:09:36 +0000</pubDate>
      <link>https://dev.to/amastr92/part-4-going-fully-autonomous-eventbridge-dynamodb-and-automated-publishing-2759</link>
      <guid>https://dev.to/amastr92/part-4-going-fully-autonomous-eventbridge-dynamodb-and-automated-publishing-2759</guid>
      <description>&lt;p&gt;&lt;em&gt;Generating content is interesting. Generating content, creating artwork, and publishing it without human intervention is where things start to get fun.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Final Challenge
&lt;/h2&gt;

&lt;p&gt;By this point, WizardThoughts had come a long way.&lt;/p&gt;

&lt;p&gt;The platform could:&lt;/p&gt;

&lt;p&gt;✅ Generate content using Amazon Bedrock&lt;br&gt;&lt;br&gt;
✅ Generate matching image prompts&lt;br&gt;&lt;br&gt;
✅ Create original artwork using Stability AI&lt;br&gt;&lt;br&gt;
✅ Store assets in Amazon S3&lt;br&gt;&lt;br&gt;
✅ Track everything in DynamoDB  &lt;/p&gt;

&lt;p&gt;The individual pieces worked.&lt;/p&gt;

&lt;p&gt;The problem was coordination.&lt;/p&gt;

&lt;p&gt;I was still manually triggering parts of the workflow.&lt;/p&gt;

&lt;p&gt;The system wasn't autonomous.&lt;/p&gt;

&lt;p&gt;It was semi-automated.&lt;/p&gt;

&lt;p&gt;There is an important difference.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Goal
&lt;/h2&gt;

&lt;p&gt;My original vision was simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Create a social media account capable of generating and publishing its own content without human involvement.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To achieve that, every component needed to operate independently.&lt;/p&gt;

&lt;p&gt;The complete workflow looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EventBridge
      ↓
GenerateContent
      ↓
Amazon Bedrock
      ↓
DynamoDB
      ↓
GenerateImage
      ↓
Stability AI
      ↓
Amazon S3
      ↓
PublishToX
      ↓
WizardThoughts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once that pipeline was running, the system would effectively become self-managing.&lt;/p&gt;




&lt;h2&gt;
  
  
  DynamoDB Becomes the Brain
&lt;/h2&gt;

&lt;p&gt;Originally, DynamoDB was simply a storage layer.&lt;/p&gt;

&lt;p&gt;As the project evolved, it became much more than that.&lt;/p&gt;

&lt;p&gt;It became the central source of truth.&lt;/p&gt;

&lt;p&gt;Every piece of content moved through a series of states.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"PostId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"post-123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Pending"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ImageStatus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Pending"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"PostId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"post-123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Pending"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ImageStatus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Complete"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ImageUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"PostId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"post-123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Published"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"TweetId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123456789"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This simple approach eliminated enormous amounts of complexity.&lt;/p&gt;

&lt;p&gt;Rather than services talking directly to each other, they only needed to understand state.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building a State Machine Without a State Machine
&lt;/h2&gt;

&lt;p&gt;Technically, I wasn't using AWS Step Functions.&lt;/p&gt;

&lt;p&gt;But the architecture behaved like one.&lt;/p&gt;

&lt;p&gt;Each Lambda function had a single responsibility.&lt;/p&gt;

&lt;h3&gt;
  
  
  GenerateContent
&lt;/h3&gt;

&lt;p&gt;Creates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Quote
Image Prompt
Database Record
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  GenerateImage
&lt;/h3&gt;

&lt;p&gt;Looks for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Status = Pending
ImageStatus = Pending
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generates artwork and updates the record.&lt;/p&gt;




&lt;h3&gt;
  
  
  PublishToX
&lt;/h3&gt;

&lt;p&gt;Looks for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Status = Pending
ImageStatus = Complete
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then publishes the post.&lt;/p&gt;




&lt;p&gt;The beauty of this approach is simplicity.&lt;/p&gt;

&lt;p&gt;Every service operates independently.&lt;/p&gt;

&lt;p&gt;Every service knows exactly what work it needs to perform.&lt;/p&gt;




&lt;h2&gt;
  
  
  EventBridge: The Invisible Conductor
&lt;/h2&gt;

&lt;p&gt;The final piece of the puzzle was scheduling.&lt;/p&gt;

&lt;p&gt;Without EventBridge, nothing happened automatically.&lt;/p&gt;

&lt;p&gt;With EventBridge, the entire pipeline came to life.&lt;/p&gt;

&lt;p&gt;I created three schedules.&lt;/p&gt;

&lt;h3&gt;
  
  
  Content Generation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Every 6 hours
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This generated fresh content throughout the day.&lt;/p&gt;




&lt;h3&gt;
  
  
  Image Generation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Every 6 hours
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This checked DynamoDB for content requiring artwork.&lt;/p&gt;




&lt;h3&gt;
  
  
  Publishing
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Every 6 hours
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This checked for completed posts ready to publish.&lt;/p&gt;




&lt;p&gt;The result was a continuous workflow that required zero manual intervention.&lt;/p&gt;




&lt;h2&gt;
  
  
  Publishing Images to X
&lt;/h2&gt;

&lt;p&gt;Getting text published was relatively straightforward.&lt;/p&gt;

&lt;p&gt;Images were another story.&lt;/p&gt;

&lt;p&gt;The publishing workflow became:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Retrieve Post
        ↓
Load Image From S3
        ↓
Upload Media To X
        ↓
Attach Media To Tweet
        ↓
Publish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple in theory.&lt;/p&gt;

&lt;p&gt;Less simple in reality.&lt;/p&gt;




&lt;h2&gt;
  
  
  Authentication: The Sequel
&lt;/h2&gt;

&lt;p&gt;Just when I thought I'd finally escaped authentication issues, another surprise appeared.&lt;/p&gt;

&lt;p&gt;Text tweeting worked.&lt;/p&gt;

&lt;p&gt;Image uploads didn't.&lt;/p&gt;

&lt;p&gt;The logs repeatedly returned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;401 Unauthorized
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;403 Unsupported Authentication
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After far more debugging than I would like to admit, I discovered the issue.&lt;/p&gt;

&lt;p&gt;Different X endpoints preferred different authentication methods.&lt;/p&gt;

&lt;p&gt;The solution was moving to OAuth 1.0a credentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CONSUMER_KEY
CONSUMER_SECRET
ACCESS_TOKEN
ACCESS_TOKEN_SECRET
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The moment I switched:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TwitterApi&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;appKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CONSUMER_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;appSecret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CONSUMER_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ACCESS_TOKEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;accessSecret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ACCESS_TOKEN_SECRET&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;everything finally started working consistently.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Moment It Became Real
&lt;/h2&gt;

&lt;p&gt;The first fully automated post felt different.&lt;/p&gt;

&lt;p&gt;Not because the content was exceptional.&lt;/p&gt;

&lt;p&gt;Not because the image was perfect.&lt;/p&gt;

&lt;p&gt;Because nobody touched anything.&lt;/p&gt;

&lt;p&gt;The workflow executed entirely on its own.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EventBridge Trigger
       ↓
Bedrock Generated Quote
       ↓
Stability Generated Artwork
       ↓
S3 Stored Asset
       ↓
DynamoDB Updated State
       ↓
X Published Content
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No buttons.&lt;/p&gt;

&lt;p&gt;No manual deployments.&lt;/p&gt;

&lt;p&gt;No intervention.&lt;/p&gt;

&lt;p&gt;The system simply did what it had been designed to do.&lt;/p&gt;

&lt;p&gt;That was the moment the project stopped feeling like a collection of Lambda functions and started feeling like a product.&lt;/p&gt;




&lt;h2&gt;
  
  
  Operational Lessons
&lt;/h2&gt;

&lt;p&gt;Building something is one thing.&lt;/p&gt;

&lt;p&gt;Keeping it running is something else entirely.&lt;/p&gt;

&lt;p&gt;Several operational challenges appeared almost immediately.&lt;/p&gt;

&lt;h3&gt;
  
  
  Timeouts
&lt;/h3&gt;

&lt;p&gt;Image generation takes time.&lt;/p&gt;

&lt;p&gt;A three-second Lambda timeout might be fine for a simple API call.&lt;/p&gt;

&lt;p&gt;It's not fine for image generation.&lt;/p&gt;

&lt;p&gt;Increasing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Timeout
Memory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;became essential.&lt;/p&gt;




&lt;h3&gt;
  
  
  Region Mismatches
&lt;/h3&gt;

&lt;p&gt;The number of issues caused by regions was surprising.&lt;/p&gt;

&lt;p&gt;Examples included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Missing models&lt;/li&gt;
&lt;li&gt;Invalid model identifiers&lt;/li&gt;
&lt;li&gt;S3 redirects&lt;/li&gt;
&lt;li&gt;Bedrock availability issues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The lesson:&lt;/p&gt;

&lt;p&gt;Always verify the region before assuming anything else is broken.&lt;/p&gt;




&lt;h3&gt;
  
  
  State Matters
&lt;/h3&gt;

&lt;p&gt;State-driven systems are incredibly forgiving.&lt;/p&gt;

&lt;p&gt;If something fails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Status = Pending
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;remains unchanged.&lt;/p&gt;

&lt;p&gt;The workflow can simply retry later.&lt;/p&gt;

&lt;p&gt;This made the platform significantly more resilient.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cost Considerations
&lt;/h2&gt;

&lt;p&gt;One of the most interesting discoveries was how inexpensive the system remained.&lt;/p&gt;

&lt;p&gt;Most usage came from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Amazon Bedrock&lt;/li&gt;
&lt;li&gt;Stability AI&lt;/li&gt;
&lt;li&gt;Lambda executions&lt;/li&gt;
&lt;li&gt;S3 storage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because everything was serverless:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;No servers
No virtual machines
No containers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system only incurred costs when it was actually doing work.&lt;/p&gt;

&lt;p&gt;For a side project, this was ideal.&lt;/p&gt;




&lt;h2&gt;
  
  
  Future Improvements
&lt;/h2&gt;

&lt;p&gt;Even though the system now functioned end-to-end, there are still plenty of opportunities for expansion.&lt;/p&gt;

&lt;p&gt;Some ideas include:&lt;/p&gt;

&lt;h3&gt;
  
  
  Engagement Analytics
&lt;/h3&gt;

&lt;p&gt;Track:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Likes&lt;/li&gt;
&lt;li&gt;Replies&lt;/li&gt;
&lt;li&gt;Reposts&lt;/li&gt;
&lt;li&gt;Follower growth&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and feed the results back into future content generation.&lt;/p&gt;




&lt;h3&gt;
  
  
  Trending Topics
&lt;/h3&gt;

&lt;p&gt;Incorporate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Current Events
Trending Conversations
Seasonal Topics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while maintaining the wizarding theme.&lt;/p&gt;




&lt;h3&gt;
  
  
  Multiple Accounts
&lt;/h3&gt;

&lt;p&gt;The architecture already supports additional social accounts.&lt;/p&gt;

&lt;p&gt;Future versions could publish to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TikTok&lt;/li&gt;
&lt;li&gt;LinkedIn&lt;/li&gt;
&lt;li&gt;Instagram&lt;/li&gt;
&lt;li&gt;Threads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;from the same content pipeline.&lt;/p&gt;




&lt;h3&gt;
  
  
  AI-Generated Threads
&lt;/h3&gt;

&lt;p&gt;Instead of publishing individual posts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 Quote
↓
5 Related Ideas
↓
Thread
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would open entirely new content formats.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;Looking back, the project taught me three important lessons.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Small Systems Compound
&lt;/h3&gt;

&lt;p&gt;The final architecture looks impressive.&lt;/p&gt;

&lt;p&gt;The reality is much simpler.&lt;/p&gt;

&lt;p&gt;It was built one small component at a time.&lt;/p&gt;

&lt;p&gt;First authentication.&lt;/p&gt;

&lt;p&gt;Then content.&lt;/p&gt;

&lt;p&gt;Then images.&lt;/p&gt;

&lt;p&gt;Then automation.&lt;/p&gt;

&lt;p&gt;Each step was manageable.&lt;/p&gt;

&lt;p&gt;Together they became something much larger.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Simplicity Scales
&lt;/h3&gt;

&lt;p&gt;The most valuable design decision wasn't an AI model.&lt;/p&gt;

&lt;p&gt;It wasn't a specific AWS service.&lt;/p&gt;

&lt;p&gt;It was keeping each component focused on a single responsibility.&lt;/p&gt;

&lt;p&gt;Simple systems are easier to build.&lt;/p&gt;

&lt;p&gt;They are also easier to debug.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. AI Is Infrastructure Now
&lt;/h3&gt;

&lt;p&gt;A few years ago, building something like this would have required significant machine learning expertise.&lt;/p&gt;

&lt;p&gt;Today, foundation models and managed services allow developers to focus on the problem instead of the infrastructure.&lt;/p&gt;

&lt;p&gt;That's a profound shift.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;WizardThoughts started as an experiment.&lt;/p&gt;

&lt;p&gt;Could a collection of cloud services create and manage a social media account without human involvement?&lt;/p&gt;

&lt;p&gt;The answer turned out to be yes.&lt;/p&gt;

&lt;p&gt;What began as a single Lambda function evolved into a complete content pipeline capable of:&lt;/p&gt;

&lt;p&gt;✅ Generating original content&lt;br&gt;&lt;br&gt;
✅ Creating custom artwork&lt;br&gt;&lt;br&gt;
✅ Managing workflow state&lt;br&gt;&lt;br&gt;
✅ Scheduling its own execution&lt;br&gt;&lt;br&gt;
✅ Publishing automatically to X  &lt;/p&gt;

&lt;p&gt;More importantly, it demonstrated something larger.&lt;/p&gt;

&lt;p&gt;Modern cloud platforms are no longer just hosting environments.&lt;/p&gt;

&lt;p&gt;Combined with foundation models, they are becoming autonomous execution environments capable of generating, creating, and distributing content with minimal human involvement.&lt;/p&gt;

&lt;p&gt;And in many ways, we're only just getting started.&lt;/p&gt;




&lt;p&gt;From a single automated tweet to a fully autonomous content engine, WizardThoughts became proof that a handful of serverless services, a few AI models, and a lot of persistence can build something genuinely magical.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>automation</category>
      <category>aws</category>
    </item>
    <item>
      <title>Part 3: AI Art at Scale: Generating Fantasy Images with Bedrock, Stability AI, and Amazon S3</title>
      <dc:creator>Ashley Armstrong</dc:creator>
      <pubDate>Sun, 12 Jul 2026 08:07:12 +0000</pubDate>
      <link>https://dev.to/amastr92/part-3-ai-art-at-scale-generating-fantasy-images-with-bedrock-stability-ai-and-amazon-s3-532b</link>
      <guid>https://dev.to/amastr92/part-3-ai-art-at-scale-generating-fantasy-images-with-bedrock-stability-ai-and-amazon-s3-532b</guid>
      <description>&lt;p&gt;&lt;em&gt;The bot could think. The bot could write. The bot could even publish. But in a social media landscape dominated by visuals, words alone weren't enough.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Missing Piece
&lt;/h2&gt;

&lt;p&gt;By this point, WizardThoughts had evolved significantly.&lt;/p&gt;

&lt;p&gt;The platform could:&lt;/p&gt;

&lt;p&gt;✅ Generate original content&lt;br&gt;&lt;br&gt;
✅ Create engagement-focused posts&lt;br&gt;&lt;br&gt;
✅ Store everything in DynamoDB&lt;br&gt;&lt;br&gt;
✅ Publish automatically to X  &lt;/p&gt;

&lt;p&gt;From a technical perspective, things were working.&lt;/p&gt;

&lt;p&gt;From a social media perspective, something was missing.&lt;/p&gt;

&lt;p&gt;The posts looked empty.&lt;/p&gt;

&lt;p&gt;I had built a content engine.&lt;/p&gt;

&lt;p&gt;What I hadn't built was a visual identity.&lt;/p&gt;

&lt;p&gt;And on modern social platforms, visuals often determine whether people stop scrolling long enough to read the content.&lt;/p&gt;

&lt;p&gt;The next challenge became obvious:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every post needed its own unique piece of artwork.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Why Images Matter
&lt;/h2&gt;

&lt;p&gt;Take these two posts:&lt;/p&gt;
&lt;h3&gt;
  
  
  Version One
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Every great wizard begins as a student.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Version Two
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Every great wizard begins as a student.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Accompanied by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An enchanted library&lt;/li&gt;
&lt;li&gt;Floating spell books&lt;/li&gt;
&lt;li&gt;Golden magical lighting&lt;/li&gt;
&lt;li&gt;A student surrounded by ancient knowledge&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The words haven't changed.&lt;/p&gt;

&lt;p&gt;The experience has.&lt;/p&gt;

&lt;p&gt;Images create context.&lt;/p&gt;

&lt;p&gt;They create atmosphere.&lt;/p&gt;

&lt;p&gt;Most importantly, they create attention.&lt;/p&gt;


&lt;h2&gt;
  
  
  Exploring Image Generation
&lt;/h2&gt;

&lt;p&gt;Since text generation was already running through Amazon Bedrock, my first instinct was to stay inside the Bedrock ecosystem.&lt;/p&gt;

&lt;p&gt;The ideal workflow looked like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Generate Quote
       ↓
Generate Image Prompt
       ↓
Generate Artwork
       ↓
Publish Together
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple.&lt;/p&gt;

&lt;p&gt;At least in theory.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Model Hunt
&lt;/h2&gt;

&lt;p&gt;The first surprise came from discovering that not all image models are available everywhere.&lt;/p&gt;

&lt;p&gt;While experimenting, I discovered models that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Existed in one AWS region&lt;/li&gt;
&lt;li&gt;Didn't exist in another&lt;/li&gt;
&lt;li&gt;Were marked as legacy&lt;/li&gt;
&lt;li&gt;Required different API formats&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Something as simple as changing AWS regions suddenly became important.&lt;/p&gt;

&lt;p&gt;I found myself bouncing between:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;London
Ireland
US East
US West
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;trying to discover which models were actually available.&lt;/p&gt;

&lt;p&gt;For a while, my most common error looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The provided model identifier is invalid.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing was wrong with the code.&lt;/p&gt;

&lt;p&gt;The model simply didn't exist in that region.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Nova Canvas Detour
&lt;/h2&gt;

&lt;p&gt;Initially, I experimented with Amazon Nova Canvas.&lt;/p&gt;

&lt;p&gt;On paper, it seemed perfect.&lt;/p&gt;

&lt;p&gt;An AWS-native image generation service integrated directly into Bedrock.&lt;/p&gt;

&lt;p&gt;Then reality arrived.&lt;/p&gt;

&lt;p&gt;The model was marked as legacy.&lt;/p&gt;

&lt;p&gt;Attempts to invoke it resulted in messages like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Access denied.
This model is marked as Legacy.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The lesson was frustrating but useful:&lt;/p&gt;

&lt;p&gt;Just because a service appears in a catalogue doesn't mean it is the right service for your account.&lt;/p&gt;




&lt;h2&gt;
  
  
  Enter Stability AI
&lt;/h2&gt;

&lt;p&gt;Eventually I shifted towards Stability AI's image models through Bedrock.&lt;/p&gt;

&lt;p&gt;These were actively supported and capable of generating entirely original artwork from text prompts.&lt;/p&gt;

&lt;p&gt;This introduced a new architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GenerateContent
        ↓
Bedrock
        ↓
Quote + Image Prompt
        ↓
GenerateImage
        ↓
Stability AI
        ↓
PNG Image
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The workflow was finally beginning to resemble the original vision.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building the GenerateImage Lambda
&lt;/h2&gt;

&lt;p&gt;The GenerateContent Lambda already produced output like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"quote"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Every wizard begins as a student."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"imagePrompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Young wizard studying glowing spell books inside an enchanted library"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I needed a second Lambda.&lt;/p&gt;

&lt;p&gt;Its job was simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read pending content from DynamoDB&lt;/li&gt;
&lt;li&gt;Use the image prompt&lt;/li&gt;
&lt;li&gt;Generate artwork&lt;/li&gt;
&lt;li&gt;Save the result&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The process looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DynamoDB
        ↓
Image Prompt
        ↓
Stability AI
        ↓
Base64 Image
        ↓
PNG File
        ↓
S3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Prompt Engineering, Part Two
&lt;/h2&gt;

&lt;p&gt;Creating image prompts required a different mindset from creating text.&lt;/p&gt;

&lt;p&gt;A poor image prompt looked like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Wizard library
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Technically valid.&lt;/p&gt;

&lt;p&gt;Visually boring.&lt;/p&gt;

&lt;p&gt;A stronger prompt looked like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ancient enchanted library, floating spell books,
golden magical lighting, cinematic fantasy artwork,
rich colours, volumetric light, highly detailed,
social media quality, no text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference was dramatic.&lt;/p&gt;

&lt;p&gt;The more descriptive the prompt became, the more consistent the results became.&lt;/p&gt;




&lt;h2&gt;
  
  
  Another Unexpected Problem
&lt;/h2&gt;

&lt;p&gt;The image generation worked.&lt;/p&gt;

&lt;p&gt;The upload didn't.&lt;/p&gt;

&lt;p&gt;After successfully creating images, I suddenly started receiving:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PermanentRedirect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;errors from S3.&lt;/p&gt;

&lt;p&gt;At first I assumed permissions were broken.&lt;/p&gt;

&lt;p&gt;They weren't.&lt;/p&gt;

&lt;p&gt;The issue was embarrassingly simple.&lt;/p&gt;

&lt;p&gt;My bucket was located in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eu-west-2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while the code was trying to access:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eu-west-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One region mismatch.&lt;/p&gt;

&lt;p&gt;Hours of debugging.&lt;/p&gt;

&lt;p&gt;A classic cloud engineering lesson.&lt;/p&gt;




&lt;h2&gt;
  
  
  The S3 Layer
&lt;/h2&gt;

&lt;p&gt;Once the region mismatch was fixed, every generated image flowed into an S3 bucket.&lt;/p&gt;

&lt;p&gt;The storage structure looked like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wizardthoughts-images/
       ├── post-123.png
       ├── post-124.png
       ├── post-125.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each image became its own reusable asset.&lt;/p&gt;

&lt;p&gt;This provided several benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Durable storage&lt;/li&gt;
&lt;li&gt;Easy retrieval&lt;/li&gt;
&lt;li&gt;Lower complexity&lt;/li&gt;
&lt;li&gt;Simpler workflow management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most importantly, it separated content generation from content publishing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Updating DynamoDB
&lt;/h2&gt;

&lt;p&gt;The image generation process wasn't complete until DynamoDB knew about it.&lt;/p&gt;

&lt;p&gt;A successful image generation updated the record:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"PostId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"post-123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Pending"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ImageStatus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Complete"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ImageUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://bucket.s3.amazonaws.com/post-123.png"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allowed downstream services to know exactly what stage every post had reached.&lt;/p&gt;

&lt;p&gt;The database effectively became a workflow engine.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building a State-Driven System
&lt;/h2&gt;

&lt;p&gt;As more functionality was added, an interesting pattern emerged.&lt;/p&gt;

&lt;p&gt;Every component only needed to know one thing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What is the current state?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The workflow became:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Pending
       ↓
Image Pending
       ↓
Image Complete
       ↓
Published
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No service needed to know what happened before.&lt;/p&gt;

&lt;p&gt;No service needed to care what happened next.&lt;/p&gt;

&lt;p&gt;Everything was driven entirely by state transitions.&lt;/p&gt;

&lt;p&gt;That dramatically simplified the architecture.&lt;/p&gt;




&lt;h2&gt;
  
  
  Seeing the First Results
&lt;/h2&gt;

&lt;p&gt;The most satisfying moment came when the first generated image appeared.&lt;/p&gt;

&lt;p&gt;Not downloaded.&lt;/p&gt;

&lt;p&gt;Not manually uploaded.&lt;/p&gt;

&lt;p&gt;Not created in a design tool.&lt;/p&gt;

&lt;p&gt;Generated automatically.&lt;/p&gt;

&lt;p&gt;Stored automatically.&lt;/p&gt;

&lt;p&gt;Ready for publishing.&lt;/p&gt;

&lt;p&gt;The project suddenly felt real.&lt;/p&gt;

&lt;p&gt;The content wasn't just being written anymore.&lt;/p&gt;

&lt;p&gt;It was being illustrated.&lt;/p&gt;

&lt;p&gt;Every quote now had a matching visual story.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;Three lessons stood out more than anything else.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Images Are a Force Multiplier
&lt;/h3&gt;

&lt;p&gt;Good content becomes significantly more engaging when paired with relevant visuals.&lt;/p&gt;

&lt;p&gt;The artwork amplified the message.&lt;/p&gt;

&lt;p&gt;It didn't replace it.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. AI Image Generation Is Prompt Engineering
&lt;/h3&gt;

&lt;p&gt;The same lesson from text generation applied to images.&lt;/p&gt;

&lt;p&gt;Better prompts produced better outcomes.&lt;/p&gt;

&lt;p&gt;The model wasn't the limiting factor.&lt;/p&gt;

&lt;p&gt;The instructions were.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Regions Matter
&lt;/h3&gt;

&lt;p&gt;Cloud services are rarely as universal as they appear.&lt;/p&gt;

&lt;p&gt;Always verify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Region support&lt;/li&gt;
&lt;li&gt;Model availability&lt;/li&gt;
&lt;li&gt;Service limitations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;before writing code.&lt;/p&gt;

&lt;p&gt;It will save hours of debugging.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;By this point, WizardThoughts could:&lt;/p&gt;

&lt;p&gt;✅ Generate content&lt;br&gt;&lt;br&gt;
✅ Generate image prompts&lt;br&gt;&lt;br&gt;
✅ Create original artwork&lt;br&gt;&lt;br&gt;
✅ Store assets in S3&lt;br&gt;&lt;br&gt;
✅ Track workflow state in DynamoDB  &lt;/p&gt;

&lt;p&gt;The system was finally capable of producing complete content packages.&lt;/p&gt;

&lt;p&gt;But one final challenge remained.&lt;/p&gt;

&lt;p&gt;Everything still required orchestration.&lt;/p&gt;

&lt;p&gt;The content engine needed a conductor.&lt;/p&gt;

&lt;p&gt;In Part 4, we'll complete the journey by introducing EventBridge scheduling, automated publishing, workflow orchestration, and the architecture that transformed WizardThoughts into a fully autonomous social media platform.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Next:&lt;/strong&gt; &lt;em&gt;Part 4 — Going Fully Autonomous: EventBridge, DynamoDB, and Automated Publishing&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>aws</category>
      <category>socialmedia</category>
    </item>
    <item>
      <title>Part 2: Prompt Engineering for Growth: Creating Viral Wizarding Content</title>
      <dc:creator>Ashley Armstrong</dc:creator>
      <pubDate>Sun, 12 Jul 2026 08:00:13 +0000</pubDate>
      <link>https://dev.to/amastr92/part-2-prompt-engineering-for-growth-creating-viral-wizarding-content-4bcm</link>
      <guid>https://dev.to/amastr92/part-2-prompt-engineering-for-growth-creating-viral-wizarding-content-4bcm</guid>
      <description>&lt;p&gt;&lt;em&gt;Building a bot that could post was easy. Building a bot that people wanted to follow was something entirely different.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Next Problem
&lt;/h2&gt;

&lt;p&gt;After solving authentication and getting AWS Lambda to post to X, I found myself staring at a new challenge.&lt;/p&gt;

&lt;p&gt;The infrastructure worked.&lt;/p&gt;

&lt;p&gt;The API worked.&lt;/p&gt;

&lt;p&gt;The automation worked.&lt;/p&gt;

&lt;p&gt;But there was one small issue.&lt;/p&gt;

&lt;p&gt;There was no content.&lt;/p&gt;

&lt;p&gt;Every post still had to be written manually.&lt;/p&gt;

&lt;p&gt;The original vision for WizardThoughts wasn't just automation.&lt;/p&gt;

&lt;p&gt;The goal was autonomy.&lt;/p&gt;

&lt;p&gt;I wanted a system that could:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Generate ideas
        ↓
Create content
        ↓
Create artwork
        ↓
Publish automatically
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make that happen, I needed the bot to think for itself.&lt;/p&gt;




&lt;h2&gt;
  
  
  Enter Amazon Bedrock
&lt;/h2&gt;

&lt;p&gt;Amazon Bedrock became the content engine behind the project.&lt;/p&gt;

&lt;p&gt;Rather than building custom AI infrastructure, Bedrock provided access to foundation models through a simple API.&lt;/p&gt;

&lt;p&gt;My first objective was straightforward:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Generate a motivational quote inspired by a magical wizarding world.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first prompt looked something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Generate a motivational quote.

Requirements:
- Under 240 characters
- Suitable for X
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Technically, it worked.&lt;/p&gt;

&lt;p&gt;The quality wasn't great.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Generic AI Problem
&lt;/h2&gt;

&lt;p&gt;The first quotes sounded like every motivational page on the internet.&lt;/p&gt;

&lt;p&gt;Examples looked like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Believe in yourself and great things will happen.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Success comes to those who never give up.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Perfectly valid.&lt;/p&gt;

&lt;p&gt;Perfectly boring.&lt;/p&gt;

&lt;p&gt;The problem wasn't the model.&lt;/p&gt;

&lt;p&gt;The problem was the instructions.&lt;/p&gt;

&lt;p&gt;I had accidentally built a generator for the world's most forgettable content.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Realisation
&lt;/h2&gt;

&lt;p&gt;Most people approach AI prompting as a writing exercise.&lt;/p&gt;

&lt;p&gt;I started viewing it as product design.&lt;/p&gt;

&lt;p&gt;The prompt wasn't just generating text.&lt;/p&gt;

&lt;p&gt;The prompt was defining the personality of the account.&lt;/p&gt;

&lt;p&gt;Every instruction influenced:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Engagement&lt;/li&gt;
&lt;li&gt;Shareability&lt;/li&gt;
&lt;li&gt;Memorability&lt;/li&gt;
&lt;li&gt;Follower growth&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal wasn't:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Generate content.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The goal became:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Generate content that people want to interact with.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That changed everything.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reverse Engineering Engagement
&lt;/h2&gt;

&lt;p&gt;I started looking at the types of posts that consistently perform well on social media.&lt;/p&gt;

&lt;p&gt;Certain patterns emerged repeatedly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Curiosity
&lt;/h3&gt;

&lt;p&gt;People love unanswered questions.&lt;/p&gt;

&lt;p&gt;Examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What if...
Have you ever...
Imagine if...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Curiosity creates a psychological need for resolution.&lt;/p&gt;

&lt;p&gt;People stop scrolling.&lt;/p&gt;




&lt;h3&gt;
  
  
  Nostalgia
&lt;/h3&gt;

&lt;p&gt;Wizarding World fans are incredibly nostalgic.&lt;/p&gt;

&lt;p&gt;Posts that trigger memories often outperform pure motivational content.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Remember when discovering a new spell felt like endless possibility?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The post isn't selling anything.&lt;/p&gt;

&lt;p&gt;It's creating a feeling.&lt;/p&gt;




&lt;h3&gt;
  
  
  Identity
&lt;/h3&gt;

&lt;p&gt;People engage with content that reinforces their identity.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Work hard and succeed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I started generating posts like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every wizard begins as a student.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The same lesson.&lt;/p&gt;

&lt;p&gt;A more relevant story.&lt;/p&gt;




&lt;h3&gt;
  
  
  Conversation
&lt;/h3&gt;

&lt;p&gt;The biggest breakthrough came from discussion-driven posts.&lt;/p&gt;

&lt;p&gt;Instead of broadcasting messages, I wanted the account to invite participation.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you could learn one magical skill in real life, would you choose courage, wisdom, or luck?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Simple.&lt;/p&gt;

&lt;p&gt;Yet far more likely to generate replies.&lt;/p&gt;




&lt;h2&gt;
  
  
  Redesigning the Prompt
&lt;/h2&gt;

&lt;p&gt;Eventually I completely replaced the original prompt.&lt;/p&gt;

&lt;p&gt;Instead of asking for a motivational quote, I asked the model to create different content categories.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Generate ONE of the following at random:

1. Inspirational wizarding quote
2. Magical life lesson
3. "What if..." wizarding thought
4. Wizarding world observation
5. Unpopular magical opinion
6. Question that encourages replies
7. Nostalgia-based magical post
8. Magic and mindset lesson
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This single change dramatically improved output variety.&lt;/p&gt;

&lt;p&gt;The account stopped sounding like a quote generator.&lt;/p&gt;

&lt;p&gt;It started sounding like a personality.&lt;/p&gt;




&lt;h2&gt;
  
  
  Designing for Followers
&lt;/h2&gt;

&lt;p&gt;Follower growth wasn't going to come from posting more.&lt;/p&gt;

&lt;p&gt;It was going to come from posting better.&lt;/p&gt;

&lt;p&gt;Every prompt was designed around three objectives.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Stop The Scroll
&lt;/h3&gt;

&lt;p&gt;The first sentence had to create interest.&lt;/p&gt;

&lt;p&gt;Examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What if...
Imagine...
Most wizards...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hooks matter.&lt;/p&gt;

&lt;p&gt;Without them, nothing else matters.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Reward The Reader
&lt;/h3&gt;

&lt;p&gt;A post should leave someone with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An insight&lt;/li&gt;
&lt;li&gt;A question&lt;/li&gt;
&lt;li&gt;A feeling&lt;/li&gt;
&lt;li&gt;A memory&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Something worth taking away.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Encourage Action
&lt;/h3&gt;

&lt;p&gt;Not every post should ask for engagement.&lt;/p&gt;

&lt;p&gt;But some should.&lt;/p&gt;

&lt;p&gt;Questions became a deliberate part of the content strategy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Which magical lesson has helped you most in real life?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple.&lt;/p&gt;

&lt;p&gt;Yet effective.&lt;/p&gt;




&lt;h2&gt;
  
  
  Generating Images Too
&lt;/h2&gt;

&lt;p&gt;At this point another problem appeared.&lt;/p&gt;

&lt;p&gt;The text was improving.&lt;/p&gt;

&lt;p&gt;The visuals didn't exist.&lt;/p&gt;

&lt;p&gt;I wanted the model to create image prompts alongside the content.&lt;/p&gt;

&lt;p&gt;So the prompt evolved again.&lt;/p&gt;

&lt;p&gt;Instead of returning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"quote"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It now returned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"quote"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"imagePrompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"quote"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"What if the strongest magic has always been the courage to keep going when the path ahead is uncertain? #Magic #WizardingWorld #Mindset"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"imagePrompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Young wizard standing on a cliff overlooking a glowing magical castle at sunrise, cinematic fantasy artwork, magical atmosphere, rich colours, volumetric lighting, highly detailed, no text"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now every post automatically contained the instructions needed to generate matching artwork.&lt;/p&gt;




&lt;h2&gt;
  
  
  Storing Content in DynamoDB
&lt;/h2&gt;

&lt;p&gt;Once content generation was working, the next step was persistence.&lt;/p&gt;

&lt;p&gt;Every generated post was stored in DynamoDB.&lt;/p&gt;

&lt;p&gt;The structure looked like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"PostId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"post-123456"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ImagePrompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Pending"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ImageStatus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Pending"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This transformed DynamoDB into the workflow engine for the entire platform.&lt;/p&gt;

&lt;p&gt;Everything became state-driven.&lt;/p&gt;

&lt;p&gt;No manual tracking required.&lt;/p&gt;




&lt;h2&gt;
  
  
  One Unexpected Problem
&lt;/h2&gt;

&lt;p&gt;Large language models are not always obedient.&lt;/p&gt;

&lt;p&gt;Despite explicitly asking for JSON, the model occasionally returned valid JSON wrapped in Markdown code fences rather than raw JSON.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"quote"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"imagePrompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My parser expected raw JSON.&lt;/p&gt;

&lt;p&gt;The model decided to be helpful.&lt;/p&gt;

&lt;p&gt;Unfortunately, that helpfulness immediately broke the workflow.&lt;/p&gt;

&lt;p&gt;The lesson?&lt;/p&gt;

&lt;p&gt;Never trust model output.&lt;/p&gt;

&lt;p&gt;Always validate and clean responses before processing them.&lt;/p&gt;

&lt;p&gt;It was a small issue, but an important one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Measuring Success
&lt;/h2&gt;

&lt;p&gt;The interesting thing about prompt engineering is that success isn't measured by whether the model responds.&lt;/p&gt;

&lt;p&gt;It's measured by whether the audience responds.&lt;/p&gt;

&lt;p&gt;A technically perfect answer can still be terrible content.&lt;/p&gt;

&lt;p&gt;What mattered was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Likes
Replies
Shares
Follows
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those became the real evaluation metrics.&lt;/p&gt;

&lt;p&gt;The AI wasn't writing for me.&lt;/p&gt;

&lt;p&gt;The AI was writing for the audience.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;Three lessons stood out.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Prompting Is Product Design
&lt;/h3&gt;

&lt;p&gt;Every instruction affects behaviour.&lt;/p&gt;

&lt;p&gt;Every sentence influences output quality.&lt;/p&gt;

&lt;p&gt;A prompt is not a command.&lt;/p&gt;

&lt;p&gt;It's a specification.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Variety Drives Engagement
&lt;/h3&gt;

&lt;p&gt;People don't follow accounts that repeat the same idea.&lt;/p&gt;

&lt;p&gt;Randomising content categories increased quality and prevented repetition.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. The Model Is Not The Product
&lt;/h3&gt;

&lt;p&gt;Bedrock generated the content.&lt;/p&gt;

&lt;p&gt;But the real value came from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prompt design&lt;/li&gt;
&lt;li&gt;Workflow design&lt;/li&gt;
&lt;li&gt;Distribution design&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The model was just one component in a larger system.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;By this point, WizardThoughts could:&lt;/p&gt;

&lt;p&gt;✅ Generate original content&lt;br&gt;&lt;br&gt;
✅ Produce engagement-focused posts&lt;br&gt;&lt;br&gt;
✅ Create image prompts&lt;br&gt;&lt;br&gt;
✅ Store content automatically  &lt;/p&gt;

&lt;p&gt;But something was still missing.&lt;/p&gt;

&lt;p&gt;The account had words.&lt;/p&gt;

&lt;p&gt;It had ideas.&lt;/p&gt;

&lt;p&gt;It had personality.&lt;/p&gt;

&lt;p&gt;What it didn't have was artwork.&lt;/p&gt;

&lt;p&gt;In Part 3, we'll build the visual side of the platform using Stability AI, Amazon S3, and automated image generation.&lt;/p&gt;

&lt;p&gt;Because on social media, attention may start with words.&lt;/p&gt;

&lt;p&gt;But it is often captured by images.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Next:&lt;/strong&gt; &lt;em&gt;Part 3 — AI Art at Scale: Generating Fantasy Images with Bedrock and Stability AI&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>aws</category>
      <category>lambda</category>
      <category>socialmedia</category>
    </item>
    <item>
      <title>Part 1: Authentication Hell: Getting AWS Lambda Talking to X</title>
      <dc:creator>Ashley Armstrong</dc:creator>
      <pubDate>Sun, 12 Jul 2026 07:50:58 +0000</pubDate>
      <link>https://dev.to/amastr92/part-1-authentication-hell-getting-aws-lambda-talking-to-x-ba7</link>
      <guid>https://dev.to/amastr92/part-1-authentication-hell-getting-aws-lambda-talking-to-x-ba7</guid>
      <description>&lt;p&gt;&lt;em&gt;How a simple idea turned into a multi-day battle with APIs, OAuth, and enough authentication errors to fill a spellbook.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Idea
&lt;/h2&gt;

&lt;p&gt;Every project starts with a simple question.&lt;/p&gt;

&lt;p&gt;For me, it was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Could I build an autonomous Wizarding World content engine that generates its own content, creates its own artwork, and publishes directly to X without human involvement?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The concept seemed straightforward.&lt;/p&gt;

&lt;p&gt;The vision was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI generates quote
        ↓
AI generates artwork
        ↓
AWS stores content
        ↓
X publishes post
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In theory, this looked like a weekend project.&lt;/p&gt;

&lt;p&gt;In reality, it became an education in authentication systems, API permissions, OAuth flows, and the many ways technology can tell you "no".&lt;/p&gt;




&lt;h2&gt;
  
  
  Building the Foundation
&lt;/h2&gt;

&lt;p&gt;I decided to build everything on AWS.&lt;/p&gt;

&lt;p&gt;The initial stack looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWS Lambda
Amazon DynamoDB
Amazon Bedrock
Amazon S3
EventBridge
X API
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first objective was intentionally small:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Post a single tweet from AWS Lambda.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not an AI-generated tweet.&lt;/p&gt;

&lt;p&gt;Not an image.&lt;/p&gt;

&lt;p&gt;Just a tweet.&lt;/p&gt;

&lt;p&gt;How hard could that be?&lt;/p&gt;




&lt;h2&gt;
  
  
  The First Lambda
&lt;/h2&gt;

&lt;p&gt;The first Lambda function was incredibly simple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create Lambda
Install twitter-api-v2
Add environment variables
Call tweet endpoint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Five minutes later everything was deployed.&lt;/p&gt;

&lt;p&gt;I clicked &lt;strong&gt;Test&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It failed.&lt;/p&gt;

&lt;p&gt;Welcome to OAuth.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Authentication Rabbit Hole
&lt;/h2&gt;

&lt;p&gt;If you've worked with X's API before, you'll know there isn't just one way to authenticate.&lt;/p&gt;

&lt;p&gt;There are several.&lt;/p&gt;

&lt;p&gt;At first glance they all appear to do similar things:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bearer Token
OAuth 2.0
OAuth 2.0 User Context
OAuth 1.0a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The challenge is understanding which one works with which endpoint.&lt;/p&gt;

&lt;p&gt;I made what seemed like the obvious choice.&lt;/p&gt;

&lt;p&gt;I used the Bearer Token.&lt;/p&gt;

&lt;p&gt;The result?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;403 Forbidden
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not very helpful.&lt;/p&gt;

&lt;p&gt;After digging through the logs, I eventually found the real message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Unsupported Authentication

Authenticating with OAuth 2.0 Application-Only is forbidden for this endpoint.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was my first major lesson.&lt;/p&gt;

&lt;p&gt;Just because you're authenticated doesn't mean you're authorised.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding the Difference
&lt;/h2&gt;

&lt;p&gt;The problem wasn't my code.&lt;/p&gt;

&lt;p&gt;The problem was the type of identity I was presenting to X.&lt;/p&gt;

&lt;p&gt;A Bearer Token represents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A User Context token represents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Posting a tweet requires a user.&lt;/p&gt;

&lt;p&gt;Not just an application.&lt;/p&gt;

&lt;p&gt;In other words:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application:
"Hello, I am WizardThoughts."

X:
"Great."

Application:
"I would like to tweet."

X:
"No."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Chasing the Right Token
&lt;/h2&gt;

&lt;p&gt;I spent hours moving between:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWS Lambda
CloudWatch Logs
X Developer Portal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Testing.&lt;/p&gt;

&lt;p&gt;Deploying.&lt;/p&gt;

&lt;p&gt;Testing again.&lt;/p&gt;

&lt;p&gt;Every change seemed to produce a different error.&lt;/p&gt;

&lt;p&gt;Sometimes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;401 Unauthorized
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other times:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;403 Forbidden
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And occasionally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Unsupported Authentication
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At one point I genuinely believed I had broken the entire account configuration.&lt;/p&gt;

&lt;p&gt;The reality was much simpler:&lt;/p&gt;

&lt;p&gt;I was using the wrong type of token.&lt;/p&gt;




&lt;h2&gt;
  
  
  The First Real Breakthrough
&lt;/h2&gt;

&lt;p&gt;Rather than trying to post tweets, I decided to verify authentication first.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;v2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I switched to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;v2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;me&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This endpoint simply returns information about the currently authenticated user.&lt;/p&gt;

&lt;p&gt;If that worked, I would know the authentication was correct.&lt;/p&gt;

&lt;p&gt;If it failed, the token was wrong.&lt;/p&gt;

&lt;p&gt;Suddenly I received:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"WizardThoughts"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That was the first real victory.&lt;/p&gt;

&lt;p&gt;For the first time, AWS Lambda had successfully authenticated with X.&lt;/p&gt;

&lt;p&gt;No guessing.&lt;/p&gt;

&lt;p&gt;No assumptions.&lt;/p&gt;

&lt;p&gt;Proof.&lt;/p&gt;




&lt;h2&gt;
  
  
  The OAuth 2 Dead End
&lt;/h2&gt;

&lt;p&gt;With authentication seemingly solved, I moved on to images.&lt;/p&gt;

&lt;p&gt;That's when another problem appeared.&lt;/p&gt;

&lt;p&gt;The basic tweet APIs worked.&lt;/p&gt;

&lt;p&gt;Media uploads did not.&lt;/p&gt;

&lt;p&gt;The logs revealed a new pattern.&lt;/p&gt;

&lt;p&gt;Every media upload attempt failed with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;403 Forbidden
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After more debugging, I discovered another important distinction.&lt;/p&gt;

&lt;p&gt;Text posting could work using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OAuth 2 User Context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Media uploads were much happier with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OAuth 1.0a User Context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which meant I needed four credentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CONSUMER_KEY
CONSUMER_SECRET
ACCESS_TOKEN
ACCESS_TOKEN_SECRET
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Suddenly the full picture started making sense.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Moment Everything Clicked
&lt;/h2&gt;

&lt;p&gt;The final authentication test looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TwitterApi&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;appKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CONSUMER_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;appSecret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CONSUMER_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ACCESS_TOKEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;accessSecret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ACCESS_TOKEN_SECRET&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;me&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;v2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;me&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"WizardThoughts"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Success.&lt;/p&gt;

&lt;p&gt;Not a partial success.&lt;/p&gt;

&lt;p&gt;Not a maybe.&lt;/p&gt;

&lt;p&gt;A genuine, repeatable success.&lt;/p&gt;

&lt;p&gt;The authentication layer was finally solved.&lt;/p&gt;




&lt;h2&gt;
  
  
  The First Automated Tweet
&lt;/h2&gt;

&lt;p&gt;Once the OAuth issues were resolved, posting a tweet became almost trivial.&lt;/p&gt;

&lt;p&gt;The same infrastructure that had spent days refusing to cooperate suddenly worked exactly as intended.&lt;/p&gt;

&lt;p&gt;The first tweet appeared.&lt;/p&gt;

&lt;p&gt;Not created manually.&lt;/p&gt;

&lt;p&gt;Not posted from a browser.&lt;/p&gt;

&lt;p&gt;Automatically.&lt;/p&gt;

&lt;p&gt;Directly from AWS Lambda.&lt;/p&gt;

&lt;p&gt;That single post represented far more than a tweet.&lt;/p&gt;

&lt;p&gt;It proved the entire foundation was viable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;Three lessons stood out above everything else.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Authentication Is a Product Feature
&lt;/h3&gt;

&lt;p&gt;Most developers treat authentication as setup.&lt;/p&gt;

&lt;p&gt;It isn't.&lt;/p&gt;

&lt;p&gt;It's part of the application.&lt;/p&gt;

&lt;p&gt;Understanding identities, scopes, permissions, and token types is essential.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Trust Error Messages
&lt;/h3&gt;

&lt;p&gt;Most of my mistakes came from ignoring what the logs were telling me.&lt;/p&gt;

&lt;p&gt;Eventually every answer was in CloudWatch.&lt;/p&gt;

&lt;p&gt;The challenge was learning how to read it.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Prove Identity First
&lt;/h3&gt;

&lt;p&gt;Before attempting any complex API action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Authenticate
↓
Verify user
↓
Then perform action
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Testing with &lt;code&gt;v2.me()&lt;/code&gt; saved hours of debugging.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;At this point the bot could successfully:&lt;/p&gt;

&lt;p&gt;✅ Authenticate with X&lt;br&gt;&lt;br&gt;
✅ Run in AWS Lambda&lt;br&gt;&lt;br&gt;
✅ Post automated tweets  &lt;/p&gt;

&lt;p&gt;But it still had one major limitation.&lt;/p&gt;

&lt;p&gt;Every tweet had to be written manually.&lt;/p&gt;

&lt;p&gt;In Part 2, we'll teach the bot how to think for itself using Amazon Bedrock, prompt engineering, and growth-focused content generation.&lt;/p&gt;

&lt;p&gt;Because a bot that can post is useful.&lt;/p&gt;

&lt;p&gt;A bot that can create its own content is where the magic begins. ⚡🪄&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Next:&lt;/strong&gt; &lt;em&gt;Part 2 — Prompt Engineering for Growth: Creating Viral Wizarding Content&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>aws</category>
      <category>lambda</category>
      <category>socialmedia</category>
    </item>
  </channel>
</rss>
