<?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: agenticemail.dev</title>
    <description>The latest articles on DEV Community by agenticemail.dev (@agenticemail).</description>
    <link>https://dev.to/agenticemail</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%2F4017121%2F4924cd4c-4936-438a-955c-3721be789c72.png</url>
      <title>DEV Community: agenticemail.dev</title>
      <link>https://dev.to/agenticemail</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/agenticemail"/>
    <language>en</language>
    <item>
      <title>Job postings are the earliest buying signal your GTM stack ignores</title>
      <dc:creator>agenticemail.dev</dc:creator>
      <pubDate>Mon, 13 Jul 2026 06:07:20 +0000</pubDate>
      <link>https://dev.to/agenticemail/job-postings-are-the-earliest-buying-signal-your-gtm-stack-ignores-3kon</link>
      <guid>https://dev.to/agenticemail/job-postings-are-the-earliest-buying-signal-your-gtm-stack-ignores-3kon</guid>
      <description>&lt;p&gt;A company's marketing site tells you what it wants you to believe. Its job postings tell you what is actually true. Nobody writes "experience with Kafka, Snowflake, and dbt required" into a job ad as branding; they write it because a hiring manager needs someone who can operate the stack they really run.&lt;/p&gt;

&lt;p&gt;That makes job postings one of the best technographic and intent data sources available, and it is almost entirely ignored by the standard GTM stack. This post covers what you can extract from hiring data, where the extraction goes wrong, and how to build a pipeline for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a job post leaks
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The current stack.&lt;/strong&gt; Requirements sections enumerate the production stack more honestly than any tech-detection crawler. Website-based technographics tools (BuiltWith, Wappalyzer, and their alternatives) see what ships to the browser: the analytics snippet, the framework, the CDN. They cannot see Datadog, Terraform, Snowflake, Airflow, or anything else that lives behind the firewall. Job posts name those tools constantly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The next stack.&lt;/strong&gt; "Nice to have" sections and phrases like "help us migrate to" leak roadmaps. A company posting three roles mentioning Kubernetes when its current infra jobs say ECS is telling you about a migration before any analyst report does.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Initiatives before announcements.&lt;/strong&gt; The first "Head of AI" posting, the first security engineer, the first hire in a new country. Each is a strategic decision that became public the moment someone needed to staff it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Timing.&lt;/strong&gt; This is the part sales teams care about. Content-based intent providers (Bombora, 6sense, ZoomInfo) detect an account researching a category, which happens when the evaluation is already underway. Hiring for a role that will use your product happens earlier: the budget exists, the initiative is committed, and the person who will run the evaluation has not been hired yet. Hiring signals typically fire one to two quarters before content intent lights up on the same account.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where naive extraction goes wrong
&lt;/h2&gt;

&lt;p&gt;If you grep descriptions for tool names, you will poison your data in three ways I know from experience:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Negation and direction.&lt;/strong&gt; "Migrating away from Oracle" is a churn signal for Oracle and a buy signal for its competitors. A keyword match reads it as Oracle usage. You need at least phrase-level context around every hit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ambiguous tokens.&lt;/strong&gt; "Go", "R", "Ray", "Spring", "Flow". Short names need co-occurrence rules ("Go" plus "goroutines" or "Golang") or you will conclude that every logistics company runs Golang because their ads say "go the extra mile".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Comparison mentions.&lt;/strong&gt; "Experience with Datadog or similar observability tools" confirms the category, not the vendor. Treat "or similar" and "such as" lists as category-level evidence, weaker than a bare "we use Datadog".&lt;/p&gt;

&lt;p&gt;The unit of belief should be company-level with decay, not posting-level: a tool mentioned across five postings over six months is stack; a tool mentioned once in one ad might be one manager's wishlist.&lt;/p&gt;

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

&lt;p&gt;You need two inputs: a stream of job postings you can filter by description text, and a way to check the visible web stack of the same companies so the two signals corroborate each other.&lt;/p&gt;

&lt;p&gt;For the postings side, I work on &lt;a href="https://jobspipe.dev" rel="noopener noreferrer"&gt;JobsPipe&lt;/a&gt;, which aggregates 30+ ATS feeds and job boards into one normalized search endpoint. Finding every company that posted a job mentioning your target technology in the last two weeks is one call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://api.jobspipe.dev/v1/jobs/search"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer jp_live_YOUR_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "description_or": ["Kubernetes", "EKS", "GKE"],
    "posted_at_max_age_days": 14,
    "include_total_results": true,
    "limit": 50
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each result is a normalized posting with &lt;code&gt;company&lt;/code&gt;, &lt;code&gt;job_title&lt;/code&gt;, &lt;code&gt;country_code&lt;/code&gt;, &lt;code&gt;date_posted&lt;/code&gt;, and &lt;code&gt;final_url&lt;/code&gt;, so the output drops straight into an enrichment table keyed by company.&lt;/p&gt;

&lt;p&gt;For the website side, the same API has a stack scanner that fetches a domain and returns detected technologies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://api.jobspipe.dev/v1/stack/scan"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer jp_live_YOUR_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"domain": "stripe.com"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interesting analysis lives in the difference between the two views. A company whose website scan shows Segment while its job posts mention Snowflake and dbt has a modern data stack that no page-source crawler would ever report. A company hiring "our first data engineer" with no warehouse mentioned anywhere is a greenfield account for every vendor in that category.&lt;/p&gt;

&lt;h2&gt;
  
  
  A concrete scoring sketch
&lt;/h2&gt;

&lt;p&gt;For a vendor selling, say, observability tooling, a workable v1 score per account:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Category hire in the last 90 days (SRE, platform engineer, DevOps): strong, this person will run the evaluation.&lt;/li&gt;
&lt;li&gt;Competitor named in "we use" context: medium, displacement play with a known incumbent.&lt;/li&gt;
&lt;li&gt;Competitor named in "or similar" context: weak, category confirmed, vendor open.&lt;/li&gt;
&lt;li&gt;Stack scan shows the category is absent from their visible tooling: greenfield modifier.&lt;/li&gt;
&lt;li&gt;Three or more engineering postings in 30 days: growth modifier, budget is moving.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of this needs ML to start. It needs clean, deduplicated, fresh postings data with searchable descriptions, which is the part that is genuinely painful to build yourself (I wrote up the per-ATS details in a separate post on public ATS feeds).&lt;/p&gt;

&lt;p&gt;JobsPipe has a free tier at &lt;a href="https://jobspipe.dev" rel="noopener noreferrer"&gt;jobspipe.dev&lt;/a&gt; (100 requests/month), &lt;a href="https://jobspipe.dev/docs" rel="noopener noreferrer"&gt;docs here&lt;/a&gt;, and an &lt;a href="https://github.com/jobspipe/jobspipe-cli" rel="noopener noreferrer"&gt;open-source CLI&lt;/a&gt; if you want to pipe results into a spreadsheet before writing any code. If you are building sales-intel, recruiting tools, or market research on top of hiring data, I would genuinely like to hear what signals you wish existed.&lt;/p&gt;

</description>
      <category>api</category>
      <category>datascience</category>
      <category>startup</category>
      <category>saas</category>
    </item>
    <item>
      <title>Every major ATS has a public job feed. Here is how to read them all.</title>
      <dc:creator>agenticemail.dev</dc:creator>
      <pubDate>Mon, 13 Jul 2026 06:04:40 +0000</pubDate>
      <link>https://dev.to/agenticemail/every-major-ats-has-a-public-job-feed-here-is-how-to-read-them-all-3k10</link>
      <guid>https://dev.to/agenticemail/every-major-ats-has-a-public-job-feed-here-is-how-to-read-them-all-3k10</guid>
      <description>&lt;p&gt;Most job-board data products are built on scraping HTML. That is the hard way. Nearly every applicant tracking system that powers a company careers page also exposes a public JSON or XML feed, because the career page itself is just a client of that feed. If you know where the feeds live, you can read most of the world's job postings without parsing a single div.&lt;/p&gt;

&lt;p&gt;I have spent the last year building integrations against these feeds for &lt;a href="https://jobspipe.dev" rel="noopener noreferrer"&gt;JobsPipe&lt;/a&gt;, and this post is the field guide I wish had existed when I started: the actual endpoints, the quirks of each, and the parts that stay hard even after you know the URLs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The endpoints
&lt;/h2&gt;

&lt;p&gt;All of these are public and unauthenticated. They exist so that companies can embed job widgets on their own sites, which means they are stable, documented (sometimes), and legal to read.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ATS&lt;/th&gt;
&lt;th&gt;Endpoint pattern&lt;/th&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Greenhouse&lt;/td&gt;
&lt;td&gt;&lt;code&gt;boards-api.greenhouse.io/v1/boards/{token}/jobs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lever&lt;/td&gt;
&lt;td&gt;&lt;code&gt;api.lever.co/v0/postings/{slug}?mode=json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ashby&lt;/td&gt;
&lt;td&gt;&lt;code&gt;api.ashbyhq.com/posting-api/job-board/{name}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SmartRecruiters&lt;/td&gt;
&lt;td&gt;&lt;code&gt;api.smartrecruiters.com/v1/companies/{id}/postings&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Workable&lt;/td&gt;
&lt;td&gt;&lt;code&gt;apply.workable.com/api/v1/widget/accounts/{account}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recruitee&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{company}.recruitee.com/api/offers/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Personio&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{company}.jobs.personio.de/xml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;XML&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BambooHR&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{company}.bamboohr.com/careers/list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Workday&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{tenant}.myworkdayjobs.com/wday/cxs/{tenant}/{site}/jobs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;JSON, POST&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A few worked examples.&lt;/p&gt;

&lt;p&gt;Greenhouse is the friendliest. One GET per board, and &lt;code&gt;content=true&lt;/code&gt; inlines the full HTML description:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://boards-api.greenhouse.io/v1/boards/stripe/jobs?content=true"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lever returns a flat array with the description split into structured &lt;code&gt;lists&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.lever.co/v0/postings/netflix?mode=json"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ashby will even give you structured compensation if the company opted in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.ashbyhq.com/posting-api/job-board/openai?includeCompensation=true"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Workday is the strange one. There is no documented public API, but every Workday career site is a single-page app talking to a JSON endpoint you can call directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://{tenant}.wd5.myworkdayjobs.com/wday/cxs/{tenant}/{site}/jobs"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"limit": 20, "offset": 0, "searchText": ""}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The quirks
&lt;/h2&gt;

&lt;p&gt;Every feed has at least one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dates lie in different ways.&lt;/strong&gt; Lever gives you &lt;code&gt;createdAt&lt;/code&gt; in epoch milliseconds. Greenhouse gives you &lt;code&gt;updated_at&lt;/code&gt;, which changes when anything about the post is edited, not when it was published. Workday gives you &lt;code&gt;postedOn&lt;/code&gt; as a human string like "Posted 3 Days Ago", so your freshness logic ends up parsing English.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pagination is five different ideas.&lt;/strong&gt; Greenhouse returns everything in one response. SmartRecruiters uses &lt;code&gt;limit&lt;/code&gt; and &lt;code&gt;offset&lt;/code&gt;. Workable's newer endpoint uses an opaque continuation token. Workday uses offset paging but silently caps how deep you can go on some tenants. Personio is one big XML document.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Location is unstructured almost everywhere.&lt;/strong&gt; You will see "Remote - US", "New York / London / Remote", "EMEA", and my favorite, an empty string on a posting that is very much in San Francisco. If you need country-level filtering you are building a location resolver, not reading a field.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compensation is in the description.&lt;/strong&gt; Outside of Ashby's opt-in field and pay-transparency jurisdictions that force structure, salary lives in free text: "up to $185k", "£70,000 - £90,000 DOE", "competitive". Extracting it reliably is an NLP problem, not a regex.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that stays hard
&lt;/h2&gt;

&lt;p&gt;Knowing the endpoints gets you one company's jobs. The actual work is everything above that:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tenant discovery.&lt;/strong&gt; The endpoint needs a board token or slug, and there is no directory of them. You find tenants by noticing that careers links point at &lt;code&gt;boards.greenhouse.io/{token}&lt;/code&gt; or &lt;code&gt;jobs.lever.co/{slug}&lt;/code&gt;, by crawling sitemaps, and by watching for companies that migrate ATSs (they do, constantly, and their old feed keeps serving stale jobs).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Normalization.&lt;/strong&gt; Nine sources means nine schemas for title, location, department, employment type, and date. A "Senior Backend Engineer (m/f/d)" in a Personio XML feed and a "Sr. Backend Engineer" in a Greenhouse JSON feed need to become the same shape before your queries mean anything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deduplication.&lt;/strong&gt; The same role appears on the company's ATS feed, on Indeed, on LinkedIn, and on three regional boards. If you count postings without cross-source dedup, your "who is hiring" numbers are fiction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Freshness.&lt;/strong&gt; Feeds do not push. You are polling tens of thousands of tenants on a schedule, diffing results, and deciding what "closed" means when a job just disappears.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you would rather not maintain all this
&lt;/h2&gt;

&lt;p&gt;This is exactly the layer &lt;a href="https://jobspipe.dev" rel="noopener noreferrer"&gt;JobsPipe&lt;/a&gt; sells. We crawl the public feeds of 30+ ATSs and job boards (all of the above plus iCIMS, Taleo, SuccessFactors, Teamtailor, ZipRecruiter, and others), normalize everything to one schema with resolved countries and parsed salary ranges, dedupe across sources, and serve it as a single REST endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://api.jobspipe.dev/v1/jobs/search"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer jp_live_YOUR_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "job_title_or": ["data engineer"],
    "job_country_code_or": ["US"],
    "remote": true,
    "posted_at_max_age_days": 7,
    "limit": 20
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every result comes back in the same shape regardless of whether it started life in a Workday tenant or a Lever board: &lt;code&gt;job_title&lt;/code&gt;, &lt;code&gt;company&lt;/code&gt;, &lt;code&gt;location&lt;/code&gt;, &lt;code&gt;country_code&lt;/code&gt;, &lt;code&gt;remote&lt;/code&gt;, &lt;code&gt;seniority&lt;/code&gt;, &lt;code&gt;date_posted&lt;/code&gt;, &lt;code&gt;final_url&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There is a free tier (100 requests/month, no code required beyond the curl above), &lt;a href="https://jobspipe.dev/docs" rel="noopener noreferrer"&gt;API docs here&lt;/a&gt;, an &lt;a href="https://github.com/jobspipe/jobspipe-cli" rel="noopener noreferrer"&gt;open-source CLI and agent skill on GitHub&lt;/a&gt;, and an MCP server at &lt;code&gt;jobspipe.dev/mcp&lt;/code&gt; if you want your coding agent to query it directly.&lt;/p&gt;

&lt;p&gt;And if you would rather build the crawler yourself: the table above is a real starting point, and honestly, it is a fun problem for about the first six ATSs. It is the 25th one that gets you.&lt;/p&gt;

</description>
      <category>api</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>data</category>
    </item>
    <item>
      <title>Give your AI agent a real inbox</title>
      <dc:creator>agenticemail.dev</dc:creator>
      <pubDate>Mon, 06 Jul 2026 06:44:59 +0000</pubDate>
      <link>https://dev.to/agenticemail/give-your-ai-agent-a-real-inbox-407g</link>
      <guid>https://dev.to/agenticemail/give-your-ai-agent-a-real-inbox-407g</guid>
      <description>&lt;p&gt;Your agent can reason, call tools, and hold a conversation. Then you ask it to handle email, and everything gets awkward. Email was built for a human sitting in front of a mail client, not for software. To make an agent "do email" today, most teams reach for one of two bad options: screen-scrape a Gmail account a human set up by hand, or bolt SMTP for sending onto IMAP for receiving and hope the OAuth tokens survive the night.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This post first appeared on the &lt;a href="https://agenticemail.dev/blog/give-your-ai-agent-a-real-inbox" rel="noopener noreferrer"&gt;AgenticEmail blog&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The problem: email was built for humans, not agents
&lt;/h2&gt;

&lt;p&gt;The moment you need two-way email - an agent that emails a customer and then reads the reply - you are wiring together three protocols that were never meant to be glued: SMTP to send, IMAP to poll, and OAuth to keep a borrowed human account alive.&lt;/p&gt;

&lt;p&gt;That stack breaks in predictable ways. OAuth tokens expire and a refresh fails at 3am. IMAP is pull-based, so your agent sits in a polling loop diffing message lists instead of reacting to events. Inbound mail arrives as raw MIME - nested multipart bodies, quoted-printable encoding, base64 attachments - so your agent needs a MIME parser before it can read a single sentence. And because everything hangs off one shared mailbox, you cannot give each agent, or each of your users' agents, its own address without provisioning real mailbox seats.&lt;/p&gt;

&lt;p&gt;None of this is the interesting part of your product. It is undifferentiated plumbing standing between your agent and the thing you actually want it to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: an inbox is an API resource
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://agenticemail.dev" rel="noopener noreferrer"&gt;AgenticEmail&lt;/a&gt; treats an inbox the way you already treat a database row. You create one with an API call, it has a stable id, and you send and receive through plain REST. No mail server to run, no IMAP loop, no OAuth dance. Inbound mail arrives already parsed into JSON, threaded into conversations, and pushed to you over a webhook or a WebSocket - or exposed to your model directly through a hosted MCP server.&lt;/p&gt;

&lt;p&gt;Here is the whole loop.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Create an inbox
&lt;/h3&gt;

&lt;p&gt;One POST gives your agent a live, addressable inbox:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://api.agenticemail.dev/v1/inboxes &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$AGENTICEMAIL_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{ "username": "billing-agent" }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response carries an id and a working address like &lt;code&gt;billing-agent@inbox.agenticemail.dev&lt;/code&gt;. On paid plans you attach your own domain and it becomes &lt;code&gt;billing-agent@yourcompany.com&lt;/code&gt;. Because provisioning is a single call, you can mint an inbox per agent, per user, or per task at runtime. Identity stops being a bottleneck.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Send
&lt;/h3&gt;

&lt;p&gt;Sending is one call against the inbox. Pass recipients and a body; threading headers are handled for you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://api.agenticemail.dev/v1/inboxes/ibx_123/messages/send &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$AGENTICEMAIL_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "to": ["customer@example.com"],
    "subject": "Your July invoice",
    "text": "Hi - your invoice for July is attached. Reply here with any questions."
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Receive parsed, threaded JSON
&lt;/h3&gt;

&lt;p&gt;When someone replies, you never touch MIME. List the inbox and every message comes back as a flat object with &lt;code&gt;from&lt;/code&gt;, &lt;code&gt;to&lt;/code&gt;, &lt;code&gt;subject&lt;/code&gt;, &lt;code&gt;text&lt;/code&gt;, &lt;code&gt;html&lt;/code&gt;, and &lt;code&gt;attachments&lt;/code&gt;, already grouped into threads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://api.agenticemail.dev/v1/inboxes/ibx_123/messages &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$AGENTICEMAIL_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;"data"&lt;/span&gt;&lt;span class="p"&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;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"msg_456"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"thread_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"thr_789"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"direction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"inbound"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"from"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"customer@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"subject"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Re: Your July invoice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Got it, thanks. Can you resend with the PO number?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-07-05T12:00:00Z"&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;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"next_page_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&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;The &lt;code&gt;thread_id&lt;/code&gt; ties the reply back to what you sent, so your agent gets the whole conversation as structured context - not a screenshot, not a raw &lt;code&gt;.eml&lt;/code&gt; file it has to decode.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. React over a webhook or an MCP tool call
&lt;/h3&gt;

&lt;p&gt;Polling is optional. Register a webhook once and every inbound message is delivered to your endpoint as a signed JSON payload the instant it lands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://api.agenticemail.dev/v1/webhooks &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$AGENTICEMAIL_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{ "url": "https://your-app.com/hooks/email", "event_types": ["message.received"] }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your agent has no public URL - a local script, a serverless job, a notebook - subscribe to the WebSocket stream at &lt;code&gt;wss://api.agenticemail.dev/v1/events&lt;/code&gt; instead and get the same payloads pushed to an outbound connection. Both paths are covered in the &lt;a href="https://agenticemail.dev/blog/email-webhook" rel="noopener noreferrer"&gt;email webhook guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Or skip the plumbing entirely and let the model drive. The hosted MCP server exposes the whole API as native tools:&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;"mcpServers"&lt;/span&gt;&lt;span class="p"&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;span class="nl"&gt;"agenticemail"&lt;/span&gt;&lt;span class="p"&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;span class="nl"&gt;"url"&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://api.agenticemail.dev/mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&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;span class="nl"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer ${AGENTICEMAIL_API_KEY}"&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;span class="p"&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;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;With that in place, your agent calls &lt;code&gt;create_inbox&lt;/code&gt;, &lt;code&gt;send_message&lt;/code&gt;, &lt;code&gt;get_thread&lt;/code&gt;, and &lt;code&gt;reply_to_message&lt;/code&gt; the same way it calls any other tool. "Create an inbox for onboarding, email &lt;a href="mailto:new@example.com"&gt;new@example.com&lt;/a&gt; a welcome, and reply when they respond" becomes a single turn.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scoped keys keep the blast radius small
&lt;/h2&gt;

&lt;p&gt;An agent should never hold god-mode credentials. AgenticEmail keys are scoped: you can pin a key to a single inbox and a specific set of permissions, so a key that leaks - through a prompt injection, a logged header, a compromised dependency - can at worst read and send from its own inbox. It cannot touch your other inboxes or your account. Give each agent its own inbox and its own scoped key, and a bad day stays contained to one address.&lt;/p&gt;

&lt;h2&gt;
  
  
  Works with any MCP client
&lt;/h2&gt;

&lt;p&gt;Because the integration is REST plus a standard MCP server, it is framework-agnostic. Claude, Cursor, and any other MCP-compatible client pick up the tools from the config above. In agent frameworks like LangChain, use the official SDKs or call the REST endpoints directly. There is nothing email-specific for your stack to support - it is just HTTP and a tool list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give your agent a real inbox
&lt;/h2&gt;

&lt;p&gt;Two-way email should not be the hardest part of shipping an agent. Create an inbox with one call, send and receive as parsed JSON, react over a webhook or an MCP tool call, and scope the key so a leak is a shrug instead of an incident.&lt;/p&gt;

&lt;p&gt;Start free at &lt;a href="https://agenticemail.dev" rel="noopener noreferrer"&gt;agenticemail.dev&lt;/a&gt;, connect the MCP server from the &lt;a href="https://agenticemail.dev/docs/mcp" rel="noopener noreferrer"&gt;MCP docs&lt;/a&gt;, or read the full endpoint reference in the &lt;a href="https://agenticemail.dev/docs" rel="noopener noreferrer"&gt;docs&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
