🌟 Automating Cover Letters with Portia AI: My AgentHack 2025 Journey

A personal story of how AI-powered agents can transform freelancing workflows and save countless hours.

💡 Introduction

Welcome Devs 👋 to the world of AI and Automation.

In this blog, I’ll be sharing my experience of building an AI Agent …


This content originally appeared on DEV Community and was authored by Pravesh Sudha

A personal story of how AI-powered agents can transform freelancing workflows and save countless hours.

💡 Introduction

Welcome Devs 👋 to the world of AI and Automation.

In this blog, I’ll be sharing my experience of building an AI Agent that writes personalized cover letters for my freelance gigs — powered by Portia AI and Gemini. As a full-time freelancer, writing cover letters for every single project often feels repetitive and time-consuming. That’s exactly where this idea was born. The journey of making this project was full of ebbs and flows — debugging errors, testing prompts, tweaking workflows — but in the end, it was worth it. The biggest takeaway? 🤔 If you use AI agents wisely, they can save you countless hours every week while still keeping your work personal and professional. So without further ado, let’s dive into how I built this project, what challenges I faced, and how Portia helped me automate one of the most tiring parts of freelancing: writing cover letters.

💡 Youtube Demo

💡 The Problem

As a full-time freelancer, one of the most time-consuming parts of my workflow is writing personalized cover lettersfor each new opportunity.

Don’t get me wrong — being thoughtful and specific in a proposal is important. It shows effort and increases the chances of landing the gig. But after doing it over and over again, it quickly becomes:

  • Repetitive 🌀

  • Exhausting 😮‍💨

  • And honestly, inefficient

I found myself spending hours every week just writing cover letters — time that could have been used to actually work on projects.

That’s when I realized I needed a smarter solution. A tool that could:

  • Read documents containing the job description along with my portfolio.

  • Generate a personalized cover letter based on that information.

  • Send the cover letter via email directly to the job poster. And if no email is provided, then simply mail it to myself to keep it safe.

This became the spark for building my own AI-powered freelancing assistant 🚀.

🚀 The Journey

After deciding to build my AI-powered Cover Letter Generator, I rolled up my sleeves and dived into the Portia AI docs.

At first, I’ll admit — it felt overwhelming. There’s a lot to take in when you’re just starting with AI agents. But after tinkering around for a while, I started to get the hang of it.

My first step was creating a Google API Key using Google AI Studio for the Gemini-2.0-flash model. I thought I’d start simple, so I ran a basic 2 + 2 test. To my surprise… it failed. 🙃

The error? Something about missing API keys — even though I had provided them correctly. That’s when I reached out to the Portia Team on Discord. And here’s the cool part: none other than Emma Burrows, Co-founder & CTO of Portia, personally helped me troubleshoot it. Turns out, it wasn’t the API at all — it was a dependency issue. Once I fixed that, everything clicked into place. That was my first small win ✅.

Next, I wanted to see how Portia works with Google Apps like Gmail and Calendar. So I went through their official examples on GitHub. That gave me clarity on how to structure tool usage and start building my own agent.

From there, I built the first version of my program (app.py). It had all the pieces I needed:

  • Reading the job description doc + my portfolio doc.

  • Extracting recipient emails.

  • Generating a personalized cover letter.

  • Sending it via Gmail automatically.

from dotenv import load_dotenv
import os

from portia import (
    Config,
    DefaultToolRegistry,
    Portia,
    LLMProvider,
    ActionClarification,
    InputClarification,
    MultipleChoiceClarification,
    PlanRunState,
)

load_dotenv(override=True)

job_doc_title = os.getenv("JOB_DOC_TITLE", "Job").strip()
portfolio_doc_title = os.getenv("PORTFOLIO_DOC_TITLE", "Portfolio").strip()
output_doc_title = os.getenv("OUTPUT_DOC_TITLE", "Cover-letter").strip()

self_email = os.getenv("SELF_EMAIL", "").strip()
if not self_email:
    self_email = input("Enter your fallback email (SELF_EMAIL):\n").strip()

socials_block = os.getenv("SOCIALS", "").strip()
if not socials_block:
    parts = []
    for key, label in [
        ("LINKEDIN_URL", "LinkedIn"),
        ("TWITTER_URL", "X/Twitter"),
        ("YOUTUBE_URL", "YouTube"),
        ("WEBSITE_URL", "Website"),
    ]:
        v = os.getenv(key, "").strip()
        if v:
            parts.append(f"{label}: {v}")
    socials_block = "\n".join(parts) if parts else "(Add your socials here)"

constraints: list[str] = []


def task_text() -> str:
    """
    We describe *what* to do. Portia will pick the right tools:
    - Google Drive: search files
    - Google Docs: read contents
    - Gmail: draft/send email
    - (Attempt) create a new Google Doc for the cover letter
    """
    return f"""
You are an expert career coach and outreach copywriter. Use available Google tools to complete the flow below.

Inputs:
- Job doc title: "{job_doc_title}"
- Portfolio doc title: "{portfolio_doc_title}"
- Output Google Doc title: "{output_doc_title}"
- Fallback recipient email (if none in Job doc): "{self_email}"
- Socials block (append after sign-off):
{socials_block}

Here’s what I need you to do:  

1. Locate the Google Doc that matches the job title and the one that matches my portfolio. If either is missing or there are multiple, stop and ask me.

2. Read both docs carefully. From the job doc, identify the role, company, and (if available) the recipient email.
    - If no recipient email is found, use the fallback.

3. Write a tailored cover letter (200–350 words).
    - Inputs: ONLY the job doc content, portfolio content
    - Begin with a strong opening.
    - Highlight 2–3 achievements that connect my skills with the job requirements.
    - Keep it professional, concise, and specific.
    - After the closing and my name, add the socials block.

4. Send the cover letter via Gmail.
    - Inputs: the cover letter text + the recipient email.
    - Subject: “Cover letter – Pravesh Sudha – AWS Community Builder”
    - Body: Use the same cover letter text, then include the Google Doc link at the end if available. 

Rules:  
- When using tools (Drive, Docs, Gmail), respond only with the correct tool call JSON (no explanations, no markdown).  
- If you need my input (e.g., choosing between multiple docs, missing info), pause and ask.  
- If a Google Doc cannot be created, fallback to emailing me a draft with the letter.  
"""


print("\n📋 Generating a plan with Portia ...\n")

config = Config.from_default(
    llm_provider=LLMProvider.GOOGLE,
    default_model="google/gemini-2.0-flash",  # fast + capable for writing
    enforce_schema=True,
)

tool_registry = DefaultToolRegistry(config)
portia = Portia(config=config, tools=tool_registry)

plan = portia.plan(task_text())
print("Here are the steps in the generated plan:\n")
print(plan.pretty_print())

while True:
    yn = input("\nAre you happy with the plan? (y/n):\n").strip().lower()
    if yn == "y":
        break
    extra = input("Any additional guidance for the planner?:\n").strip()
    if extra:
        constraints.append(extra)
    plan = portia.plan(task_text())
    print("\nUpdated plan:\n")
    print(plan.pretty_print())


print("\n🚀 Executing the plan ...\n")
plan_run = portia.run_plan(plan)

while plan_run.state == PlanRunState.NEED_CLARIFICATION:
    for clarification in plan_run.get_outstanding_clarifications():
        if isinstance(clarification, (InputClarification, MultipleChoiceClarification)):
            prompt = clarification.user_guidance or "Please provide a value"
            # Some multiple-choice clarifications expose options
            options = getattr(clarification, "options", None)
            if options:
                prompt += "\n" + "\n".join(str(o) for o in options)
            user_input = input(prompt + "\n")
            plan_run = portia.resolve_clarification(clarification, user_input, plan_run)

        elif isinstance(clarification, ActionClarification):
            # Typically OAuth. You’ll get a link to click.
            print("\n🔐 Authorization required. Open this link to continue:")
            print(clarification.action_url)
            print("Waiting for authorization to complete...\n")
            plan_run = portia.wait_for_ready(plan_run)


    plan_run = portia.resume(plan_run)


print("\n✅ Plan run complete. Raw output below:\n")
print(plan_run.model_dump_json(indent=2))

I saved all my sensitive info in a .env file (API keys, fallback emails, socials), making the setup clean and flexible.

PORTIA_API_KEY="your-portia-key"
GOOGLE_API_KEY="your-gemini-key"

# Optional (defaults shown in code)
JOB_DOC_TITLE="Job"
PORTFOLIO_DOC_TITLE="Portfolio"
OUTPUT_DOC_TITLE="Cover-letter"

# Email handling
SELF_EMAIL="example@gmail.com"   # used if Job doc has no email

# Socials — either provide a single SOCIALS block or individual links
LINKEDIN_URL="Your-Linkedin"
TWITTER_URL="Your-Twitter"
YOUTUBE_URL="Your-Youtube"
WEBSITE_URL="Your-Website"

Here’s the repo if you want to check out the code: 👉 GitHub – agent-hack-2025

Of course, the road wasn’t smooth. Along the way, I hit service quota limits, tried different LLMs like OpenAI, Anthropic, and Mistral — none of them worked seamlessly. Eventually, I generated a new Google API key on a different account, which fixed the quota issue.

But then came the real grind: getting the task prompt right. Portia is powerful, but it requires clear instructions. I had to refactor the prompt over 25 times before the execution flow was smooth. Finally, the magic happened:

  • Portia read the job + portfolio docs,

  • Generated a crisp cover letter,

  • And emailed it directly to the client.

Seeing that email land in the inbox felt… dope. 😎

⚙️ How It Works

Now that the agent was finally up and running, here’s what the flow looks like in action:

  • Input Stage – Job & Portfolio Docs 📂

    The agent starts by reading two files:

    • A Job Description doc (the opportunity I want to apply for).
    • My Portfolio doc (skills, past projects, and relevant achievements).

    This ensures every cover letter is tailored, not generic.

  • Processing Stage – Gemini + Portia Magic

    • Portia passes the docs into the Gemini model.
    • Gemini analyzes the job requirements alongside my portfolio.
    • The output? A personalized cover letter that feels thoughtful, not copy-pasted.
  • Email Handling 📧

    • If the client’s email is available in the doc, the cover letter gets sent directly to them.
    • If no email is found, the agent sends the cover letter to my fallback email (saved in .env), so I can reuse it later.
  • Smart Fail-safes 🛡️

    • All sensitive data (API keys, fallback emails, socials) are stored securely in .env.
    • If an email fails, the letter isn’t lost — I still get it in my inbox.
  • Automation in Action 🚀

    • No more copy-pasting job descriptions.
    • No more staring at a blank page wondering how to start a letter.
    • Everything is automated: read → generate → email → done.

Here’s a simple visual flow for clarity:

Job Doc + Portfolio → Gemini via Portia → Cover Letter → Gmail → Client/Me

What used to take me 30–40 minutes per job now takes less than 30 seconds. That’s the kind of efficiency freelancers dream of.

📄 Portfolio Doc

📄 Job Doc

💡 The Impact

This small project ended up creating a huge impact on my workflow as a freelancer.

  • What once took me hours every week now happens in seconds.

  • Instead of wasting energy on repetitive writing, I can focus more on client work and skill-building.

  • The best part? Each cover letter still feels personalized and human, not like a bland AI template.

It’s not just about saving time — it’s about working smarter. AI agents like this one give freelancers the leverage to scale their efforts without burning out.

🚀 Conclusion

Building this project with Portia AI and Gemini was more than just a hackathon entry — it was a lesson in how AI agents can redefine productivity for freelancers.

As a full-time freelancer, I know the hustle of juggling multiple gigs, proposals, and deadlines. With Portia, I’ve turned a time-consuming, repetitive task into something swift, efficient, and stress-free.

This is just the beginning — AI agents are not here to replace us, but to empower us. If we learn how to harness them wisely, they can free us from routine work and let us focus on what truly matters: creating, solving, and delivering value.

Thanks for reading my journey! 💙

If you found this project inspiring or want to connect, feel free to reach out to me on:


This content originally appeared on DEV Community and was authored by Pravesh Sudha


Print Share Comment Cite Upload Translate Updates
APA

Pravesh Sudha | Sciencx (2025-08-25T08:03:47+00:00) 🌟 Automating Cover Letters with Portia AI: My AgentHack 2025 Journey. Retrieved from https://www.scien.cx/2025/08/25/%f0%9f%8c%9f-automating-cover-letters-with-portia-ai-my-agenthack-2025-journey/

MLA
" » 🌟 Automating Cover Letters with Portia AI: My AgentHack 2025 Journey." Pravesh Sudha | Sciencx - Monday August 25, 2025, https://www.scien.cx/2025/08/25/%f0%9f%8c%9f-automating-cover-letters-with-portia-ai-my-agenthack-2025-journey/
HARVARD
Pravesh Sudha | Sciencx Monday August 25, 2025 » 🌟 Automating Cover Letters with Portia AI: My AgentHack 2025 Journey., viewed ,<https://www.scien.cx/2025/08/25/%f0%9f%8c%9f-automating-cover-letters-with-portia-ai-my-agenthack-2025-journey/>
VANCOUVER
Pravesh Sudha | Sciencx - » 🌟 Automating Cover Letters with Portia AI: My AgentHack 2025 Journey. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2025/08/25/%f0%9f%8c%9f-automating-cover-letters-with-portia-ai-my-agenthack-2025-journey/
CHICAGO
" » 🌟 Automating Cover Letters with Portia AI: My AgentHack 2025 Journey." Pravesh Sudha | Sciencx - Accessed . https://www.scien.cx/2025/08/25/%f0%9f%8c%9f-automating-cover-letters-with-portia-ai-my-agenthack-2025-journey/
IEEE
" » 🌟 Automating Cover Letters with Portia AI: My AgentHack 2025 Journey." Pravesh Sudha | Sciencx [Online]. Available: https://www.scien.cx/2025/08/25/%f0%9f%8c%9f-automating-cover-letters-with-portia-ai-my-agenthack-2025-journey/. [Accessed: ]
rf:citation
» 🌟 Automating Cover Letters with Portia AI: My AgentHack 2025 Journey | Pravesh Sudha | Sciencx | https://www.scien.cx/2025/08/25/%f0%9f%8c%9f-automating-cover-letters-with-portia-ai-my-agenthack-2025-journey/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.