Plyto

ai.plyto/crm · v1.1.0 · sales-and-crm

Plyto is healthy: it completed an MCP handshake in 2215 ms and exposes 195 tools.

AI that makes and runs your Google and Facebook ads from your own account, with a CRM behind it.

Endpoint: https://plyto.ai/api/mcp (streamable-http)

No repository listed · Website · JSON · Add to my inventory

Facts

Users (last 30 days)0
HandshakeHealthy
Auth neededNone
Tool count195
Latency2215 ms
Last checked (UTC)2026-10-16T23:05:38.678Z

Health history

Checked (UTC)StatusLatencyError
2026-10-16T23:05:38.678ZHealthy2215
2026-10-15T16:15:08.773ZHealthy1510
2026-10-14T11:15:34.708ZHealthy1821

Tools (100)

memory__saveSave (or update) a durable memory: a standing preference, playbook rule, correction, or business fact that should hold across ALL future conversations. Use when the user says "remember…", "always…", "never…", "from now on…", states a lasting preference, or corrects how you did something. One distill
memory__listList every durable memory you hold for this business (business-wide plus the current user's personal ones), with ids and when each was last confirmed. Use before consolidating, when the user asks what you remember, or to find a memoryId to update/forget.
memory__forgetPermanently delete a saved memory. Destructive: confirm with the user first unless they just explicitly asked you to forget exactly this. Users can also remove memories themselves under Settings → Business profile.
business__updateSave what you learn about the business the moment the owner says it: real name, website, industry, who their customers are, what sets them apart, the phone to show on pages, where an ad's click should go (clickTarget page or url, and conversionUrl: their signup, booking or checkout link), and durabl
brand__scan__waitWait for the website scan business.update started (up to 45 seconds) and get what it read: industry, audience, value proposition, claims. Call it right after saving a website on a first draft, then build from the result. Returns what is on file even when the scan is still running or failed, with a n
account__profileReturn the Plyto profile this connection is signed in as: an opaque id that stays the same across token refresh and reconnection, the person's name and email for display, and the business the connection manages as a label. Takes no arguments.
documents__listList the documents the owner has given you, newest first: files attached in the chat (PDF, Word, text), long messages kept as documents, and links you read and kept. Each has an id to read it with documents.read. Use it when the owner refers to something they sent before ("the brand doc", "my course
documents__readRead a document the owner gave you (an attached PDF, Word or text file, a long message kept as a document, or a link you kept), 30,000 characters at a time; pass offset to go on. Read before you answer about it: never describe a document from memory.
brand__colors__setSave the brand colors the owner names in chat, as hex codes: the primary (what graphics build on) and, when they give one, an accent. They replace the colors on file (a website scan's guess, a logo's extraction), so pass both when the owner names both. Returns what the next graphic will use. Nothing
business__claims__removeTake claims off the business profile once the owner says they no longer hold: the business moved and no longer serves the old area, an offer ended, a credential lapsed. Pass each claim as it is on file (a draft's placeClaimsNote quotes it). Only on the owner's word in this conversation, never on you
crm__contacts__createCreate a contact (or find-and-update if one with the same email already exists in this business). Returns the created/updated contact. Use when the user mentions adding, capturing, or tracking a new person. Email is the natural dedup key; if you have an email, always pass it.
crm__contacts__updateUpdate fields on an existing contact. Only pass fields you want to change. No-op fields are silently skipped.
crm__contacts__getFetch a single contact by id, with where they came from: the ad campaign and search keyword that brought them (when an ad did), where they were and on what device, what they asked for on the landing page, how many forms they submitted, and whether they have created an account. Use this, not source_d
crm__contacts__listList contacts in this business with simple filters. For free-text search use crm.contacts.search. For complex filters use crm.contacts.query (W3).
crm__contacts__searchFree-text search across contact email, first/last name, and phone. Case-insensitive substring match. Returns up to 20 contacts by default.
crm__contacts__get_propertiesReturn all custom property values for a contact as a {key: value} map.
crm__companies__createCreate a company (or find-and-update if one with the same domain already exists). Domain is the natural dedup key; always pass the bare domain like 'acme.com' (not the full URL).
crm__companies__updateUpdate fields on an existing company. Only pass fields you want to change.
crm__companies__getFetch a single company by id. Returns null if not found.
crm__companies__listList companies in this business. For text search use crm.companies.search.
crm__companies__searchFree-text search across company name, domain, and industry. Case-insensitive substring match.
crm__companies__get_propertiesReturn all custom property values for a company as a {key: value} map.
crm__tags__createCreate a new tag (or find one with the same name; idempotent). Tags are lightweight orthogonal categories like 'VIP', 'Newsletter-only', 'Cold'.
crm__tags__listList all tags defined in this business.
crm__tags__add_to_contactApply a tag to a contact. Idempotent: adding an already-applied tag is a no-op.
crm__tags__remove_from_contactRemove a tag from a contact. Idempotent.
crm__tags__list_for_contactList all tags applied to a contact.
crm__tags__add_to_companyApply a tag to a company. Idempotent.
crm__tags__remove_from_companyRemove a tag from a company. Idempotent.
crm__tags__list_for_companyList all tags applied to a company.
crm__properties__defineDefine a new custom property on contact or company. Keys must be lowercase snake_case (e.g. 'plan_tier'). For single_select / multi_select, options is required.
crm__properties__listList custom property definitions for an entity type. Excludes archived by default.
crm__properties__archiveArchive a custom property. Existing values are preserved but the property is hidden from UI and rejected for new value sets. To 'rename', archive + redefine.
crm__properties__set_contact_valueSet the value of a custom property on a contact. Value type must match property type. For multi_select pass an array of option values.
crm__properties__set_company_valueSet the value of a custom property on a company.
crm__properties__set_deal_valueSet the value of a custom property on a deal.
crm__properties__clear_contact_valueRemove a custom property value from a contact.
crm__properties__clear_company_valueRemove a custom property value from a company.
crm__properties__clear_deal_valueRemove a custom property value from a deal.
crm__properties__get_deal_valuesReturn all custom property values for a deal as a {key: value} map.
crm__pipelines__listList all pipelines in this business (excludes archived by default).
crm__pipelines__list_stagesList the ordered stages in a pipeline. Use to look up stageIds for crm.deals.move_stage.
crm__pipelines__get_defaultGet the default pipeline for this business (the one new deals land in by default). Returns null if no default is set.
crm__deals__createCreate a deal in a pipeline. If pipelineId/stageId omitted, lands in the business's default pipeline at the first open stage. Attach to a contact and/or company for full context. valueCents is the deal size in cents (e.g. $5,000 = 500000).
crm__deals__updateUpdate fields on a deal. Use crm.deals.move_stage to change stage (it handles close events). Use crm.deals.close_won / close_lost for the won/lost transitions.
crm__deals__move_stageMove a deal to a different stage in the same pipeline. If the target stage is a won/lost stage, this auto-sets closedAt and emits the appropriate close event.
crm__deals__close_wonClose a deal as won: moves it to the won stage in its pipeline. Sets closedAt and emits deal.won.
crm__deals__close_lostClose a deal as lost: moves it to the lost stage in its pipeline. Records lostReason and emits deal.lost.
crm__deals__getFetch a single deal by id.
crm__deals__listList deals with simple filters. Filter by pipeline/stage for Kanban view, or by contact/company for deal history. `open: true` returns only open-stage deals; omit for all.
crm__deals__searchFree-text search across deal names.
crm__activities__logLog an activity: something that happened (past or scheduled). Distinguished from tasks (future intent) and notes (no structure). Use 'log a 30-min discovery call with Jane' style prompts. happenedAt defaults to now if omitted.
crm__activities__listList activities, optionally filtered by entity (contact/company/deal) or owner. Returned in reverse chronological order by happenedAt.
crm__activities__getFetch a single activity by id.
crm__tasks__createCreate a task: future intent ('follow up with Acme by Friday'). Distinguished from activities (past event) and notes (no structure). Auto-assigns to the calling user if assignedToUserId omitted.
crm__tasks__completeMark a task as completed. Sets completedAt to now.
crm__tasks__cancelMark a task as cancelled (rejected, different from completed). Use when the user decides a task is no longer relevant.
crm__tasks__listList tasks. Open tasks sort by due date (closest first). Completed tasks sort by completion (most recent first).
crm__tasks__getFetch a single task by id.
reports__previewRun a report config WITHOUT saving. Use it to answer analytical questions ('deals by stage', 'contacts added per month', 'email opens last 30 days') and to sanity-check a config before reports.create. Returns rows + totals. Money values are in cents.
crm__sources__performanceMarketing-source attribution: for every source (ad campaigns, UTM-tagged traffic, forms, inbound webhook, manual, import…; the webhook also accepts POST {email, outcome: qualified|customer} at /outcome so an external system of record can report when a lead pays) the full funnel (leads acquired, deal
reports__createSave a custom report as a live card on the user's Reports dashboard. Use when the user asks for a report or says yes to saving an analysis. The config is re-executed on every dashboard view (always live). Validates by running once; returns the saved report + current data.
reports__listList the saved custom reports on the Reports dashboard.
crm__notes__addAdd a freeform note attached to a contact, company, deal, or any combination. Use for 'remember that Jane prefers email over phone' style facts.
crm__notes__listList notes, optionally filtered by attached entity.
crm__lists__createCreate a contact list. Manual lists are curated by hand; dynamic lists have a filter DSL (DNF: {or:[{and:[conditions]}]}) that defines membership. For dynamic, pass `criteria`. For manual, omit it.
crm__lists__getFetch a single list by id.
crm__lists__listList all contact lists in this business.
crm__lists__membersReturn the contacts that are members of a list. For manual lists this returns explicit memberships; for dynamic lists it evaluates the criteria filter against contacts (lazy recompute).
crm__lists__add_contactAdd a contact to a MANUAL list. Idempotent.
crm__lists__remove_contactRemove a contact from a manual list.
crm__lists__archiveArchive a list. Membership rows are preserved but the list is hidden from default queries.
forms__createCreate a form. Slug becomes the URL: /forms/<slug>. Fields are JSON-defined with type matching the contact property type system (text/email/url/number/date/checkbox/select/multi_select/textarea/phone). If captureToListId is set, every submission is auto-added to that list (use crm.lists.create first
forms__updateUpdate a form: rename it, replace its fields, change appearance (theme), button text, thank-you behavior, or the capture list. Omitted properties keep their current value; `fields` and `theme` replace wholesale when provided, so send the COMPLETE new array/object (use forms.get first). Keep existing
forms__getFetch a form by id.
forms__listList all forms in this business.
forms__list_submissionsList submissions for a form, newest first.
forms__get_embed_codeGenerate the JS embed snippet and iframe alternative for a form. Returns both; the user picks whichever fits their site.
crm__contacts__queryQuery contacts with a structured filter DSL. Use this for complex 'show me contacts where X and Y but not Z' queries. Simpler cases prefer crm.contacts.search (free text) or crm.contacts.list (single filter). Field reference forms: email, firstName, ... = stock fields custom:<key> = custom prop
agency__clients__listThe client workspaces this agency workspace runs (each client is its own workspace under the agency's brand), with white-label state, who gets the reports and when the last one went out. On a client workspace, says which agency runs it. Set up under Settings, Agency (the agency side) and Settings, C
agency__client_report__previewWhat this workspace's client report would say for the last 7 or 30 days: ad spend, leads, cost per lead, revenue won, by campaign, and the recent leads. Read-only; agency.client_report.send sends it.
ads__campaign__draftCreate a draft ad campaign on Meta (Facebook and Instagram), Google, or ChatGPT (OpenAI's ChatGPT Ads), the platforms Plyto can actually launch and manage. Drafts are NOT live: they require user approval and a UI launch action before spending money. Use when the user asks to create, generate, or set
ads__listing__readRead a property listing page (Zillow, Realtor.com, Redfin, an MLS or brokerage page): the address, beds, baths, size, price, photos (imported to the library as the ad pictures), the agent and brokerage, and what is still missing. Call it first when a real estate agent or property manager shares a li
ads__campaign__listList ad campaigns for the active business, optionally filtered by status and/or platform. Use when the user asks to see their campaigns or get an overview. The status of a launched campaign here is what Plyto last read from the ad platform (nightly, or the last refresh); before saying whether a camp
ads__performance__summaryTHE authoritative source for a campaign's spend, impressions, clicks and conversions: use this for any question about how much a campaign has spent or how it is performing. Defaults to last 7 days. Numbers are aggregated from daily snapshots written by the performance poller, and `asOf` says when th
ads__performance__analyzeAnalyze recent performance for the active business: surface ads/ad sets that aren't getting impressions, ones spending without converting, and concentration patterns where a small slice drives most results. Also returns CRM attribution per campaign: leads captured (via stamped UTMs), cost per lead,
ads__creative__draftCreate a draft ad creative attached to an existing ad set. Combines an uploaded image with copy + CTA + landing URL. The creative goes live with the ad set when the campaign is launched. Use after ads.campaign.draft and after the user has uploaded at least one image. PLACEMENT SIZES: a campaign need
ads__creative__generate_imagesGenerate AI ad photographs from a prompt + the business's brand profile, sized for Meta placements, and save them to the creative library. For a physical business the picture is the work or the product, and a REAL photo from the library (ads.creative.compose_from_photo) comes first when one exists;
ads__creative__compose_graphicCompose a DESIGNED ad graphic deterministically: real type, the real logo, the real product image, proof from the claims on file. No AI image model; the only AI step is one quick look at the product image to pick the part to show (about a cent, free while the first campaign is set up, remembered per
ads__creative__flyerMake a flyer, poster or handout the owner can download and print, post or send: their title, the facts (when, where, what is included), how to reach them, their logo and one real photo, in their brand. Returns each size with a download link. For anything that is not an ad: an event, a class, an open
ads__creative__compose_from_photoTurn a REAL photo from the library into a finished ad set: the photo cropped for every placement size, the headline in real type, the real logo. No image model, no credits, nothing invented. THE FIRST CHOICE for a physical business whenever the owner has uploaded photos or the website scan saved som
ads__creative__edit_photoEdit one of the owner's REAL photos: take something out of it or change something in it (a coffee table, rugs, clutter, a sign, the background) while their product stays exactly as it is. The edit is checked against the original and saved as a NEW copy beside it (the original is kept); pass adCreati
ads__creative__generate_videoRender a short video clip (4, 6 or 8 seconds, vertical 9:16 or wide 16:9) for Reels, Stories, TikTok-style and feed placements. Use for ANY request for a video, promo clip, reel or animated ad. It renders in the background for a few minutes; the finished clip lands in Creative studio (Marketing, Cre
ads__creative__compose_videoTurn library clips and images into a FINISHED video ad: a hook line over the opening seconds, an optional caption per clip, and a closing brand card with the real logo and a button, at 9:16 for Reels, Stories and TikTok (or 16:9, 1:1). A raw clip from ads.creative.generate_video is footage, not an a
ads__ads__listList every ad on a campaign with its ID, grouped by ad group. Run this BEFORE ads.creative.update or ads.creative.delete: those tools take the ad's id, never its headline text. Also the honest way to answer 'what ads does this campaign have'.
ads__assets__listList the creative library (images, videos) for the active business, with what each asset IS: role 'site-capture' is the real picture of their website the scan saved (the hero for ads.creative.compose_graphic layout screenshot on software), role 'reference' is a real photo of the business, source 'up
ads__assets__deletePermanently delete an UNUSED image or video from the creative library (the file and its record). Refused when the asset — or any of its placement-size siblings (same name, different ratio) — is attached to an ad, because launch resolves sibling sizes by name. Use for library hygiene: failed or super
ads__campaign__updateEdit an existing ad campaign: name, objective, daily budget, plain-English audience, Google search keywords, or a listing campaign's property facts (listing: the price, beds, baths, size, address or open house the owner corrected). Draft campaigns are freely editable. For LIVE campaigns only the bud
ads__campaign__locations__getWhere a campaign's ads show: the location targeting as the ad platform itself reports it for a live or paused campaign (Google: the places plus whether only people physically there see the ads; Meta: the places on every ad set), or what the draft holds. Use before changing locations, when someone as
ads__campaign__changesThe change history of a campaign: every edit made through Plyto (budget, locations, keywords, bidding, status, ads), by whom and when, plus, for a live Google campaign, Google's own change log including edits made directly in Google Ads by a person. Use before explaining why results changed, before