MCP HeartbeatTHE MCP OBSERVATORY
Public workspace

Explore

Evidence for your next connection.

Independent measurements of remote MCP servers. Every observation has a timestamp.

Read-only probes

MCP server · Code & DevOps

Hermoso: Social Media Manager

AI marketing agent for social: post, schedule and answer comments and DMs across 15 channels.

Checks passed
15 / 22

0 failed · 7 not measured

Median response
1051 ms

Median of successful probes over 7 days

30-day uptime
Not measured

Share of countable probes over 30 days, including daily rollups. Auth challenges are excluded.

Discovered tools
427

Tool definitions returned by this server

Independent monitoring

Health & checks

Status history

Responded

The server completed its last measured handshake. This is an observation, not a guarantee.

Last checked
Check summary by test group
Test areaPassedFailedUnmeasured
ConnectionCan a client connect and discover tools?
500
Reliability & speedDoes it respond consistently and quickly?
103
Access & authenticationWhat is required to use this server?
102
Tool qualityAre the tool definitions usable by an agent?
400
Publisher identityCan the publisher be traced to its source?
402

Unmeasured means there is not enough evidence to decide. How these checks work

Inspect individual checks 22 recorded

Core

  • Pass
    Reachable

    Latest probe returned HTTP 200 in 1093 ms.

  • Pass
    Valid handshake

    protocolVersion 2025-11-25, serverInfo.name hermoso.

  • Pass
    Lists tools

    tools/list returned 427 tools.

  • Pass
    Current protocol

    Negotiated 2025-11-25. The bar is 2025-06-18 or newer.

  • Pass
    HTTPS

    The endpoint URL is https.

Performance

  • Not measured
    Fast (p50)

    Median latency 1051 ms over 2 samples in 7 days. The bar is 1000 ms. Need at least 5 samples.

  • Not measured
    Consistent (p95)

    95th percentile 1089 ms over 2 samples in 7 days. The bar is 3000 ms. Need at least 20 samples.

  • Not measured
    Reliable (uptime)

    2 of 2 attempts returned a tool list in 30 days (100%). The bar is 99%. Need at least 30 samples.

  • Pass
    Stable tool list

    No tool was removed and no required parameter was added in the last 30 days.

Access

  • Pass
    Open access

    tools/list succeeded with no credential, and the record declares none.

  • Not measured
    Standard auth

    Auth is not required, so the OAuth metadata check does not apply.

  • Not measured
    Free tier cited

    No publisher quote has been checked.

Tools

  • Pass
    Tools described

    Every tool description is at least 20 characters.

  • Pass
    Valid input schemas

    Every inputSchema is a JSON Schema object.

  • Pass
    Parameters documented

    2378 of 2549 parameters have a description (93%). The bar is 80%.

  • Pass
    Safety hints

    Every tool sets readOnlyHint or destructiveHint.

Publisher

  • Pass
    Verified namespace

    Listed in the Official MCP Registry.

  • Not measured
    Website matches publisher

    This namespace is not a reverse domain, so the website comparison does not apply.

  • Pass
    Source repo linked

    A repository URL is on file.

  • Pass
    Repo owner matches

    The GitHub repository owner matches the namespace login.

  • Pass
    Recently updated

    Updated 2026-11-06T21:42:13.294818Z, within 180 days.

  • Not measured
    Open-source licence

    The GitHub licence lookup has not returned an OSI result yet.

Start using Hermoso: Social Media Manager

Connect to this server

Choose your client, copy the configuration, then enable the server in your client’s MCP settings.

Run this command in your terminal, then restart Claude Code.

Claude Code

claude mcp add --transport http hermoso-social-media-manager https://app.hermoso.ai/mcp?tools=app-social&src=registry

Capabilities

Tools 427

Showing 100 of 427 tools.

add_intercom_note

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, or connect_connector).

Discovered
answer_google_business_question

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.

Discovered
append_to_doc

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.

Discovered
append_to_notion_page

Add to the END of an existing Notion page (pageId or its link, from list_notion_pages): markdown content, a swipefileCollection, or includeSchedule:true. Nothing already on the page changes. Free.

Discovered
append_to_sheet

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.

Discovered
backfill_posts

Import this brand's PAST posts from a channel into the performance record, so 'which hook works' can draw on history rather than only on what was published since Hermoso started recording. Supports facebook, instagram, threads, youtube, tiktok, pinterest and bluesky; the others say plainly why they cannot (LinkedIn has no enumerate-my-posts endpoint on our grant, X bills per read so it is excluded from bulk import, Google Business has had no per-post insights since 2023, and Telegram's Bot API cannot read a chat's past messages at all — nothing published before Hermoso is recoverable through a bot token). BOUNDED, RESUMABLE AND QUOTED: it runs as a DRY RUN by default and tells you how many posts it found and what reading them will cost — pass confirm:true to import, and pass the returned cursor to continue. AN IMPORTED POST IS WEAKER EVIDENCE THAN A RECORDED ONE and is labelled 'backfilled': its hook is recovered ONLY where the post matches a Hermoso creation by asset or caption. A post made outside Hermoso stays UNATTRIBUTED — it counts toward channel and format totals but never votes on which hook works. Never guess a hook from a caption. Free.

Discovered
billing_status

Show this account's billing at a glance: current plan (quote plan.priceUsd per plan.period), credit balance, whether auto-reload is on, whether a card is on file, and whether YOU (this key) have ADMIN rights to change billing. Read-only, free. Call it before upgrade_plan / set_auto_reload to know what's possible — members have read-only billing. IN A SHARED TEAM WORKSPACE A MEMBER SEES THE PLAN AND THE BALANCE ONLY: the workspace owner's payment card and auto-reload belong to them and are not reported (billingScope:'member'). Never tell a member there is no card on file — the honest answer is that you cannot see it. plan.memberRate: media models at cost + 10%; plan.legacy: an older plan kept at its price. posting: this month's posting allowance ("N of 5 posts used this month" on Free, unlimited on every paid plan).

Discovered
bluesky_account

ACCOUNT-LEVEL numbers for a Bluesky account — FOLLOWERS, following and total posts — plus display name, bio, avatar and when it was created. With no argument it reads the CONNECTED account, which is the answer to “how many followers do we have on Bluesky”; pass `actor` (a handle or DID) to read any other account, which is free and is how you size a competitor there. The reply says which one it read: `self` is true only for the connected account, compared on the DID because a Bluesky handle can change. A counter Bluesky does not return comes back under `absent` with the reason and is NEVER reported as zero — all three are optional in the lexicon. Read-only, 0 credits, no extra permission. Needs Bluesky connected (Settings > Connectors > Bluesky, or connect_connector).

Discovered
bluesky_post_metrics

Read live engagement for up to 25 of the connected account’s Bluesky posts — likes, reposts, replies, quotes and bookmarks. Address a post by its AT-URI (the `at://…` value post_to_bluesky returns), not its web URL. Bluesky publishes NO impression or view count in any AT Protocol lexicon, so these are COUNTS with no denominator and no engagement rate can be computed from them — do not present one. A uri Bluesky returns nothing for is reported as MISSING (deleted, or not on the connected account), never as zero engagement. Read-only, 0 credits. Needs Bluesky connected.

Discovered
business_google_updated

What GOOGLE thinks a Business Profile should say — the pending edits it has inferred from the web, from user submissions and from its own sources, with `changedFields` naming exactly which fields it would change. THESE CAN GO LIVE IF NOBODY LOOKS, which is why a business needs to see them. An empty result is a real and good answer (Google is proposing nothing) and is reported as such, never as a failure. Read-only — accepting or rejecting an edit is a write this tool does not do. 0 credits. Needs Google Business Profile connected.

Discovered
buy_credits

Out of credits? Top up with a credit PACK. Call with no argument to list the available packs (id · credits · price). If the account has a saved card and you have billing-admin rights, calling with `pack` quotes the exact charge and calling again with confirm:true AND the quote's quote_token charges the saved card instantly (same one-click top-up as the app — no redirect). If there's no saved card yet, you get a Stripe checkout URL to hand your human for the FIRST purchase; their card saves for one-click after that. Packs only; subscriptions are managed by a person in Billing. IF YOU ARE AN AGENT HOLDING YOUR OWN PAYMENT CREDENTIAL, there is a third path that needs no human at all: `POST /api/billing/machine-payment` with a `packId` answers HTTP 402 carrying an MPP challenge, and grants the pack once you authorise and retry with the credential — the same packs, the same prices, the same credits. `GET /api/billing/config` carries a `machinePayments` block listing the packs with their per-credit rates and saying whether that lane is enabled on this server. Most agents do NOT have their own credential yet, so the checkout link above remains the normal path. To stop running out entirely, turn on auto-reload with set_auto_reload (admin) — low balances then top themselves up from the saved card automatically.

Discovered
call_tool

Run ANY Hermoso tool by name — including the paid-campaign, analytics and channel-admin tools that are not in this session's starting list — with the same permissions, the same account and the same result as calling it directly. Get the exact `name` and its `args` from find_tools first. This is the route on hosts that cannot reload their tool list mid-conversation (claude.ai, ChatGPT): enable_tools switches a group on server-side, but such a host keeps the list it fetched at connect time. Arguments are validated against the tool's own schema and a mistake is answered with the expected parameters, not a silent default. Refused by name, with the way out, when the tool needs a connector this workspace has not made or is withheld by the host's own policy.

Discovered
cancel_klaviyo_campaign

Stop a Klaviyo campaign that is scheduled or sending: action 'revert' puts it back to a draft (only before it goes), 'cancel' cancels it for good. Emails already delivered stay delivered. Without confirm:true nothing changes and you get what would happen. Free. Needs Klaviyo connected (Settings > Connectors > Klaviyo, or connect_connector with a private API key).

Discovered
cancel_mailchimp_campaign

Stop a Mailchimp campaign: action 'unschedule' puts a scheduled one back to a draft; 'cancel' stops one that is sending (emails already delivered stay delivered). Without confirm:true nothing changes and you get what would happen. Free. Needs Mailchimp connected (Settings > Connectors > Mailchimp, or connect_connector with an API key).

Discovered
cancel_scheduled

Remove a queued post before it goes out. Get the id from list_scheduled. Only works while it is still queued — something already published cannot be unsent (use manage_meta_post to delete a Facebook/Instagram post after the fact).

Discovered
cancel_slack_scheduled

Cancel a message scheduled in Slack (from list_slack_scheduled). Slack refuses once it is within 60 seconds of posting. Free.

Discovered
clear_sheet_range

Empty a range of cells in a Google Sheet, leaving the rows themselves in place. DESTRUCTIVE: call it WITHOUT confirm first and nothing is cleared — you get back the real number of filled cells in that exact range. Show the user that number, get an unambiguous yes, then call again with confirm:true AND confirmCells set to it. The echo is not ceremony: it is what catches naming A1:Z1000 when you meant A1:Z10, which is the mistake that actually happens. There is deliberately NO default range. The clear is read back and reported as confirmed only if the range really is empty afterwards. To remove a whole tab instead, use manage_sheet_tabs.

Discovered
collect_post_metrics

Fetch fresh performance numbers for this brand's recorded posts and store them as a time-series. Metrics ACCRUE, so a post is read at ~24 hours and again at ~7 days; this collects whichever readings are due and skips the ones already taken. A channel that cannot report a metric records it as ABSENT with the reason — never as zero — and a read that fails is recorded as 'could not tell', which contributes to nothing. X IS SKIPPED BY DEFAULT because X bills us per API call: pass includeMetered:true to include it, and tell the user it costs credits BEFORE you do. The skip is always reported so a channel missing from the numbers is never mistaken for one that performed badly. Free except for X.

Discovered
comment_on_meta_post

Leave a NEW top-level comment on one of the brand’s own posts — a Facebook Page post OR an Instagram post/Reel — as the brand. This is the "link in the first comment" tactic (Facebook suppresses reach on a post with an outbound link in the body, and an Instagram caption link is not clickable), and it is also how you add a pinned clarification, an offer code or a correction without editing the post. It is PUBLIC and posted as the brand: show the user the exact wording and get their go-ahead first. postId comes from list_meta_posts (target:"instagram" for Instagram media ids). INSTAGRAM: text only (Meta documents no attachment on that edge, so attachmentUrl is refused by name), not on live videos; pass account for an Instagram-Login account or one of several. Free.

Discovered
comment_on_tiktok_video

Write a NEW top-level comment on a TikTok post the authorized account owns. Text (≤1,200 characters, UTF-8) or an image, and TikTok requires at least one of the two. AN IMAGE HERE MUST BE UPLOADED FIRST — a raw URL is refused on a new comment; call upload_tiktok_comment_image and pass back imageUri + imageWidth + imageHeight together (a reply is the one place TikTok accepts a plain URL). TIKTOK SILENTLY HIDES COMMENTS IT FLAGS AS SPAM and sends no signal when it does, so avoid posting many near-identical comments in a short window, and read the comment back with list_tiktok_comments(status:'PUBLIC') if it matters that it is visible. NEEDS THE TIKTOK ACCOUNT AUTHORIZATION (see tiktok_account_status).

Discovered
connect_connector

Connect an account that links with a KEY rather than a sign-in screen, from here, with no browser: AppLovin Ads, Stripe, Intercom, Mailchimp, Klaviyo, Vercel, ChatGPT Ads, Apple Ads, Bluesky, Telegram, Bing Webmaster Tools, PostHog, Mixpanel, Amplitude, WordPress, Slack, Discord, Webhook. Pass provider and fields in that provider's own field names (listed below, * = required). The key is checked live with the provider before anything is saved, exactly as the app's Connectors page checks it, and the reply is read back from the saved connection. OFFER BOTH WAYS AND LET THE USER CHOOSE: a key pasted into this chat stays in this conversation's history, while pasting it in the app (Workspace > Connectors, or the one-click link https://app.hermoso.ai/?connect=<provider>) keeps it out of the chat. Hermoso never repeats a submitted key back. An account that connects through the provider's own sign-in screen (OAuth) cannot be connected here: this answers with the link to hand the user instead. Apple Ads with no key material first generates a signing key pair and returns the public key to register with Apple plus a setupToken to send back. Fields: applovin_ads {reportKey, eventKey, apiKey, capiKey, accountId} · stripe {apiKey*} · intercom {accessToken*, region} · mailchimp {apiKey*, server} · klaviyo {apiKey*} · vercel {token*, team} · openai_ads {apiKey*} · apple_ads {clientId, teamId, keyId, privateKey, setupToken, orgId} · bluesky {identifier*, appPassword*, pds} · telegram {token*} · bing_webmaster {apiKey*} · posthog {apiKey*, region, host, projectId} · mixpanel {username*, secret*, projectId*, region, workspaceId} · amplitude {apiKey*, secretKey*, region, host} · wordpress {siteUrl*, username*, appPassword*} · slack {webhookUrl, announceWebhookUrl} · discord {webhookUrl, announceWebhookUrl} · webhook {webhookUrl*}.

Discovered
convert_onedrive_file

Turn a file already in the user’s OneDrive into a PDF or a JPG — Microsoft does the conversion on its own servers, so nothing is re-encoded here and nothing is lost in a screenshot. It reads about 130 source formats, which is the point: PowerPoint and Word decks, Excel, Photoshop PSD, Illustrator AI, Sketch, 3D (fbx/glb/obj), video (mp4/mov/webm), HEIC from an iPhone, and the raw camera formats (CR2, NEF, ARW, DNG) that nothing else in this product can open. Use it to turn a client’s deck into images you can actually put in an ad, to get a usable JPG out of a designer’s PSD or a photographer’s raw file, or to hand someone a PDF of a spreadsheet. CONVERTING TO JPG REQUIRES BOTH width AND height — Microsoft refuses the call without them. The result is stored at a durable Hermoso URL you can pass straight to a render or a post; Microsoft’s own conversion link expires within minutes, so do not hand that one to anyone. Needs OneDrive connected — no new permission.

Discovered
create_brand

Add a NEW profile (a brand, client, creator or personal workspace) and switch to it. Each has its OWN brand details, memory, swipefile, Library, avatars, skills, playbooks and connectors; nothing leaks between them. Use draft_brand to FILL it and update_brand to edit it. Re-running with the same name returns the existing profile instead of a duplicate. Creating it is free. Drafting a NEW brand into it (draft_brand) needs the credits for its research on the balance, about 100; with fewer, draft_brand refuses before anything runs or is charged and says how many it needs and how many the account has.

Discovered
create_doc

Create a new Google Doc in the user’s Drive with a title + optional body text — e.g. export ad copy, a creative brief, or a report. Returns the document id + URL. Needs Google Drive connected (Settings > Connectors > Google Drive — one connection covers Drive, Sheets and Docs).

Discovered
create_drive_folder

Create a folder in the user’s Google Drive (optionally nested under parentId) to organize saved files. Returns the folder id + webViewLink. Use that ID as update_drive_file’s moveToFolderId or as parentId for a nested folder. NOTE: save_to_drive’s `folder` is a NAME, not this id — it find-or-creates a folder by that name, so pass the folder NAME there (or omit and just save, then move with update_drive_file).

Discovered
create_klaviyo_campaign

Create a Klaviyo email CAMPAIGN DRAFT: templateId (create_klaviyo_template), subject, previewText, the audience as listIds and/or segmentIds (excludeIds leaves people out), optional name, fromEmail / fromLabel (default: the account's default sender) and replyTo. It is created as a DRAFT and NOTHING IS SENT; the answer is Klaviyo's own row read back. To send or schedule it use send_klaviyo_campaign, which shows the recipient count first. Free. Needs Klaviyo connected (Settings > Connectors > Klaviyo, or connect_connector with a private API key).

Discovered
create_klaviyo_template

Make an EMAIL TEMPLATE in Klaviyo from a creative: image (any reference upload_klaviyo_image takes; it goes into the image library first) plus copy YOU write in the brand's voice (read get_brand for voice and colours): headline, body (paragraphs split by a blank line), ctaText + ctaUrl for the button, accent (#RRGGBB button colour). It is laid out as a 600px email with the unsubscribe link and sender address Klaviyo requires. Or pass html for the user's own markup (that footer is added if it has no unsubscribe link). Saves the template only, nothing is sent; returns the templateId for create_klaviyo_campaign. Free. Needs Klaviyo connected (Settings > Connectors > Klaviyo, or connect_connector with a private API key).

Discovered
create_linkedin_event

Create an event on a LinkedIn company Page the brand administers: an EXTERNAL online event (a webinar on Zoom, YouTube or your own site — pass url), an IN-PERSON event (pass address), or a LinkedIn Live event (format:"live" — that one needs LinkedIn Live access on the member, and without it LinkedIn refuses with “The member does not have LINKEDIN_LIVE creation access”, measured 2026-08-31). Optionally attach a LinkedIn REGISTRATION FORM (registrationForm:true + privacyPolicyUrl, or your own approved leadFormId); sign-ups arrive as EVENT leads in list_linkedin_leads(leadType:"EVENT"). Read back from LinkedIn. Warning: ANNOUNCING IS A SEPARATE, PUBLIC STEP (announce:true does it in this call): LinkedIn requires a SEPARATE post to make an event publicly visible, so creating one puts nothing in anyone’s feed and the brand decides when to tell people. That separation is LinkedIn’s own and it is why an agent cannot accidentally broadcast to a company’s followers. Edit it with update_linkedin_event until it starts. A custom background image is not offered yet. Needs the Events permission (administrator flag) and a reconnect, the same two conditions as list_linkedin_events. 0 credits.

Discovered
create_mailchimp_campaign

Create a Mailchimp email CAMPAIGN DRAFT: templateId (create_mailchimp_template), subject, previewText, audienceId and optionally segmentId (part of the audience), optional name, fromName and replyTo (default: the audience's own defaults). It is created as a DRAFT and NOTHING IS SENT; the answer is Mailchimp's own row read back with its send checklist. To send or schedule it use send_mailchimp_campaign, which shows the recipient count first. Free. Needs Mailchimp connected (Settings > Connectors > Mailchimp, or connect_connector with an API key).

Discovered
create_mailchimp_template

Make an EMAIL TEMPLATE in Mailchimp from a creative: image (any reference upload_mailchimp_image takes; it goes into the File Manager first) plus copy YOU write in the brand's voice (read get_brand for voice and colours): headline, body (paragraphs split by a blank line), ctaText + ctaUrl for the button, accent (#RRGGBB button colour). It is laid out as a 600px email with the unsubscribe link and mailing address Mailchimp requires. Or pass html for the user's own markup (that footer is added if it has no *|UNSUB|*). Saves the template only, nothing is sent; returns the templateId for create_mailchimp_campaign. Free. Needs Mailchimp connected (Settings > Connectors > Mailchimp, or connect_connector with an API key).

Discovered
create_messenger_marketing_campaign

The container a paid Messenger marketing message is sent from (Meta act_<AD>/message_campaign). Budgets are in the AD ACCOUNT’s currency (the *Usd parameter names are historical); Meta bills per DELIVERED message against the campaign budget, and creating it sends and charges nothing. A NEW campaign cannot send for about an hour (61 minutes measured 2026-09-14) while Meta populates and prepares it (2300012 then 2300041, each with remaining_seconds), so create it ahead of time or reuse one from list_messenger_marketing_campaigns. dailyBudgetUsd OR lifetimeBudgetUsd, and Meta refuses either under USD 30 (code 1885272, measured 2026-09-14) (omit both and Meta sets an estimated daily cap; Meta may spend up to 175% of a daily budget on one day, never more than 7× per week). REGION LAW: Meta lets us serve businesses in 20 countries only — US, Mexico, Brazil, India, Australia, Singapore, UAE, Saudi Arabia, Hong Kong, Taiwan, Thailand, Malaysia, Indonesia, Philippines, Vietnam, New Zealand, Chile, Colombia, Peru, Israel — NOT Canada, the EU or the UK — and cannot deliver to people in the EU, UK, Japan, South Korea or Australia. Political Pages are excluded. dryRun:true shows the exact body.

Discovered
create_meta_catalog

Create a NEW product catalog on one of the merchant’s Meta business portfolios. A catalog is the input to Advantage+ catalog ads (dynamic product ads) — build it first, then fill it from the merchant’s feed or Shopify, then point a campaign at it. Name the owning portfolio EITHER by its numeric businessId OR by businessName, exactly as list_meta_catalogs prints it (“DMDB Group”) — a name that matches no portfolio, or more than one, is refused BY NAME with the portfolios that really exist and their ids, never guessed, and it never falls back to “the only portfolio”. Vertical defaults to commerce and CANNOT be changed afterwards, nor can the owning portfolio, so both are worth getting right the first time. IMPORTANT — META DOES NOT REFUSE A DUPLICATE NAME: it silently creates a SECOND catalog with the same name and nothing to tell the two apart, so Hermoso refuses a name the portfolio already uses and tells you the existing id (if a previous create seemed to fail, that id is very likely the catalog it actually made). Pass allowDuplicateName:true if you genuinely want a twin. The confirmation is READ BACK from Meta — its create returns only an id, so the name, vertical and portfolio in the summary were fetched, not echoed. Free, 0 credits.

Discovered
create_onedrive_folder

Create a folder in the user’s OneDrive (optionally nested under parentId) to organize saved files. Returns the folder id + webViewLink. Use that id as update_onedrive_file’s moveToFolderId or as parentId for a nested folder. NOTE: save_to_onedrive’s `folder` is a NAME (find-or-created), not this id.

Discovered
create_pinterest_board

Create a board on the connected Pinterest account. Needed because a Pin cannot exist without a board, and a NEW Pinterest business account has none — if list_pinterest_boards comes back empty, make one here rather than telling the user you can't pin. Boards are PUBLIC unless you pass privacy 'SECRET'; a Pin on a secret board is invisible to everyone, so only choose that if the user asked for it.

Discovered
create_pinterest_catalog

Create a Pinterest product CATALOG (RETAIL for products, or HOTEL) — the container feeds and items fill. Read back from Pinterest. Most merchants can skip it: a feed created without catalogId lands in a default retail catalog. 0 credits.

Discovered
create_sheet

Create a new Google Spreadsheet in the user’s Drive and optionally fill it with rows — e.g. export a swipefile, ad list, or performance report. Pass rows as an array of row arrays (first row = headers). Returns the spreadsheet id + URL. Needs Google Drive connected (Settings > Connectors > Google Drive — one connection covers Drive, Sheets and Docs).

Discovered
create_telegram_invite_link

Create a NAMED invite link to a Telegram channel or group, optionally with an expiry, a member cap, or join approval. THIS IS THE ONLY SUBSCRIBER ATTRIBUTION TELEGRAM OFFERS: the Bot API exposes no per-post views and no post history, so “which campaign brought these subscribers” is otherwise unanswerable — give each campaign its own named link and Telegram reports how many joined through each. A member limit and join approval are mutually exclusive in Telegram’s own docs and passing both is refused by name. The bot must be an administrator with invite rights. Free, 0 credits. Needs Telegram connected.

Discovered
create_whatsapp_template

Submit a new WhatsApp message template for Meta’s review. This is the ONLY way a business can start a WhatsApp conversation: outside the 24-hour window that opens when a customer messages first, WhatsApp accepts approved templates and nothing else. CATEGORY IS NOT COSMETIC — MARKETING (anything promotional), UTILITY (a transactional follow-up to something the customer did) or AUTHENTICATION (a one-time passcode) decides both what Meta charges for the conversation and the policy the content is reviewed against, and Meta RE-CATEGORISES a template whose content does not match the claim; the reply reports the category Meta actually assigned, not the one requested. The name is lowercase letters, digits and underscores only. Language is never defaulted: en and en_US are different templates to Meta. Submitting does NOT make it sendable — it comes back PENDING and has to be re-read with list_whatsapp_templates. CAROUSEL TEMPLATES ARE SUPPORTED: add a “CAROUSEL” component whose “cards” array holds 2–10 cards, each card carrying its own HEADER (format IMAGE or VIDEO for a media carousel, PRODUCT for a catalog one), an optional card BODY of at most 160 characters — a different and far shorter limit than the message bubble’s 1024 — and up to 2 buttons. Meta requires every card to carry THE SAME components, and if any card has body text then all of them must. A MEDIA HEADER NEEDS AN UPLOADED ASSET: any IMAGE, VIDEO or DOCUMENT header — including every card header in a media carousel — must carry example: { header_handle: [ … ] } holding a handle from Meta’s Resumable Upload API, or Meta refuses the whole template with a bare “Invalid parameter”. Pass `mediaUrl` on that header (a public JPEG/PNG for IMAGE, MP4 for VIDEO, PDF for DOCUMENT) and Hermoso uploads it through Meta's Resumable Upload API and fills the handle; a header with neither is refused locally and by name. A TEXT header needs no handle, and a PRODUCT card carousel takes its pictures from a connected catalog. 0 credits.

Discovered
create_wordpress_term

Create a category or tag on the WordPress site. (post_to_wordpress also creates any category or tag NAME it is given that the site lacks.) Free.

Discovered
delete_bluesky_post

PERMANENTLY delete one of the connected Bluesky account's OWN posts. IRREVERSIBLE — the AT Protocol removes the record from the account's repo, there is no trash and no undelete, and the post's likes, reposts, replies and quotes go with it. Call it WITHOUT confirm first: nothing is deleted, and it reports the post's REAL text and its live like / repost / reply / quote counts read back from Bluesky. Show the user that, get an unambiguous yes, then call again with confirm:true — plus, once the post has ANY engagement, confirmText echoing the post's own text (the first 40 characters is enough; any longer leading run works too). confirmText exists because confirming that you meant to delete SOMETHING does not prove you aimed at the right post, and a wrong id must not be confirmable blind. A brand-new post with nothing on it stays a ONE-call delete. Identify the post by its AT-URI or by just its RECORD KEY — the short id at the end of its bsky.app link, e.g. 3mtc4n3fibn2x. Deleting only ever works on the connected account's own posts; another account's URI is refused. 0 credits. Needs Bluesky connected (Settings > Connectors > Bluesky, or connect_connector).

Discovered
delete_brand

PERMANENTLY delete a profile and EVERYTHING in it — brand details, memory, swipefile, Library creations, generated assets, avatars, skills, playbooks, chats — and disconnect its connected accounts. Irreversible, and it applies to everyone the workspace is shared with. Call it WITHOUT confirm first: it reports exactly what that workspace holds. Show the user that inventory verbatim, get an unambiguous yes, then call again with confirm:true — plus, if the workspace is not empty, confirmName set to its exact name and confirmConnectors set to the number of connected accounts it reported. Those two exist because confirming INTENT does not prove you picked the right WORKSPACE, and a wrong target is how a live profile was destroyed. The account's FIRST/anchor profile cannot be deleted this way (it holds the workspace's root storage) — that one is replaced from the app.

Discovered
delete_creator

Remove a saved creator from this workspace’s cast by id (from list_creators). Records a cross-device delete so they don’t reappear on the user’s other devices. It only drops the roster entry — ads already rendered with that person are untouched — and the same portrait can be saved again with save_creator, so no confirm is needed.

Discovered
delete_discord_message

PERMANENTLY delete a Discord message Hermoso posted (the bot's or the announcement webhook's). Call it WITHOUT confirm first: nothing is deleted and you get a sentence quoting the message to show the user. Free.

Discovered
delete_dm_automation

Delete one automation by id on any channel. Nothing further is sent by it; messages already sent stay; on X the event subscription is removed when no rule needs it. To pause instead, save it with enabled:false. Free.

Discovered
delete_drive_file

Delete a Drive file. By default it goes to Trash (recoverable); pass permanent:true to delete it forever. Pass fileId (from list_drive_files) + confirm:true. Irreversible when permanent — confirm with the user first.

Discovered
delete_google_business_post

Remove a Post from the brand’s Google Business Profile listing. This takes it off Google Search and Maps immediately and CANNOT be undone — confirm with the user first. Pass the full post name from list_google_business_posts. Needs Google Business Profile connected.

Discovered
delete_instagram_dm_automation

Delete one automation by id. Nothing further is sent by it; DMs already sent stay. To pause instead, save it with enabled:false. Free.

Discovered
delete_klaviyo_campaign

PERMANENTLY delete a Klaviyo campaign (a draft, a sent or a cancelled one; a scheduled one must be reverted first). Without confirm:true nothing is deleted and you get the campaign it would delete; call again with confirm:true only after the user says yes. Free. Needs Klaviyo connected (Settings > Connectors > Klaviyo, or connect_connector with a private API key).

Discovered
delete_klaviyo_template

PERMANENTLY delete a Klaviyo template. Without confirm:true nothing is deleted and you get the template it would delete; call again with confirm:true only after the user says yes. Free. Needs Klaviyo connected (Settings > Connectors > Klaviyo, or connect_connector with a private API key).

Discovered
delete_library_item

PERMANENTLY delete images/videos from this brand's Library: the entry and the stored file. Pass ids (L0… from list_library) or urls. Without confirm it lists what would be deleted; ask the user, then call again with confirm:true. Free.

Discovered
delete_linkedin_comment

Remove a comment from one of your LinkedIn company Page's posts — your own Page's comment, or somebody else's on your Page. Community Management surface, same separate connection. CONFIRM-GATED: call once without `confirm` to be refused and shown what would go, then again with confirm:true. Deleting is immediate and public. `organizationId` is required because LinkedIn refuses a Page-level delete that does not name the acting organization. The delete is idempotent, so a repeat reports success rather than an error.

Discovered
delete_linkedin_lead_subscription

Remove a lead notification webhook (subscriptionId from list_linkedin_lead_subscriptions). Leads themselves are unaffected and stay readable; only the real-time delivery stops. Read back from LinkedIn. Free.

Discovered
delete_location

Remove a saved place by id (list_elements). Renders already made are untouched.

Discovered
delete_mailchimp_campaign

PERMANENTLY delete a Mailchimp campaign (a draft or a sent one; a scheduled one must be unscheduled first). Without confirm:true nothing is deleted and you get the campaign it would delete; call again with confirm:true only after the user says yes. Free. Needs Mailchimp connected (Settings > Connectors > Mailchimp, or connect_connector with an API key).

Discovered
delete_mailchimp_template

PERMANENTLY delete a Mailchimp template. Without confirm:true nothing is deleted and you get the template it would delete; call again with confirm:true only after the user says yes. Free. Needs Mailchimp connected (Settings > Connectors > Mailchimp, or connect_connector with an API key).

Discovered
delete_messenger_marketing_campaign

Delete a marketing-message campaign. Irreversible: the first call names it and refuses; confirm:true deletes.

Discovered
delete_meta_catalog

PERMANENTLY delete a product catalog. Meta does not let a catalog be removed from a business portfolio and then restored, and it cannot be transferred to another portfolio — there is no archive and no undo, and its product sets go with it, so any ad set still bound to one of those sets keeps spending and stops having products to show. Confirming is therefore not enough on a catalog that holds anything: pass confirm:true PLUS confirmName set to the catalog’s exact name (and confirmChildren when asked), which proves you inspected the catalog you are aiming at rather than merely meaning to delete something. An EMPTY catalog stays a one-call delete. Call meta_catalog_blast_radius first to get those numbers and to show the user what they are agreeing to. The result is the READ-BACK, never Meta’s acknowledgement — if the catalog still resolves afterwards you are told it is NOT confirmed. Free, 0 credits.

Discovered
delete_onedrive_file

Delete a OneDrive item — it moves to the OneDrive recycle bin (recoverable there). Pass fileId (from list_onedrive_files) + confirm:true. Confirm the exact file with the user first.

Discovered
delete_pinterest_board

PERMANENTLY delete a board AND EVERY PIN ON IT. This is the heaviest thing that can be done to a Pinterest account and there is no undelete. Call it WITHOUT confirm first: nothing is deleted, and it answers with the board’s real name, how many Pins are on it, how many people FOLLOW it and how many collaborators lose access — all read back from Pinterest. Show the user that, then call again with confirm:true plus confirmName (its exact name) and confirmChildren (the Pin count it reported); those echoes exist because a caller who has not looked at the board cannot supply them, and confirming intent alone does not prove aim. IF THEY ONLY WANT IT OUT OF PUBLIC VIEW: making the board SECRET with update_pinterest_board is reversible, but it needs a Pinterest permission (boards:write_secret) that an older connection may not hold. The unconfirmed refusal says which applies to this connection, read off the live scope set, so relay it verbatim rather than guessing. 0 credits. Needs Pinterest connected.

Discovered
delete_pinterest_pin

PERMANENTLY delete a Pin. Pinterest has no undelete and no archive for one. Call it WITHOUT confirm first: nothing is deleted, and it answers with the Pin’s real title, its lifetime saves and impressions, and whether it HAS BEEN PROMOTED in an ad — all read back from Pinterest. Show the user that, get an unambiguous yes, then call again with confirm:true plus confirmName (its exact title) once it has saves or has been promoted, because confirming that you meant to delete SOMETHING does not prove you aimed at the right Pin. DELETING A PIN THAT AN AD PROMOTES pulls the creative out from under that ad, so check the promoted flag before agreeing. The verdict is read back from Pinterest, never taken from its 2xx. 0 credits. Needs Pinterest connected.

Discovered
delete_playbook

Delete a saved playbook by id (from list_playbooks). Records a cross-device delete so it does not come back on the next sync. Minor + re-creatable, so no confirm needed.

Discovered
delete_skill

Delete one of the workspace’s CUSTOM skills by id (from list_skills). Built-in skills/recipes can’t be deleted. Minor + re-creatable, so no confirm needed.

Discovered
delete_slack_message

PERMANENTLY delete a Slack message the Hermoso app posted. Call it WITHOUT confirm first: nothing is deleted and you get a sentence quoting the message to show the user. Free.

Discovered
delete_telegram_message

PERMANENTLY delete one message the bot posted to a Telegram chat. Call it WITHOUT confirm first: nothing is deleted and you get a sentence to show the user. There is deliberately NO preview of the message — the Bot API has no method that reads one message back, so anything shown would be invented, and for the same reason the result after deleting is Telegram’s own success answer rather than a verified read-back. TWO VENDOR LIMITS, both Telegram’s and neither ours: "A message can only be deleted if it was sent less than 48 hours ago", and in a CHANNEL the bot needs the Post Messages right to remove even its own posts. Takes the same chatId as post_to_telegram plus the messageId post_to_telegram returned. 0 credits. Needs Telegram connected (Settings > Connectors > Telegram, or connect_connector).

Discovered
delete_thread

PERMANENTLY delete one of the brand’s Threads posts. IRREVERSIBLE — Threads has no undelete. Call it WITHOUT confirm first: nothing is deleted, and it answers with the post’s real text plus its views, likes, replies and reposts read back from Threads. Show the user that, get an unambiguous yes, then call again with confirm:true — plus confirmName (the post’s exact text as it was reported) once anyone has engaged with it. confirmName exists because confirming that you meant to delete SOMETHING does not prove you aimed at the right post, and a wrong id must not be confirmable blind. Threads allows only 100 deletions per account per rolling 24 hours; threads_publishing_limit says how many are left, and a quota refusal otherwise reads like a broken connection. Note Meta documents nothing about what a delete does to the replies underneath a post, so do not promise the conversation survives. 0 credits.

Discovered
delete_tiktok_post_ad_authorization

Delete the Spark-Ads authorization code for a post the authorized account owns. CONFIRM-GATED, and not because it spends: ads already built on the post keep running, but no NEW ad can be built against it until a fresh code exists, and TikTok REFUSES this outright while any campaign or ad using the post is still ACTIVE — pause those first. Calling it without confirm changes nothing and returns the sentence describing exactly what would happen. THE READ-BACK INVERTS: proof of success is that TikTok no longer reports a code for the post, and a code that is still there is reported as not confirmed rather than done. NEEDS THE TIKTOK ACCOUNT AUTHORIZATION (see tiktok_account_status).

Discovered
delete_whatsapp_template

PERMANENTLY delete a WhatsApp message template. META DELETES EVERY LANGUAGE OF A NAME AT ONCE, which is a wider blast radius than most callers picture, so call it WITHOUT confirm first: nothing is deleted and it reports exactly how many templates and which languages would go. Then show the user that, get an unambiguous yes, and call again with confirm:true AND confirmName set to the template’s exact name — confirming that you meant to delete SOMETHING does not prove you aimed at the right template. Any message sent against a deleted template afterwards is refused. The answer is a RE-READ from Meta, never the 200: if the template still comes back it is reported as not deleted. 0 credits.

Discovered
delete_wordpress_post

Delete a WordPress post. Call it WITHOUT confirm first: nothing is deleted and you get a sentence naming the post to show the user. By default it goes to WordPress's TRASH (restorable from Posts > Trash); permanent:true skips the trash and cannot be undone. Free.

Discovered
delete_x_post

Permanently delete one of the connected account’s posts on X. This CANNOT be undone — confirm the exact post with the user first. Costs credits (X bills per API call). Needs X connected.

Discovered
delete_youtube_report_job

Stop a YouTube bulk reporting job. IRREVERSIBLE IN A WAY THAT IS EASY TO MISS: the job IS the history — deleting it discards every daily CSV it has accumulated, and a replacement job starts over with only a 30-day backfill, so anything older than that is gone for good. Call WITHOUT confirm first: nothing is deleted and you get the real job read back from YouTube (its report type and when it was scheduled) to show the user. Then call again with confirm:true. Needs a connected YouTube channel.

Discovered
delete_youtube_video

PERMANENTLY delete a video from the connected YouTube channel. IRREVERSIBLE — YouTube has no trash and no undelete, and the video's views, comments and every link or embed pointing at it go with it. Call it WITHOUT confirm first: nothing is deleted, and it reports the video's REAL title, privacy, view count and comment count read back from YouTube. Show the user that, get an unambiguous yes, then call again with confirm:true — plus, once the video is public or has any views or comments, confirmTitle set to its exact title. confirmTitle exists because confirming that you meant to delete SOMETHING does not prove you aimed at the right video, and a wrong id must not be confirmable blind. If the user only wants it out of public view, use update_youtube_video(privacy:"private") instead — that is reversible and this is not. Get the videoId from list_youtube_videos. 0 credits. Needs a connected YouTube channel.

Discovered
diagnose_posts

WHAT TO FIX NEXT, post by post — and the tool that fixes it. Post_performance tells you which hook is AHEAD; this tells you what is WRONG with a given post and where the next edit goes. Under-distributed is A HOOK PROBLEM. Seen but not held is A RETENTION PROBLEM. Seen, held, and still not converting is AN OFFER PROBLEM. FOUR REFUSALS, AND YOU SHOULD REPEAT THEM RATHER THAN PAPER OVER THEM: (1) a post younger than ~24h is TOO EARLY and is never called a failure — it has not had its run; (2) a metric the platform does not publish is UNMEASURED, never zero — Facebook has published no post reach since 2026-06-15, Reddit publishes no impressions, and Google Business publishes nothing per-post at all; (3) below 5 measured posts on a channel there is no baseline of the brand's own, and the ONLY fallback is a published short-video hook floor that is NOT our measured number and does not transfer off TikTok/Instagram/YouTube — it is attributed in the output and you should attribute it too; (4) it does not always find a problem, and 'nothing here needs fixing' is a real answer rather than a failure to look. Hermoso cannot see conversions for an organic post — no channel reports installs or purchases against a post id — so the offer rung runs ONLY when the user tells you they are not converting and you pass converting:false. Print `summary` verbatim. Read-only, 0 credits.

Discovered
disconnect_connector

Disconnect a third-party account from this workspace (Meta, Google Ads, Google Drive/Sheets/Docs, YouTube, TikTok, LinkedIn, X, Reddit Ads, Pinterest, Google Business, Microsoft Advertising/OneDrive, Slack, …). This always drops the stored credentials, so every tool for that provider stops working immediately and posts/campaigns already published are NOT affected. IT NEVER REVOKES THE GRANT AT THE PROVIDER: the authorisation stays in place, so the same account keeps working in any other brand it is connected to, until the user removes Hermoso in that provider's own settings. The unconfirmed call names where to do that (list_connectors carries revokesAtProvider:"local") — relay it rather than promising a revoke. RECONNECTING IS THE USER'S JOB: a sign-in account needs their browser (the provider's consent screen), so an agent cannot undo this. Name the provider to the user, then call with confirm:true. Use list_connectors for the exact provider ids.

Discovered
draft_brand

Fill in a profile (a business brand or a creator) from a website domain, a free-text description, or a social handle: its name, products, logo and summary, returned as a {name, products, logo, …} object and saved as the brand profile when none is saved yet. IMPORTANT: a domain can resolve to a DIFFERENT company than intended (e.g. Bala.com is an engineering firm, not the Bala fitness brand at shopbala.com). Before spending any credits on research or renders, VERIFY the returned `name` (and `summary`) match the brand the user meant; if it looks wrong, re-draft with the correct domain or a description (pass save:false until confirmed) — this tool cannot ask the user, so the caller owns that check.

Discovered
duplicate_scheduled

Copy an existing scheduled or already-published post into a NEW queued post — the way to run a creative again, reuse a post that worked as the starting point for the next one, or re-send something after it went out. It copies the caption, media, per-channel captions, title, description, tags and the target board / Page / company Page / listing, and ANY of those can be overridden in the same call. Give a new time in `at`, or useQueue:true to drop it into the brand’s next free posting slot. The copy is INDEPENDENT — editing or cancelling it never touches the original — and it is a genuinely new post rather than a re-send, so it publishes even where the original already did. To re-fire only the channels that FAILED, use retry_scheduled instead.

Discovered
edit_discord_message

Change the text of a Discord message Hermoso posted (≤2000 characters) — the bot's or the announcement webhook's. Pass messageId exactly as post_to_discord returned it. Mentions never ping on an edit. Free.

Discovered
edit_slack_message

Change the text of a Slack message the Hermoso app posted (≤4,000 characters, Slack's edit limit). messageId is what post_to_slack returned ("<channel>/<ts>"). A message posted through a pasted webhook has no id and cannot be edited. Free.

Discovered
edit_telegram_message

Change the TEXT or CAPTION of a message already posted to a Telegram channel, group or chat — the fix for a typo that would otherwise cost the post AND its accumulated views, since deleting and reposting starts from zero. Pass `text` for a plain post or `caption` for one carrying media; Telegram refuses the wrong one and its error names the other. It overwrites the published text, and the previous wording is not kept. Free, 0 credits. Needs Telegram connected.

Discovered
edit_x_post

EDIT the text of one of the connected account’s own posts on X. Three things about X’s edit model change how you must use this and none is guessable: (1) X REPLACES THE WHOLE TEXT — there is no partial patch, so pass the complete new post; (2) an edit MINTS A NEW POST ID, and the old id keeps resolving and keeps showing the OLD text, so always hand the user the NEW url afterwards or they will circulate a link to the version they just corrected; (3) X’s window is ONE HOUR from the ORIGINAL post and DOES NOT RESTART when a post is edited, and each post has a limited number of edits. Pass whichever id the user has — Hermoso reads X’s edit chain and aims at the newest id, which is the only one X accepts (an edit aimed at the id the user was originally given is refused by X once the post has been edited once). A published ARTICLE can never be edited whatever the subscription, and this says so rather than trying. Editing needs X Premium on the POSTING account; Hermoso attempts it and reports X’s own refusal rather than pre-refusing on a guess about the plan. Everything knowable for free — window closed, edits used up, post ineligible — is refused before anything is billed. Costs credits. Needs X connected.

Discovered
enable_tools

LIST a group of tools that is not in this session's roster. IT IS NOT HOW YOU REACH A TOOL — call_tool runs any Hermoso tool whether or not it is listed, and that works everywhere. Use this when the session will use MANY tools from one area and you want them in your list. WORKS ON CLIENTS THAT RE-READ THE TOOL LIST (stdio, the CLI, Cursor, Claude Code); a host that fixed its roster at connect time — claude.ai and ChatGPT do — will not show the new tools until it reconnects, and this tool says so in its reply rather than reporting a success you cannot use. The connect-time route that always works is `?tools=all` on the server URL. The default roster is CORE-FIRST: the core tools plus a few that make the connection drivable. Every other tool is held out of the LIST on SIZE alone — the whole registry is several hundred thousand tokens of schema re-sent on every turn, and a roster far past the 30-50 tool mark measurably degrades tool choice. The heaviest groups are `ads`, `analytics`, `channel_admin`: paid-campaign management across every ad platform is most of the total schema weight. NOTHING held out is unfinished or unsafe, and nothing is unreachable — find_tools finds it and call_tool runs it. CALL THIS WHEN A WHOLE AREA IS IN PLAY. If the user settles into building, budgeting, targeting or reporting on ad campaigns, call enable_tools({groups:['ads']}) and the tools appear. If they ask about their own site or product analytics, a tag/tracking container, or how a search engine crawls, indexes or ranks their site, call enable_tools({groups:['analytics']}). Groups: core, research, create, channels, channel_admin, analytics, ads, files, workspace — or 'all'. Free, instant, and it never turns anything off.

Discovered
error_detail

One error group in full by fingerprint (from list_errors): every field, plus the most recent redacted occurrences — status, connector, job id, workspace, and a shape-only echo of the inputs. This is what makes a bug reproducible. Read-only, 0 credits.

Discovered
estimate_messenger_marketing_delivery

Meta’s own estimate of how many marketing messages a budget would deliver from a Page and what it would cost, before anything is created. Free. Meta marks the metric “in development”, so treat it as a range.

Discovered
export_schedule_to_notion

Export the posting schedule to a Notion database, one row per post: date, channels, caption, media link and status. Creates a new database (under parentPageId, or private in the workspace) or writes into an existing one (databaseId), matching its columns by name. historyDays adds posts already published. Up to 100 rows per run; the answer says if more were left. Free.

Discovered
export_swipefile_deck

Turn a SWIPEFILE COLLECTION into a real Google Slides deck — one slide per saved ad, carrying the creative, the brand, the ad copy, the run dates with the run length, and the platform. This is the thing a marketer actually presents to a client or a team. Returns the presentation id + URL. Creates a NEW deck every time: under the drive.file scope Hermoso can only touch files it created, so it cannot add slides to a deck the user already has. A creative whose ad-library link has expired cannot be embedded — Meta signs those URLs with a short expiry — so that slide says so in words and keeps its copy and run dates, and the reply reports how many. Needs Google Drive connected (Settings > Connectors > Google Drive — one connection covers Drive, Sheets, Docs and Slides).

Discovered
extend_tiktok_post_ad_authorization

Extend how long a post the authorized account owns stays promotable as a Spark Ad — and REGENERATE its code if it was deleted. THE DAYS ARE ADDED, NOT SET: TikTok's own example is that a post with 180 days remaining, extended by 180, ends up at 360 — so passing '365' to a post that already has time left does not mean 'expires in a year'. Must be one of 7, 30, 60, 180, 365. Ad authorization has to be ON already (set_tiktok_post_ad_authorization) or TikTok refuses. The reply reads the new window back, because TikTok's own response body is empty. NEEDS THE TIKTOK ACCOUNT AUTHORIZATION (see tiktok_account_status).

Discovered
fetch_asset

Resolve a generated asset reference (a /generated/… path or any URL) to a clickable absolute URL + a direct download URL.

Discovered
find_instagram_marketplace_creators

SEARCH INSTAGRAM'S CREATOR MARKETPLACE: Meta's own directory of creators who partner with brands, searched AS the brand's Instagram account, so the recommendations are personalised to it. Use it when the user wants creators with first-party data (followers, audience, badges such as Partnership ads / Branded content / Strong hooks / Responsive, marketplace email) or Meta's recommendations. Search by query (keywords), by similarTo (up to 5 handles, not with query), by recommendation (most_relevant_for_me, high_ad_performance, most_ads_experience, similar_brands, similar_audience, interested_in_collaboration), or filters on the creator (countries, states with exactly one country, minFollowers/maxFollowers bands, ageBucket, gender, up to 5 interests, minEngaged/maxEngaged, language, followerGrowth, lastPostWithin, verifiedAccount, hasPortfolio, hasPublicContactEmail, featuredInPaidAds, excludeMessagedCreators) and their audience (audienceCountries, audienceStates, audienceAgeBucket, audienceGender, audienceDevices), or customAudienceId from list_instagram_marketplace_audiences. Username looks up one creator (no other filter allowed). Needs the Meta (Facebook Login) connector with the Page linked to the brand's Instagram, and Meta's instagram_creator_marketplace_discovery permission; when it cannot run it says exactly why and what fixes it, and when the reason is that Meta has not approved Hermoso's app for that permission yet (read LIVE from Meta, and named under Meta by list_connectors) it says so: that is Meta's decision about Hermoso, never a reason to tell the user to reconnect. Free.

Discovered
find_tools

Search EVERY Hermoso tool — your starting list is deliberately short, and everything else in the product is here — by name, task or group. Each row gives the tool's PARAMETERS in one line, its CREDIT COST (free means free on every plan; a tool that runs a model quotes the live per-model figure) and its recent HEALTH on this server (failure rate and typical duration, or "no recent calls", which means unseen and not broken). Use it the moment the user asks for something you do not see a tool for (a campaign, an ad set, a lead form, a click-to-WhatsApp ad, a report, keywords, audiences): a tool missing from your list is NEVER proof the feature is missing. Then run the tool with call_tool. A tool that is failing or needs a connector this workspace has not made is ranked last and marked, never hidden — pass onlyHealthy:true if you want those left out. Free, read-only.

Discovered
forget

Delete a saved Memory item by its id (from list_memory). Records a cross-device delete so it doesn’t come back. Minor + re-creatable (you can remember it again), so no confirm needed.

Discovered
format_sheet

Make an exported sheet readable: bold the header row, FREEZE it so it stays visible while scrolling, and auto-size the columns so nothing is cut off. Worth calling right after create_sheet — a raw export with unsized columns and a header that scrolls away is the difference between a spreadsheet someone reads and one they close. Changes no cell VALUE, so it is never gated. The defaults do all three on the first tab; pass `tab` to pick another, freezeRows:0 to skip freezing, boldHeader:false or autoResize:false to skip those.

Discovered
get_brand

What Hermoso ALREADY KNOWS for the active profile: the same saved profile (products, logos, palette, positioning) + learned memory the web Studio uses. Call it when you need to know whether a brand is on file: if hasBrand is true you can omit brand everywhere; if false, onboard with draft_brand. Not a required first step before a render: the create tools read the saved brand by themselves. 0 credits.

Discovered
get_business_location

Read everything Google holds on one of the brand’s Google Business Profile listings — business name, address, phone numbers, website, categories, description, regular and special hours, service area, labels, store code, open state, and whether the listing can carry a Post at all. This is the listing AS THE MERCHANT LAST SET IT, which is exactly what update_business_location edits; it can differ from what Google Maps shows today, because Google and the public can suggest changes on top. Call it before offering to change anything, and to answer “what does our Google listing actually say?”. Read-only, 0 credits. Needs Google Business Profile connected (Settings > Connectors > Google Business Profile).

Discovered
get_discord_message

Read back a Discord message Hermoso posted: its text, link and REACTIONS — the only engagement Discord exposes (there is no view count). Free.

Discovered
get_drive_file

Fetch one Drive file’s metadata — name, type, size, modified time, a webViewLink to open it and a webContentLink to download it. Pass fileId (from list_drive_files). Read-only.

Discovered
get_job

Poll a render job by id. Returns status (queued|running|done|error), progress, and on done the served media URL. Renders take 1–3 minutes: keep calling this until done/error without asking the user — several calls is normal, not a stall. An id that does not exist on this account answers status "not_found" — that is FINAL: stop polling it, and do not re-fire the render (that double-charges).

Discovered
get_linkedin_lead

One LinkedIn lead by id (from list_linkedin_leads), with every answer named by field. Personal data — show, never republish. Read-only, free.

Discovered
get_messenger_marketing_message_status

Whether a paid Messenger marketing message was delivered. send_messenger_marketing_message returns a tracking id, which only means Meta ACCEPTED it; this reads what Meta pushed back on the Page webhook for each tracking id: sent, delivered (the event Meta bills on), read, clicked (with count), or failed (with Meta’s reason). "unknown" means NO event has arrived, never that it failed: events arrive only when the Page is subscribed to the marketing_message_* fields (the reply says whether it is, read back from Meta; fix with subscribe_meta_webhooks fields ["marketing_messages"]) and only for messages sent after Hermoso began recording them on 2026-09-22. A failure carries no tracking id, so it is matched through Hermoso’s own send log. Read-only, 0 credits.

Discovered
get_meta_story

What can be known about one story id, usually from a story_reply or story_mention item in list_inbox: its caption, posted time and permalink while it is live, or that it has expired. `state` is live, readable, not_a_story, expired, not_found or unknown. Meta serves a Story for 24 hours and answers an expired one with the same error as a wrong id, so pass repliedAt (the item’s `at`) and the url so an expiry can be stated and a match found. unknown means a read failed, not that the Story is gone. Read-only, 0 credits.

Discovered
get_onedrive_file

Fetch one OneDrive item’s metadata — name, type, size, modified time, a webViewLink to open it and a webContentLink to download it. Pass fileId (from list_onedrive_files). Read-only.

Discovered
get_post_refill

Show AUTOPILOT POSTING (autoposting, the posting refill) for this profile — "is autopilot on?", "what is waiting for review?": whether it is on, its mode (review = each batch of fresh posts waits as drafts for approval; auto = fresh posts are scheduled straight away), WHICH CHANNELS it posts to with each one’s posts per day and posting times, roughly what it costs a day, when it next runs, how many posts are queued, the DRAFTS waiting for approval (id, time, channels, caption, the new image or video), and the notes it learned from reviews. It also names the channels that CANNOT be posted to and why. On the Free plan, paidOnly is true and note explains the status. Read-only, free.

Discovered

Recent measurements

Probe history

View status
Latest stored probes. Raw probe rows are kept for 14 days.
CheckedResultResponseTools
Connected1093 ms427
Connected1009 ms427