- Brand
- Hermoso AI
- Category
- Marketing
- Primary Subcategory
- AI Search & LLM Visibility (AEO/GEO)
Integration details
Description
Hermoso runs your marketing from ChatGPT. Ask in plain words and it does the work across four areas you can use together or on their own. Research: see the ads competitors are running right now, find a brand's competitors from its website, break down which hooks and themes their longest-running ads use, and mine ad angles from real customer language. Create: make on-brand image and video ads from your saved brand profile (products, logo, colours and voice), remake a video or static ad you like for your brand from its link, and turn one finished ad into headline or hook variations for testing. Finished files land in your Hermoso Library. Publish: schedule posts to the social accounts you connect in your Hermoso workspace, such as Instagram, Facebook, TikTok, YouTube, LinkedIn, X and Threads, and see what is queued and what already went out. Measure: see which posts and hooks perform best on each channel, and get a review of your paid campaigns across the ad accounts you connect (it changes nothing in them), with suggestions on what to scale, pause, fix or test next. Who it is for: founders, marketers, creators and agencies who want their marketing handled from one conversation. Limits: Hermoso needs a Hermoso account, and it only acts on the brands and accounts you connect to it. It never posts without your instruction, and scheduled posts go out on the accounts you choose. Research and creating ads use credits from your Hermoso account. Ad libraries only show what the platforms make public, so some advertisers return few or no ads. Video ads take one to three minutes to render.
- Integration type
- Plugin
- Verification status
- Not applicable
- Platform
- ChatGPT
- Primary Subcategory
- AI Search & LLM Visibility (AEO/GEO)
- Secondary Subcategories
- None listed
- Brand
- Hermoso AI
- Access
- Account required
- First tracked
- 2026-10-16
- Tool count
- 887
- Geography
- US
The Primary Subcategory used for this profile’s headline score.
Other Subcategories where the Integration is listed.
Your score is coming
ChatGPT now suggests Plugins on its own when they match a user's request.Your Plugin Discovery Score measures how often yours appears, and it will show here as soon as it’s ready.
What discovery looks like

Get alerts for Hermoso AI
Get updates when Hermoso AI’s Discoverability Score or category rank changes.
Competing in ChatGPT AI Search & LLM Visibility (AEO/GEO)
View Category887 tools agents can invoke
Use this when a Bluesky DM request should move out of the requests folder into the inbox. Get convoId from list_bluesky_convos. Free.
accept_bluesky_convo
Use this when a message request should move into the brand’s X inbox. id is the sender’s numeric id or the request’s conversationId from list_x_chats view message_requests. Show the user who sent it first; the message itself cannot be read here. Needs X connected; X bills per call, so it costs credits.
accept_x_chat_request
Answer a collab invite from list_instagram_collab_invites. accept:true makes this account a co-author and the creator’s post appears on its profile; accept:false declines. `accept` is REQUIRED — never guess which way the user wants it. The reply is read back from Instagram (the invite leaves the pending list), not taken from the vendor’s 200. Free. Instagram allows 50 answers per account per day.
respond_instagram_collab_invite
THE ONE SWITCH THAT ARMS REAL MONEY ON SNAPCHAT. ACTIVE starts real spend on the next auction; PAUSED stops it. EVERY change needs confirm:true, and WITHOUT confirm NOTHING CHANGES — you get a sentence naming each object AS READ FROM SNAPCHAT, with its real name and its current status, which you must show the user before asking for a yes. Confirming proves the caller meant to change SOMETHING; only reading the object back proves they aimed at the right one. EVERY TIER must be ACTIVE for a single impression to serve — a live ad under a paused ad squad shows nothing. THE ANSWER IS THE READ-BACK: report what Snapchat STORED per id, never the status you asked for. Snapchat has NO delete status — removal is a real DELETE verb, so use delete_snapchat_ads_object.
set_snapchat_ads_status
Turn a LinkedIn campaign group, campaign or CREATIVE (the ad itself) on (ACTIVE) or off (PAUSED). Pass level:"campaign" + campaignId, level:"campaignGroup" + campaignGroupId, or level:"creative" + creativeId. All three tiers must be ACTIVE for an ad to serve — activating only the campaign leaves a DRAFT creative sitting there showing nothing. ACTIVATING STARTS REAL AD SPEND — you MUST first show the user the campaign and its budget, get an explicit yes, then call with status:"ACTIVE" and confirm:true. Pausing is always safe. The resulting status is READ BACK from LinkedIn along with its servingStatuses before you are told it took: LinkedIn defers validation on drafts, so activation is exactly where a hidden problem surfaces, and the note reports what LinkedIn actually says rather than what was requested.
set_linkedin_ads_status
Turn a campaign ON (ACTIVE) or OFF (PAUSED). ACTIVATING STARTS REAL AD SPEND — you MUST first show the user the campaign name + its daily budget, get an explicit yes, then call with status:"ACTIVE" and confirm:true. Pausing is always safe. Needs ads-management permission.
set_meta_campaign_status
Turn a Microsoft Advertising campaign, AD GROUP or AD on (Active) or off (Paused). Pass level:"campaign" + campaignId, level:"adGroup" + adGroupId, or level:"ad" + BOTH adGroupId and adId. ACTIVATING STARTS REAL AD SPEND — you MUST first show the user the campaign name + its daily budget, get an explicit yes, then call with status:"Active" and confirm:true. Pausing is always safe. Microsoft has only these two statuses — its Deleted state is internal-only and cannot be SET — so to remove something use delete_microsoft_ads_object, which is a real delete operation, not a status. The resulting status is READ BACK from Microsoft before you are told it took — and Microsoft may report BudgetPaused / BudgetAndManualPaused / Suspended instead, which the note names explicitly.
set_microsoft_ads_status
Turn a ChatGPT Ads AD ACCOUNT, campaign, AD GROUP or AD on (active), off (paused), or ARCHIVE it. Pass level:"account" (NO id — the connection IS the account), level:"campaign" + campaignId, level:"adGroup" + adGroupId, or level:"ad" + adId. LEVEL "account" IS THE EMERGENCY STOP AND BEHAVES DIFFERENTLY FROM EVERY OTHER LEVEL: BOTH directions take confirm:true, because pausing it stops ALL ChatGPT Ads delivery for the brand at once and activating it un-suppresses EVERY active campaign underneath — Hermoso cannot tell you in advance which of them will start spending. An account cannot be archived. ACTIVATING STARTS REAL AD SPEND — you MUST first show the user the object and its budget, get an explicit yes, then call with status:"active" and confirm:true. Pausing is always safe and stops all spend. ARCHIVING IS IRREVERSIBLE: it is this platform’s only teardown (there is no delete and no un-archive, and OpenAI’s own guidance is "only archive objects you have no further use for"), so it ALSO requires confirm:true — prefer pausing unless the user is certain. Remember an ad only serves when the ad, its ad group AND its campaign are all active and the ad has passed OpenAI’s review. The resulting status is READ BACK from OpenAI before you are told it took.
set_openai_ads_status
Turn a Pinterest campaign, AD GROUP or AD on (ACTIVE) or off (PAUSED) — and, because Pinterest has NO DELETE anywhere in its API, this is also the only way to retire one (ARCHIVED). Pass level:"campaign" + campaignId, level:"adGroup" + adGroupId, or level:"ad" + adId. ACTIVATING STARTS REAL AD SPEND, and ARCHIVING is effectively a delete: both require you to show the user exactly what changes, get an explicit yes, and call again with confirm:true. Pausing is always safe. The resulting status is READ BACK from Pinterest before you are told it took.
set_pinterest_ads_status
THE ONE SWITCH THAT ARMS REAL MONEY ON TIKTOK. Pass level ("campaign", "adgroup" or "ad"), the ids, and a status: ENABLE starts real spend on the next auction, DISABLE stops it, DELETE removes the objects (TikTok models removal as a STATUS — it publishes no delete verb — which is why delete_tiktok_ads_object is a thin wrapper over this same route). EVERY status change here needs confirm:true, and CALLING IT WITHOUT confirm CHANGES NOTHING and hands back the sentence describing exactly what would happen — show the user that, get an unambiguous yes, then confirm. EVERY TIER HAS TO BE ENABLED FOR AN IMPRESSION TO SERVE: Hermoso creates all three paused, so enabling the campaign alone does nothing while its ad group and ad are still disabled, and each level is a separate call. Ids can be passed in bulk, but TikTok’s QPS is 1 and calls are serialized, so a long list is simply slow. THE ANSWER IS THE READ-BACK: the result carries what TikTok STORED per id, plus a note when the read-back did not return every id — repeat that rather than the status you asked for.
set_tiktok_ads_status
The one switch that arms real money on Reddit. Pass kind ("campaign", "ad_group" or "ad") plus the object id. ACTIVE starts real spend as soon as Reddit approves — show the user exactly what will run and get an explicit yes, then call again with confirm:true. PAUSED is always safe and never gated. REDDIT HAS NO DELETE OPERATION for these three: removal is a status, and ARCHIVED/DELETED here run the SAME blast-radius gate as delete_reddit_ads_object (call that one instead when you mean to remove something — it is the same code path and its unconfirmed call reports what goes with it). Remember Reddit’s three tiers all have to be ACTIVE for a single impression to serve: activating the campaign alone does nothing if its ad group and ad are still paused. The resulting status is READ BACK from Reddit.
set_reddit_ads_status
Add targeting keywords to an Apple Ads ad group. keywords is [{text, matchType, bid}] — matchType EXACT (default), BROAD, PHRASE or CATEGORY, and bid optionally overrides the ad group default for that one term. Up to 1000 per call and 5000 per ad group. They are sent ONE AT A TIME on purpose: a single term Apple refuses cannot take the rest of the batch down, and the reply names every refusal with Apple’s own reason, then reads the ad group’s keywords back to prove what actually landed. Keyword text and match type are IMMUTABLE — to change either, delete the keyword and add it again. A keyword is created ENABLED because it cannot serve unless its ad group and campaign are enabled too; if the ad group is ALREADY live, adding keywords serves on the next auction and therefore takes confirm:true. Free.
add_apple_ads_keywords
Add negative keywords to an Apple Ads campaign or ad group — search terms your ads must NOT show for. Pass exactly one of campaignId (campaign-wide) or adGroupId (that ad group only); Apple rejects a request carrying both. keywords is [{text, matchType}] with matchType BROAD (default) or EXACT. Never confirm-gated and never a spend risk, because a negative keyword only ever RESTRICTS where you show. Text and match type are immutable — delete and re-add to change either. Free.
add_apple_ads_negative_keywords
Add keywords — and NEGATIVE keywords — to a Google Ads ad group. A Search ad group with no keywords never shows. Each keyword takes text (≤80 chars, ≤10 words) and matchType EXACT | PHRASE | BROAD (default PHRASE). Set negative:true to BLOCK a term instead of targeting it, which is the cheapest way to stop wasted spend. Read back from Google before you are told they exist. If the parent campaign and ad group are already LIVE, a positive keyword starts bidding real money at once — get an explicit yes and pass confirm:true, or add it with paused:true. Negative keywords only restrict spend and never need confirmation.
add_google_ads_keywords
ADD locations and languages to an existing Google Ads campaign. THIS ADDS; IT DOES NOT REPLACE — Google campaign criteria are a list, this call only ever creates entries, and there is no remove operation here. So a campaign already targeting the United States that you "change to Canada" ends up targeting BOTH and still spending in the US; the read-back names every pre-existing location and language it kept, and you MUST relay that rather than reporting the new total as the answer. Removing targeting is done in Google Ads (Campaign > Settings > Locations). Pass locations by NAME ("United States", "California", "Toronto") — they are resolved to Google's geo target ids for you; excludedLocations adds a NEGATIVE criterion (the reliable way to stop serving somewhere from here); languages takes ISO codes ("en","fr"). A campaign with NO location targeting runs WORLDWIDE, which is the most expensive default in Google Ads. Changing a LIVE campaign's targeting moves real spend immediately, so that needs confirm:true.
set_google_ads_targeting
Add keywords to a Microsoft Advertising ad group. Match types are Exact, Phrase and Broad — Microsoft has no broad-match-modifier. Keywords are added Paused unless you set status:"Active"; an Active keyword on a live ad group makes the campaign bid on a new term immediately, so that needs confirm:true. Note that per-keyword bids are honoured but ad-group / keyword BID STRATEGIES are silently ignored by Microsoft — they inherit the campaign’s. Only the keywords Microsoft confirms on the read-back are reported as added.
add_microsoft_ads_keywords
Add an ad group to an existing ChatGPT Ads campaign. Created PAUSED by default. Bidding: pass maxBid for a fixed bid, or bidStrategy "maximize_clicks" / "maximize_conversions" to let ChatGPT Ads set the bid for the budget (OpenAI’s "Maximize results"), in which case maxBid is optional. Its context hints ARE the targeting on this platform: up to 2,000 natural-language descriptions of the conversations, topics or questions where this offering is relevant — not exact-match keywords, and no guarantee of delivery. Write many distinct ones rather than variations of the same phrase. If the parent campaign is already LIVE (active), creating this ad group active starts REAL AD SPEND on the next auction, exactly like activating it — show the user what would begin serving, get an explicit yes, then pass confirm:true. Leaving it paused never needs confirmation. The whole tree is READ BACK from OpenAI before you are told it exists.
create_openai_ads_ad_group
Add an ad group to an existing Microsoft Advertising campaign — optionally with its responsive search ad and keywords in the same call. Created Paused by default. If the parent campaign is already LIVE (Active), creating this ad group Active starts REAL AD SPEND on the next auction, exactly like activating it — show the user what would begin serving, get an explicit yes, then pass confirm:true. Leaving it Paused never needs confirmation. Read back from Microsoft before you are told it exists.
create_microsoft_ads_ad_group
Add an ad group to an existing Pinterest campaign — optionally with its ad in the same call. Created PAUSED by default. Pinterest REQUIRES every ad group to target at least one place, so targetingSpec must carry a GEO array or a LOCATION object. If the parent campaign is already LIVE (ACTIVE), creating this ad group ACTIVE starts REAL AD SPEND on the next auction, exactly like activating it — show the user what would begin serving, get an explicit yes, then pass confirm:true. Leaving it paused never needs confirmation. Read back from Pinterest before you are told it exists.
create_pinterest_ads_ad_group
Use this when the brand wants a new shelf on its channel homepage. Public immediately. singlePlaylist and multiplePlaylists take playlist ids, multipleChannels takes channel ids; only multiplePlaylists and multipleChannels take a title. Free.
create_youtube_channel_section
Add a property to the connected Google account. TWO THINGS THAT MUST REACH THE USER, and the answer states both: (1) ADDING IS NOT VERIFYING — the property arrives with the account as an unverified user and EVERY read on it is refused until ownership is proven with a DNS record, an HTML file or a tag, which no API can do and which the user completes in Search Console itself; (2) the new property is NOT yet shared with this profile, so someone has to tick it under Settings > Connectors > Google Search Console > Manage accounts (or call list_connector_accounts with provider google_search_console, then set_connector_accounts) before any tool here can use it. The permission level is READ BACK from Google, so the answer says which of those two states it is actually in. 0 credits.
add_search_console_site
Add a site to the connected Bing Webmaster account. Important: ADDING IS NOT VERIFYING, and saying so is most of this tool's value: the site arrives UNVERIFIED and every read on it is refused until an ownership proof is placed on the site itself — an XML file at the root, a meta tag in the home page <head>, or a CNAME DNS record — which no API can do. Place one, then call verify_bing_webmaster_site. Microsoft documents that adding a site which is already there does NOT error, so a success here is not even evidence anything changed, which is why the answer is read back from Bing's site list. It is also NOT shared with this profile until the user ticks it under Settings > Connectors > Bing Webmaster Tools > Manage accounts (or call list_connector_accounts with provider bing_webmaster, then set_connector_accounts). 0 credits.
add_bing_webmaster_site
Use this when a video should be added to one of the channel’s playlists. Returns the new entry’s itemId. position is zero-based; omit it to append. Free.
add_video_to_youtube_playlist
Use this when someone should moderate the chat on every current and future broadcast of the brand. Pass userId or username. Needs X connected; X bills per call, so it costs credits.
add_x_broadcast_chat_moderator
Add an ad group to an EXISTING Google Ads campaign — the level between a campaign and its ads. Google requires it: a campaign with no ad group cannot serve. Optionally build its ad and keywords in the same ATOMIC call. The ad-group type is taken from the campaign's channel automatically. Created PAUSED and read back from Google before you are told it exists. If the parent campaign is already LIVE (ENABLED), creating this ENABLED starts REAL AD SPEND immediately — show the user what would begin serving, get an explicit yes, then pass confirm:true. Leaving it PAUSED never needs confirmation.
create_google_ads_ad_group
Leave an INTERNAL NOTE on an Intercom conversation: only the brand's teammates see it, never the customer (a summary, a suggested reply, a handover). Without confirm:true it adds nothing and returns the preview; call again with confirm:true once the user agrees. Read back from the conversation. Free. Needs Intercom connected (Settings > Connectors > Intercom).
add_intercom_note
Add creators or ad accounts to an existing TikTok One campaign. IT ONLY EVER ADDS: TikTok appends what you pass to what the campaign already holds and publishes nothing that removes either, so there is no way to un-invite a creator or unlink an ad account through the API: the reply therefore reports what the campaign NOW HOLDS rather than what you sent. The name, description, brand and anchor cannot be changed after creation at all; TikTok’s own create endpoint accepts a campaign_id and then ignores every field except the account and the handles, which is why a rename is impossible rather than merely unsupported here. handleNames is not accepted on a BRAND_LINK campaign, because a brand-level link is open to whoever opens it. Free.
update_tiktok_tto_campaign
Add a creator to — or remove one from — this brand’s Partnership Ads lists. Name at least one list: `allowAds` (whose content this profile may RUN AS AN ADVERT) and/or `allowTagging` (who may TAG this brand as a paid partner on their own post). NEITHER DEFAULTS ON, because one of them lets somebody attach this brand’s name to their own post. ADDING IS A REQUEST, NOT AN APPROVAL: Meta’s own name for the operation is "send permission request", the creator accepts on their own Instagram account in their own time, and until the read-back reports approved this profile cannot advertise their content. Never tell anyone a partnership is live off the back of this call — report the status the reply carries. THE OUTCOME IS READ BACK OFF META’S OWN LIST, NEVER TAKEN FROM THE WRITE’S REPLY — because Meta answers the ADD with HTTP 500 (code 1, “Please reduce the amount of data you’re asking for”) while performing it, measured 4/4 on 2026-08-25. If this reply carries `metaWriteError` alongside a successful summary, that error is Meta noise about a write that HAPPENED: do not retry, and do not tell the user it failed. The reply’s `verdict` is one of applied / already / unchanged / unknown, and `unknown` means the list could not be re-read — which is not the same as nothing having changed. REMOVING FLIPS A STATUS TO “Canceled”; it does not delete the row. One tool does both lists because Meta removes from them with two DIFFERENT verbs (DELETE on one, a `revoke` flag on the other) and a caller who assumes one silently does nothing on the other. 0 credits.
manage_meta_partnership_creator
Add people to, or remove people from, a Reddit custom audience. Pass ordinary email addresses and/or mobile advertising ids — each one is canonicalised the way Reddit specifies and SHA-256 hashed on our server before it is sent, so raw customer data never reaches Reddit, and an identifier you already hashed is passed through untouched. Up to 2,500 rows per call; send bigger lists as repeated calls and the audience accumulates. EVERY ROW MUST CARRY THE SAME FIELDS: Reddit’s upload is positional, so if some rows have an email and others do not, the values shift into the wrong column and match nobody — split those into separate calls instead. After Reddit accepts the upload the size does not move for up to 4 hours and processing can take 36, so never re-send the same batch because the count looks unchanged. Free.
update_reddit_ads_audience_users
Add SITELINKS, CALLOUTS or STRUCTURED SNIPPETS to a Google Ads campaign or ad group — and ATTACH them, which is the part that makes them do anything (an asset sitting in the account library shows nothing at all). Sitelinks are the highest-CTR free win on Search: extra links under the ad, each with its own landing page. Pass assetType plus assets[]: SITELINK needs {linkText (≤25 chars), finalUrl, and optionally description1 / description2}; CALLOUT needs {calloutText (≤25)}; STRUCTURED_SNIPPET needs {header, values[] — at least 3}. Or link assets that already exist with assetResourceNames[]. Assets and links go up in ONE atomic operation, so a rejected link never strands an orphan asset, and the links are READ BACK from Google before you are told they exist. Attaching a live asset to a LIVE (ENABLED) campaign changes what that ad shows on the very next auction — show the user what would appear, get an explicit yes, then pass confirm:true. On a paused campaign it never needs confirmation.
add_google_ads_assets
Burn subtitles into ANY video and get the .srt too; nothing is cut or re-rendered. Set textStyle only when the user describes a look (default: white sentence case, thin outline, bottom). Timed per sentence, or auto:'words' for phrases on their spoken words. Or pass cues to burn your own lines exactly, untranscribed (reel-style phrases, *stage directions*). burn:false returns only the .srt. Takes a video file or URL, a YouTube/Vimeo-style page, or a TikTok / Reel / X post link. No speech is refused and refunded. Runs in the background and lands in the Library.
add_subtitles
Change who is IN a ChatGPT Ads custom audience. Pass plain emails, phone numbers and/or Google Advertising IDs (GAID, UUID-shaped) in ONE list — identifier types may be mixed in a single request: Hermoso normalises and SHA-256 hashes emails and phones locally and only the digests are sent; a GAID rides raw, as the API defines it. SIZE RULES (OpenAI, read 2026-09-03): an audience of ANY size, even empty, can be used for EXCLUSION once ready; for inclusion or bid adjustments plan on 25,000 matched users; there is no 5M ceiling any more, and the reported size is a privacy band (under_25k, 25k_100k, 100k_500k, 500k_1m, 1m_5m, 5m_plus). THREE OPERATIONS: "add" puts people in, "remove" takes the named people OUT (the only way to stop advertising to a segment already in a list), "replace" swaps the WHOLE membership. THIS IS ASYNCHRONOUS — ChatGPT Ads returns an operation id and the change is NOT applied when this returns; poll it with get_openai_ads_audience_operation until it reports succeeded or failed. A replace REQUIRES expectedRevision (read membershipRevision from get_openai_ads_audience) so a wholesale swap cannot land on top of someone else’s change; a mismatch is refused by ChatGPT Ads and applies nothing.
update_openai_ads_audience_members
Change the ad account’s blocked-word list — the standing filter that auto-hides any comment containing one of these words across every ad on the account. operation "add" (up to TikTok’s 500-word account limit, refused here for free if the list would overflow rather than failing at TikTok), "remove", or "replace" one word with another. Warning: REMOVING A WORD REPUBLISHES HISTORY: TikTok’s own words are that comments already hidden by it "will become public instead of being visible only to the commentor" — a bulk unhide across every ad on the account, for which TikTok reports no count. So a remove without confirm changes nothing and tells you which of the words are actually on the list first. EVERY WRITE HERE IS READ BACK: TikTok answers all three operations with an empty response and its delete explicitly does not error on a word that was never on the list, so reporting off the response would report a no-op as a change — the reply is the diff of the list before and after.
manage_tiktok_ads_blocked_words
Amplitude ACTIVE or NEW user counts over a date range — the top-line "is the product growing" number, and the cheapest Amplitude call to run first after connecting because it proves the credential with no setup. metric is "active" or "new"; interval 1 daily, 7 weekly, 30 monthly. An empty series on a new project is the CORRECT answer and means the connection works and there is no data yet — never report it as a failure. Read-only, 0 credits.
amplitude_active_users
AMPLITUDE EVENT SEGMENTATION — an event over time, with filters and group-bys. metric is uniques / totals / pct_dau / average / histogram / sums / value_avg / formula (and "formula" additionally REQUIRES `formula`, or Amplitude refuses it). Dates take YYYY-MM-DD or YYYYMMDD. AMPLITUDE'S RATE LIMIT IS COST-BASED, not per-request: cost = days × conditions × query type, 1000 per 5-minute window, so one very wide query can exhaust the budget on its own — the fix for a 429 here is to NARROW the range or the conditions, not to retry. AN EMPTY RESULT IS A VALID ANSWER, not a failure: a new or low-traffic Amplitude project genuinely has no data, and reporting that as a broken connection sends the user to fix something that is working. Read-only, 0 credits.
amplitude_segmentation
AMPLITUDE RETENTION — of the people who did startEvent, how many came back and did returnEvent. The two magic values are "_new" (first-time users) and "_active" (any active user), which is what makes "new-user retention" a single call. retentionMode is n-day / bracket / rolling, and "bracket" additionally REQUIRES `brackets` — it is refused without them rather than silently switched to another mode, because a retention curve computed under a different definition than the one asked for is a wrong answer that looks right. Read-only, 0 credits.
amplitude_retention
The Amplitude TAXONOMY — the project's declared events, event properties, user properties or group properties, so a query names something real instead of a guess. category is event (default) / category / event-property / user-property / group-property. IF AMPLITUDE REFUSES THIS, REPORT THEIR REFUSAL AND MOVE ON. The Taxonomy API is widely believed to require an enterprise/Govern entitlement, but Amplitude documents no such gating on either the taxonomy page or the Dashboard API page (both checked), so a 403 here is surfaced as AMPLITUDE'S OWN message and is never presented as a Hermoso plan rule, as a broken connection, or as a fact about the user's plan that we cannot actually know. Every other Amplitude tool is unaffected by that refusal. Read-only, 0 credits.
list_amplitude_events
A video ad's structure: verbatim transcript (voiceover + on-screen text), beat list, duration and sampled frame times; study a reference before remixing it. ~A transcription call.
analyze_video
Post the business’s answer to a public question on the brand’s Google Business Profile listing, or delete the answer already there. THIS IS AN UPSERT — one answer per account, so answering again REPLACES the previous one rather than adding a second. Deleting is public and immediate and is confirm-gated. Needs Google Business Profile connected and the project approved.
answer_google_business_question
AppLovin Ads performance: impressions, clicks, CTR, spend, sales, ROAS and checkout revenue by day, campaign, creative set or country. Works with the Reporting Key alone, which every AppLovin account has. Web reports reach back 90 days, app 45; UTC. Read-only, free.
applovin_ads_report
The AppLovin pixel for the brand’s website: AppLovin’s own install snippet with the account’s Event Key filled in, where it goes (every page’s <head>, Google Tag Manager, or the AppLovin Shopify app), the events to fire for a store or a lead-gen site with copy-ready examples, dedupe ids, and how to validate it. The Event Key is public by design. Calls nothing at AppLovin. Free.
applovin_ads_pixel
Append rows to a Google Sheet Hermoso can reach — one it created (pass the spreadsheetId from create_sheet) or one the user handed over with the Google file picker in the app (find its id with list_drive_files). rows = array of row arrays.
append_to_sheet
Append text to the end of a Google Doc Hermoso can reach — one it created (pass the documentId from create_doc) or one the user handed over with the Google file picker in the app (find its id with list_drive_files). Optional `dropdown:{title, options:[2-50], selected}` appends a Google Docs dropdown chip after the text — e.g. text "Status: " + dropdown {title:"Status", options:["Draft","In review","Approved"]} gives a brief a status the team can click to change; the chip is confirmed by reading the doc back. Set it later with update_doc dropdowns.
append_to_doc
Who changed what on this Apple Ads account, and when. Call it with start and/or end to get one row per TRANSACTION GROUP — a single user action that may have touched several fields, which is what changeCount counts — with the user, the entity and the event type (CREATE / UPDATE / DELETE). Then pass a row’s detailId back as detailId to get the field-by-field before and after. THE TIME BOUND IS MANDATORY: Apple answers 400 without one, so pass start (meaning "since then"), end ("up to then"), or both. Narrow with campaignId, adGroupId, entityType, eventType or userId. Useful for "why did this campaign’s budget change overnight" and for reconstructing what an agent did on the account. On a CREATE event the old values are empty by design; a DELETE is implemented as a record update so its old values ARE populated. Read-only, free.
apple_ads_change_history
COMPETITIVE INTELLIGENCE: what fraction of the available impressions your app actually captures for each search term, by market and day or week. This is the report that finds where you are being out-bid or out-budgeted — a LOW share on a term with high search popularity means budget, bid or eligibility is capping your reach on a term people are really searching, which is the most actionable row Apple publishes. READ THE ENCODING BEFORE QUOTING A NUMBER: lowImpressionShare and highImpressionShare are TIERED, not continuous. Below 91% the two are equal and read as a single percentage; a row where they diverge to 0.91 and 1.0 means ">90%", not "91 to 100". granularity DAILY covers at most 30 days; WEEKLY_SUN_SAT covers at most 4 weeks and its start date MUST be a Sunday (both are refused for free before any request). reportType FIRST_SLOT (default) is the top ad position only; ALL_SLOTS aggregates every position. Timezone is fixed to UTC by Apple. Read-only, free.
apple_ads_impression_share
Apple’s OWN search data for an app you advertise — the fastest way to build a real Apple Search Ads keyword list instead of guessing. THE THREE KINDS DO NOT SHARE A FILTER VOCABULARY and Apple 400s any field that is not an attribute of the one you query, so each takes different parameters. kind:"keyword" (the default) returns suggested keyword strings and accepts `terms` + `countriesOrRegions` — it has NO queryType, and passing one is refused. kind:"phrase" returns longer search phrases and REQUIRES `phrases` (the phrases to look up); kind:"category" returns App Store categories and REQUIRES `categories`. Neither accepts `terms`. POPULARITY IS RELATIVE, NOT A VOLUME: it ranks terms against each other and cannot be read as searches per month. queryType defaults to "SEARCH", which looks up phrases/categories you name; "SUGGESTION" asks Apple to discover new ones and IS CURRENTLY BROKEN AT APPLE — measured 2026-08-17, it hangs ~34s and returns a server error for a request identical to Apple’s own published example, so retrying will not clear it. REQUIRED: promotedObjectId — the app’s adamId (search_apple_ads_apps with no arguments lists the apps this organization owns) or an Apple Maps brandId. An empty result is about the APP, not the account. Feed the winners straight into add_apple_ads_keywords or bulk_apple_ads_keywords. Read-only, free, spends nothing.
apple_ads_keyword_suggestions
Apple Ads performance — impressions, taps, installs, spend, TTR, CPT, CPA. level is campaign / adgroup / ad / keyword / searchterm; promotedObjectType is apps (App Store, the default) or business-brands (Ads on Apple Maps). startTime and endTime are REQUIRED, as YYYY-MM-DD. campaignId is REQUIRED for every level except campaign — it moved from the URL into a filter, but Apple still enforces it (measured: “campaignId filter is required for AD_GROUP reports when promotedObjectType is APPS”), so only a campaign-level report may be account-wide. adGroupId narrows it further. groupBy is restricted PER LEVEL and an unsupported dimension is refused by name, never dropped: campaign and adgroup take deviceClass/ageRange/gender/countryCode/adminArea/locality/storefront/countryOrRegion, keyword and searchterm take only deviceClass/storefront/countryOrRegion, and ad takes only storefront/countryOrRegion. granularity is optional and carries Apple’s own date rules (HOURLY reaches back 7 days and is unavailable on ad and searchterm reports; DAILY 90 days and needs a range longer than one day; WEEKLY 365 days with an end date at least 14 days ago; MONTHLY needs an end date at least 90 days ago). FOR A SINGLE DAY, OMIT granularity — the totals come back in each row’s totalMetrics. Search-term reports are ORTZ-only. grandTotals adds a summary row; emptyMetrics includes entities with no delivery — App Store reports only, never together with groupBy, and never on a search-term report. A report with NO rows genuinely means there was NO delivery in that window and scope: say exactly that, and never present zeros as measured performance. Read-only and free, so run it first after connecting — it proves the credentials work with zero spend risk.
apple_ads_report
The most-searched App Store terms in a genre and market — MARKET-WIDE DEMAND, not your own performance, so it works for finding terms to target before you have spent anything. Cross-read it against apple_ads_impression_share: a term ranking high here where your impression share is low is exactly where budget or bid is leaving installs on the table. genre is a FREE-TEXT App Store genre name (e.g. "PRODUCTIVITY_UTILITIES", "TRAVEL") and Apple publishes no fixed enum, so it is forwarded as given rather than validated against a list that would go stale. granularity WEEKLY_SUN_SAT uses fixed Sunday–Saturday weeks generated Mondays at 07:00 UTC with 65 weeks of retention; MONTHLY uses calendar months refreshed on the 5th UTC with 15 months of retention — an empty result for a very recent window usually means Apple has not generated that period yet rather than that there is no demand. NOTE THE GRANULARITY SETS DIFFER between the two insights reports: DAILY exists on impression share and NOT here. Read-only, free.
apple_ads_search_term_popularity
What Apple thinks an install should cost you. Returns the suggested target CPA for an app — the MAXIMUM tap-install CPI Apple observed across that app’s eligible markets over the last 28 days, together with the market that produced it. Only markets with at least 10 installs in that window qualify, so a brand-new or low-volume app legitimately has no suggestion yet and that is an answer, not a failure. This is the number to put on a MAX_CONVERSIONS campaign, where the campaign-level bid IS the target CPA — it is NOT a per-tap bid and does not belong on a MANUAL_CPT campaign, which refuses a campaign-level bid outright. Scope it with countriesOrRegions to ask about specific markets. Read-only, free, spends nothing.
apple_ads_target_cpa_suggestion
Apply Microsoft Advertising recommendations by id. THIS SPENDS REAL MONEY AND HAS NO UNDO. It raises daily budgets, adds keywords, widens keywords to broad match, DELETES negative keywords so the account buys more searches, and publishes Microsoft-written ads under the brand’s name, all live at the next auction. THE GATE IS UNLIKE THE OTHER MICROSOFT SPEND SWITCHES, AND confirm:true ALONE IS REFUSED: it proves you meant to apply something, not that you know what THESE ones do, and it reads identically for one keyword suggestion and forty budget raises. So call it ONCE with no confirmation. Nothing is applied, and you get every recommendation named with its campaign, exactly what it changes, the current -> recommended budget where there is one, and Microsoft’s own cost estimate. SHOW THAT LIST TO THE USER, get an explicit yes, then call again with confirm:true, confirmCount:<n> and confirmCostIncrease:<n>. BOTH NUMBERS ARE RECOMPUTED FROM A FRESH READ at that moment, so a recommendation Microsoft has withdrawn, already applied or re-priced since you looked fails the check instead of being applied unseen. If ANY id you name is no longer on offer, NOTHING is applied, not even the ones that still are, because applying part of a set you inspected whole is not what you asked for. Microsoft can reject individual items and apply the rest, so the result reports per item: never call a partial apply a failure, and never call it a success. 0 credits to us; the spend lands on the ad account.
apply_microsoft_ads_recommendations
Hermoso AI ChatGPT Plugin FAQ
How the directory, categories and Discoverability Score work.
Read the methodologyHow do I improve Hermoso AI's ChatGPT Plugin discoverability?
The levers are the listing surface agents actually read: names, descriptions, keywords, tool metadata, and registry health. Which lever matters depends on where discovery breaks, which is what continuous measurement shows.
What are Hermoso AI alternatives on ChatGPT?
As of 2026-10-23, Hermoso AI competes with 3RD, Actionbase, AEO/GEO by VibeSEO, Agent Ready, Agent Ready Checker, Agenticaso, AI Interpretation Layer Audit™, AI Visibility Check and 82 more in ChatGPT AI Search & LLM Visibility (AEO/GEO), ranked by public Discoverability Score.
Where is this profile measured?
This profile uses the geography attached to the latest public registry snapshot: US. Locale tags are intentionally omitted.