GBP Post Writer
[Live] ·
agent__gbp-post-writer· Claude Sonnet 4.6
Writes a single Google Business Profile post — What’s New, Offer, or Event — formatted to GBP’s character constraints with a platform-appropriate call-to-action and a photo suggestion for the content team.
Overview
| Function | Write a Google Business Profile post for a specific post type and month |
| Type | Worker — Content |
| Model | Claude Sonnet 4.6 |
| Queue | agent__gbp-post-writer |
| Concurrency | 6 |
| Timeout | 3 min |
| Est. cost / task | ~$0.15 |
| Plan | Free+ |
Triggers
| Trigger type | When | Who initiates |
|---|---|---|
| Activity Planner dispatch | Monthly GBP post batch — Strategist enqueues one job per GBP post in the monthly pipeline | Activity Planner |
| Human on-demand | User clicks “Write GBP post” in DM Portal content tab or from the Dashboard GBP management screen | Tenant admin / DM reviewer |
| Scheduled / cron | Monthly cron on the 1st of each month if tenant has gbpAutoPost: true enabled in settings | Platform scheduler |
Input
interface GbpPostWriterInput {
tenantId: string;
month: string; // e.g. "2026-04" — used to scope seasonal relevance
postType: 'whats-new' | 'offer' | 'event';
topic?: string; // optional — if omitted, agent selects topic from RAG + seasonal context
// Required when postType === 'offer'
offerDetails?: {
title: string; // e.g. "Spring Cleaning Special"
discount: string; // e.g. "20% off all services"
coupon?: string; // e.g. "SPRING20"
startDate: string; // ISO date
endDate: string; // ISO date
};
// Required when postType === 'event'
eventDetails?: {
title: string;
date: string; // ISO date
time?: string; // e.g. "10:00 AM – 2:00 PM"
url?: string; // event registration or info URL
};
}Output
interface GbpPostWriterOutput {
bodyText: string; // max 1,500 characters — hard enforced
characterCount: number;
callToAction: {
type: 'book' | 'order' | 'buy' | 'learn-more' | 'sign-up' | 'call';
url: string;
};
photoSuggestion: string; // description of ideal photo for this post
postType: 'whats-new' | 'offer' | 'event';
topicUsed: string; // the topic chosen (useful when input topic was omitted)
}Sample output excerpt
bodyText:
Spring is here — and so is your chance to finally tick off those home maintenance jobs you've
been putting off.
Throughout April, Apex Home Services is offering 20% off all gutter cleaning, exterior washing,
and driveway sealing bookings. No job too small. Fully insured, locally owned, and operating
across the greater Melbourne metro area since 2011.
Why April? It's the perfect window before winter — we've seen more callouts for blocked gutters
and water damage in June and July than any other time of year. A clean-out now costs a fraction
of what repairs cost later.
📅 Book before 30 April to lock in your discount. Use code SPRING20 at checkout.
Slots are filling fast — our April calendar is already 60% booked. Don't leave it too late.
---
callToAction:
type: "book"
url: "https://apexhomeservices.com.au/book"
photoSuggestion:
"Before/after photo of a freshly cleaned gutter or driveway. Natural light, realistic setting.
Avoid stock photography — a real job site photo performs significantly better on GBP."How It Works
-
Load post type and tenant context. Post type, month, and any offer/event details are injected alongside the Client Context File. The agent understands the business, its services, and its audience before any content is generated.
-
RAG: select topic if not provided. If no topic is specified in the input, query Client Documents for upcoming promotions, seasonal services, or recent business updates relevant to the target month. Use the first strong match as the post topic. If nothing is found, default to a seasonal or evergreen service highlight.
-
RAG: previous GBP posts. Query Published Content for previous GBP posts to avoid repeating the same angle, hook, or CTA within the last 90 days. If a similar post ran recently, select a fresh angle on the same topic or switch topics entirely.
-
RAG: offer and event accuracy. If
postType === 'offer', query Client Documents for the offer terms to verify nothing contradicts the providedofferDetails. Flag any discrepancy in output. IfpostType === 'event', query Client Documents for event details to supplement any missing fields. -
Draft the post body. Write the body text using GBP content best practices — local relevance, urgency, specific service mentions, genuine voice. Avoid generic phrases (“world-class service”, “industry-leading quality”). Keep the tone conversational and the value proposition concrete. Include the offer/event details exactly as provided — no paraphrasing of prices, dates, or coupon codes.
-
Select and validate CTA. Choose the most appropriate CTA type for the post goal. For offers →
bookorbuy. For events →sign-uporlearn-more. For What’s New →learn-moreorcall. Resolve the CTA URL from the tenant’s website — use the booking page, contact page, or event URL from the input. -
Enforce character limit and output. Count body text characters. If > 1,500, trim the least essential sentence — never trim offer terms, dates, or the CTA. Return final character count with output.
System Prompt
You are a local SEO content writer creating a Google Business Profile post for a small or
medium-sized business. Your goal is to write a post that drives real customer actions —
bookings, calls, visits — not just impressions.
CLIENT CONTEXT:
{{CLIENT_CONTEXT}}
TENANT SETTINGS:
{{TENANT_SETTINGS}}
KNOWLEDGE BASE CONTEXT (previous posts, offer details, upcoming events):
{{RAG_CONTEXT}}
POST BRIEF:
{{BRIEF}}
You are writing a {{POST_TYPE}} post for {{MONTH}}.
Rules you must follow:
1. Body text must be 1,500 characters or fewer — this is a hard platform limit. Count carefully.
2. Write in the business's voice — not formal corporate, not startup-casual. Match the tone
described in the client context.
3. Be specific: name the actual service, actual discount amount, actual dates. Vague posts
perform poorly on GBP.
4. Local signals matter — mention the service area or city naturally if it fits. Google uses
GBP posts as local relevance signals.
5. Every post needs genuine urgency or value — why should someone act today? Answer that
question in the first 2 sentences.
6. Do not mention competitor business names under any circumstances.
7. Do not make misleading claims — if the offer has conditions, state them clearly.
8. Do not use prohibited words from tenant settings.
9. Emoji usage: 1–2 relevant emoji are acceptable for What's New and Offer posts if the brand
voice is conversational. Zero emoji for formal/professional brands.
10. End with a clear action signal — the body text should lead naturally into the CTA button.
For OFFER posts: include the discount, coupon code (if provided), and both start and end dates.
For EVENT posts: include the event title, date, time, and registration URL if provided.
For WHAT'S NEW posts: focus on a specific service update, seasonal highlight, or business news.
Output as JSON matching the GbpPostWriterOutput schema.Skills Injected
| Skill file | Purpose |
|---|---|
client-context-file.md | Always injected — company, services, location, brand voice, audience |
gbp-content-guide.md | GBP post best practices, character limits, CTA type selection, what performs well on the platform |
gbp-content-guide.md — content
# Google Business Profile Post Guide
## What GBP Posts Are For
GBP posts appear on a business's Knowledge Panel in Google Search and Maps. They are read by
customers who have already found the business — the audience is warm. The goal is to convert
interest into action: a booking, a call, a visit.
## Post Types and When to Use Each
### What's New
Use for: service announcements, seasonal tips, business updates, recent achievements, community
involvement. Avoid: generic "we're open" posts that add no value.
Character limit: 1,500. Recommended: 300–600 characters for best readability.
### Offer
Use for: time-limited discounts, seasonal promotions, new customer deals.
Required fields in Google: offer title, start date, end date. Coupon code is optional but
improves tracking.
Always state the discount clearly. "Up to X% off" requires a qualifying minimum — if there is
no minimum, say "X% off" directly.
### Event
Use for: workshops, open days, community events, webinars, in-store events.
Required fields: event title, start date/time, end date/time.
Include the registration or info URL wherever possible.
## Writing for GBP: What Works
**Open with the value, not the brand name.**
Weak: "Apex Home Services is pleased to offer..."
Strong: "20% off all gutter cleaning this April — book before slots fill up."
**Be local.** Mention the suburb, city, or service area once. Google treats this as a local
relevance signal and customers trust businesses that know their area.
**Use real specifics.** "Over 200 5-star reviews" beats "highly rated". "Available Monday to
Saturday, 7 AM to 5 PM" beats "flexible scheduling".
**Create genuine urgency.** Date-limited offers, capped availability, and seasonal timing are
all legitimate urgency signals. Never fabricate scarcity.
## CTA Type Selection Guide
| Post goal | Best CTA type |
|---|---|
| Book an appointment | `book` |
| Purchase a product | `buy` or `order` |
| Event registration | `sign-up` |
| Learn about a service | `learn-more` |
| Call for a quote | `call` |
## Photo Guidance
GBP posts with photos receive 3–5× more views than text-only posts.
- Real job site photos outperform stock images significantly
- For offers: show the service being performed or the product
- For events: show the venue or a previous event
- For What's New: show the team, the location, or the updated serviceRAG Usage
| Dataset | Query example | When used |
|---|---|---|
| Client Documents | "upcoming promotions events seasonal services [month]" | Step 2 — topic selection when no topic is provided; offer and event detail verification |
| Published Content | "GBP posts published last 90 days" | Step 3 — duplicate angle check; ensures each month’s post feels fresh |
| Website Content | "booking page contact page services [topic]" | Step 6 — to resolve the correct CTA URL for the post type |
| Competitor Research | Not used for GBP posts | — |
Tools Required
| Tool | Method | Purpose | Required? |
|---|---|---|---|
rag_search | search | Query client documents for offer details and published content for duplicate check | Yes |
HITL Gates
The completed GBP post is submitted for human review before it is published or scheduled on Google Business Profile.
- Review type:
content_review - Risk level:
low - Reviewer sees: Post body text with character count displayed, selected CTA type and URL, and photo suggestion. Post type and month are shown in the review header.
- Approval triggers: Post is staged for publishing via the Google Business Profile API integration. If
gbpAutoPostis disabled, approval stages the post for manual publishing. - Edit capability: Reviewer can edit the body text directly. Character count updates live. CTA type and URL can be changed via dropdowns.
- Rejection triggers: Post is re-generated with reviewer feedback. If offer details were incorrect, corrected details should be passed in the re-run input.
Guardrails
| Rule | Enforcement |
|---|---|
| Body text ≤ 1,500 characters | Hard character count check post-generation; trim if exceeded — never trim dates, prices, or coupon codes |
| No competitor business names mentioned | String match against known competitor names from Client Context; any match → regenerate |
| No misleading offer terms | If offerDetails contains a discount percentage with no stated minimum, the post must not add unstated conditions |
| Offer dates must appear verbatim in body text | If offerDetails.startDate and endDate are provided, both must appear in the output body |
| Coupon code must appear verbatim if provided | String match check on offerDetails.coupon against body text |
| No prohibited words from tenant settings | Filter prohibitedWords[] against body text before returning output |
| CTA URL must be a valid URL | Regex validation; fail with error if URL is malformed |
Tenant Settings Used
| Setting | How it’s used |
|---|---|
brandVoice | Controls tone — conversational vs. professional, emoji usage, sentence style |
companyName | Used when referencing the business by name in the post body |
industry | Informs seasonal relevance and service terminology |
targetAudience | Shapes what value proposition is emphasised — homeowners vs. businesses, local vs. regional |
prohibitedWords[] | Filtered from the generated body text |
preferredKeywords[] | Agent attempts to naturally include preferred keywords in the body text |
Cost Profile
| Avg input tokens | ~3,000 (brief + RAG results + skills) |
| Avg output tokens | ~500 (post body + JSON output) |
| Est. cost / task | ~$0.15 |
Error Handling
| Error | Response |
|---|---|
postType === 'offer' but offerDetails is missing | Fail job with validation error: “offerDetails required when postType is ‘offer‘“ |
postType === 'event' but eventDetails is missing | Fail job with validation error: “eventDetails required when postType is ‘event‘“ |
| RAG returns no topic candidates and no topic was provided | Use seasonal default for the given month — log “No topic found in client documents — using seasonal default” |
| RAG returns no Published Content results | Proceed without duplicate check; log “No previous GBP posts found — all topics eligible” |
| Body text exceeds 1,500 chars after 2 trim attempts | Return trimmed output with warning: “Character limit required content trimming — verify offer terms are complete” |
| CTA URL cannot be resolved from RAG or input | Return post with callToAction.url: "" and a note: “CTA URL could not be resolved — please add manually before publishing” |
| Google Business Profile API returns error on publish | Stage post as pending_manual_publish; notify reviewer via HITL record |