Use casesWorkflow
AI Content Collaboration Agent
Build an AI agent that manages content review workflows over email. It sends drafts to reviewers, collects feedback, tracks revisions, and coordinates approval across teams — without anyone leaving their inbox.
The problem
Content teams juggle reviews across multiple tools — Google Docs comments, Slack threads, Notion pages, and email chains. A blog post might need sign-off from marketing, legal, and engineering, each using different tools and response timelines. Feedback gets scattered. Revisions pile up with no clear status tracking. Deadlines slip because nobody knows who still needs to review.
Email remains the universal lowest common denominator. External reviewers, freelancers, executives, and subject matter experts all check email. But using email for content review manually means endless forwarding, lost context, and a spreadsheet to track who approved what.
The real bottleneck is coordination — sending the right draft version to the right reviewers, parsing their feedback into actionable items, notifying authors, and tracking progress toward publication. This is administrative overhead that does not require human judgment.
How an AI agent solves this
An AI content collaboration agent operates as an automated editor at an address like editor@contentops.co. It manages the entire review cycle through email — distributing drafts, collecting feedback, parsing revision requests, and tracking approvals.
- Sends review requests to designated reviewers with draft links and deadlines
- Parses free-form feedback into structured, actionable items
- Tracks approval status across multiple reviewers per content piece
- Sends reminder nudges when review deadlines approach
- Notifies authors when feedback arrives, with a summary of changes requested
- Detects "LGTM" or approval signals and marks the review as complete
How it works with Robotomail
Robotomail provides the email infrastructure your agent needs. A real mailbox for sending review requests, receiving feedback, and maintaining threaded conversations across the entire revision lifecycle.
1. Provision an editor mailbox
curl -X POST https://api.robotomail.com/v1/mailboxes \
-H "Authorization: Bearer rm_live_key" \
-H "Content-Type: application/json" \
--data-binary @- <<'JSON'
{
"address": "editor",
"domainId": "dom_contentops",
"displayName": "ContentOps Editor"
}
JSON2. Send review requests with context
Your agent sends each reviewer a personalized email with the draft link, revision summary, and a clear deadline.
curl -X POST https://api.robotomail.com/v1/mailboxes/{mailbox_id}/messages \
-H "Authorization: Bearer rm_live_key" \
-H "Content-Type: application/json" \
--data-binary @- <<'JSON'
{
"to": [
"reviewer@example.com"
],
"subject": "Review request: Q2 Product Launch Blog Post (Draft 2)",
"bodyText": "Hi Maria,\n\nDraft 2 of the Q2 product launch blog post is ready for your review. I've incorporated the technical accuracy feedback from Draft 1.\n\nKey changes:\n- Updated performance benchmarks with latest data\n- Added migration guide section per your suggestion\n- Shortened the intro paragraph\n\nPlease reply with your feedback or approve by replying \"LGTM\".\n\nDraft link: https://docs.contentops.co/drafts/q2-launch-v2\n\nDeadline: March 25, 2026\n\nThanks,\nContentOps Editor"
}
JSON3. Register a webhook for feedback
curl -X POST https://api.robotomail.com/v1/webhooks \
-H "Authorization: Bearer rm_live_key" \
-H "Content-Type: application/json" \
--data-binary @- <<'JSON'
{
"url": "https://your-app.com/api/content-inbox",
"events": [
"message.received"
],
"mailboxId": "mbx_editor_01"
}
JSON4. Receive reviewer feedback
When a reviewer replies, Robotomail delivers the message to your webhook with full thread context.
{
"event": "message.received",
"timestamp": "2026-03-22T16:45:00Z",
"data": {
"message_id": "11111111-1111-4111-8111-111111111111",
"mailbox_id": "22222222-2222-4222-8222-222222222222",
"mailbox_address": "editor@contentops.co",
"from": "reviewer@example.com",
"to": [
"editor@contentops.co"
],
"cc": [],
"subject": "Re: Review request: Q2 Product Launch Blog Post (Draft 2)",
"body_text": "The benchmarks section looks great now. Two notes:\n\n1. The migration guide needs a prerequisites section\n2. Can we add a comparison table vs the old version?\n\nOtherwise close to approval.",
"thread_id": "33333333-3333-4333-8333-333333333333",
"received_at": "2026-03-22T16:45:00Z",
"attachments": []
}
}5. Agent parses feedback and coordinates revisions
async function getReplyMessageId(mailboxId, messageId) {
const response = await fetch(
`https://api.robotomail.com/v1/mailboxes/${encodeURIComponent(mailboxId)}/messages/${encodeURIComponent(messageId)}`,
{ headers: { Authorization: `Bearer ${process.env.ROBOTOMAIL_API_KEY}` } },
);
if (!response.ok) throw new Error(`Fetch message failed: ${response.status}`);
const { message } = await response.json();
if (!message?.messageId) throw new Error("Original email has no Message-ID");
return message.messageId;
}
async function handleReviewFeedback(payload) {
const { from, body_text: text, thread_id: threadId, mailbox_id: mailboxId, message_id: messageId } = payload.data;
// Find the content piece and reviewer
const review = await db.reviews.findByThread(threadId);
const reviewer = await db.reviewers.findByEmail(from);
// Parse feedback with LLM
const feedback = await parseFeedback(text);
// => {
// status: "changes_requested",
// items: [
// { type: "addition", description: "Prerequisites section in migration guide" },
// { type: "addition", description: "Comparison table vs old version" }
// ],
// sentiment: "positive"
// }
// Record feedback items
for (const item of feedback.items) {
await db.feedbackItems.create({
reviewId: review.id,
reviewerId: reviewer.id,
type: item.type,
description: item.description,
status: "pending",
});
}
// Update review status
await db.reviews.update(review.id, {
status: feedback.status === "approved" ? "approved" : "revision_needed",
lastFeedbackAt: new Date(),
});
// Acknowledge feedback and notify the author
const ack = await generateAcknowledgment({
reviewerName: reviewer.name,
feedbackItems: feedback.items,
contentTitle: review.contentTitle,
});
await fetch(`https://api.robotomail.com/v1/mailboxes/${mailboxId}/messages`, {
method: "POST",
headers: {
"Authorization": "Bearer rm_live_key",
"Content-Type": "application/json",
},
body: JSON.stringify({
to: [from],
subject: `Re: ${review.subject}`,
bodyText: ack,
inReplyTo: await getReplyMessageId(mailboxId, messageId),
}),
});
// Notify the content author about the feedback
await fetch(`https://api.robotomail.com/v1/mailboxes/${mailboxId}/messages`, {
method: "POST",
headers: {
"Authorization": "Bearer rm_live_key",
"Content-Type": "application/json",
},
body: JSON.stringify({
to: [review.authorEmail],
subject: `Feedback received: ${review.contentTitle}`,
bodyText: `${reviewer.name} reviewed Draft ${review.draftNumber} and requested changes:\n\n${feedback.items.map(i => `- ${i.description}`).join("\n")}\n\nPlease update the draft and reply when ready for re-review.`,
}),
});
}Key benefits
- Inbox-native workflow. Reviewers do not need to learn a new tool or log into a platform. They receive review requests in email and reply with feedback — the way they already work.
- Structured from unstructured. The agent parses free-form email feedback into discrete, trackable items. No more digging through threads to find what needs changing.
- Multi-reviewer coordination. Track who has reviewed, who is pending, and who approved — across any number of reviewers per content piece.
- Automatic follow-ups.Gentle reminder emails go out as deadlines approach. No more manual "just checking in" messages.
- Version-aware threads.Robotomail maintains conversation threads across draft versions. Reviewers see the full history of a content piece's evolution in their inbox.
Read the documentation to get started, or learn more about the platform in our launch post. Ready to build? Get started — paid plans come with a 30-day money-back guarantee.
Connect the workflow to a real reply
The webhook's data.message_id is a database ID used to retrieve or deduplicate a message. Fetch it with the GET message endpoint, then use the response's message.messageId (the RFC Message-ID) as inReplyTo. Keep data.thread_id for your application's conversation state. Do not use a database ID or thread ID as a reply header. Stop and investigate if the fetched message has no Message-ID.
Examples use illustrative IDs. Replace them with IDs from your account; never construct an email Message-ID from a database ID. Exchange mail only between your agent and your verified email address. Upgrade to email anyone. See plans for higher limits.
Verify the webhook signature over the raw body, persist the event and deduplicate on the database message ID before acknowledging it. Run your application logic from a durable queue. The database, calendar, CRM and model functions shown above belong to your application.
This shell example fetches an incoming email and builds all required send fields. Set ROBOTOMAIL_API_KEY, ROBOTOMAIL_MAILBOX_ID and ROBOTOMAIL_MESSAGE_ID in your environment. Apply your recipient and approval rules before sending.
# Requires jq. Use mailbox_id and message_id from the verified webhook.
original=$(curl --fail --silent --show-error \
"https://api.robotomail.com/v1/mailboxes/$ROBOTOMAIL_MAILBOX_ID/messages/$ROBOTOMAIL_MESSAGE_ID" \
-H "Authorization: Bearer $ROBOTOMAIL_API_KEY") || exit 1
payload=$(printf '%s' "$original" | jq -e --arg body "Thanks, I received your message." '
.message | if (.messageId | type == "string" and length > 0) then
{to: [.fromAddress], subject: ("Re: " + .subject), bodyText: $body, inReplyTo: .messageId}
else error("Original email has no Message-ID") end
') || exit 1
curl --fail --silent --show-error \
"https://api.robotomail.com/v1/mailboxes/$ROBOTOMAIL_MAILBOX_ID/messages" \
-H "Authorization: Bearer $ROBOTOMAIL_API_KEY" \
-H "Content-Type: application/json" \
--data-binary "$payload"
Read receive and reply and webhook verification for the full setup.