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
| Users (last 30 days) | 0 |
| Handshake | Healthy |
| Auth needed | None |
| Tool count | 195 |
| Latency | 2215 ms |
| Last checked (UTC) | 2026-10-16T23:05:38.678Z |
| Checked (UTC) | Status | Latency | Error |
|---|---|---|---|
| 2026-10-16T23:05:38.678Z | Healthy | 2215 | |
| 2026-10-15T16:15:08.773Z | Healthy | 1510 | |
| 2026-10-14T11:15:34.708Z | Healthy | 1821 |
memory__save | Save (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__list | List 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__forget | Permanently 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__update | Save 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__wait | Wait 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__profile | Return 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__list | List 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__read | Read 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__set | Save 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__remove | Take 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__create | Create 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__update | Update fields on an existing contact. Only pass fields you want to change. No-op fields are silently skipped. |
crm__contacts__get | Fetch 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__list | List 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__search | Free-text search across contact email, first/last name, and phone. Case-insensitive substring match. Returns up to 20 contacts by default. |
crm__contacts__get_properties | Return all custom property values for a contact as a {key: value} map. |
crm__companies__create | Create 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__update | Update fields on an existing company. Only pass fields you want to change. |
crm__companies__get | Fetch a single company by id. Returns null if not found. |
crm__companies__list | List companies in this business. For text search use crm.companies.search. |
crm__companies__search | Free-text search across company name, domain, and industry. Case-insensitive substring match. |
crm__companies__get_properties | Return all custom property values for a company as a {key: value} map. |
crm__tags__create | Create a new tag (or find one with the same name; idempotent). Tags are lightweight orthogonal categories like 'VIP', 'Newsletter-only', 'Cold'. |
crm__tags__list | List all tags defined in this business. |
crm__tags__add_to_contact | Apply a tag to a contact. Idempotent: adding an already-applied tag is a no-op. |
crm__tags__remove_from_contact | Remove a tag from a contact. Idempotent. |
crm__tags__list_for_contact | List all tags applied to a contact. |
crm__tags__add_to_company | Apply a tag to a company. Idempotent. |
crm__tags__remove_from_company | Remove a tag from a company. Idempotent. |
crm__tags__list_for_company | List all tags applied to a company. |
crm__properties__define | Define 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__list | List custom property definitions for an entity type. Excludes archived by default. |
crm__properties__archive | Archive 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_value | Set 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_value | Set the value of a custom property on a company. |
crm__properties__set_deal_value | Set the value of a custom property on a deal. |
crm__properties__clear_contact_value | Remove a custom property value from a contact. |
crm__properties__clear_company_value | Remove a custom property value from a company. |
crm__properties__clear_deal_value | Remove a custom property value from a deal. |
crm__properties__get_deal_values | Return all custom property values for a deal as a {key: value} map. |
crm__pipelines__list | List all pipelines in this business (excludes archived by default). |
crm__pipelines__list_stages | List the ordered stages in a pipeline. Use to look up stageIds for crm.deals.move_stage. |
crm__pipelines__get_default | Get the default pipeline for this business (the one new deals land in by default). Returns null if no default is set. |
crm__deals__create | Create 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__update | Update 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_stage | Move 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_won | Close a deal as won: moves it to the won stage in its pipeline. Sets closedAt and emits deal.won. |
crm__deals__close_lost | Close a deal as lost: moves it to the lost stage in its pipeline. Records lostReason and emits deal.lost. |
crm__deals__get | Fetch a single deal by id. |
crm__deals__list | List 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__search | Free-text search across deal names. |
crm__activities__log | Log 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__list | List activities, optionally filtered by entity (contact/company/deal) or owner. Returned in reverse chronological order by happenedAt. |
crm__activities__get | Fetch a single activity by id. |
crm__tasks__create | Create 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__complete | Mark a task as completed. Sets completedAt to now. |
crm__tasks__cancel | Mark a task as cancelled (rejected, different from completed). Use when the user decides a task is no longer relevant. |
crm__tasks__list | List tasks. Open tasks sort by due date (closest first). Completed tasks sort by completion (most recent first). |
crm__tasks__get | Fetch a single task by id. |
reports__preview | Run 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__performance | Marketing-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__create | Save 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__list | List the saved custom reports on the Reports dashboard. |
crm__notes__add | Add 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__list | List notes, optionally filtered by attached entity. |
crm__lists__create | Create 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__get | Fetch a single list by id. |
crm__lists__list | List all contact lists in this business. |
crm__lists__members | Return 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_contact | Add a contact to a MANUAL list. Idempotent. |
crm__lists__remove_contact | Remove a contact from a manual list. |
crm__lists__archive | Archive a list. Membership rows are preserved but the list is hidden from default queries. |
forms__create | Create 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__update | Update 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__get | Fetch a form by id. |
forms__list | List all forms in this business. |
forms__list_submissions | List submissions for a form, newest first. |
forms__get_embed_code | Generate the JS embed snippet and iframe alternative for a form. Returns both; the user picks whichever fits their site. |
crm__contacts__query | Query 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__list | The 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__preview | What 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__draft | Create 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__read | Read 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__list | List 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__summary | THE 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__analyze | Analyze 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__draft | Create 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_images | Generate 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_graphic | Compose 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__flyer | Make 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_photo | Turn 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_photo | Edit 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_video | Render 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_video | Turn 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__list | List 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__list | List 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__delete | Permanently 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__update | Edit 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__get | Where 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__changes | The 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 |