---
name: gentic-startup
description: "Give your AI agent the ability to research markets, create and track business ideas, save in-progress research notes, validate them, generate names, check domain availability, research competitors, design brand identities, generate and deploy branded landing pages with version history on custom domains, capture leads, generate ad creatives, launch Meta and Google Ads campaigns, and recall cross-conversation memory — through the Model Context Protocol. Built for bootstrappers, not VC-backed startups."
license: MIT
metadata:
  author: gentic
  version: "1.0.0"
---

# Gentic Startup

Create an idea, research the market, save free-form research notes as you ideate, validate it, generate names, check domains, research competitors, build a full brand identity, generate and deploy a branded landing page (with full version history) on your own domain, capture leads, launch Meta and Google Ads campaigns, and recall what was decided across past Slack/Telegram conversations — all through your AI agent. Every tool call is scoped to an idea, so you can explore multiple concepts in parallel and pick up exactly where you left off. Built for solo founders, side-project builders, home-based businesses, and vibe-coders. Not for VC-backed startups.

## When to apply

- User is exploring a new business idea and wants a structured gut-check before committing time.
- User wants market signal — Reddit sentiment, Google Trends interest, or web coverage — before building.
- User wants to save free-form research notes against an idea — supplier deep-dives, founder stories, lift-able quotes, tagline drafts, packaging hooks — so in-progress findings outlive the chat.
- User needs to name a business, product, or side project and wants candidates with domain suggestions.
- User wants to check domain availability and pricing before buying.
- User wants a competitive landscape overview for a bootstrapped business.
- User needs a brand identity foundation (positioning, voice, colors, fonts, persona).
- User wants to extract a canonical brand guidelines document (colors, typography, voice, imagery, aesthetic) from an existing public website, or look up a previously extracted record for the idea's brand.
- User wants the agent to generate landing-page HTML (rather than hand-writing it) and ship it as a live page.
- User wants to ship a branded landing page with built-in email lead capture to a live URL.
- User describes a change to a live landing page in prose — e.g. 'add a testimonial section after the hero', 'remove the pricing block' — rather than spelling out exact strings to replace.
- User wants to see the version history of a landing page, inspect a prior HTML revision, or roll back to a previous version.
- User wants the idea's landing pages to live on a specific brand subdomain on gentic.run (e.g. `pistash.gentic.run`), or wants to pivot the brand name and reattach the idea to a different subdomain before deploying.
- User wants to connect a custom domain they own so a landing page serves from their own URL.
- User wants to generate on-brand ad images before launching paid campaigns.
- User wants to launch, monitor, or iterate Meta ad campaigns (Facebook/Instagram).
- User wants to launch, monitor, or analyze Google Ads search campaigns — keyword research, campaign/ad-group/RSA creation, budget management, performance reporting, search-terms mining, GAQL queries, or AI-narrated period-over-period analysis.
- User wants to recall what was said or decided in past Slack/Telegram conversations about an idea, decision, or topic.
- User wants to retrieve or revisit results from earlier startup tool calls.
- User wants to track multiple business ideas in parallel and see which ones are exploring, validated, active, or paused.
- User wants to mark an idea as the one they're actively building (or shelve one to revisit later).

## Tools

| Tool | Description | Cost |
|------|-------------|------|
| `google_ads_add_keywords` | Bulk-add keywords to an ad group. Each keyword has a text and a match type (BROAD, PHRASE, or EXACT). 1–200 keywords per call. | 25¢ / call |
| `google_ads_add_negative_keywords` | Bulk-add negative keywords at either the campaign or ad-group level. Set level="campaign" with campaignResourceName to exclude across the whole campaign (most common), or level="adGroup" with adGroupResourceName for narrower exclusion. Each keyword has a text and a match type (BROAD, PHRASE, or EXACT). 1–200 keywords per call. | 25¢ / call |
| `google_ads_analyze_campaign` | AI-narrated campaign analysis: pulls performance for the requested period and the prior equivalent period, then returns a ranked narrative comparing the two with winners, losers, and concrete recommendations. | 50¢ / call |
| `google_ads_connection_status` | Check whether this organization has connected a Google Ads account. Returns the connection status plus stored customer IDs. Does not call Google. | Free |
| `google_ads_create_ad_group` | Create a SEARCH_STANDARD ad group inside a campaign with a default CPC bid. Defaults to PAUSED. | 25¢ / call |
| `google_ads_create_campaign_budget` | Create a Google Ads campaign budget. amountMicros is the daily budget in micros (1 USD = 1,000,000). ⚠️ Double-check the digit count before submitting — values ≥ 100,000,000 ($100/day) are typo-prone (an extra zero turns $10/day into $100/day). Confirm the intended dollar amount with the user when amountMicros exceeds 100,000,000. | 25¢ / call |
| `google_ads_create_responsive_search_ad` | Create a responsive search ad inside an ad group. Requires 3–15 headlines (≤30 chars each) and 2–4 descriptions (≤90 chars each). Defaults to PAUSED. ⚠️ path1/path2 render in the ad's DISPLAYED URL (e.g. example.com/path1/path2) — they're cosmetic breadcrumbs that should describe the landing page's content ("hydration", "sale") and do NOT need to match a real route on finalUrls. Don't fabricate paths that imply a section that doesn't exist; omit them entirely to show only the bare domain. | 25¢ / call |
| `google_ads_create_search_campaign` | Create a SEARCH-channel Google Ads campaign referencing a budget. Defaults to PAUSED — to create an ENABLED campaign that spends immediately, you MUST pass status="ENABLED" AND confirmEnableSpend=true. Bidding is either manual CPC or target CPA. Defaults `containsEuPoliticalAdvertising` to DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING (required by EU TTPA regulation on every new campaign); pass CONTAINS_EU_POLITICAL_ADVERTISING only if the campaign actually runs EU political ads. STRONGLY recommended to pass `geoTargetLocations` (e.g. ["US"]) and `language` (e.g. "en") — if omitted, the campaign serves globally in all languages, which usually wastes budget. These are written as separate CampaignCriterion records after the campaign is created. | 25¢ / call |
| `google_ads_generate_keyword_ideas` | Discover new keyword ideas relevant to a business using Google's Keyword Planner data (KeywordPlanIdeaService). Provide seed keywords, a landing-page URL, or both. Returns per-keyword average monthly searches, competition level, and top-of-page bid range. | 25¢ / call |
| `google_ads_get_ad_group_performance` | Structured ad-group performance metrics for a date range. Optionally filter to a single campaign. | 15¢ / call |
| `google_ads_get_campaign_performance` | Structured campaign performance: impressions, clicks, cost, conversions, CTR, CPA, ROAS for a date range, grouped by campaign and sorted by spend. | 15¢ / call |
| `google_ads_get_keyword_performance` | Structured keyword-level performance including quality score, for a date range. | 15¢ / call |
| `google_ads_get_search_terms_report` | Search-terms report — the actual user queries that triggered ads. Useful for identifying negative-keyword candidates. Filter by campaign, ad group, and/or minimum impressions. | 15¢ / call |
| `google_ads_list_accessible_customers` | List all Google Ads customer accounts the connected credentials can access, enriched with account name, currency, time zone, manager flag, and status (ENABLED / CANCELED / SUSPENDED / CLOSED). Only ENABLED non-manager accounts can serve campaigns — agents should filter to status=='ENABLED' && manager==false when picking a target customer, and use google_ads_select_account to set it. | Free |
| `google_ads_run_gaql_query` | Execute an arbitrary Google Ads Query Language (GAQL) query via GoogleAdsService.search. Paginated; capped at GOOGLE_ADS_MAX_ROWS_PER_QUERY (default 1000) per call. | 15¢ / call |
| `google_ads_select_account` | Update which Google Ads account this org operates against. Set `selectedCustomerId` to change the default customer used by other tools when no customerId is passed. Set `loginCustomerId` to the manager (MCC) ID that contains that customer — required when the account is accessed via an MCC. Pass `loginCustomerId: null` to clear it (use only for direct-access non-managed accounts). Run `google_ads_list_accessible_customers` first to see valid IDs and identify which one is a manager. After saving, runs a smoke GAQL query against the new combination and returns `verified: true` on success or `verified: false` plus `verificationError` when the credentials don't actually work — check this before making real API calls. | Free |
| `google_ads_update_ad_group_bid` | Update an ad group's default CPC bid (cpcBidMicros). ⚠️ cpcBidMicros is in micros (1 USD = 1,000,000; e.g. $3.50 = 3500000) — double-check the digit count before submitting. Confirm the intended dollar amount with the user when cpcBidMicros exceeds 20,000,000 ($20/click). | 25¢ / call |
| `google_ads_update_ad_group_status` | Change an ad group's status to ENABLED, PAUSED, or REMOVED. status="ENABLED" requires confirmEnableSpend=true (explicit opt-in to start serving ads). status="REMOVED" requires confirmRemove=true (irreversible — Google Ads cannot restore a removed ad group; prefer PAUSED if you only want to stop serving). | 25¢ / call |
| `google_ads_update_ad_status` | Change an ad's status to ENABLED, PAUSED, or REMOVED. Operates on the AdGroupAd resource (resource name format: customers/{cid}/adGroupAds/{adGroupId}~{adId}), which is what `google_ads_create_responsive_search_ad` returns. status="ENABLED" requires confirmEnableSpend=true. status="REMOVED" requires confirmRemove=true (irreversible — Google Ads cannot restore a removed ad; prefer PAUSED if you only want to stop serving). | 25¢ / call |
| `google_ads_update_campaign_budget` | Update the daily amount (in micros) of a campaign budget. ⚠️ amountMicros is in micros (1 USD = 1,000,000) — double-check the digit count before submitting. Confirm the intended dollar amount with the user when amountMicros exceeds 100,000,000 ($100/day). | 25¢ / call |
| `google_ads_update_campaign_status` | Change a campaign's status to ENABLED, PAUSED, or REMOVED. status="ENABLED" requires confirmEnableSpend=true (explicit opt-in to start spending). status="REMOVED" requires confirmRemove=true (irreversible — Google Ads cannot restore a removed campaign; prefer PAUSED if you only want to stop spending). | 25¢ / call |
| `landing_page_claim_subdomain` | Claim an additional subdomain under gentic.run for this organization. Landing pages deployed to the claimed subdomain will serve at {subdomain}.gentic.run. Subdomains are globally unique across all organizations — the call fails with a structured "taken" error if another org already owns it. An org's original slug is auto-claimed as its default subdomain; use this tool when the org runs multiple brands and wants each to have its own subdomain (e.g. a holdco with brands "rhodeandfrond" and "pistash"). | Free |
| `landing_page_connect_domain` | Connect a customer-owned domain (e.g. acme.com) to serve this org's landing pages. Provisions a Let's Encrypt cert via Fly and returns the CNAME record the customer must add. After the CNAME propagates (usually a few minutes), call landing_page_verify_domain to finalize. | 25¢ / call |
| `landing_page_delete` | Remove a landing page from public hosting. Lead data is preserved and still queryable. | Free |
| `landing_page_deploy` | Deploy a landing page to a live URL at {subdomain}.gentic.run/{page-slug}. Either pass a draft_id from a generate_landing_page job (preferred — 25¢, server promotes the draft to live) OR pass complete html directly (legacy — $7.50). The page is publicly accessible immediately. Subdomain precedence: (1) explicit `subdomain` input when provided, (2) the idea's auto-claimed subdomain when `idea_slug` is provided and the idea has one (see startup_create_idea), (3) the org's default subdomain. Call `claim_subdomain` first for brand hosts you haven't reserved yet. Slug collisions are scoped per subdomain — the same path slug can exist on two different subdomains without conflict. If the slug is already in use on the chosen subdomain, behavior depends on on_conflict — by default the call fails with a structured error payload that includes suggested_slug and existing_page_id so the caller can retry with the suggested slug or switch to replace_landing_page with the existing_page_id. | 25¢–$7.50 |
| `landing_page_disconnect_domain` | Disconnect a custom domain. Removes the Fly-issued cert, deletes the DB mapping, and stops serving landing pages on this domain. | Free |
| `landing_page_edit` | Edit a landing page from a freeform brief, e.g. 'add a testimonial section after the hero' or 'remove the pricing block'. Targets either a deployed page (page_id) or an undeployed generation draft (draft_id) — pass exactly one. The server fetches the HTML, asks Gemini to compute minimal-diff string-replacement edits, and applies them. $1.00 — cheaper than replace_landing_page ($7.50, full rewrite via Opus) and more capable than patch_landing_page (FREE, mechanical substitution where the caller already knows the strings). For trivial swaps where you can spell out the exact old/new strings yourself, prefer patch_landing_page. When editing a draft (draft_id), the result is saved as a NEW draft with a fresh draft_id returned in the response — chain these calls to iterate before deploying. | 100¢ / call |
| `landing_page_export_leads` | Export collected leads as a downloadable CSV file. Returns a signed URL that expires in 1 hour. | Free |
| `landing_page_generate` | Generate a landing page HTML document. Async — returns a job_id and draft_id immediately and runs ~1–5 minutes on a Modal worker. Defaults to Gemini 3.1 Pro Preview for fast turnaround; pass model='claude' to route through Claude Opus 4.7 for the strongest cloning quality on brand-heavy briefs at the same price. The result lands as an immutable draft in S3; promote to a live URL with deploy_landing_page(draft_id=...) once the job reports complete via startup_list_asset_jobs (filter type='landing_page'). $7.50 for generation, charged only when generation succeeds. When reference_url is set, the server auto-fetches the rendered HTML via ScrapingBee so the model has source to clone — that adds a separate $0.10 surcharge (same price as fetch_rendered_page). Pass base_html instead if you've already fetched the source page yourself. | 750¢ / call |
| `landing_page_get_html` | Fetch the current HTML of either a deployed landing page (by page_id) or an undeployed generation draft (by draft_id). Pass exactly one. Pair with landing_page_patch to make small targeted edits — the returned content_hash can be passed back as expected_content_hash for optimistic concurrency. Use the draft_id form to QA a generated page before paying to deploy it. | Free |
| `landing_page_get_leads` | Query collected leads (email submissions) for a specific landing page. Returns results in reverse chronological order with pagination. | Free |
| `landing_page_get_version` | Fetch the HTML for a specific historical version of a landing page. Use list_landing_page_versions to discover version_ids. Pair with restore_landing_page_version to roll back. | Free |
| `landing_page_list` | List landing pages for the organization with metadata, lead counts, and the subdomain each page is hosted on. Optional `subdomain` filter restricts results to a single brand host. | Free |
| `landing_page_list_domains` | List all custom domains connected to this organization along with their verification status and the gentic.run subdomain each one serves from. | Free |
| `landing_page_list_subdomains` | List all subdomains claimed by this organization under gentic.run. The "is_default" entry is the org's original slug (auto-claimed) and is used as the default for landing-page deploys when no explicit subdomain is given. | Free |
| `landing_page_list_versions` | List the immutable HTML version history for a deployed landing page. Each entry includes a version_id, created_at timestamp, content hash, byte size, and source ('deploy', 'replace', 'patch', or 'restore'). Pair with get_landing_page_version to inspect historical content or restore_landing_page_version to revert. | Free |
| `landing_page_patch` | Apply small, targeted string-replacement edits to a landing page HTML without regenerating it. Targets either a deployed page (page_id) or an undeployed generation draft (draft_id) — pass exactly one. Call landing_page_get_html first to fetch current content. Each edit's old_string must appear exactly once in the HTML (or set replace_all: true) — include enough surrounding context to make it unique. Pass new_string: '' to delete the matched substring. Use this for copy fixes, image src swaps, color tweaks where you can spell out the exact strings yourself. For prose-described changes ('add a testimonial section after the hero'), prefer edit_landing_page. For full rewrites, use landing_page_replace. When patching a draft (draft_id), the result is saved as a NEW draft with a fresh draft_id returned in the response — chain these calls to iterate on a generation before deploying. | Free |
| `landing_page_release_subdomain` | Release a subdomain claim owned by this organization. The org's default subdomain (its original slug) cannot be released. Refuses if any active landing page is still pinned to the subdomain — delete or migrate those pages first. | Free |
| `landing_page_replace` | Replace an existing landing page's HTML with a full new document and/or update its metadata. Either pass draft_id from a generate_landing_page job (preferred — 25¢) OR pass complete html ($7.50). Use this for layout changes, major restructures, or metadata-only updates (title, description). For small edits (copy fixes, image swaps, color tweaks), prefer landing_page_patch — it is much cheaper and faster because it does not require regenerating the whole page. | 25¢–$7.50 |
| `landing_page_restore_version` | Roll a landing page back to a previous version's HTML. Discover version_ids via list_landing_page_versions. The restore writes a new immutable version (source='restore') pointing at the historical content, then promotes it to the live URL — the prior version remains in history. | 25¢ / call |
| `landing_page_verify_domain` | Check whether a pending custom domain's Fly-issued Let's Encrypt cert has been provisioned. Flips the mapping to 'verified' and starts serving landing pages on the custom domain once ready. | Free |
| `meta_create_adset` | Create a new Meta ad set within a campaign. Define targeting, budget, optimization, and scheduling. IMPORTANT: All IDs (campaign_id, ad_account_id, bid_amount, daily_budget) MUST be quoted strings, NEVER bare numbers. | Free |
| `meta_create_campaign` | Create a new Meta advertising campaign. Defaults to PAUSED status and OUTCOME_SALES objective. IMPORTANT: All IDs (ad_account_id, daily_budget) MUST be quoted strings, NEVER bare numbers. | Free |
| `meta_create_image_ad` | Create a new Meta static image ad. Provide image_hash (from meta_upload_image) or image_url. All Advantage+ enhancements are disabled. IMPORTANT: All IDs (adset_id, page_id) MUST be quoted strings, NEVER bare numbers. | 100¢ / call |
| `meta_fetch_insights` | Get Meta ad performance insights for campaigns, ad sets, or ads. Supports date filtering, custom fields, and breakdowns. IMPORTANT: All IDs MUST be quoted strings (e.g. ids: "120239005302490769"), NEVER bare numbers — JavaScript rounds large integers. | 10¢ / call |
| `meta_get_ids` | Get Meta Ad IDs hierarchically. Start with level="adaccounts" to discover accounts, then drill down: campaigns → adsets → ads → adcreatives. IMPORTANT: All IDs MUST be passed as quoted strings (e.g. "120239005302490769"), NEVER as bare numbers — JavaScript rounds large integers and the call will fail silently with a wrong ID. | Free |
| `meta_get_pages` | Get list of Facebook Pages associated with the authenticated user. Returns page IDs needed for creating ads. | Free |
| `meta_upload_image` | Upload an image to a Meta ad account for use in static image ads. Provide a public URL — we download the bytes and upload them to Meta as multipart form data. | Free |
| `research_get_results` | Retrieve stored research results across sources (Reddit, web). Pass a run_id to get full results from a specific search, or omit it to list recent research runs with metadata. | Free |
| `research_google_trends` | Get Google Trends data for keywords — interest over time, related queries, and interest by region. Use this to identify trending topics, compare keyword popularity, and spot emerging market opportunities. Returns trend data and stores results for later retrieval. | 10¢ / call |
| `research_search_reddit` | Search Reddit for consumer discussions, problems, and sentiment around a product category or topic. Returns structured posts with title, body, score, subreddit, and URL. Results are stored for later retrieval. Billed per result. | 5¢ / result (min 5¢) |
| `research_search_web` | Search the web (Google) via Serper.dev for current information — news, competitors, reviews, sentiment, press coverage. Returns ranked organic results with title, URL, snippet, and date, plus an answer box / knowledge graph summary when available. Results are stored for later retrieval. Billed at a flat rate per call. | 5¢ / call |
| `startup_check_domain_availability` | Check domain name availability and get alternative suggestions. Two modes: (1) pass specific domainNames to check exact availability, (2) pass a keyword to search for available domains across TLDs. You can use both together. Returns purchasability, pricing, and TLD info for each result. | Free |
| `startup_create_idea` | Create a new business idea to explore. All startup tools (validation, naming, brand identity, etc.) are scoped to an idea. Create an idea first, then run tools against it. Ideas start in 'exploring' status. Best-effort auto-claims a matching subdomain under gentic.run so landing pages deployed under this idea land on the right brand host (e.g. idea 'Tez' → tez.gentic.run). On cross-org collision, tries base-2..base-5 before giving up. Skipped when the slugified name is reserved, equals the org's default subdomain, the org is at its claim cap, or the name can't be slugified. The response surfaces `subdomain`, `subdomain_auto_claimed`, and a `subdomain_auto_claim_reason` when auto-claim didn't complete. Remediation depends on the reason: `reserved` / `invalid_base` / `matches_default` / `all_suffixes_taken` / `cap_reached` — suggest calling `claim_subdomain` with a different label. `attach_failed` — the claim was made and auto-released; no action needed, though the user can retry with a different name or run `list_subdomains` to confirm nothing is lingering. | Free |
| `startup_export_report` | Export a saved startup result as a formatted markdown document. Renders naming research, business validation, competitive analysis, brand identity, or domain reports into polished markdown files and returns a permanent download link. Provide either a specific resultId or a toolName to export the most recent result of that type. | Free |
| `startup_extract_brand_guidelines` | Extract comprehensive, production-grade brand guidelines from a brand's public website. Crawls key pages, captures screenshots, and combines HTML/CSS parsing with vision-model analysis to produce a canonical brand document with colors, typography, voice, imagery, aesthetic direction, heritage, and generative guardrails. Every field includes source, confidence, and evidence. Pass webhook_url for async execution. | 300¢ / call |
| `startup_generate_ad_asset` | Generate ad images asynchronously. Supports batch generation — use the 'count' parameter to generate multiple images in one call (e.g. count=5 for 5 images). Do NOT call this tool multiple times to generate multiple images; instead, call it once with the desired count. Optionally provide inspiration_image_urls with competitor/reference ads to emulate their style. Returns job_id(s) immediately. The user will receive an email notification when the job completes. Use startup_list_asset_jobs to check status and results. | 100¢ / result (min 100¢) |
| `startup_generate_brand_identity` | Generate a complete brand identity foundation for a business. Covers positioning (statement, elevator pitch, taglines), brand personality (archetype, voice, values), visual direction (color palette with hex codes, typography with free font suggestions, logo direction), target audience persona, and brand story (mission, vision, origin narrative). Designed for bootstrapped founders who need to build a brand without a design agency. Pass a webhook_url to run async. | 15¢ / call |
| `startup_generate_names` | Generate creative startup/business name candidates with professional naming frameworks. Produces 20 names across 5 categories (descriptive, experiential, evocative, invented, identity), scores each on memory/meaning/range, suggests domain strategies, and picks a top 3. Pair with check_domain_availability to verify domain options for your favorites. Note: always verify trademark availability and cross-language meanings independently — AI-generated linguistic checks are directional, not definitive. Pass a webhook_url to run async — the tool returns immediately with a job_id and POSTs results to the webhook when done. | 15¢ / call |
| `startup_get_brand_guidelines` | Look up canonical brand guidelines for your organization. Pass `domain` or `url` to fetch a specific record. With no arguments, returns the single brand on file if the org has exactly one; returns a `candidates` list to disambiguate if there are several; returns a friendly not-found message if there are none. All responses use a `{ found: true/false, ... }` envelope. Free. | Free |
| `startup_get_results` | Retrieve saved results from previous startup tool calls (naming, validation, domain checks, competitor research, saved research notes). Returns results newest-first. Filter by tool name to see only naming results, only validations, only research notes, etc. Research-note rows include a top-level `url` pointing at the full markdown in S3 — surface it to the user as a clickable link so they can read the whole note (don't just summarize the body). | Free |
| `startup_list_asset_jobs` | List recent asset generation jobs (images, videos, and landing pages). Returns job IDs, statuses, prompts, output URLs, and (for landing-page jobs) draft_id + content_hash you can pass to deploy_landing_page. Pass job_id to fetch a single job (useful for polling a specific generation). Use this to check on generation progress. | Free |
| `startup_list_ideas` | List all business ideas for this organization with their status and tool result counts. | Free |
| `startup_recall_memory` | Search long-term cross-conversation memory captured from Slack and Telegram threads. Answers questions like 'what did we decide last week?' or 'what has the team said about X?'. Vector-searches the org-scoped `org_memory` table and returns rows ranked by semantic relevance. Each row includes text, role, surface, thread_key, agent_name, created_at, and similarity_score. Use surface/role/since/until/thread_key filters to narrow scope. Free. | Free |
| `startup_research_competitors` | Research the competitive landscape for a business idea. Identifies 6-10 competitors (direct, indirect, adjacent), analyzes their positioning, pricing, strengths and weaknesses, maps market maturity and consolidation, and surfaces differentiation opportunities a bootstrapper can act on. Pass a webhook_url to run async — the tool returns immediately with a job_id and POSTs results to the webhook when done. | 15¢ / call |
| `startup_save_brand_style_guide` | Save or update a brand style guide for a specific idea. Sets brand colors, fonts, and tone that are automatically applied to future landing pages. You can set individual fields — any field you omit keeps its current value (if a guide already exists). | Free |
| `startup_save_research_note` | Save in-progress research findings against an idea — supplier deep-dives, founder stories, lift-able quotes, tagline drafts, packaging hooks, anything the agent has synthesized while ideating that should outlive the chat. Writes the markdown body to S3 (canonical, editable artifact) AND inserts a row in startup_results so the note shows up in get_startup_results (filter by tool_name='startup_save_research_note') and counts toward list_ideas.result_count. Append-only — to revise, save a new note. **Auto-ingests into the wiki** in the background (~60s) — the response carries `wiki_view_url` so you can tell the user where to read the rendered wiki once ingest completes. Save itself is free; auto-ingest bills $0.25 separately as `wiki_ingest_auto`. For org-level industry knowledge that should NOT be tied to a specific idea, use `save_research_note` on the /research endpoint instead. | Free |
| `startup_update_idea_status` | Update the status of a business idea. Use this to mark ideas as validated, active (this is the one!), or paused (didn't work out, revisit later). | Free |
| `startup_update_idea_subdomain` | Reassign the subdomain attached to an idea — typically used when the user iterates on a brand name before deploying any landing pages (e.g. 'Blueberry Bar' → 'Bluey'). Claims `new_subdomain` if not already owned, attaches it to the idea row, and releases the previously-attached subdomain when no active landing page still references it. Idempotent: passing the current subdomain returns success with no changes. Setting `new_subdomain` to your org's default subdomain is allowed and behaves the same as having no subdomain attached (deploys default to the org host either way). Use `landing_page_claim_subdomain` directly if you just want to reserve a label without attaching it to an idea. Does NOT rename the idea_slug or any landing-page path slugs already deployed — those continue to serve at their existing URLs until renamed separately. If the underlying database UPDATE fails, the call throws a structured `attach_failed` error, the new claim is rolled back automatically (when this call created it), and the idea row is left unchanged — caller can retry. | Free |
| `startup_validate_business_idea` | Evaluate a business idea for bootstrapped entrepreneurs. Scores the idea on 5 criteria (market demand, competition, feasibility, revenue clarity, customer reach) and returns a verdict (Strong/Promising/Needs Work/Rethink), strengths, risks, and concrete next steps. Designed for solo founders, side-project builders, small e-commerce brands, and home-based businesses — not VC-backed startups. Pass a webhook_url to run async — the tool returns immediately with a job_id and POSTs results to the webhook when done. | 15¢ / call |

## Workflow

### 1. Start by creating an idea with `startup_create_idea` (free)

Before most other tools, call `startup_create_idea` with a unique kebab-case `idea_slug` (e.g. `pet-subscription-box`, `ai-tutor-app`) and a human-readable `name`. Optionally pass a `description`. Most subsequent tools require the same `idea_slug`, so all research, validations, names, competitor findings, brand identities, landing pages, and ads accumulate against that idea. The call also best-effort auto-claims a matching brand subdomain on gentic.run (e.g. name `Tez` → `tez.gentic.run`) and attaches it to the idea so future landing-page deploys land on the right brand host. Inspect the response: `subdomain` (attached label), `subdomain_auto_claimed` (boolean), and `subdomain_auto_claim_reason` when the auto-claim was skipped (`reserved` / `invalid_base` / `matches_default` / `all_suffixes_taken` / `cap_reached` / `attach_failed`). On a skip with a remediable reason, suggest calling `landing_page_claim_subdomain` with a different label. Use `startup_list_ideas` (free) any time to see what's already there before creating a new one — pass `include_paused: true` to include shelved ideas.

### 2. Run market research with `research_search_web`, `research_search_reddit`, and `research_google_trends`

Three complementary signals, all org-scoped and persisted. `research_search_web` (5¢/call) uses Serper.dev for current news, reviews, competitors, and press. `research_search_reddit` (5¢/result, min 5¢) pulls consumer discussions with filterable subreddits, sort order, and time window — great for raw pain-point language. `research_google_trends` (10¢/call) returns interest-over-time and regional breakdowns for 1-5 keywords. Replay any prior run with `research_get_results` (free) — filter by `run_id`, `source`, or `limit`.

### 3. Save in-progress research notes with `startup_save_research_note` (free)

Use this to persist anything the agent synthesizes between formal tool calls — supplier deep-dives, founder stories, lift-able quotes, tagline drafts, packaging hooks — so findings outlive the chat. Pass the `idea_slug`, a short `title` (≤200 chars; used as the S3 filename slug and the row title — pick something specific like 'Rhody Wild supplier deep-dive'), a `markdown` body (≤200,000 chars; stored verbatim, use headings/lists/blockquotes freely), and an optional `sources` array of up to 50 cited URLs (each ≤2000 chars). The note's markdown body is written to S3 as a canonical, editable artifact and a row is inserted into `startup_results`, so it surfaces in `startup_get_results` (filter `tool_name='startup_save_research_note'`) and counts toward `startup_list_ideas.result_count`. Append-only — to revise, save a new note rather than overwriting.

**Critical disambiguation.** When the user says "save this research" / "save that research note" / "save these findings" after running `research_search_web`, `research_google_trends`, or `research_search_reddit`, they mean **synthesize the findings into a markdown note and call `startup_save_research_note`** — they do NOT mean "the run_ids are already stored." Auto-stored run_ids are raw search rows in `research_results`; a research note is the agent's synthesis (themes, strategy, recommendations, keyword tiers, action items) that the user will actually read again. Consider calling `startup_save_research_note` whenever the synthesis represents a meaningful next-step the user will revisit — and never substitute "your run_ids are saved" for an actual note.

### 4. Validate the idea with `startup_validate_business_idea` ($0.15/call)

Pass the `idea_slug` plus the full `businessIdea` (what it is, who it's for, how it makes money) and optional `founderContext` (skills, budget, time availability, location, audience). Scores 5 criteria and returns a verdict (Strong / Promising / Needs Work / Rethink), strengths, risks, and concrete next steps. Frame the verdict honestly — it is tuned for bootstrappers, not VC growth stories. Pass `webhook_url` to run async (30-90s otherwise).

### 5. Generate names with `startup_generate_names` ($0.15/call)

Pass the `idea_slug` plus `businessDescription`, `targetEmotion` (the single dominant feeling — relief, momentum, wonder, belonging), `brandTone` (playful↔serious, innovative↔traditional), `nonStarters` (words or associations to avoid), and `industry`. Returns 20 names across 5 categories with memory/meaning/range scores and a top 3. Pair with `startup_check_domain_availability` to verify favorites. Pass `webhook_url` to run async.

### 6. Check domains with `startup_check_domain_availability` (free)

Scoped to an `idea_slug`. Two modes, usable together: pass `domainNames` (up to 50) for exact availability on specific domains, and/or a `keyword` for TLD-wide search. Use `tldFilter` to restrict keyword results (e.g. `['com', 'io', 'co']`). Returns purchasability, pricing from Name.com's live API, and TLD info. Free — run this aggressively.

### 7. Research competitors with `startup_research_competitors` ($0.15/call)

Pass the `idea_slug` plus `businessDescription`, optional `industry`, `knownCompetitors` (names/sites you already know), and `focusAreas` (pricing, customer acquisition, support weaknesses). Identifies 6-10 competitors across direct, indirect, and adjacent categories, maps positioning and pricing, and surfaces differentiation opportunities. Pass `webhook_url` to run async.

### 8. Build brand identity with `startup_generate_brand_identity` ($0.15/call)

Pass the `idea_slug` plus `businessDescription` and optional `businessName`, `industry`, `brandPreferences` (reference brands, desired tone), and `targetAudience`. Returns positioning (statement, elevator pitch, taglines), personality (archetype, voice, values), visual direction (color palette with hex, typography with free font suggestions, logo direction), a target persona, and the brand story. Pass `webhook_url` to run async.

### 9. Extract brand guidelines from a live site with `startup_extract_brand_guidelines` ($3/call, async)

When the idea already has a brand online — or the user wants to model an existing reference — call `startup_get_brand_guidelines` (free) first with a `domain` or `url` to check for a cached record. If nothing is stored, run `startup_extract_brand_guidelines` (**$3/call, async — pass `webhook_url`**) to crawl the site, combine HTML/CSS parsing with vision-model analysis, and write a canonical document (colors, typography, voice, imagery, aesthetic direction, heritage, generative guardrails — each field with source + confidence + evidence). Feed the returned colors/fonts/voice into `startup_save_brand_style_guide` to make every landing page deployed for the idea match the extracted brand automatically.

### 10. Persist brand choices with `startup_save_brand_style_guide` (free)

Pass the `idea_slug` plus any subset of brand fields (`primary_color`, `secondary_color`, `accent_color`, the `*_color_name` variants, `heading_font`, `body_font`, `voice`, `personality_traits`, `visual_mood`, `logo_direction`). Omitted fields keep their current value if a guide already exists. The saved guide is applied automatically to any landing page deployed with that `idea_slug`.

### 11. Manage brand subdomains: `landing_page_claim_subdomain` / `landing_page_list_subdomains` / `landing_page_release_subdomain` (free)

Each org's original slug is auto-claimed as its default subdomain (`{org-slug}.gentic.run`). To run multiple brands off one account, call `landing_page_claim_subdomain` with a `subdomain` label (3-63 chars, lowercase alphanumeric + hyphens; reserved labels like `www`, `api`, `app`, `admin`, `mail`, `assets`, `static`, `cdn`, `mcp`, `auth`, `docs`, `blog`, `dashboard`, `console` are rejected) to reserve additional brand hosts. Subdomains are globally unique across orgs — collisions return a structured `taken` error. Use `landing_page_list_subdomains` to see everything the org owns; the entry with `is_default: true` is the auto-claimed org slug and cannot be released. `landing_page_release_subdomain` frees a label but refuses if any active landing page is still pinned to it. These are aliases of the `claim_subdomain` / `list_subdomains` / `release_subdomain` tools on `/landing-pages`.

### 12. Pivot the brand mid-stream with `startup_update_idea_subdomain` (free)

When the user iterates on the brand name *before* deploying landing pages (e.g. 'Blueberry Bar' → 'Bluey'), call `startup_update_idea_subdomain` with the `idea_slug` and a `new_subdomain` label. It claims `new_subdomain` if not already owned, attaches it to the idea row, and releases the previously-attached subdomain when no active landing page still references it. Idempotent — passing the current subdomain is a no-op. Setting `new_subdomain` to the org's default behaves like having no subdomain attached (deploys still default to the org host). Does **not** rename `idea_slug` or any landing-page path slugs already deployed — those continue to serve at their existing URLs. On a `taken` error (another org owns the label), suggest a variant. On a structured `attach_failed` error, the new claim is rolled back automatically and the idea row is unchanged — safe to retry.

### 13. Generate landing-page HTML with `landing_page_generate` ($7.50/call, async)

Pass a freeform `brief` describing the page (purpose, audience, sections, voice, calls-to-action) and Claude Opus 4.7 produces a complete, self-contained HTML document on a Modal worker. Returns immediately with a `job_id` and a `draft_id`. The result posts back to the conversation automatically when the job completes (~1–5 min), and `list_asset_jobs(type='landing_page')` shows status. For programmatic integrations, pass `webhook_url` to receive a POST callback to your own service. Optionally pass `idea_slug` (drives colors/fonts/tone from the saved brand style guide), `reference_url` (preferred over legacy style guides — uses canonical brand guidelines and reproduces the original's structure when cloning; adds a **10¢ surcharge** because the server auto-fetches the rendered source via ScrapingBee, same price as `fetch_rendered_page`. Pass `base_html` instead if you've already fetched the source yourself), `base_html` (iterate on existing markup), and `style_hints` (free-form overrides). $7.50 is charged **only on success**.

### 14. Deploy a landing page with `landing_page_deploy` (25¢ from a draft, $7.50 from raw HTML)

Pass a `slug` (3–64 chars, lowercase alphanumeric + hyphens) and a `title`. Then either pass `draft_id` from `landing_page_generate` (**preferred — 25¢**, server promotes the S3 draft) or complete self-contained `html` (legacy raw-html path — $7.50). They're mutually exclusive. Optionally pass `subdomain` to host under a specific brand host (must be claimed by your org). Subdomain precedence when omitted: explicit input → idea's auto-claimed subdomain (when `idea_slug` is provided) → org default. Optionally pass `idea_slug` to auto-inject brand CSS, `description`, or `reference_url` for provenance. Set `ignore_brand_style_guide: true` to skip injection. The page goes live instantly at `{subdomain}.gentic.run/{slug}` and the response includes `subdomain` + `url` reflecting the actual host — never reconstruct URLs from `org_slug + slug`. Slug uniqueness is scoped per subdomain. Use `landing_page_list` (free) to see everything currently deployed; rows include `subdomain` per page and accept an optional `subdomain` filter.

### 15. Iterate mechanically with `landing_page_patch` (50¢/call)

Use this when **you can spell out the exact old/new strings** — copy fixes, image src swaps, color tweaks. Call `landing_page_get_html` (free) to read the current HTML plus its `content_hash`, then `landing_page_patch` with `page_id` and an `edits` array of `{ old_string, new_string, replace_all? }`. Pass the hash as `expected_content_hash` for optimistic concurrency. For prose-described changes ("add a testimonial section after the hero"), prefer `landing_page_edit` ($1) — the server uses Gemini to compute the patch ops for you. Reach for `landing_page_replace` (25¢ from a draft, $7.50 from raw HTML) only for layout changes or full rewrites.

### 16. Describe edits in prose with `landing_page_edit` ($1/call)

Use this when the user describes a change in natural language — "add a 3-card testimonial section after the hero", "remove the pricing block", "swap the CTA from 'Sign up' to 'Get early access' everywhere". Pass `page_id` and a freeform `brief`. The server fetches the live HTML, asks Gemini to compute minimal-diff string-replacement edits, and applies them. Cheaper than `landing_page_replace` (full Opus rewrite) and more capable than `landing_page_patch` (mechanical only). Optionally pass `expected_content_hash` from `landing_page_get_html` for concurrency safety. **Tier picker:** mechanical and you know the strings → `landing_page_patch` (50¢). Prose-described targeted change → `landing_page_edit` ($1). Layout overhaul or full rewrite → `landing_page_replace`.

### 17. Browse and roll back history: `landing_page_list_versions` / `landing_page_get_version` / `landing_page_restore_version`

Every `deploy`, `replace`, `patch`, and `restore` writes an immutable HTML version. Call `landing_page_list_versions` (free) with a `page_id` to enumerate versions with `version_id`, `created_at`, content hash, byte size, and `source` (`deploy` / `replace` / `patch` / `restore`). Use `landing_page_get_version` (free) to fetch any historical HTML for inspection or diffing. Roll back with `landing_page_restore_version` (25¢) — the restore writes a new `source: "restore"` version pointing at the historical content and promotes it live, so the prior current version stays in history (a restore is itself reversible).

### 18. Capture and export leads (all free)

Every deployed page includes a lead capture endpoint at `POST /api/leads/:orgSlug/:pageId` (accepts `email` required, plus optional `name`, `source`, `metadata`). Query submissions inline with `landing_page_get_leads` (paginated, max 500/page). Export a signed CSV with `landing_page_export_leads` — tell the user the link expires in **1 hour**. `landing_page_delete` soft-deletes the page but **preserves all lead data**.

### 19. Connect a custom domain: `landing_page_connect_domain` → `landing_page_verify_domain` → serve

Once the landing page is live, call `landing_page_connect_domain` (25¢) with a fully-qualified `domain` (e.g. `acme.com`) — this registers the mapping and kicks off a Fly Let's Encrypt cert. The domain starts in `pending` state. Poll `landing_page_verify_domain` (free) with the same domain to check cert provisioning; once ready, the mapping flips to `verified` and landing pages start serving from the custom host. `landing_page_list_domains` (free) shows all domains with current verification status. `landing_page_disconnect_domain` (free) removes the cert and stops serving. Mention the 25¢ cost on connect before running.

### 20. Generate ad creative with `startup_generate_ad_asset` ($1/image)

Pass a `prompt` (image description) plus optional `inspiration_image_urls` (up to 5 reference ads for style) and `brand_image_urls` (up to 5 brand assets — logos, product shots). Control output with `aspect_ratio`, `image_size` (1K/2K/4K), and `count` (1-10 images). Pick the backing model with `image_model`: `nano_banana` (default) is faster and cheaper; `gpt_image_2` is stronger on text rendering and brand consistency. When you use `gpt_image_2`, `quality` (`low`/`medium`/`high`/`auto`) tunes fidelity vs. speed; **it defaults to `medium` to keep OpenAI costs in check — pass `high` explicitly for hero or final creative.** `quality` is ignored on the `nano_banana` path, and unknown `image_model` values fall back to `nano_banana` silently so existing calls are unaffected. Billed **per image** at $1.00 each (min $1.00) — confirm `count` before firing. Pass `webhook_url` to run async.

### 21. Browse Meta ad accounts with `meta_get_ids` and `meta_get_pages` (free)

Navigate top-down with `meta_get_ids`: `level: "adaccounts"` → `"campaigns"` (with `parent_id`) → `"adsets"` → `"ads"` → `"adcreatives"`. Pass `date_preset` or `time_range` to include spend inline. `meta_get_pages` lists Facebook Pages connected to the authenticated user — you'll need a page_id for ad creation. **Critical**: all Meta IDs must be passed as **quoted strings** — bare numbers get rounded and silently mis-target.

### 22. Pull insights with `meta_fetch_insights` (10¢/call)

Query ad performance with `level` (`campaign`/`adset`/`ad`), `ids` as a comma-separated quoted string, and either `date_preset` OR `time_range` (never both). Add `breakdowns` like `"age,gender"` when the user wants slices. Default `fields` cover impressions, spend, clicks, CTR, actions, conversions — only override when the user specifies. Mention the 10¢ cost before running many calls in a row.

### 23. Launch Meta campaigns: campaign → adset → image ad

`meta_create_campaign` (free) is created in `PAUSED` status with `OUTCOME_SALES` by default — never un-pause automatically. Budgets are in **cents as quoted strings** (`"500000"` = $5,000). `meta_create_adset` (free) holds the targeting (geo, age, interests) and inherits CBO budget from the campaign (or sets its own). Upload imagery first with `meta_upload_image` (free, needs a publicly accessible URL) to get an `image_hash`. Then `meta_create_image_ad` ($1/call) combines the hash with copy, CTA, and landing page URL — Advantage+ enhancements auto-disabled. Confirm all inputs before firing the $1 call.

### 24. Connect Google Ads with `google_ads_connection_status`, `google_ads_list_accessible_customers`, `google_ads_select_account`

Start with `google_ads_connection_status` (free) to see whether the org has a Google Ads OAuth connection and which customer is currently selected. `google_ads_list_accessible_customers` (free) returns every account the connected credentials can reach, enriched with name, currency, time zone, manager flag, and status — agents should filter to `status == 'ENABLED' && manager == false` for accounts that can actually serve ads. Switch the active account with `google_ads_select_account` (free) by passing `selectedCustomerId` and, when the account sits behind an MCC, `loginCustomerId` (the manager ID). After saving, the tool runs a smoke GAQL query and returns `verified: true` on success or `verified: false` plus `verificationError` — check this before making real billable calls.

### 25. Mine keyword ideas with `google_ads_generate_keyword_ideas` (25¢/call)

Pass `keywords` (seed terms), `pageUrl` (landing page to mine), or both — plus optional `language` (default `en`), `geoTargetLocations` (default `['US']`, accepts 2-letter country codes or `geoTargetConstants/{id}`), `network`, and `pageSize`. Returns Google Keyword Planner data: per-keyword average monthly searches, competition level, and top-of-page bid range. Use the output to seed campaigns and ad groups before any spend.

### 26. Build a Google Ads search campaign top-down (25¢ per creation step)

Bottom-up sequence: `google_ads_create_campaign_budget` (sets the daily `amountMicros` — `1 USD = 1,000,000` micros; ⚠️ confirm the digit count out loud when ≥ $100/day) → `google_ads_create_search_campaign` (references the budget; pass `geoTargetLocations` and `language` or it serves globally, defaults `containsEuPoliticalAdvertising` to `DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING`, defaults to `PAUSED`) → `google_ads_create_ad_group` (PAUSED, sets a default CPC bid via `cpcBidMicros`) → `google_ads_create_responsive_search_ad` (3–15 headlines ≤30 chars, 2–4 descriptions ≤90 chars; `path1`/`path2` are **cosmetic display-URL breadcrumbs only** — they don't need to match real routes on `finalUrls`) → `google_ads_add_keywords` (1–200 per call, with BROAD/PHRASE/EXACT match types). Use `google_ads_add_negative_keywords` to exclude queries at campaign or ad-group level. **All resources start PAUSED** — flipping to ENABLED via `google_ads_update_campaign_status` / `update_ad_group_status` / `update_ad_status` requires `confirmEnableSpend: true`. `REMOVED` is irreversible and requires `confirmRemove: true` — prefer PAUSED if the user just wants to stop spend.

### 27. Pull Google Ads performance and an AI narrative

Structured reports (15¢/call each): `google_ads_get_campaign_performance` (impressions, clicks, cost, conversions, CTR, CPA, ROAS grouped by campaign), `google_ads_get_ad_group_performance`, `google_ads_get_keyword_performance` (includes quality score), `google_ads_get_search_terms_report` (actual user queries that triggered ads — gold for negative-keyword candidates). For anything custom, run `google_ads_run_gaql_query` (15¢/call, capped at 1000 rows) with a raw GAQL string. When the user wants a written takeaway, `google_ads_analyze_campaign` (50¢) pulls the requested period and the prior equivalent period, then returns an AI-narrated comparison with winners, losers, and concrete recommendations. Update budgets and bids with `google_ads_update_campaign_budget` and `google_ads_update_ad_group_bid` (25¢ each) — both take `amountMicros` / `cpcBidMicros` in micros, so confirm the digit count when amounts get large.

### 28. Recall cross-conversation memory with `startup_recall_memory` (free)

Vector-search the org's `org_memory` table — populated automatically from Slack and Telegram threads the agent has been part of. Pass a natural-language `query` (e.g. 'what did we decide about pricing last week?', 'what has the team said about onboarding?') and an optional `limit` (default 10, max 50). Filter scope with `surface` (slack/telegram), `role` (`user` for human turns, `assistant` for agent replies), `since`/`until` (ISO dates — useful when the user says 'last week' or 'in March'), and `thread_key` to pin to a single thread. Returns rows ranked by semantic relevance with `text`, `role`, `surface`, `thread_key`, `agent_name`, `created_at`, and `similarity_score`. Use this when the current conversation lacks context the user is referring to.

### 29. Track progression with `startup_update_idea_status` and `startup_list_ideas` (free)

Use `startup_update_idea_status` to move an idea through the lifecycle: `exploring` → `validated` → `active` (this is the one you're building!) → `paused` (didn't work out, revisit later). Pass the `idea_slug` plus the target `status`. `startup_list_ideas` returns every idea in your org with current status and tool-result counts; pass `include_paused: true` to surface shelved ones.

### 30. Retrieve and export results (all free)

`startup_get_results` returns saved output for a given `idea_slug`, newest-first — pass `toolName` to filter (e.g. only `startup_generate_names` runs) and `limit` (1-50). `research_get_results` does the same for web/Reddit runs. `startup_export_report` renders any saved result as a markdown document with a permanent download link — pass a specific `resultId`, or `idea_slug` + `toolName` to export the most recent of that type.

### 31. Async via webhook_url (optional but recommended)

`startup_validate_business_idea`, `startup_generate_names`, `startup_research_competitors`, `startup_generate_brand_identity`, and `startup_generate_ad_asset` each accept `webhook_url`. Without it, the tool blocks 30-90 seconds waiting on Gemini (or longer for batch image generation). With it, the tool returns immediately with `{ job_id, status: "processing" }` and POSTs the result to your webhook when done. Webhook payload: `{ job_id, tool_name, status: "completed" | "failed", result_data?, error? }`.

## Notes

- Most tools require an `idea_slug`. Call `startup_create_idea` first, then reuse the same slug across research, validation, naming, domains, competitors, brand identity, landing pages, and ads so results accumulate against one idea.
- Ideas flow through four statuses: `exploring` → `validated` → `active` → `paused`. Use `startup_update_idea_status` to move between them and `startup_list_ideas` to see the whole portfolio (pass `include_paused: true` to include shelved ones).
- `startup_save_research_note` is free and append-only — use it whenever the agent has synthesized something worth keeping (supplier intel, founder quotes, tagline drafts, packaging hooks). Markdown body is stored on S3 as a canonical artifact and a row is written to `startup_results`, so notes show up in `startup_get_results` and count toward `startup_list_ideas.result_count`. To revise a note, save a new one rather than expecting an in-place edit.
- Custom domains go through two steps: `landing_page_connect_domain` (25¢) registers the mapping and starts Fly cert provisioning in `pending` state; `landing_page_verify_domain` (free) polls until the cert is issued and flips the mapping to `verified`.
- Built for bootstrappers — solo founders, side-project builders, small e-commerce brands, home-based businesses, vibe-coders. Not tuned for VC-backed growth stories.
- Trademark and cross-language linguistic checks from AI are **directional, not definitive**. Always verify independently before committing to a name.
- Seven tools (`startup_validate_business_idea`, `startup_generate_names`, `startup_research_competitors`, `startup_generate_brand_identity`, `startup_generate_ad_asset`, `startup_extract_brand_guidelines`, `landing_page_generate`) accept `webhook_url` for async execution — pass one to avoid blocking on Gemini / image generation / brand crawling / Opus landing-page generation.
- `startup_extract_brand_guidelines` costs **$3/call** (async) and writes a canonical brand document keyed by domain. `startup_get_brand_guidelines` is always free for subsequent reads — extract once per brand, reuse everywhere.
- `startup_check_domain_availability` is backed by Name.com's live pricing API — prices reflect actual registrar costs at the moment of the call.
- Landing pages deploy to `{subdomain}.gentic.run/{page-slug}`. Every org gets its original slug auto-claimed as a default subdomain; additional brand hosts can be reserved with `landing_page_claim_subdomain`. Deploy precedence when no `subdomain` is passed: explicit input → idea's auto-claimed subdomain → org default. The `url` and `subdomain` fields on every deploy/list/replace/get response reflect the actual host — never reconstruct URLs from `org_slug + slug`.
- **Generate-then-promote is the cheapest path for landing pages.** `landing_page_generate` ($7.50, async via Claude Opus 4.7, charged only on success) writes an immutable draft and returns a `draft_id`. Promote with `landing_page_deploy(draft_id=...)` or `landing_page_replace(draft_id=...)` for **25¢** instead of $7.50. The legacy raw-html path still works at $7.50 — only use it when you already have hand-written markup. On the raw-html path, HTML must be **complete and self-contained** (inline all CSS/JS); generated drafts already are. Setting `reference_url` adds a **10¢** surcharge — the server auto-fetches the rendered source via ScrapingBee so the model has HTML to clone (same price as `fetch_rendered_page`); pass `base_html` instead if you've already fetched the source yourself. Pass `webhook_url` to receive a POST when the job completes.
- **Three tiers for editing a live page, by intent and cost.** `landing_page_patch` (50¢) — mechanical string replacement when you already know the exact old/new strings (typos, image swaps, color tweaks). `landing_page_edit` ($1) — describe the change in prose and Gemini computes the minimal diff ("add a testimonial section after the hero", "remove the pricing block"). `landing_page_replace` (25¢ from a draft, $7.50 raw) — layout changes and full rewrites only.
- **Every landing-page change is versioned.** `deploy`, `replace`, `patch`, and `restore` each write an immutable version row. List with `landing_page_list_versions` (free), inspect any historical HTML via `landing_page_get_version` (free), roll back with `landing_page_restore_version` (25¢) — restores create a new version rather than overwriting history, so they're themselves reversible.
- `startup_create_idea` best-effort auto-claims a brand subdomain matching the idea name and attaches it to the idea row. If auto-claim is skipped, the response includes `subdomain_auto_claim_reason` (`reserved` / `invalid_base` / `matches_default` / `all_suffixes_taken` / `cap_reached` / `attach_failed`) — handle it by suggesting `landing_page_claim_subdomain` with a different label. Use `startup_update_idea_subdomain` to swap the attached subdomain when the user pivots the brand name before deploying.
- Slug uniqueness on landing pages is **scoped per subdomain** — the same path slug (e.g. `/launch`) can exist on two different brand subdomains without collision.
- Always call `landing_page_get_html` before `landing_page_patch` and pass the returned `content_hash` as `expected_content_hash` to avoid clobbering concurrent edits.
- Lead capture is built in at `POST /api/leads/:orgSlug/:pageId`. Soft-deleted pages preserve lead data. CSV export links expire in **1 hour**.
- All Meta IDs (ad account, campaign, adset, ad, creative) must be passed as **quoted strings** — JavaScript rounds large integers and causes silent mis-targeting. Budgets are in **cents as quoted strings** (`"270000"` = $2,700).
- All Meta campaigns, ad sets, and ads default to `PAUSED` — never un-pause automatically. Advantage+ creative enhancements are auto-disabled on `meta_create_image_ad` for full control.
- `startup_generate_ad_asset` is billed **per image** at $1.00 (min $1.00). Confirm `count` before running — a 10-image batch is $10.
- Google Ads campaigns, ad groups, and ads also default to `PAUSED`. Flipping to `ENABLED` via `google_ads_update_campaign_status` / `update_ad_group_status` / `update_ad_status` requires `confirmEnableSpend: true`. `REMOVED` is **irreversible** in Google Ads (no restore) and requires `confirmRemove: true` — prefer `PAUSED` if the user only wants to stop serving.
- Google Ads budgets and bids use **micros** (1 USD = 1,000,000 micros). `amountMicros` ≥ 100,000,000 ($100/day) and `cpcBidMicros` > 20,000,000 ($20/click) are typo-prone — confirm the dollar amount with the user before submitting. New campaigns must declare `containsEuPoliticalAdvertising` (defaults to `DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING`); only flip when the campaign actually runs EU political ads. Pass `geoTargetLocations` and `language` on `google_ads_create_search_campaign` or it serves globally in all languages.
- RSA `path1`/`path2` are **cosmetic display-URL breadcrumbs only** — they render as `example.com/{path1}/{path2}` in the ad and do not need to match a real route on `finalUrls`. Don't fabricate paths that imply sections that don't exist.
- `startup_recall_memory` is free and reads from the org-scoped `org_memory` table populated automatically by Slack/Telegram capture. Use it when the user references prior decisions or threads the current conversation doesn't contain. Filter with `surface`, `role`, `since`/`until`, or `thread_key` to keep results tight.
- All tools are organization-scoped — users only see their own ideas, research, landing pages, leads, custom domains, ad accounts (Meta + Google), and memory rows.

## Tool details

- `google_ads_add_keywords` — Bulk-add keywords to an ad group. Each keyword has a text and a match type (BROAD, PHRASE, or EXACT). 1–200 keywords per call.
  - `customerId` (string) — Google Ads customer ID (10-digit numeric, or a full resource name like customers/1234567890). If omitted, uses the selected_customer_id stored on the connection.
  - `adGroupResourceName` (string, required)
  - `keywords` (array of object, required)
- `google_ads_add_negative_keywords` — Bulk-add negative keywords at either the campaign or ad-group level. Set level="campaign" with campaignResourceName to exclude across the whole campaign (most common), or level="adGroup" with adGroupResourceName for narrower exclusion. Each keyword has a text and a match type (BROAD, PHRASE, or EXACT). 1–200 keywords per call.
  - `customerId` (string) — Google Ads customer ID (10-digit numeric, or a full resource name like customers/1234567890). If omitted, uses the selected_customer_id stored on the connection.
  - `level` (string, enum: `campaign` | `adGroup`, required)
  - `campaignResourceName` (string) — Required when level="campaign".
  - `adGroupResourceName` (string) — Required when level="adGroup".
  - `keywords` (array of object, required)
- `google_ads_analyze_campaign` — AI-narrated campaign analysis: pulls performance for the requested period and the prior equivalent period, then returns a ranked narrative comparing the two with winners, losers, and concrete recommendations.
  - `customerId` (string) — Google Ads customer ID (10-digit numeric, or a full resource name like customers/1234567890). If omitted, uses the selected_customer_id stored on the connection.
  - `dateRange` (required)
  - `campaignIds` (array of string)
- `google_ads_connection_status` — Check whether this organization has connected a Google Ads account. Returns the connection status plus stored customer IDs. Does not call Google.
- `google_ads_create_ad_group` — Create a SEARCH_STANDARD ad group inside a campaign with a default CPC bid. Defaults to PAUSED.
  - `customerId` (string) — Google Ads customer ID (10-digit numeric, or a full resource name like customers/1234567890). If omitted, uses the selected_customer_id stored on the connection.
  - `campaignResourceName` (string, required)
  - `name` (string, required)
  - `cpcBidMicros` (string, required)
  - `status` (string, enum: `PAUSED` | `ENABLED`)
- `google_ads_create_campaign_budget` — Create a Google Ads campaign budget. amountMicros is the daily budget in micros (1 USD = 1,000,000). ⚠️ Double-check the digit count before submitting — values ≥ 100,000,000 ($100/day) are typo-prone (an extra zero turns $10/day into $100/day). Confirm the intended dollar amount with the user when amountMicros exceeds 100,000,000.
  - `customerId` (string) — Google Ads customer ID (10-digit numeric, or a full resource name like customers/1234567890). If omitted, uses the selected_customer_id stored on the connection.
  - `name` (string, required)
  - `amountMicros` (string, required)
  - `deliveryMethod` (string, enum: `STANDARD` | `ACCELERATED`)
- `google_ads_create_responsive_search_ad` — Create a responsive search ad inside an ad group. Requires 3–15 headlines (≤30 chars each) and 2–4 descriptions (≤90 chars each). Defaults to PAUSED. ⚠️ path1/path2 render in the ad's DISPLAYED URL (e.g. example.com/path1/path2) — they're cosmetic breadcrumbs that should describe the landing page's content ("hydration", "sale") and do NOT need to match a real route on finalUrls. Don't fabricate paths that imply a section that doesn't exist; omit them entirely to show only the bare domain.
  - `customerId` (string) — Google Ads customer ID (10-digit numeric, or a full resource name like customers/1234567890). If omitted, uses the selected_customer_id stored on the connection.
  - `adGroupResourceName` (string, required)
  - `finalUrls` (array of string, required)
  - `headlines` (array of object, required)
  - `descriptions` (array of object, required)
  - `path1` (string) — Cosmetic display-URL segment (≤15 chars). Renders as example.com/{path1} in the ad — does NOT need to exist as a real route on finalUrls. Use to describe content (e.g. "hydration"), not to imply non-existent sections. Omit to show only the bare domain.
  - `path2` (string) — Cosmetic display-URL second segment (≤15 chars). Renders as example.com/{path1}/{path2}. Same rules as path1: cosmetic only, not a real route.
  - `status` (string, enum: `PAUSED` | `ENABLED`)
- `google_ads_create_search_campaign` — Create a SEARCH-channel Google Ads campaign referencing a budget. Defaults to PAUSED — to create an ENABLED campaign that spends immediately, you MUST pass status="ENABLED" AND confirmEnableSpend=true. Bidding is either manual CPC or target CPA. Defaults `containsEuPoliticalAdvertising` to DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING (required by EU TTPA regulation on every new campaign); pass CONTAINS_EU_POLITICAL_ADVERTISING only if the campaign actually runs EU political ads. STRONGLY recommended to pass `geoTargetLocations` (e.g. ["US"]) and `language` (e.g. "en") — if omitted, the campaign serves globally in all languages, which usually wastes budget. These are written as separate CampaignCriterion records after the campaign is created.
  - `customerId` (string)
  - `name` (string, required)
  - `budgetResourceName` (string, required)
  - `biddingStrategy` (required)
  - `networkSettings` (object)
  - `startDate` (string)
  - `endDate` (string)
  - `status` (string, enum: `PAUSED` | `ENABLED`)
  - `confirmEnableSpend` (boolean)
  - `containsEuPoliticalAdvertising` (string, enum: `DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING` | `CONTAINS_EU_POLITICAL_ADVERTISING`)
  - `geoTargetLocations` (array of string)
  - `language` (string)
- `google_ads_generate_keyword_ideas` — Discover new keyword ideas relevant to a business using Google's Keyword Planner data (KeywordPlanIdeaService). Provide seed keywords, a landing-page URL, or both. Returns per-keyword average monthly searches, competition level, and top-of-page bid range.
  - `customerId` (string) — Google Ads customer ID (10-digit numeric, or a full resource name like customers/1234567890). If omitted, uses the selected_customer_id stored on the connection.
  - `keywords` (array of string)
  - `pageUrl` (string)
  - `language` (string) — ISO language code (e.g. "en") or raw languageConstants/{id}. Default "en".
  - `geoTargetLocations` (array of string) — 2-letter country codes (e.g. ["US","CA"]) or raw geoTargetConstants/{id}. Default ["US"].
  - `network` (string, enum: `GOOGLE_SEARCH` | `GOOGLE_SEARCH_AND_PARTNERS`)
  - `includeAdultKeywords` (boolean)
  - `pageSize` (integer)
  - `pageToken` (string)
- `google_ads_get_ad_group_performance` — Structured ad-group performance metrics for a date range. Optionally filter to a single campaign.
  - `customerId` (string) — Google Ads customer ID (10-digit numeric, or a full resource name like customers/1234567890). If omitted, uses the selected_customer_id stored on the connection.
  - `dateRange` (required)
  - `campaignId` (string)
- `google_ads_get_campaign_performance` — Structured campaign performance: impressions, clicks, cost, conversions, CTR, CPA, ROAS for a date range, grouped by campaign and sorted by spend.
  - `customerId` (string) — Google Ads customer ID (10-digit numeric, or a full resource name like customers/1234567890). If omitted, uses the selected_customer_id stored on the connection.
  - `dateRange` (required)
  - `campaignIds` (array of string)
- `google_ads_get_keyword_performance` — Structured keyword-level performance including quality score, for a date range.
  - `customerId` (string) — Google Ads customer ID (10-digit numeric, or a full resource name like customers/1234567890). If omitted, uses the selected_customer_id stored on the connection.
  - `dateRange` (required)
  - `campaignId` (string)
  - `adGroupId` (string)
- `google_ads_get_search_terms_report` — Search-terms report — the actual user queries that triggered ads. Useful for identifying negative-keyword candidates. Filter by campaign, ad group, and/or minimum impressions.
  - `customerId` (string) — Google Ads customer ID (10-digit numeric, or a full resource name like customers/1234567890). If omitted, uses the selected_customer_id stored on the connection.
  - `dateRange` (required)
  - `campaignId` (string)
  - `adGroupId` (string)
  - `minImpressions` (integer)
- `google_ads_list_accessible_customers` — List all Google Ads customer accounts the connected credentials can access, enriched with account name, currency, time zone, manager flag, and status (ENABLED / CANCELED / SUSPENDED / CLOSED). Only ENABLED non-manager accounts can serve campaigns — agents should filter to status=='ENABLED' && manager==false when picking a target customer, and use google_ads_select_account to set it.
- `google_ads_run_gaql_query` — Execute an arbitrary Google Ads Query Language (GAQL) query via GoogleAdsService.search. Paginated; capped at GOOGLE_ADS_MAX_ROWS_PER_QUERY (default 1000) per call.
  - `customerId` (string) — Google Ads customer ID (10-digit numeric, or a full resource name like customers/1234567890). If omitted, uses the selected_customer_id stored on the connection.
  - `query` (string, required) — Raw GAQL query string
  - `pageSize` (integer)
  - `pageToken` (string)
- `google_ads_select_account` — Update which Google Ads account this org operates against. Set `selectedCustomerId` to change the default customer used by other tools when no customerId is passed. Set `loginCustomerId` to the manager (MCC) ID that contains that customer — required when the account is accessed via an MCC. Pass `loginCustomerId: null` to clear it (use only for direct-access non-managed accounts). Run `google_ads_list_accessible_customers` first to see valid IDs and identify which one is a manager. After saving, runs a smoke GAQL query against the new combination and returns `verified: true` on success or `verified: false` plus `verificationError` when the credentials don't actually work — check this before making real API calls.
  - `selectedCustomerId` (string) — 10-digit customer ID (or customers/{id} resource name) to set as the default customer for subsequent tool calls.
  - `loginCustomerId` — 10-digit manager (MCC) ID to send as the login-customer-id header. Pass null to clear. Required when the selected customer is accessed via an MCC.
- `google_ads_update_ad_group_bid` — Update an ad group's default CPC bid (cpcBidMicros). ⚠️ cpcBidMicros is in micros (1 USD = 1,000,000; e.g. $3.50 = 3500000) — double-check the digit count before submitting. Confirm the intended dollar amount with the user when cpcBidMicros exceeds 20,000,000 ($20/click).
  - `customerId` (string) — Google Ads customer ID (10-digit numeric, or a full resource name like customers/1234567890). If omitted, uses the selected_customer_id stored on the connection.
  - `adGroupResourceName` (string, required)
  - `cpcBidMicros` (string, required)
- `google_ads_update_ad_group_status` — Change an ad group's status to ENABLED, PAUSED, or REMOVED. status="ENABLED" requires confirmEnableSpend=true (explicit opt-in to start serving ads). status="REMOVED" requires confirmRemove=true (irreversible — Google Ads cannot restore a removed ad group; prefer PAUSED if you only want to stop serving).
  - `customerId` (string)
  - `adGroupResourceName` (string, required)
  - `status` (string, enum: `ENABLED` | `PAUSED` | `REMOVED`, required)
  - `confirmEnableSpend` (boolean)
  - `confirmRemove` (boolean)
- `google_ads_update_ad_status` — Change an ad's status to ENABLED, PAUSED, or REMOVED. Operates on the AdGroupAd resource (resource name format: customers/{cid}/adGroupAds/{adGroupId}~{adId}), which is what `google_ads_create_responsive_search_ad` returns. status="ENABLED" requires confirmEnableSpend=true. status="REMOVED" requires confirmRemove=true (irreversible — Google Ads cannot restore a removed ad; prefer PAUSED if you only want to stop serving).
  - `customerId` (string)
  - `adGroupAdResourceName` (string, required)
  - `status` (string, enum: `ENABLED` | `PAUSED` | `REMOVED`, required)
  - `confirmEnableSpend` (boolean)
  - `confirmRemove` (boolean)
- `google_ads_update_campaign_budget` — Update the daily amount (in micros) of a campaign budget. ⚠️ amountMicros is in micros (1 USD = 1,000,000) — double-check the digit count before submitting. Confirm the intended dollar amount with the user when amountMicros exceeds 100,000,000 ($100/day).
  - `customerId` (string) — Google Ads customer ID (10-digit numeric, or a full resource name like customers/1234567890). If omitted, uses the selected_customer_id stored on the connection.
  - `budgetResourceName` (string, required)
  - `amountMicros` (string, required)
- `google_ads_update_campaign_status` — Change a campaign's status to ENABLED, PAUSED, or REMOVED. status="ENABLED" requires confirmEnableSpend=true (explicit opt-in to start spending). status="REMOVED" requires confirmRemove=true (irreversible — Google Ads cannot restore a removed campaign; prefer PAUSED if you only want to stop spending).
  - `customerId` (string)
  - `campaignResourceName` (string, required)
  - `status` (string, enum: `ENABLED` | `PAUSED` | `REMOVED`, required)
  - `confirmEnableSpend` (boolean)
  - `confirmRemove` (boolean)
- `landing_page_claim_subdomain` — Claim an additional subdomain under gentic.run for this organization. Landing pages deployed to the claimed subdomain will serve at {subdomain}.gentic.run. Subdomains are globally unique across all organizations — the call fails with a structured "taken" error if another org already owns it. An org's original slug is auto-claimed as its default subdomain; use this tool when the org runs multiple brands and wants each to have its own subdomain (e.g. a holdco with brands "rhodeandfrond" and "pistash").
  - `subdomain` (string, required) — Subdomain label to claim, e.g. 'pistash' for pistash.gentic.run. Lowercase alphanumeric + hyphens, 3-63 chars, cannot start or end with a hyphen. Reserved words (www, api, app, admin, mail, assets, static, cdn, mcp, auth, docs, blog, dashboard, console) are rejected.
- `landing_page_connect_domain` — Connect a customer-owned domain (e.g. acme.com) to serve this org's landing pages. Provisions a Let's Encrypt cert via Fly and returns the CNAME record the customer must add. After the CNAME propagates (usually a few minutes), call landing_page_verify_domain to finalize.
  - `domain` (string, required) — Fully-qualified domain the customer wants to use, e.g. 'acme.com' or 'www.acme.com'
  - `subdomain` (string) — gentic.run subdomain whose pages this custom domain should serve, e.g. 'theivoryshell' to serve pages from theivoryshell.gentic.run. Must be claimed by this org (see claim_subdomain). Defaults to the org's default subdomain when omitted — required when pages live on a non-default subdomain.
- `landing_page_delete` — Remove a landing page from public hosting. Lead data is preserved and still queryable.
  - `page_id` (string, required) — Page ID to delete
- `landing_page_deploy` — Deploy a landing page to a live URL at {subdomain}.gentic.run/{page-slug}. Either pass a draft_id from a generate_landing_page job (preferred — 25¢, server promotes the draft to live) OR pass complete html directly (legacy — $7.50). The page is publicly accessible immediately. Subdomain precedence: (1) explicit `subdomain` input when provided, (2) the idea's auto-claimed subdomain when `idea_slug` is provided and the idea has one (see startup_create_idea), (3) the org's default subdomain. Call `claim_subdomain` first for brand hosts you haven't reserved yet. Slug collisions are scoped per subdomain — the same path slug can exist on two different subdomains without conflict. If the slug is already in use on the chosen subdomain, behavior depends on on_conflict — by default the call fails with a structured error payload that includes suggested_slug and existing_page_id so the caller can retry with the suggested slug or switch to replace_landing_page with the existing_page_id. _(pricing: $0.25 when promoting a generate_landing_page draft (draft_id), $7.50 on the legacy raw-html path.)_
  - `slug` (string, required) — URL path segment (lowercase, alphanumeric + hyphens, 3-64 chars)
  - `title` (string, required) — Page title for display and metadata
  - `draft_id` (string) — Draft ID returned by generate_landing_page — server fetches the HTML from S3 and deploys it. Cheapest path (25¢) and the recommended way to publish a generated page. Mutually exclusive with html.
  - `html` (string) — Complete, self-contained HTML document. Legacy path ($7.50) — only use when you don't have a draft_id. Mutually exclusive with draft_id.
  - `description` (string) — Brief description of the page's purpose
  - `reference_url` (string) — Original reference URL used for generation
  - `brand_domain` (string) — Explicit brand-domain key for canonical brand-guidelines lookup (e.g. 'irestore.com'). Use whenever the user names a brand and `reference_url` is a template / asset URL — without this, the lookup uses `reference_url` as the key and can never match a stored record.
  - `subdomain` (string) — Subdomain label to host this page under, e.g. 'pistash' for pistash.gentic.run. Must be a subdomain claimed by this org (see `claim_subdomain` / `list_subdomains`). Defaults to the org's default subdomain when omitted.
  - `idea_slug` (string) — Idea slug to pull brand style guide from. If provided, injects the idea's brand CSS into the page.
  - `ignore_brand_style_guide` (boolean, default: `false`, required) — Skip injecting the brand style guide CSS into this page (default: false)
  - `on_conflict` (string, enum: `fail` | `suffix`, default: `"fail"`, required) — Behavior when the slug is already taken on the chosen subdomain. 'fail' (default) returns a structured slug_taken error with suggested_slug + existing_page_id so the caller can retry in one call. 'suffix' auto-appends -2, -3, ... until it finds a free slug and deploys under that. Use 'suffix' only when the user didn't name the slug; keep 'fail' when the slug is meaningful.
- `landing_page_disconnect_domain` — Disconnect a custom domain. Removes the Fly-issued cert, deletes the DB mapping, and stops serving landing pages on this domain.
  - `domain` (string, required) — The custom domain to disconnect
- `landing_page_edit` — Edit a landing page from a freeform brief, e.g. 'add a testimonial section after the hero' or 'remove the pricing block'. Targets either a deployed page (page_id) or an undeployed generation draft (draft_id) — pass exactly one. The server fetches the HTML, asks Gemini to compute minimal-diff string-replacement edits, and applies them. $1.00 — cheaper than replace_landing_page ($7.50, full rewrite via Opus) and more capable than patch_landing_page (FREE, mechanical substitution where the caller already knows the strings). For trivial swaps where you can spell out the exact old/new strings yourself, prefer patch_landing_page. When editing a draft (draft_id), the result is saved as a NEW draft with a fresh draft_id returned in the response — chain these calls to iterate before deploying.
  - `page_id` (string) — Page ID of a deployed landing page. Mutually exclusive with draft_id.
  - `draft_id` (string) — Draft ID from generate_landing_page or a prior draft-targeted patch/edit. Must be a UUID. Mutually exclusive with page_id. The edited HTML is written to a NEW draft; the response includes the new draft_id.
  - `brief` (string, required) — Freeform description of the change. Be specific about location and intent — e.g. 'add a 3-card testimonial section after the hero, with names and photo URLs as placeholders'.
  - `expected_content_hash` (string) — Optional sha256 of the HTML the brief was authored against (from get_landing_page_html). If provided and the stored content has changed, the edit fails without writing.
- `landing_page_export_leads` — Export collected leads as a downloadable CSV file. Returns a signed URL that expires in 1 hour.
  - `page_id` (string) — Specific page ID (if omitted, exports leads from all pages)
- `landing_page_generate` — Generate a landing page HTML document. Async — returns a job_id and draft_id immediately and runs ~1–5 minutes on a Modal worker. Defaults to Gemini 3.1 Pro Preview for fast turnaround; pass model='claude' to route through Claude Opus 4.7 for the strongest cloning quality on brand-heavy briefs at the same price. The result lands as an immutable draft in S3; promote to a live URL with deploy_landing_page(draft_id=...) once the job reports complete via startup_list_asset_jobs (filter type='landing_page'). $7.50 for generation, charged only when generation succeeds. When reference_url is set, the server auto-fetches the rendered HTML via ScrapingBee so the model has source to clone — that adds a separate $0.10 surcharge (same price as fetch_rendered_page). Pass base_html instead if you've already fetched the source page yourself.
  - `brief` (string, required) — Freeform description of the page you want — purpose, audience, sections, voice, calls-to-action. The more specific, the better.
  - `idea_slug` (string) — Idea slug whose saved brand style guide should drive colors, fonts, and tone. Falls back to canonical brand guidelines (when reference_url is set) if the idea has none.
  - `reference_url` (string) — Clone target — when set, the server fetches this page and the model reproduces every section in full with brand-substituted copy. Used as a fallback brand-domain key only when `brand_domain` is not supplied AND this URL points at a real brand site (not an asset host like *.s3.amazonaws.com or *.gentic.run). Pass `brand_domain` separately when the clone target is a template / asset URL.
  - `brand_domain` (string) — Explicit brand-domain key for canonical brand-guidelines lookup (e.g. 'irestore.com' or 'https://irestore.com'). Use whenever the user names a brand — even if `reference_url` is set, since `reference_url` is often a template/asset URL that won't match any stored brand record. Takes precedence over deriving the key from `reference_url`. Normalised the same way as records inserted by `extract_brand_guidelines`.
  - `base_html` (string) — Existing HTML to revise. Pass when you want to iterate on a current page rather than generate from scratch.
  - `style_hints` (object) — Free-form overrides on top of the brand guide (tone, density, color overrides, etc.).
  - `webhook_url` (string) — Optional callback URL fired when the draft completes (or fails). Receives a JSON payload with job_id, status, draft_id, draft_url (S3), byte_length, stop_reason, prompt, error. Used by gentic-computer to post the completion notice back to the originating Slack / Telegram thread.
  - `model` (string, enum: `claude` | `gemini`) — LLM backend. Defaults to 'gemini' (Gemini 3.1 Pro Preview) — faster turnaround at the same $7.50 price. Pass 'claude' (Claude Opus 4.7) for the strongest output on cloned designs and brand-heavy briefs. The reference_url surcharge still applies to both.
- `landing_page_get_html` — Fetch the current HTML of either a deployed landing page (by page_id) or an undeployed generation draft (by draft_id). Pass exactly one. Pair with landing_page_patch to make small targeted edits — the returned content_hash can be passed back as expected_content_hash for optimistic concurrency. Use the draft_id form to QA a generated page before paying to deploy it.
  - `page_id` (string) — Page ID of a deployed landing page. Mutually exclusive with draft_id.
  - `draft_id` (string) — Draft ID returned by generate_landing_page (or by a prior draft-targeted patch_landing_page / edit_landing_page call). Must be a UUID. Mutually exclusive with page_id.
- `landing_page_get_leads` — Query collected leads (email submissions) for a specific landing page. Returns results in reverse chronological order with pagination.
  - `page_id` (string, required) — Page ID to query leads for
  - `limit` (number, default: `100`) — Max results (default: 100, max: 500)
  - `offset` (number, default: `0`) — Pagination offset (default: 0)
- `landing_page_get_version` — Fetch the HTML for a specific historical version of a landing page. Use list_landing_page_versions to discover version_ids. Pair with restore_landing_page_version to roll back.
  - `page_id` (string, required) — Page ID
  - `version_id` (string, required) — Version ID returned by list_landing_page_versions (the {ISO8601}-{short_hash} stem)
- `landing_page_list` — List landing pages for the organization with metadata, lead counts, and the subdomain each page is hosted on. Optional `subdomain` filter restricts results to a single brand host.
  - `include_deleted` (boolean, default: `false`) — Include deleted pages (default: false)
  - `subdomain` (string) — Filter to pages under a specific claimed subdomain. Omit to list pages across all the org's subdomains.
- `landing_page_list_domains` — List all custom domains connected to this organization along with their verification status and the gentic.run subdomain each one serves from.
- `landing_page_list_subdomains` — List all subdomains claimed by this organization under gentic.run. The "is_default" entry is the org's original slug (auto-claimed) and is used as the default for landing-page deploys when no explicit subdomain is given.
- `landing_page_list_versions` — List the immutable HTML version history for a deployed landing page. Each entry includes a version_id, created_at timestamp, content hash, byte size, and source ('deploy', 'replace', 'patch', or 'restore'). Pair with get_landing_page_version to inspect historical content or restore_landing_page_version to revert.
  - `page_id` (string, required) — Page ID to list versions for
- `landing_page_patch` — Apply small, targeted string-replacement edits to a landing page HTML without regenerating it. Targets either a deployed page (page_id) or an undeployed generation draft (draft_id) — pass exactly one. Call landing_page_get_html first to fetch current content. Each edit's old_string must appear exactly once in the HTML (or set replace_all: true) — include enough surrounding context to make it unique. Pass new_string: '' to delete the matched substring. Use this for copy fixes, image src swaps, color tweaks where you can spell out the exact strings yourself. For prose-described changes ('add a testimonial section after the hero'), prefer edit_landing_page. For full rewrites, use landing_page_replace. When patching a draft (draft_id), the result is saved as a NEW draft with a fresh draft_id returned in the response — chain these calls to iterate on a generation before deploying.
  - `page_id` (string) — Page ID of a deployed landing page. Mutually exclusive with draft_id.
  - `draft_id` (string) — Draft ID from generate_landing_page or a prior draft-targeted patch/edit. Must be a UUID. Mutually exclusive with page_id. The patched HTML is written to a NEW draft; the response includes the new draft_id.
  - `edits` (array of object, required) — Sequential edits. Each sees the result of prior edits.
  - `expected_content_hash` (string) — Optional sha256 of the HTML the edits were authored against (from landing_page_get_html). If provided and the stored content has changed, the patch fails without writing. Detects stale reads — not a lock; concurrent patches between the hash check and write are still possible.
- `landing_page_release_subdomain` — Release a subdomain claim owned by this organization. The org's default subdomain (its original slug) cannot be released. Refuses if any active landing page is still pinned to the subdomain — delete or migrate those pages first.
  - `subdomain` (string, required) — Subdomain label to release, e.g. 'pistash'.
- `landing_page_replace` — Replace an existing landing page's HTML with a full new document and/or update its metadata. Either pass draft_id from a generate_landing_page job (preferred — 25¢) OR pass complete html ($7.50). Use this for layout changes, major restructures, or metadata-only updates (title, description). For small edits (copy fixes, image swaps, color tweaks), prefer landing_page_patch — it is much cheaper and faster because it does not require regenerating the whole page. _(pricing: $0.25 when promoting a draft (draft_id), $7.50 on the legacy raw-html or metadata-only path.)_
  - `page_id` (string, required) — Page ID to replace
  - `draft_id` (string) — Draft ID returned by generate_landing_page — server fetches the HTML from S3 and replaces the page with it. Cheapest path (25¢). Mutually exclusive with html.
  - `html` (string) — New full HTML content. Legacy path ($7.50). Mutually exclusive with draft_id. If both html and draft_id are omitted, existing content is unchanged (metadata-only update).
  - `title` (string) — New title
  - `description` (string) — New description
  - `idea_slug` (string) — Idea slug to pull brand style guide from. If provided, injects the idea's brand CSS into the page.
  - `ignore_brand_style_guide` (boolean, default: `false`, required) — Skip injecting the brand style guide CSS into this page (default: false)
- `landing_page_restore_version` — Roll a landing page back to a previous version's HTML. Discover version_ids via list_landing_page_versions. The restore writes a new immutable version (source='restore') pointing at the historical content, then promotes it to the live URL — the prior version remains in history.
  - `page_id` (string, required) — Page ID to restore
  - `version_id` (string, required) — Version ID to restore (from list_landing_page_versions)
- `landing_page_verify_domain` — Check whether a pending custom domain's Fly-issued Let's Encrypt cert has been provisioned. Flips the mapping to 'verified' and starts serving landing pages on the custom domain once ready.
  - `domain` (string, required) — The domain previously registered via landing_page_connect_domain
- `meta_create_adset` — Create a new Meta ad set within a campaign. Define targeting, budget, optimization, and scheduling. IMPORTANT: All IDs (campaign_id, ad_account_id, bid_amount, daily_budget) MUST be quoted strings, NEVER bare numbers.
- `meta_create_campaign` — Create a new Meta advertising campaign. Defaults to PAUSED status and OUTCOME_SALES objective. IMPORTANT: All IDs (ad_account_id, daily_budget) MUST be quoted strings, NEVER bare numbers.
- `meta_create_image_ad` — Create a new Meta static image ad. Provide image_hash (from meta_upload_image) or image_url. All Advantage+ enhancements are disabled. IMPORTANT: All IDs (adset_id, page_id) MUST be quoted strings, NEVER bare numbers.
- `meta_fetch_insights` — Get Meta ad performance insights for campaigns, ad sets, or ads. Supports date filtering, custom fields, and breakdowns. IMPORTANT: All IDs MUST be quoted strings (e.g. ids: "120239005302490769"), NEVER bare numbers — JavaScript rounds large integers.
- `meta_get_ids` — Get Meta Ad IDs hierarchically. Start with level="adaccounts" to discover accounts, then drill down: campaigns → adsets → ads → adcreatives. IMPORTANT: All IDs MUST be passed as quoted strings (e.g. "120239005302490769"), NEVER as bare numbers — JavaScript rounds large integers and the call will fail silently with a wrong ID.
- `meta_get_pages` — Get list of Facebook Pages associated with the authenticated user. Returns page IDs needed for creating ads.
- `meta_upload_image` — Upload an image to a Meta ad account for use in static image ads. Provide a public URL — we download the bytes and upload them to Meta as multipart form data.
  - `ad_account_id` (string, required) — Ad account ID
  - `image_url` (string, required) — Public URL of the image (e.g. AWS S3 pre-signed URL)
  - `name` (string, required) — Name/description for the image
- `research_get_results` — Retrieve stored research results across sources (Reddit, web). Pass a run_id to get full results from a specific search, or omit it to list recent research runs with metadata.
  - `run_id` (string) — Specific research run ID to retrieve. Omit to list recent runs.
  - `source` (string, enum: `reddit` | `web`) — Filter listing to a single source. Omit to list across all sources.
  - `limit` (number) — Max results to return. Defaults to 25.
- `research_google_trends` — Get Google Trends data for keywords — interest over time, related queries, and interest by region. Use this to identify trending topics, compare keyword popularity, and spot emerging market opportunities. Returns trend data and stores results for later retrieval.
  - `keywords` (array of string, required) — Keywords to analyze (1-5). Multiple keywords are compared against each other, e.g. ['minoxidil', 'finasteride', 'hair transplant'].
  - `geo` (string) — Country code to scope results, e.g. 'US', 'GB', 'CA'. Omit for worldwide.
  - `timeRange` (string, enum: `3m` | `6m` | `12m` | `5y`) — Time range for trend data. Defaults to '12m' (past 12 months).
  - `includeRelatedQueries` (boolean) — Include top and rising related queries. Defaults to true.
  - `includeRegions` (boolean) — Include interest by region/country. Defaults to false.
  - `idea_slug` (string) — Optional idea slug. When provided, the response includes a `next_step_hint` nudging you to synthesize these findings into a research note via `startup_save_research_note` so the synthesis is queryable + auto-ingests into the wiki.
- `research_search_reddit` — Search Reddit for consumer discussions, problems, and sentiment around a product category or topic. Returns structured posts with title, body, score, subreddit, and URL. Results are stored for later retrieval. Billed per result.
  - `query` (string, required) — Search query — be descriptive for better results, e.g. 'hair loss treatment side effects' rather than 'hair loss'
  - `subreddits` (array of string) — Filter to specific subreddits (max 5), e.g. ['tressless', 'HaircareScience']. Omit for global search.
  - `sort` (string, enum: `relevance` | `hot` | `new` | `top` | `comments`) — Sort order. Defaults to 'relevance'.
  - `timeFilter` (string, enum: `hour` | `day` | `week` | `month` | `year` | `all`) — Time filter for results. Defaults to 'all'.
  - `limit` (number) — Number of results to return (1-100). Defaults to 25.
  - `idea_slug` (string) — Optional idea slug. When provided, the response includes a `next_step_hint` nudging you to synthesize these findings into a research note via `startup_save_research_note` so the synthesis is queryable + auto-ingests into the wiki.
- `research_search_web` — Search the web (Google) via Serper.dev for current information — news, competitors, reviews, sentiment, press coverage. Returns ranked organic results with title, URL, snippet, and date, plus an answer box / knowledge graph summary when available. Results are stored for later retrieval. Billed at a flat rate per call.
  - `query` (string, required) — Search query — be descriptive, e.g. 'iRESTORE laser cap reviews 2026' rather than 'iRESTORE'
  - `limit` (number) — Number of organic results to return (1-20). Defaults to 10.
  - `country` (string) — Country code for geo-targeting, e.g. 'us', 'gb', 'ca'. Defaults to 'us'.
  - `language` (string) — Language code, e.g. 'en', 'es'. Defaults to 'en'.
  - `timeRange` (string, enum: `day` | `week` | `month` | `year`) — Restrict to results published within this window. Omit for no time filter.
  - `idea_slug` (string) — Optional idea slug. When provided, the response includes a `next_step_hint` nudging you to synthesize these findings into a research note via `startup_save_research_note` so the synthesis is queryable + auto-ingests into the wiki.
- `startup_check_domain_availability` — Check domain name availability and get alternative suggestions. Two modes: (1) pass specific domainNames to check exact availability, (2) pass a keyword to search for available domains across TLDs. You can use both together. Returns purchasability, pricing, and TLD info for each result.
  - `idea_slug` (string, required) — Slug identifying the idea this tool call belongs to (e.g. 'pet-subscription-box'). Create an idea first with create_idea.
  - `domainNames` (array of string) — Specific domain names to check availability for (e.g. ['mybrand.com', 'mybrand.io']). Max 50.
  - `keyword` (string) — Keyword to search for available domains (e.g. 'mybrand'). Returns suggestions across TLDs.
  - `tldFilter` (array of string) — Limit keyword search results to specific TLDs (e.g. ['com', 'io', 'co']). Only applies to keyword search.
- `startup_create_idea` — Create a new business idea to explore. All startup tools (validation, naming, brand identity, etc.) are scoped to an idea. Create an idea first, then run tools against it. Ideas start in 'exploring' status. Best-effort auto-claims a matching subdomain under gentic.run so landing pages deployed under this idea land on the right brand host (e.g. idea 'Tez' → tez.gentic.run). On cross-org collision, tries base-2..base-5 before giving up. Skipped when the slugified name is reserved, equals the org's default subdomain, the org is at its claim cap, or the name can't be slugified. The response surfaces `subdomain`, `subdomain_auto_claimed`, and a `subdomain_auto_claim_reason` when auto-claim didn't complete. Remediation depends on the reason: `reserved` / `invalid_base` / `matches_default` / `all_suffixes_taken` / `cap_reached` — suggest calling `claim_subdomain` with a different label. `attach_failed` — the claim was made and auto-released; no action needed, though the user can retry with a different name or run `list_subdomains` to confirm nothing is lingering.
  - `idea_slug` (string, required) — Short kebab-case slug for this idea (e.g. 'pet-subscription-box', 'ai-tutor-app'). Must be unique.
  - `name` (string, required) — Human-readable name for the idea (e.g. 'Pet Subscription Box')
  - `description` (string) — Brief description of the idea
- `startup_export_report` — Export a saved startup result as a formatted markdown document. Renders naming research, business validation, competitive analysis, brand identity, or domain reports into polished markdown files and returns a permanent download link. Provide either a specific resultId or a toolName to export the most recent result of that type.
  - `idea_slug` (string) — The idea to export from. Required when using toolName. Not needed when using resultId.
  - `resultId` (string) — The specific result ID to export (from get_startup_results). If omitted, exports the most recent result for the given toolName.
  - `toolName` (string, enum: `startup_generate_names` | `startup_validate_business_idea` | `startup_check_domain_availability` | `startup_research_competitors` | `startup_generate_brand_identity` | `startup_save_research_note`) — Export the most recent result for this tool. Required if resultId is not provided.
- `startup_extract_brand_guidelines` — Extract comprehensive, production-grade brand guidelines from a brand's public website. Crawls key pages, captures screenshots, and combines HTML/CSS parsing with vision-model analysis to produce a canonical brand document with colors, typography, voice, imagery, aesthetic direction, heritage, and generative guardrails. Every field includes source, confidence, and evidence. Pass webhook_url for async execution.
  - `url` (string, required) — The brand's public website URL. Homepage is always crawled; sub-pages auto-selected from links.
  - `brand_name` (string) — Optional override. If omitted, extracted from meta tags / <title>.
  - `force_refresh` (boolean, default: `false`, required) — If true, bypass the 30-day cache and run a fresh extraction. Default false.
  - `webhook_url` (string) — If provided, the tool returns immediately with a job_id and POSTs results here on completion.
  - `depth` (integer, default: `4`, required) — Max number of pages to crawl. Default 4 (homepage + about + product + collection). Capped at 6.
- `startup_generate_ad_asset` — Generate ad images asynchronously. Supports batch generation — use the 'count' parameter to generate multiple images in one call (e.g. count=5 for 5 images). Do NOT call this tool multiple times to generate multiple images; instead, call it once with the desired count. Optionally provide inspiration_image_urls with competitor/reference ads to emulate their style. Returns job_id(s) immediately. The user will receive an email notification when the job completes. Use startup_list_asset_jobs to check status and results.
  - `prompt` (string, required) — Detailed text prompt describing the ad image to generate
  - `inspiration_image_urls` (array of string, default: `[]`, required) — 0-5 reference/competitor ad URLs to use as style inspiration. The AI will emulate the visual style, layout, and aesthetic of these images — but NOT copy their content.
  - `brand_image_urls` (array of string, default: `[]`, required) — 0-5 brand asset URLs (product photos, logos) to incorporate into the generated image
  - `aspect_ratio` (string, enum: `1:1` | `2:3` | `3:2` | `3:4` | `4:3` | `4:5` | `5:4` | `9:16` | `16:9` | `21:9`, default: `"1:1"`, required) — Aspect ratio for the generated image
  - `image_size` (string, enum: `1K` | `2K` | `4K`) — Image resolution: 1K (1024px), 2K (2048px), or 4K (4096px). Default is '4K' for nano_banana (Gemini credits are abundant so we want the best output) and '2K' for gpt_image_2 (OpenAI bills steeply at 2880×2880 — pass '4K' explicitly only when the user asks for a large/print-quality output).
  - `image_model` (string, enum: `nano_banana` | `gpt_image_2`) — Image generation model. 'nano_banana' (default) uses Google Gemini 3 Pro Image — faster and cheaper. 'gpt_image_2' uses OpenAI's GPT Image 2 — stronger on text rendering and brand consistency.
  - `quality` (string, enum: `low` | `medium` | `high` | `auto`) — Output quality. Only applies when image_model is 'gpt_image_2'. Defaults to 'medium' — ONLY pass 'high' when the user has explicitly asked for hero or final-quality output. For all drafts, iteration, and routine generations, omit this param and let the medium default apply ('high' + 4K is materially more expensive per image). Ignored for nano_banana.
  - `count` (integer, default: `1`, required) — Number of images to generate in parallel (1-10, default 1). IMPORTANT: To generate multiple images, set this parameter instead of calling the tool multiple times.
  - `webhook_url` (string) — Optional webhook URL to receive a POST request when the job completes. The payload includes job_id, status, asset_url, prompt, and other metadata.
- `startup_generate_brand_identity` — Generate a complete brand identity foundation for a business. Covers positioning (statement, elevator pitch, taglines), brand personality (archetype, voice, values), visual direction (color palette with hex codes, typography with free font suggestions, logo direction), target audience persona, and brand story (mission, vision, origin narrative). Designed for bootstrapped founders who need to build a brand without a design agency. Pass a webhook_url to run async.
  - `idea_slug` (string, required) — Slug identifying the idea this tool call belongs to (e.g. 'pet-subscription-box'). Create an idea first with create_idea.
  - `businessDescription` (string, required) — What the business does, its value proposition, and who it serves. Include any existing brand elements (name, colors, etc.) if you have them. Max 5000 characters.
  - `businessName` (string) — The business/brand name, if already decided. Helps tailor positioning and taglines.
  - `industry` (string) — The industry or category (e.g. 'home bakery / desserts', 'SaaS / developer tools'). Helps inform visual and tone direction.
  - `brandPreferences` (string) — Any existing preferences: colors you like, brands you admire, tone you want (e.g. 'warm and approachable like Mailchimp', 'minimal and premium like Aesop'). Max 2000 characters.
  - `targetAudience` (string) — Who the business serves — demographics, psychographics, or customer description. Max 1000 characters.
  - `webhook_url` (string) — Optional webhook URL to receive a POST when the job completes. Payload includes job_id, tool_name, status, and result_data. When provided, the tool returns immediately with a job_id instead of waiting.
- `startup_generate_names` — Generate creative startup/business name candidates with professional naming frameworks. Produces 20 names across 5 categories (descriptive, experiential, evocative, invented, identity), scores each on memory/meaning/range, suggests domain strategies, and picks a top 3. Pair with check_domain_availability to verify domain options for your favorites. Note: always verify trademark availability and cross-language meanings independently — AI-generated linguistic checks are directional, not definitive. Pass a webhook_url to run async — the tool returns immediately with a job_id and POSTs results to the webhook when done.
  - `idea_slug` (string, required) — Slug identifying the idea this tool call belongs to (e.g. 'pet-subscription-box'). Create an idea first with create_idea.
  - `businessDescription` (string, required) — What the business does, its unique value proposition, and who it serves. The more detail, the better the names. Max 5000 characters.
  - `targetEmotion` (string) — The single dominant emotion the brand should evoke (e.g. 'relief', 'momentum', 'wonder', 'belonging', 'rebellion', 'control', 'delight'). Helps anchor the naming direction.
  - `brandTone` (string) — The brand's personality and tone. Use spectrums: playful↔serious, innovative↔traditional, accessible↔exclusive. E.g. 'playful and accessible, slightly irreverent' or 'serious and premium, but approachable'.
  - `nonStarters` (string) — Words, styles, or associations to avoid. E.g. 'nothing with AI in the name, no pharmaceutical-sounding names, avoid anything similar to competitor XYZ'. Max 1000 characters.
  - `industry` (string) — The industry or category (e.g. 'home bakery / desserts', 'SaaS / developer tools', 'fitness / wellness'). Helps differentiate from competitors.
  - `webhook_url` (string) — Optional webhook URL to receive a POST when the job completes. Payload includes job_id, tool_name, status, and result_data. When provided, the tool returns immediately with a job_id instead of waiting.
- `startup_get_brand_guidelines` — Look up canonical brand guidelines for your organization. Pass `domain` or `url` to fetch a specific record. With no arguments, returns the single brand on file if the org has exactly one; returns a `candidates` list to disambiguate if there are several; returns a friendly not-found message if there are none. All responses use a `{ found: true/false, ... }` envelope. Free.
  - `domain` (string) — Brand domain, e.g. 'beekman1802.com'. Optional — if omitted, the server resolves to the org's single extracted brand (or returns a candidate list to disambiguate).
  - `url` (string) — Any URL on the brand's site; domain is normalized from this. Optional — see `domain` for the no-args resolution behavior.
- `startup_get_results` — Retrieve saved results from previous startup tool calls (naming, validation, domain checks, competitor research, saved research notes). Returns results newest-first. Filter by tool name to see only naming results, only validations, only research notes, etc. Research-note rows include a top-level `url` pointing at the full markdown in S3 — surface it to the user as a clickable link so they can read the whole note (don't just summarize the body).
  - `idea_slug` (string, required) — Slug identifying the idea this tool call belongs to (e.g. 'pet-subscription-box'). Create an idea first with create_idea.
  - `toolName` (string, enum: `startup_generate_names` | `startup_validate_business_idea` | `startup_check_domain_availability` | `startup_research_competitors` | `startup_generate_brand_identity` | `startup_save_research_note`) — Filter by tool name. Omit to see all saved startup results.
  - `limit` (integer, default: `10`, required) — Number of results to return (default 10, max 50).
- `startup_list_asset_jobs` — List recent asset generation jobs (images, videos, and landing pages). Returns job IDs, statuses, prompts, output URLs, and (for landing-page jobs) draft_id + content_hash you can pass to deploy_landing_page. Pass job_id to fetch a single job (useful for polling a specific generation). Use this to check on generation progress.
  - `job_id` (string) — Fetch a specific job by id. Use this when polling a generate_landing_page / generate_ad_asset / generate_video job you just kicked off — returns the same row shape as listing, just filtered to one. Other filters (limit/status/type) are ignored when set.
  - `limit` (integer, default: `20`, required) — Number of recent jobs to return (1-100, default 20)
  - `status` (string, enum: `processing` | `completed` | `failed`) — Filter by job status. Omit to return all statuses.
  - `type` (string, enum: `image` | `video` | `video_edit` | `voice_replace` | `split_screen` | `landing_page`) — Filter by asset type. Omit to return all types.
- `startup_list_ideas` — List all business ideas for this organization with their status and tool result counts.
  - `include_paused` (boolean, default: `false`, required) — Include paused ideas (default: false)
- `startup_recall_memory` — Search long-term cross-conversation memory captured from Slack and Telegram threads. Answers questions like 'what did we decide last week?' or 'what has the team said about X?'. Vector-searches the org-scoped `org_memory` table and returns rows ranked by semantic relevance. Each row includes text, role, surface, thread_key, agent_name, created_at, and similarity_score. Use surface/role/since/until/thread_key filters to narrow scope. Free.
  - `query` (string, required) — Natural-language question — what do you want to recall from prior conversations? e.g. 'what did we decide about pricing last week?' or 'what has the team said about onboarding flow?'
  - `limit` (integer, default: `10`, required) — Max memory rows to return (default 10, max 50).
  - `surface` (string, enum: `slack` | `telegram`) — Restrict results to a single surface.
  - `since` (string) — Only include memories created on/after this ISO date (e.g. '2026-04-01').
  - `until` (string) — Only include memories created on/before this ISO date.
  - `role` (string, enum: `user` | `assistant`) — Filter by message author — 'user' for human turns, 'assistant' for agent replies.
  - `thread_key` (string) — Pin retrieval to a single conversation thread (matches the thread_key written by the capture side).
- `startup_research_competitors` — Research the competitive landscape for a business idea. Identifies 6-10 competitors (direct, indirect, adjacent), analyzes their positioning, pricing, strengths and weaknesses, maps market maturity and consolidation, and surfaces differentiation opportunities a bootstrapper can act on. Pass a webhook_url to run async — the tool returns immediately with a job_id and POSTs results to the webhook when done.
  - `idea_slug` (string, required) — Slug identifying the idea this tool call belongs to (e.g. 'pet-subscription-box'). Create an idea first with create_idea.
  - `businessDescription` (string, required) — What the business does, its value proposition, and who it serves. The more specific, the better the competitive analysis. Max 5000 characters.
  - `industry` (string) — The industry or category (e.g. 'home bakery / desserts', 'SaaS / developer tools', 'fitness / wellness'). Helps narrow the competitor search.
  - `knownCompetitors` (string) — Competitors you already know about (names, websites). Helps the analysis include them and find others in the same space. Max 2000 characters.
  - `focusAreas` (string) — Specific aspects to focus on (e.g. 'pricing strategy', 'how they acquire customers', 'their weaknesses in customer support'). Max 1000 characters.
  - `webhook_url` (string) — Optional webhook URL to receive a POST when the job completes. Payload includes job_id, tool_name, status, and result_data. When provided, the tool returns immediately with a job_id instead of waiting.
- `startup_save_brand_style_guide` — Save or update a brand style guide for a specific idea. Sets brand colors, fonts, and tone that are automatically applied to future landing pages. You can set individual fields — any field you omit keeps its current value (if a guide already exists).
  - `idea_slug` (string, required) — The idea this style guide belongs to
  - `primary_color` (string) — Primary brand color (hex, e.g. '#2D5A3D')
  - `secondary_color` (string) — Secondary brand color (hex)
  - `accent_color` (string) — Accent brand color (hex)
  - `primary_color_name` (string) — Name for the primary color (e.g. 'Forest Green')
  - `secondary_color_name` (string) — Name for the secondary color
  - `accent_color_name` (string) — Name for the accent color
  - `heading_font` (string) — Font family for headings (e.g. 'DM Serif Display')
  - `body_font` (string) — Font family for body text (e.g. 'Inter')
  - `voice` (string) — Brand voice description (e.g. 'Warm, approachable, and confident')
  - `personality_traits` (array of string) — Personality trait keywords (e.g. ['friendly', 'minimal', 'bold'])
  - `visual_mood` (array of string) — Visual mood keywords (e.g. ['warm', 'organic', 'handcrafted'])
  - `logo_direction` (string) — Logo direction notes
- `startup_save_research_note` — Save in-progress research findings against an idea — supplier deep-dives, founder stories, lift-able quotes, tagline drafts, packaging hooks, anything the agent has synthesized while ideating that should outlive the chat. Writes the markdown body to S3 (canonical, editable artifact) AND inserts a row in startup_results so the note shows up in get_startup_results (filter by tool_name='startup_save_research_note') and counts toward list_ideas.result_count. Append-only — to revise, save a new note. **Auto-ingests into the wiki** in the background (~60s) — the response carries `wiki_view_url` so you can tell the user where to read the rendered wiki once ingest completes. Save itself is free; auto-ingest bills $0.25 separately as `wiki_ingest_auto`. For org-level industry knowledge that should NOT be tied to a specific idea, use `save_research_note` on the /research endpoint instead.
  - `idea_slug` (string, required) — Slug identifying the idea this tool call belongs to (e.g. 'pet-subscription-box'). Create an idea first with create_idea.
  - `title` (string, required) — Short title for the note — used as the S3 filename slug and the row's title. Pick something specific (e.g. 'Rhody Wild supplier deep-dive') so notes are easy to find later.
  - `markdown` (string, required) — Note body as markdown. Stored verbatim — feel free to use headings, lists, blockquotes for lift-able quotes, etc.
  - `sources` (array of string) — Optional list of source URLs cited in the note (max 50, each ≤2000 chars). Echoed back in get_startup_results.
- `startup_update_idea_status` — Update the status of a business idea. Use this to mark ideas as validated, active (this is the one!), or paused (didn't work out, revisit later).
  - `idea_slug` (string, required) — Slug identifying the idea this tool call belongs to (e.g. 'pet-subscription-box'). Create an idea first with create_idea.
  - `status` (string, enum: `exploring` | `validated` | `active` | `paused`, required) — New status for the idea
- `startup_update_idea_subdomain` — Reassign the subdomain attached to an idea — typically used when the user iterates on a brand name before deploying any landing pages (e.g. 'Blueberry Bar' → 'Bluey'). Claims `new_subdomain` if not already owned, attaches it to the idea row, and releases the previously-attached subdomain when no active landing page still references it. Idempotent: passing the current subdomain returns success with no changes. Setting `new_subdomain` to your org's default subdomain is allowed and behaves the same as having no subdomain attached (deploys default to the org host either way). Use `landing_page_claim_subdomain` directly if you just want to reserve a label without attaching it to an idea. Does NOT rename the idea_slug or any landing-page path slugs already deployed — those continue to serve at their existing URLs until renamed separately. If the underlying database UPDATE fails, the call throws a structured `attach_failed` error, the new claim is rolled back automatically (when this call created it), and the idea row is left unchanged — caller can retry.
  - `idea_slug` (string, required) — Idea slug to reassign (created via startup_create_idea).
  - `new_subdomain` (string, required) — Subdomain label to attach. Lowercase alphanumeric + hyphens, 3-63 chars. Reserved labels (www, api, app, admin, mail, etc.) are rejected. If another org owns the label, the call fails with a structured 'taken' error.
- `startup_validate_business_idea` — Evaluate a business idea for bootstrapped entrepreneurs. Scores the idea on 5 criteria (market demand, competition, feasibility, revenue clarity, customer reach) and returns a verdict (Strong/Promising/Needs Work/Rethink), strengths, risks, and concrete next steps. Designed for solo founders, side-project builders, small e-commerce brands, and home-based businesses — not VC-backed startups. Pass a webhook_url to run async — the tool returns immediately with a job_id and POSTs results to the webhook when done.
  - `idea_slug` (string, required) — Slug identifying the idea this tool call belongs to (e.g. 'pet-subscription-box'). Create an idea first with create_idea.
  - `businessIdea` (string, required) — Describe the business idea. Include: what the product/service is, who it's for, and how it makes money. The more detail, the better the evaluation. Max 5000 characters.
  - `founderContext` (string) — Optional context about the founder: skills, budget, time availability, location, existing audience, or relevant experience. Helps tailor feasibility and next-step recommendations. Max 2000 characters.
  - `webhook_url` (string) — Optional webhook URL to receive a POST when the job completes. Payload includes job_id, tool_name, status, and result_data. When provided, the tool returns immediately with a job_id instead of waiting.

---

_This SKILL.md is generated from the live Gentic MCP manifest. Tool names, descriptions, and pricing are always current. Connect Gentic Startup at https://gentic.co/startup._
