hermosodocs

Workspace & account

Profiles, memory, skills, saved creators, connectors, settings, credits and jobs.

71 tools. Every name, description and parameter on this page is generated from the running MCP server — see how this reference is built.

read-only / discovery

enable_tools#

Turn on more Hermoso 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.

ParameterTypeDescription
groupsrequiredstring[]Groups to switch on, e.g. ['ads']. Unknown names are refused by name rather than ignored.

QUERY SELF-HEAL

find_tools#

Find a Hermoso tool by name or task read-only

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.

ParameterTypeDescription
querystringwords from the task or the tool name, e.g. "lead form", "whatsapp", "google ads keyword", "meta insights"
groupstringlimit to one group: core, research, create, channels, channel_admin, analytics, ads, files, workspace
limitnumberhow many to return (default 12, max 40)
onlyHealthybooleanleave out tools that are failing their recent calls or that need a connector this workspace has not made. Default false — nothing is hidden unless you ask, because a missing row reads as a missing capability.

call_tool#

Run any Hermoso tool by name destructive

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.

ParameterTypeDescription
namerequiredstringthe tool name exactly as find_tools returned it, e.g. create_meta_lead_form
argsobjectthe tool's arguments as an object, exactly as its own schema takes them

FEEDBACK: let the AGENT report a bug or ask for a capability…

report_bug#

Report a bug

Report a bug in Hermoso to the team. Use this when something in Hermoso genuinely misbehaves — a tool errors unexpectedly, returns a wrong or malformed result, a render comes back broken, or documented behaviour doesn't match what happened. Include what you were trying to do, the exact tool call and arguments, and what came back. Do NOT use it for out-of-credits, a policy refusal, or a missing capability (use request_feature for that). Free, no credits.

ParameterTypeDescription
summaryrequiredstringone-line summary of the bug
detailsrequiredstringwhat you were doing, the tool + arguments you called, what you expected, and what actually happened (paste the exact error)
severity"low" | "medium" | "high"high = blocks the task or loses paid work; medium = wrong output but workable; low = cosmetic

request_feature#

Request a feature

Ask the Hermoso team for a capability that doesn't exist yet. Use this when you need something Hermoso genuinely can't do — an unsupported platform or channel, a missing model, an export format, a tool that would have completed the user's task but isn't available. Say what the user was trying to achieve, not just the feature name — the use case is what gets built. Free, no credits.

ParameterTypeDescription
summaryrequiredstringone line: the capability you need
detailsrequiredstringwhat the user was actually trying to achieve, why the current tools couldn't do it, and what you'd expect the capability to do

billing_status#

Billing status read-only

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.

No parameters.

upgrade_plan#

Upgrade plan

Change the SUBSCRIPTION plan (admin only): monthly plans with no markup on models. No argument lists them. New subscriber: a Checkout URL for the human. Existing subscriber upgrading: a quote + quote_token; once the human agrees, call again with confirm:true and quote_token to charge the card on file. Downgrade: a billing-page link; it starts at the term's end.

ParameterTypeDescription
planstringthe plan id to move to (e.g. pro) — omit to list the available plans first
period"mo" | "yr"monthly only ('yr' buys monthly)
confirmbooleantrue (with quote_token) to make a quoted upgrade
quote_tokenstringthe quoteToken from the quote step

set_auto_reload#

Set auto-reload

Turn automatic credit reloads on or off (admin only): when the balance drops below a threshold, the card on file is charged for a top-up pack — SERVER-SIDE, even with no app open. Requires a saved card, added once in the app at first checkout/top-up; if there's none the tool tells you exactly where to add it. After that one-time card setup, agents can manage auto-reload, top-ups and plan links fully. Members (read-only billing) get an 'ask an admin' message.

ParameterTypeDescription
enabledrequiredbooleantrue to turn auto-reload on, false to turn it off
thresholdCreditsintegerreload when the balance drops below this many credits
reloadCreditsintegerhow many credits to add each reload — must match a credit pack size (see buy_credits)

list_brands#

List profiles read-only

List every profile on this account (id + name; a profile is a brand, a creator or a personal workspace) and which one this connection acts on, PLUS any profile another account shared with you. Call this, then use_brand to switch; a SHARED profile switches the same way (pass its name or the profile id printed here). If a profile looks empty when the app shows it full, you are acting on a different one: call this first. Read-only, free.

No parameters.

use_brand#

Switch profile

Pin which profile this connection acts on. Pass the profile id or exact name from list_brands. Works for your own profiles AND one another account SHARED with you (pass its name or the profile id list_brands prints); access is verified against your real membership before it is pinned. Persists for this API key until changed, on every surface (hosted connector included — no environment variables, no restart).

ParameterTypeDescription
brandrequiredstringprofile id (e.g. default / p_xxx), its exact name from list_brands, or the profile id of one shared with you

BRAND WORKSPACE LIFECYCLE

create_brand#

Create a profile

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.

ParameterTypeDescription
namerequiredstringthe name for the new profile (a brand, client or creator name)
activatebooleanswitch this connection to the new profile (default true); everything you do next scopes to it

delete_brand#

Delete a profile destructive

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.

ParameterTypeDescription
brandrequiredstringprofile id or exact name from list_brands
confirmbooleanREQUIRED true — this destroys the whole profile and cannot be undone
confirmNamestringthe workspace's EXACT name, required when it is not empty — copy it from the inventory this tool returned, after the user has agreed to it
confirmConnectorsnumberthe number of connected accounts the inventory reported, required when there is at least one — the user must specifically agree to losing them, because reconnecting each needs a browser and no agent can do it

Stripe

stripe_report#

Stripe revenue report read-only

Revenue from the brand's OWN Stripe account: gross, refunds, net and succeeded-charge count per day/week/month, new customers in the window, and active subscriptions + MRR where the key can read them. This is the money side of the loop — read an ad, a hook or a launch against real revenue instead of clicks. Default window is the last 30 days; a window with no charges answers a real zero, not a failed read. Read-only, never moves money, free. Needs Stripe connected (Settings > Connectors > Stripe, or connect_connector: paste a restricted key from Developers > Restricted keys).

ParameterTypeDescription
sincestringYYYY-MM-DD (default 30 days ago)
untilstringYYYY-MM-DD (default today)
granularity"day" | "week" | "month"default day
currencystringISO code to isolate one currency, e.g. USD; omit to sum every currency as-is

list_stripe_customers#

List Stripe customers read-only

Recent customers on the brand's OWN Stripe account — email, name, created, currency, delinquent — newest first, or one customer by exact email. Read-only, free. Needs Stripe connected.

ParameterTypeDescription
emailstringexact email match
limitnumber1–100, default 25
afterstringpagination cursor: the last id of the previous page

list_stripe_charges#

List Stripe charges read-only

Recent charges on the brand's OWN Stripe account — amount, currency, status, refunds, customer email, description — newest first, optionally for one customer. Read-only, free. Needs Stripe connected.

ParameterTypeDescription
customerIdstringa Stripe customer id (cus_…)
limitnumber1–100, default 25
afterstringpagination cursor: the last id of the previous page

Stripe, the full surface

list_stripe_subscriptions#

list stripe subscriptions read-only

Subscriptions on the brand's OWN Stripe account with each item's price in MAJOR units (19.00 means $19.00), interval and quantity, and the MRR each contributes. With NO status it returns everything that counts toward MRR — active + trialing + past_due — and says so; pass status all, canceled, unpaid, incomplete, incomplete_expired or paused for the rest, or filter by customer or price. Read-only, free. Needs Stripe connected (Settings > Connectors > Stripe, or connect_connector).

ParameterTypeDescription
statusstring
customerIdstring
priceIdstring
limitnumber
afterstring

list_stripe_invoices#

list stripe invoices read-only

Invoices on the brand's Stripe account, newest first: number, status (draft, open, paid, uncollectible, void), amount due and paid, customer email, dates and the hosted invoice link; filter by status or customer. Read-only, free. Needs Stripe connected (Settings > Connectors > Stripe, or connect_connector).

ParameterTypeDescription
statusstring
customerIdstring
limitnumber
afterstring

list_stripe_products#

list stripe products read-only

Products on the brand's Stripe account with their active prices (amount, currency, one-time or recurring interval), which is what create_stripe_payment_link and create_stripe_subscription take. active:false lists archived products too. Read-only, free. Needs Stripe connected (Settings > Connectors > Stripe, or connect_connector).

ParameterTypeDescription
activeboolean
limitnumber
afterstring

stripe_balance#

stripe balance read-only

The brand's Stripe balance (available and pending, per currency) and the most recent payouts with status and arrival date. Read-only, free. Needs Stripe connected (Settings > Connectors > Stripe, or connect_connector).

ParameterTypeDescription
limitnumber

list_stripe_refunds#

list stripe refunds read-only

Refunds on the brand's Stripe account, newest first: amount, status, reason and the charge refunded; filter by charge. Read-only, free. Needs Stripe connected (Settings > Connectors > Stripe, or connect_connector).

ParameterTypeDescription
chargeIdstring
limitnumber
afterstring

list_stripe_coupons#

list stripe coupons read-only

Coupons on the brand's Stripe account: percent or amount off, duration, validity and redemptions. Read-only, free. Needs Stripe connected (Settings > Connectors > Stripe, or connect_connector).

ParameterTypeDescription
limitnumber
afterstring

create_stripe_product#

create stripe product

Create a product on the brand's Stripe account together with its default price: name, optional description, `amount` in MAJOR units — 19 or 19.00 both mean $19.00, and a zero-decimal currency like JPY is whole — a 3-letter currency, and an optional recurring interval (day, week, month, year) with intervalCount. Returns both ids, read back. On a LIVE key it shows what it is about to create and needs confirm:true; on a test key it just does it. Idempotent on its arguments. Needs Stripe connected (Settings > Connectors > Stripe, or connect_connector).

ParameterTypeDescription
namerequiredstring
descriptionstring
amountrequirednumberMAJOR units — 19 or 19.00 is $19.00, never 1900
currencyrequiredstring
intervalstring
intervalCountnumber
nicknamestring
metadataobject
confirmboolean

create_stripe_price#

create stripe price

Add a price to an existing Stripe product: `amount` in MAJOR units (19 or 19.00 is $19.00, never 1900), currency, optional recurring interval and intervalCount, optional nickname. Read back. LIVE key: confirm:true after showing the preview. Idempotent on its arguments. Needs Stripe connected (Settings > Connectors > Stripe, or connect_connector).

ParameterTypeDescription
productIdrequiredstring
amountrequirednumberMAJOR units — 19 or 19.00 is $19.00, never 1900
currencyrequiredstring
intervalstring
intervalCountnumber
nicknamestring
confirmboolean

create_stripe_coupon#

create stripe coupon

Create a Stripe coupon: exactly one of percentOff (1–100) or amountOff (major units) + currency; duration once (default), repeating (with durationInMonths) or forever; optional name, id and maxRedemptions. Read back. LIVE key: confirm:true after showing the preview. Needs Stripe connected (Settings > Connectors > Stripe, or connect_connector).

ParameterTypeDescription
namestring
idstring
percentOffnumber
amountOffnumber
currencystring
durationstring
durationInMonthsnumber
maxRedemptionsnumber
confirmboolean

create_stripe_customer#

create stripe customer

Create a Stripe customer by email (optional name, phone, description, metadata). If a customer with that email already exists it is returned instead and nothing is created — pass allowDuplicate:true to create a second one on purpose. LIVE key: confirm:true after showing the preview. Needs Stripe connected (Settings > Connectors > Stripe, or connect_connector).

ParameterTypeDescription
emailrequiredstring
namestring
phonestring
descriptionstring
metadataobject
allowDuplicateboolean
confirmboolean

create_stripe_subscription#

create stripe subscription destructive

Subscribe a Stripe customer (cus_…) to a price (price_…), optional quantity and trialDays. THIS CHARGES THE CUSTOMER'S SAVED PAYMENT METHOD when there is no trial, so it ALWAYS needs confirm:true (test keys included) after you show the user exactly what will be created. Read back; an INCOMPLETE status means no chargeable card is on file. Needs Stripe connected (Settings > Connectors > Stripe, or connect_connector).

ParameterTypeDescription
customerIdrequiredstring
priceIdrequiredstring
quantitynumber
trialDaysnumber
metadataobject
confirmboolean

cancel_stripe_subscription#

cancel stripe subscription destructive

Cancel a Stripe subscription: at the end of the current period by default (the customer keeps access until then), or immediately:true to end it now. ALWAYS needs confirm:true, test keys included. The read-back distinguishes the two — a period-end cancel reports the date it will end and the status it keeps until then, an immediate one reports status canceled. Needs Stripe connected (Settings > Connectors > Stripe, or connect_connector).

ParameterTypeDescription
subscriptionIdrequiredstring
immediatelyboolean
confirmboolean

refund_stripe_charge#

refund stripe charge destructive

Refund a Stripe charge (ch_… or a pi_… payment intent): the full refundable amount, or a partial `amount` in major units; optional reason duplicate / fraudulent / requested_by_customer. ALWAYS needs confirm:true, test keys included. Read back. Needs Stripe connected (Settings > Connectors > Stripe, or connect_connector).

ParameterTypeDescription
chargeIdrequiredstring
amountnumber
reasonstring
confirmboolean

PULL A WHOLE CLOUD FOLDER INTO THE LIBRARY

import_from_cloud#

Import a Drive / OneDrive folder into the Library

Pull the files in a Google Drive or OneDrive FOLDER into this profile's Library, so they can be used like anything rendered here — published, scheduled, cloned, used as a product photo or a reference. Hermoso downloads each file with the user's own connected account (a Drive/OneDrive file is not public, so this is the only way in) and stores a durable Hermoso url for each. Give `folderId` from list_drive_files / list_onedrive_files with onlyFolders — omit it for the root. GOOGLE DRIVE ONLY SHOWS WHAT THE USER HANDED OVER: our Drive scope is `drive.file`, so Hermoso can see the files and folders it created plus the ones the user picked with the Google picker in the app, and NEVER their whole Drive. Picking a FOLDER hands over the folder alone, not the files in it, so if a folder comes back empty the reply carries a link that opens the picker inside it, where the user selects every file at once (Cmd+A / Ctrl+A); give them that link. OneDrive has no such limit. SUBFOLDERS ARE NOT WALKED and Google-native docs (Docs/Sheets/Slides) have no file to download: both are reported back BY NAME rather than silently dropped, along with anything too large or unreadable, so you can tell the user exactly what did and did not come across.

ParameterTypeDescription
providerrequired"drive" | "onedrive"which cloud — `drive` is Google Drive, `onedrive` is Microsoft OneDrive
folderIdstringthe folder to import, from list_drive_files / list_onedrive_files (onlyFolders:true). Omit for the root of the drive.
limitnumberhow many files to bring across in this call (default 10, max 25). Anything over the limit is listed as skipped so you know what is left.

list_drive_files#

List Google Drive files read-only

List the Google Drive files & folders Hermoso can reach: the ones it created plus any the user picked with the Google file picker in the app (drive.file, never their whole Drive). This is how you find a picked file's id. Filter by query (name contains …), folderId, or onlyFolders:true; paginate with pageToken. Read-only. A FOLDER: picking a folder hands over the folder alone, not its files, so a folder can list empty or partly; the reply then carries `pickUrl`, an app link that opens the picker inside that folder (Cmd+A / Ctrl+A selects every file and subfolder). Give the user that exact link, then list again. recursive:true walks every subfolder Hermoso can see (each row has its `path`) and reports picked files whose folder it cannot see.

ParameterTypeDescription
querystringonly files whose name contains this
folderIdstringlist the contents of this folder id
recursivebooleanwith folderId: the whole tree
onlyFoldersbooleanlist folders only
pageSizenumberrows per page (1–200, default 50)
pageTokenstringcursor from a previous call
includeTrashedbooleaninclude trashed files (default false)

get_drive_file#

Get a Drive file’s details read-only

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.

ParameterTypeDescription
fileIdrequiredstringthe Drive file id (from list_drive_files)

update_drive_file#

Rename / move / trash a Drive file destructive

Update a Drive file: rename (name), move it into a folder (moveToFolderId, optionally removeFromFolderId to move OUT of the old one), or trash / untrash it (trash:true|false). Pass fileId (from list_drive_files). To delete permanently, use delete_drive_file.

ParameterTypeDescription
fileIdrequiredstringthe Drive file id
namestringnew name
moveToFolderIdstringfolder id to move the file into (from create_drive_folder / list_drive_files)
removeFromFolderIdstringthe old parent folder id to remove (when moving)
trashbooleantrue -> move to Trash; false -> restore from Trash

delete_drive_file#

Delete a Drive file destructive

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.

ParameterTypeDescription
fileIdrequiredstringthe Drive file id
permanentbooleantrue = delete forever; default trashes (recoverable)
confirmbooleanREQUIRED true

create_drive_folder#

Create a 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).

ParameterTypeDescription
namerequiredstringfolder name
parentIdstringparent folder id for a nested folder (default: Drive root)

skills

list_skills#

List skills read-only

List the bundled Hermoso SKILLS — multi-step workflow instructions (SKILL.md) that orchestrate the other tools (research an ad space, plan+render a finished ad, product photoshoot, raw generation) — plus the in-app strategy skills and creative recipes. Call get_skill to load a bundle. Read-only, free.

No parameters.

get_skill#

Get skill read-only

Load a bundled skill’s full SKILL.md workflow instructions by name (from list_skills). Follow the loaded instructions to run that workflow with the other tools. Read-only, free.

ParameterTypeDescription
namerequiredstringbundle name from list_skills, e.g. hermoso-generate

workspace management: Memory / Skills / Brand / Connectors /…

save_skill#

Save a skill

Save a reusable custom SKILL — a named creative directive/playbook applied to future ads (a hook formula, a UGC recipe, a compliance rule, a named specialist persona like “our founder-story style” or “short-form ad strategist”). Distill an imperative, self-contained directive. Merges into the workspace Skills library (list_skills shows built-ins + your custom skills).

ParameterTypeDescription
brandstringprofile id/name from list_brands, this call only
namerequiredstringshort skill name, e.g. “Founder-story hook”
directiverequiredstringthe full instruction the skill applies when used (1–6 sentences, imperative)

delete_skill#

Delete a custom skill destructive

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.

ParameterTypeDescription
brandstringprofile id/name from list_brands, this call only
idrequiredstringthe custom skill id (from list_skills)

list_memory#

List memory read-only

List the durable facts & preferences saved in this workspace’s Memory (what the studio remembers about the brand, audience, taste, and do/don’t rules) — the same Memory the web app shows. These shape every future ad. Read-only, free.

ParameterTypeDescription
categorystringfilter to one bucket (Brand/Audience/Taste/Do/Don’t/Preference)
limitnumbermax items (default 50, max 200)

remember#

Remember a fact

Save a durable fact or PREFERENCE about the brand, audience, or the user’s creative TASTE (e.g. “audience is first-time homebuyers”, “prefers bold lime accents”, “always captions off”) into the workspace Memory so it shapes FUTURE ads. For lasting things, not one-off requests. Merges into the existing Memory (never overwrites); de-dupes on identical text. NEVER how Hermoso, a tool, a connector or a platform API behaves (what a call returns, errors, permissions, limits, ids) and never a phone number or email — those are refused.

ParameterTypeDescription
brandstringprofile id/name from list_brands, this call only
textrequiredstringthe fact/preference, concise
categorystringshort bucket: Brand, Audience, Taste, Do, Don’t, or Preference (default General)

forget#

Forget a memory destructive

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.

ParameterTypeDescription
brandstringprofile id/name from list_brands, this call only
idrequiredstringthe memory item id (from list_memory)

SWIPEFILE

tidy_memory#

Tidy the Memory list destructive

Clean up and consolidate the workspace Memory: drops entries that are about how Hermoso, a tool, a connector or a platform API behaves (product behaviour, not the brand), drops phone numbers and emails, and merges near-duplicate facts into one sentence each. Call with no argument to get the PROPOSAL (what would be removed and merged, with reasons) — nothing changes. Call again with confirm:true to apply it through the same typed writers the app uses (deletes carry tombstones so they stay deleted on every device). One small model call; a few credits.

ParameterTypeDescription
brandstringprofile id/name from list_brands, this call only
confirmbooleantrue to APPLY the proposal; omit to only see it

list_swipefile#

List the swipefile read-only

List this workspace's SWIPEFILE — the saved-ad research board: every named collection and the ads/creatives kept in it (advertiser, headline, body copy, media URL, platform, when it was saved, and any taste tags). The SAME board the web app's Swipefile tab shows. Use it to answer "what have we saved?", to mine the user's own taste before planning an ad, or to find a reference to remix. Read-only, free.

ParameterTypeDescription
collectionstringonly list ads in this collection (by name or id) — omit for every collection
limitnumbermax ads to return (default 50, max 500)

save_to_swipefile#

Save ads to the swipefile

Save one or more ads/creatives to a named SWIPEFILE collection, creating the collection if it does not exist — the headless twin of the heart on every ad card in the web app. Use it whenever research turns up something worth keeping: a competitor ad from search_meta_ads / pull_competitor_ads, an organic post, or one of your own renders. Saved ads persist to the workspace board the web Swipefile tab shows, and feed the taste signal every future ad is planned against. De-dupes: re-saving the same ad (same key/link/media) MOVES it into the named collection instead of duplicating it. Free.

ParameterTypeDescription
brandstringprofile id/name from list_brands, this call only
collectionrequiredstringthe collection name — an existing one, or a new one to create
itemsrequiredobject[]the ads to save

SAVED CREATORS: outreach status + notes

update_saved_creator#

Update a saved creator (outreach status, note) destructive

Set the OUTREACH STATUS and/or a NOTE on a creator already saved in the swipefile (find_creators -> save_to_swipefile, or the heart on a creator card). Status is one of new | contacted | replied | booked | passed. The note is free text (deal terms, rate, what was sent). Reads back the updated row. Use list_swipefile to find the key. Free.

ParameterTypeDescription
brandstringprofile id/name from list_brands, this call only
keyrequiredstringthe saved row's key from list_swipefile, e.g. tiktok:handle
status"new" | "contacted" | "replied" | "booked" | "passed"
notestringreplaces the existing note; pass "" to clear it

PLAYBOOKS

list_playbooks#

List playbooks read-only

List the PLAYBOOKS saved in this workspace — the reusable strategy cards (winning hooks, angles, formats and the concrete plays to run) kept from teardowns, angle mining and creatives worth repeating. The same Playbooks the web app's Playbooks tab lists. Read one before planning an ad so you re-run what already worked instead of starting cold. Read-only, free.

ParameterTypeDescription
limitnumbermax playbooks to return (default 25, max 100)
fullbooleantrue to return every hook/angle/play in the text, not just the headline counts

save_playbook#

Save a playbook

Save a reusable PLAYBOOK — the strategy takeaways worth re-running: the hooks that work, the angles, the formats, and the concrete plays. Use it to keep what a competitor_teardown or mine_angles just found, or to bank a creative you want to repeat. Lands in the same Playbooks library the web app lists, runs and manages. Distinct from save_skill (a directive applied to every ad) and from the swipefile (raw saved creative). Free.

ParameterTypeDescription
brandstringprofile id/name from list_brands, this call only
namerequiredstringthe playbook headline — what it is, in a few words
hooksstring[]the opening hooks worth reusing, verbatim
anglesobject[]the persuasion angles ({title, detail})
formatsstring[]the formats/recipes this plays best in (e.g. ugc_selfie, cinematic, static)
playsobject[]the concrete plays to run ({title, detail}) — the actionable half
sourcestringwhere it came from, e.g. “teardown · Ridge”

delete_playbook#

Delete a playbook destructive

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.

ParameterTypeDescription
brandstringprofile id/name from list_brands, this call only
idrequiredstringthe playbook id (from list_playbooks)

SAVED CREATORS

list_creators#

List saved creators read-only

List this workspace’s SAVED CREATORS — the reusable on-camera cast (AI creators made here, a person pulled from a social profile, a consented photo upload). Read-only, FREE. Each entry gives the name, the PORTRAIT URL, where the portrait came from and whether a real person’s likeness consent is on file, how many extra pose plates exist, and any chosen or cloned voice. TO PUT ONE IN A FINISHED AD, pass their id or name as render_ad’s `creator` — that casts them for the whole spot (and skips the character-portrait render, so it costs less than not casting anyone). THE PORTRAIT URL IS THE REUSE HANDLE for the raw lanes — pass it as generate_avatar’s `image` (a talking clip of them), generate_video’s `refImage` (they star in the scene), recast_motion’s `image` (they perform a reference clip’s motion), or generate_image’s `refImages`. CALL THIS BEFORE OFFERING TO GENERATE A NEW PERSON: re-casting somebody the workspace already has keeps the SAME face across every ad, while a fresh person costs credits and breaks that continuity. An empty answer means the workspace genuinely has no cast yet — say so and offer generate_avatar / save_creator, never invent a roster.

ParameterTypeDescription
limitnumbermax creators to return (default 24)
gender"female" | "male"filter the PRESET creators by gender (the saved cast is never filtered)

save_creator#

Save a creator

Add a portrait to this workspace’s reusable CAST so the SAME person can star in future ads — the headless twin of the app’s + > Pick a creator > save. Pass the portrait’s public url (a generate_image render of an AI person, or a photo of a real person you have permission to use, or of yourself; never a photo just because it is public) plus a name to call them by; from then on list_creators returns them and their url can be re-passed to generate_avatar / generate_video / recast_motion. Saving is FREE and renders nothing. LIKENESS: leave `source` "generated" for an AI-made person (free on every plan) and use "upload"/"social" for a REAL person. Using a real person’s face, likeness or voice confirms you are them or have their consent, take full, unlimited responsibility for its use and will cover any claim against Hermoso (hermoso.ai/terms). Paid plan only; recorded.

ParameterTypeDescription
brandstringprofile id/name from list_brands, this call only
namerequiredstringwhat to call this creator (e.g. “Sarah”) — list_creators and the app’s picker match on it
imagestringREQUIRED except with useAnyway. public https url of the portrait (an existing render’s url, or any public photo). Not a local file path — upload it with upload_file first and save the url that returns
source"generated" | "upload" | "social""generated" (default) = an AI-made person; "upload" / "social" = a REAL person
voicestringa default voice name for this persona (engines + voices are in hermoso_capabilities)
posesstring[]up to 4 extra full-body / angle plates of the SAME person (public urls) — they make a wider shot hold the identity
lookstringtheir canonical wardrobe/appearance in words — reused to hold the look steady across ads
useAnywaybooleanonly for a creator whose saved photo was flagged too unclear to cast (render_ad says so): true casts the current photo as it is, no new image needed

delete_creator#

Delete a creator destructive

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.

ParameterTypeDescription
brandstringprofile id/name from list_brands, this call only
idrequiredstringthe creator id (from list_creators)

update_brand#

Update profile fields destructive

Patch SPECIFIC fields of the active profile (name, domain, sells, summary, category, audience, positioning, voice, style, goal, logos, brand/product name pronunciations, profileType: explore = kept but unused) WITHOUT overwriting the rest — a read-modify-write on the saved brand. Use for “change our voice to playful”, “we sell to dentists now”. To onboard a brand from scratch, use draft_brand. If no brand is saved yet and you only INFERRED one from what the user is making, ask them to confirm it is their brand before saving it (they may be working for a client or just trying things). Only pass the fields you’re changing.

ParameterTypeDescription
brandstringprofile id/name from list_brands, this call only
namestring
domainstringwebsite domain
sellsstringwhat the brand sells
summarystringone-line description
categorystring
audiencestring
positioningstring
voicestringbrand voice/tone
stylestringvisual style — palette, typography, aesthetic
goalstringcurrent marketing goal
pronouncestringhow the brand NAME is said aloud, as a simple respelling with the stressed syllable in capitals (e.g. "KOH-dee-ak"). Videos use it as a delivery note beside the spoken line; set it when a render mispronounced the name.
pronunciationsobjecthow PRODUCT names are said aloud, e.g. {"Power Cakes": "POW-er cakes"}. Merged into the saved ones; an empty string removes one.
logostring
logoLightstringfor dark pictures; "none" removes
logoDarkstringfor light pictures
profileType"website" | "social" | "describe" | "explore"

store_get#

Read a workspace store read-only

Read one of this workspace’s data stores by key, for visibility into what the app holds — playbooks, swipefile, saved locations, avatars, creations, chats, brand, memory, skills. Read-only, free. Allowed keys: heist.memory.v1, heist.skills.v1, heist.playbooks.v1, heist.avatars.v1, heist.locations.v1, heist.chats.v1, heist.creations.v1, heist.assets.v1, heist.brand.v1, adInspo.swipefile.v1. (The typed tools — list_memory / list_skills / get_brand — are friendlier for those; use store_get for the rest.)

ParameterTypeDescription
keyrequiredstringthe store key to read (one of the allowlisted keys)
limitnumbermax array items to return (default 50)

APP SETTINGS

get_settings#

Read app settings read-only

Read this account's app settings — the LANGUAGE Hermoso writes ads, copy and answers in, the app appearance (theme), and whether the weekly competitor-watch email is on. Same settings as the web app's Settings pane. Read-only, free.

No parameters.

update_settings#

Change app settings destructive

Change this account's app settings. language = the language EVERY ad, script, plan and answer is written in from now on (say the language in plain English, e.g. "German", "Japanese", "Brazilian Portuguese") — it applies to renders made over MCP as well as in the app. theme = the app's appearance, "dark" or "light". watchEmail = the weekly competitor-watch email on/off. Only pass what you are changing. Account-wide (every profile), and it takes effect on the next call.

ParameterTypeDescription
languagestringlanguage for generated ads, copy and answers — e.g. "English", "German", "Japanese"
theme"dark" | "light"app appearance
watchEmailbooleanweekly competitor-watch email on/off

list_connectors#

List connectors read-only

List the third-party accounts connected to this workspace (Meta, Google Ads, Google Drive/Sheets/Docs, YouTube, LinkedIn, OneDrive, Slack, …) — provider, status and the connected account label — PLUS which providers are available to connect. IT ALSO FLAGS A CONNECTION WHOSE PERMISSIONS ARE OUT OF DATE: a provider writes its granted permission set into the token at consent time, so a connection authorized before a permission was approved does not carry it and never will — those calls are refused by the provider and no retry or wait can change it. Check this FIRST when a connected provider starts refusing things. The fix is a browser: the user reconnects under Workspace > Connectors. Read-only, free.

No parameters.

CONNECT A PASTE-A-KEY ACCOUNT FROM HERE

connect_connector#

Connect a paste-a-key account destructive

Connect an account that links with a KEY rather than a sign-in screen, from here, with no browser: AppLovin Ads, Stripe, Intercom, Vercel, ChatGPT Ads, Apple Ads, Bluesky, Telegram, Bing Webmaster Tools, PostHog, Mixpanel, Amplitude, 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} · 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} · slack {webhookUrl*} · discord {webhookUrl*} · webhook {webhookUrl*}.

ParameterTypeDescription
providerrequiredstringthe connector id: applovin_ads, stripe, intercom, vercel, openai_ads, apple_ads, bluesky, telegram, bing_webmaster, posthog, mixpanel, amplitude, slack, discord, webhook
fieldsobjectthat provider's own field names and values, e.g. {"apiKey":"…"}; the names for each provider are in the description

REMOVE ONLY WHAT I CONTRIBUTED

leave_connector#

Remove my own account from a shared connection destructive

On a connector that several teammates can contribute their OWN account to (see list_connectors — the row reports multiContributor), remove YOURS from this profile: your stored credential is dropped and the accounts you shared stop being shared. Your teammates' accounts on the same connection keep working, and nothing changes at the provider — reconnecting in a browser shares again. Use this instead of disconnect_connector when the connection is not yours to remove: disconnect_connector revokes the grant at the provider and only the person who created the connection may call it.

ParameterTypeDescription
providerrequiredstringprovider id exactly as list_connectors reports it, e.g. "linkedin", "tiktok_ads", "meta"

list_team#

List team members read-only

List the members of the current profile — email, role (admin/member) and status. Read-only, free.

No parameters.

invite_member#

Invite a teammate destructive

Invite someone to this profile by email (role: member = read-only on billing, admin = full). This SENDS a real email invite / share link — an account change. Confirm the exact email + role with the user, then call with confirm:true.

ParameterTypeDescription
emailrequiredstringthe invitee’s email
role"member" | "admin"default member
confirmbooleanREQUIRED true — this invites a real person

remove_member#

Remove a teammate destructive

Remove a member from this profile by email — they lose access (you can re-invite them later), AND every connection THEY made on this profile is disconnected with them: their own X, LinkedIn, TikTok, YouTube, Pinterest, Threads… connections (on a channel holding several accounts, only the accounts they added), and any extra login or authorization they added. Connections the owner or anyone else made are never touched, and one with no record of who connected it is treated as the owner's. Queued posts that would publish through their accounts, or that they scheduled themselves, will not go out. The unconfirmed call reports exactly which connections and how many scheduled posts — relay that to the user, then call with confirm:true.

ParameterTypeDescription
emailrequiredstringthe member’s email
confirmbooleanREQUIRED true

set_role#

Change a teammate’s role destructive

Change a workspace member’s role — admin (full access incl. billing) or member (read-only on billing). A privilege change: confirm the exact person + new role with the user, then call with confirm:true.

ParameterTypeDescription
emailrequiredstringthe member’s email
rolerequired"admin" | "member"the new role
confirmbooleanREQUIRED true

list_jobs#

List render jobs read-only

List the most recent render jobs + how many are currently running, so you can report on or resume in-flight work.

No parameters.

error triage

list_errors#

List errors users hit read-only

The errors actually recorded against this workspace, GROUPED by fingerprint — the same failure at the same call site is one row with a hit count and first/last seen, sorted defects-first. Each row says whose side it is: `ours` (a defect worth fixing), `user` (a refusal we deliberately authored, e.g. not-connected or out-of-credits), or `unknown` (a vendor 4xx we cannot attribute — never guessed). Free text, tokens, emails and creative are redacted before anything is stored, so an input echo shows shapes and lengths, not content. Filter by surface (http/mcp/agent/job/client) or kind. Read-only, 0 credits. Scoped to your own workspace; an operator whose client carries the admin key sees every account.

ParameterTypeDescription
kind"ours" | "user" | "unknown"'ours' = a defect; 'user' = a refusal we authored; 'unknown' = we could not tell
surface"http" | "mcp" | "agent" | "job" | "client"where it happened: http (an API route), mcp (an agent tool), agent (the in-app Studio agent), job (an async render/publish), client (a browser crash)
sincestringISO timestamp — only groups last seen at or after this
limitnumberhow many groups to return (default 50, max 200)

error_detail#

Error detail read-only

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.

ParameterTypeDescription
fingerprintrequiredstringthe `fp` value from list_errors

brand onboarding

get_brand#

Get saved profile read-only

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.

ParameterTypeDescription
brandstringprofile id/name from list_brands, this call only

draft_brand#

Draft profile destructive

Fill in a profile (a business brand or a creator) from a website domain, a free-text description, or a social handle, into a {name, products, logo, …} object you can pass to plan_ad / generate. 0 credits. 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.

ParameterTypeDescription
domainstringa website to scrape
descriptionstringa free-text brand description (no website)
socialHandlestringa social handle to draft from (influencers/creators) — pair with platform
platformstringplatform for socialHandle (instagram/tiktok/…)
savebooleansave onto the active profile (like Studio onboarding) so plan_ad/create use it automatically. Default: saves only when NO brand is saved yet; pass true to REPLACE the saved profile (the drafted fields overwrite the saved ones), false to never save