{"$schema":"https://verifymcp.io/schemas/trust-report.json","schemaVersion":1,"license":{"name":"CC BY-NC 4.0","url":"https://creativecommons.org/licenses/by-nc/4.0/"},"generatedAt":"2026-11-06T17:19:25.352Z","url":"https://verifymcp.io/servers/com-usespur-spur/mcp","markdownUrl":"https://verifymcp.io/servers/com-usespur-spur/mcp.md","server":{"slug":"com-usespur-spur","name":"Spur","registryName":"com.usespur/spur","description":"Real estate leads, skip tracing, direct mail, SMS & email outreach, and marketing automations","registryStatus":"active","deletedAt":null,"statusMessage":null,"liveness":"live","publisher":{"class":"unknown","claimed":false}},"component":{"slug":"mcp","type":"remote","identifier":"https://mcp.usespur.com/mcp","withdrawn":false,"url":"https://verifymcp.io/servers/com-usespur-spur/mcp","version":"1.0.0","transport":null,"scoredAt":"2026-11-06T03:17:31.512Z"},"score":{"total":86,"tier":"high trust","status":"scored","weekDelta":3,"history":[{"date":"2026-11-06","total":86},{"date":"2026-11-05","total":85},{"date":"2026-11-04","total":85},{"date":"2026-11-03","total":84},{"date":"2026-11-02","total":84},{"date":"2026-11-01","total":84},{"date":"2026-10-31","total":83},{"date":"2026-10-30","total":83},{"date":"2026-10-29","total":82},{"date":"2026-10-28","total":82},{"date":"2026-10-27","total":81},{"date":"2026-10-26","total":81},{"date":"2026-10-25","total":80},{"date":"2026-10-24","total":80}]},"categories":[{"name":"Endpoint Security","score":94,"verdict":"fail","reasons":["The endpoint's TLS certificate is valid, in date, and uses a strong key.","Authorisation is enforced on tool calls, advertised via RFC 9728 protected-resource metadata. Discovery is public, which costs nothing: no tool can be invoked without a token.","HTTPS is enforced; there's no plaintext access path.","The HSTS (Strict-Transport-Security) header is present.","DNSSEC check failed: this domain isn't protected by DNSSEC.","The authorisation server offers only Dynamic Client Registration (RFC 7591), which MCP 2026-07-28 deprecated in favour of Client ID Metadata Documents."],"codes":[{"code":"tls.valid","args":{}},{"code":"auth.rfc9728_invoke","args":{}},{"code":"https.enforced","args":{}},{"code":"headers.hsts_present","args":{}},{"code":"dnssec.absent","args":{}},{"code":"auth.registration_dcr_only","args":{}}]},{"name":"Transport & Reachability","score":100,"verdict":"pass","reasons":["Verified streamable-http transport via a live MCP handshake."],"codes":[{"code":"transport.verified","args":{"transport":"streamable-http"}}]},{"name":"Schema Quality & AI Usability","score":79,"verdict":"fail","reasons":["100% of prompts and resources have a non-trivial description (not blank, and not just the item's name).","AI-judged instruction clarity (excellent).","Context-footprint check failed: tool/resource definitions use about 16491 tokens (~171/item across 96 items; 87 tools + 9 resources), over budget; trim descriptions and params.","Usage-examples check failed: none of the tools include examples."],"codes":[{"code":"schema.descriptions","args":{"pct":"100"}},{"code":"schema.ai_quality","args":{"band":"excellent"}},{"code":"schema.footprint_heavy","args":{"avg":"171","items":"96","tools":"87","tokens":"16491","resources":"9"}},{"code":"schema.examples_absent","args":{}}]},{"name":"Stability & Change Management","score":43,"verdict":"partial","reasons":["Stability observed for 13 of 30 days with no destabilising changes; credit accrues until the full window elapses."],"codes":[{"code":"stability.building_history","args":{"days":"13"}}]},{"name":"Tool Coverage","score":99,"verdict":"partial","reasons":["100% of tools have a non-trivial description (not blank, and not just the tool's name).","98% of tool parameters carry a description."],"codes":[{"code":"toolquality.descriptions","args":{"pct":"100"}},{"code":"toolquality.param_docs","args":{"pct":"98"}}]},{"name":"Tool Safety","score":100,"verdict":"pass","reasons":["No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.","All 12 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation.","An AI judge read all 89 captured unit(s) of tool text and found none that tries to manipulate the model reading it."],"codes":[{"code":"toolsafety.injection_clean","args":{}},{"code":"toolsafety.destructive_declared","args":{"pct":"100","annotated":"12","destructive":"12"}},{"code":"toolsafety.manipulation_clean","args":{"units":"89","judged":"89"}}]},{"name":"Capabilities","score":100,"verdict":"pass","reasons":["Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.","Supports UI / widget rendering."],"codes":[{"code":"protocol.supported","args":{"latest":"2026-07-28","version":"2025-11-25"}},{"code":"cap.ui_supported","args":{}}]}],"inconclusive":[],"malware":[],"components":[{"slug":"mcp","type":"remote","identifier":"https://mcp.usespur.com/mcp","withdrawn":false,"score":86,"current":true,"url":"https://verifymcp.io/servers/com-usespur-spur/mcp","markdownUrl":"https://verifymcp.io/servers/com-usespur-spur/mcp.md"}],"facts":[{"label":"Endpoint","value":"https://mcp.usespur.com/mcp"},{"label":"Transports","value":"streamable-http"},{"label":"Auth","value":"required"},{"label":"Version","value":"1.0.0"}],"install":{"available":true,"bundleSha256":null},"tools":[{"name":"spur_search_properties","title":"Search properties","description":"Search live listings (Zillow data) in Spur with plain English and/or structured filters, including event filters. Results show as cards and a map.\n\nUses 1 property-search credit per call from the user's Spur plan, or 2 credits when the search makes more than 3 ZHomes calls (e.g. several event types, tax/sale-history events, or description-based filters). To check how a sentence will be interpreted before spending a credit, call spur_parse_search_query first.\n\nExamples:\n  - \"3 bed houses under $2,500/mo in Austin owned 10+ years\" -> location=\"Austin, TX\", listing_status=\"forRent\", home_type=\"singleFamily\", min_beds=3, max_price=2500, min_ownership_years=10\n  - \"duplexes for sale in 78704 built before 1980 with absentee owners\" -> location=\"78704\", listing_status=\"forSale\", home_type=\"multiFamily\", max_year_built=1979, only_absentee=true\n  - \"new pre-foreclosures or price drops of 10%+ in Dallas this week\" -> location=\"Dallas, TX\", events=[\"preforeclosure\",\"price_drop\"], event_within_days=7, event_min_change_pct=10\n  - \"motivated sellers in Houston: stale, back on market, or fired their agent\" -> location=\"Houston, TX\", events=[\"stale_listing\",\"back_on_market\",\"agent_to_fsbo\"]\n\nEvents (pass in `events`; a listing matches when ANY selected event fired, and is tagged with each):\n- Listing lifecycle: new_listing, coming_soon, coming_soon_active, price_drop, price_increase, pending, sold, back_on_market, delisted (automations only), open_house, rental_listed, rent_change, move_in_moved_up\n- Distress & motivation: preforeclosure, foreclosure, auction, bank_owned, distress_escalated, fsbo, agent_to_fsbo, stale_listing, new_construction\n- Valuation & tax: tax_assessment_posted, assessed_value_jump, tax_paid_dropped (weak), sale_recorded, zestimate_moved, equity_gap, price_below_assessed\nDirect events (price_drop, pending, sold, preforeclosure, fsbo, new_listing, …) work on the first search. Change events (price_increase, back_on_market, distress_escalated, agent_to_fsbo, re…","inputs":[{"name":"center_lat","type":"number","required":false,"description":"Radius search centre (with center_lng and radius_miles)"},{"name":"center_lng","type":"number","required":false,"description":"Radius search centre longitude (with center_lat and radius_miles)"},{"name":"days_posted","type":"integer","required":false,"description":"Only listings posted within this many days"},{"name":"event_min_change_pct","type":"number","required":false,"description":"Minimum % move for price / rent / Zestimate / assessment events"},{"name":"event_min_equity_gap_pct","type":"number","required":false,"description":"Minimum % Zestimate above last sale price for equity_gap (default 30)"},{"name":"event_stale_days","type":"integer","required":false,"description":"Days on market that counts as stale (default 90)"},{"name":"event_within_days","type":"integer","required":false,"description":"Lookback window for events, in days (default 7)"},{"name":"events","type":"array","required":false,"description":"Only listings where any of these events happened; see the tool description"},{"name":"has_3d_tour","type":"boolean","required":false,"description":"true = only listings with a 3D virtual tour"},{"name":"has_air_conditioning","type":"boolean","required":false,"description":"true = only listings with air conditioning"},{"name":"has_garage","type":"boolean","required":false,"description":"true = only listings with a garage"},{"name":"has_open_house","type":"boolean","required":false,"description":"true = only listings with an upcoming open house"},{"name":"has_pool","type":"boolean","required":false,"description":"true = only listings with a pool"},{"name":"has_view","type":"boolean","required":false,"description":"true = only listings with any view (use views on live search for a specific kind)"},{"name":"home_type","type":"string","required":false,"description":"Zillow home type code"},{"name":"is_price_reduced","type":"boolean","required":false,"description":"true = only listings whose asking price has been cut"},{"name":"is_single_story","type":"boolean","required":false,"description":"true = only single-story homes"},{"name":"is_waterfront","type":"boolean","required":false,"description":"true = only waterfront listings"},{"name":"keywords","type":"string","required":false,"description":"Comma-separated listing keywords, e.g. \"pool, granite\""},{"name":"limit","type":"integer","required":false,"description":"Maximum results to show"},{"name":"listing_status","type":"string","required":false,"description":"Defaults to forRent when omitted and the query doesn't imply one"},{"name":"listing_type","type":"string","required":false,"description":"Seller / listing kind; for catching NEW entries into a distress stage prefer events"},{"name":"location","type":"string","required":false,"description":"City and state (\"Dallas, TX\") or ZIP code"},{"name":"max_age","type":"integer","required":false,"description":"At most this many years since built"},{"name":"max_baths","type":"number","required":false,"description":"Maximum bathrooms"},{"name":"max_beds","type":"integer","required":false,"description":"Maximum bedrooms"},{"name":"max_days_on_market","type":"integer","required":false,"description":"Listed at most this many days"},{"name":"max_lot_size","type":"integer","required":false,"description":"Square feet"},{"name":"max_ownership_years","type":"number","required":false,"description":"Owner has held the property at most this long"},{"name":"max_price","type":"number","required":false,"description":"Maximum price (monthly rent for rentals), USD"},{"name":"max_sqft","type":"integer","required":false,"description":"Maximum living area in square feet"},{"name":"max_year_built","type":"integer","required":false,"description":"Built in or before this year"},{"name":"max_zip","type":"integer","required":false,"description":"Highest ZIP code in a numeric ZIP range (with min_zip)"},{"name":"min_age","type":"integer","required":false,"description":"Years since built"},{"name":"min_baths","type":"number","required":false,"description":"Minimum bathrooms (half baths count as 0.5)"},{"name":"min_beds","type":"integer","required":false,"description":"Minimum bedrooms"},{"name":"min_days_on_market","type":"integer","required":false,"description":"Listed at least this many days (higher suggests a motivated seller)"},{"name":"min_equity","type":"number","required":false,"description":"Estimated equity in USD (Zestimate or price minus assessed value; rough)"},{"name":"min_lot_size","type":"integer","required":false,"description":"Square feet (1 acre = 43,560)"},{"name":"min_ownership_years","type":"number","required":false,"description":"Owner has held the property at least this long"},{"name":"min_price","type":"number","required":false,"description":"Minimum price (monthly rent for rentals), USD"},{"name":"min_sqft","type":"integer","required":false,"description":"Minimum living area in square feet"},{"name":"min_year_built","type":"integer","required":false,"description":"Built in or after this year"},{"name":"min_zip","type":"integer","required":false,"description":"Lowest ZIP code in a numeric ZIP range (with max_zip); prefer zip_list for specific ZIPs"},{"name":"neighborhoods","type":"array","required":false,"description":"Neighborhood / area names matched against address and description"},{"name":"only_absentee","type":"boolean","required":false,"description":"Absentee-owned (owner mails elsewhere)"},{"name":"only_pre_foreclosure","type":"boolean","required":false,"description":"Distressed / pre-foreclosure (heuristic, includes description keywords)"},{"name":"only_vacant","type":"boolean","required":false,"description":"Likely vacant (heuristic: description keywords + long days on market)"},{"name":"ownership_type","type":"string","required":false,"description":"business = LLC / corp / trust owners (heuristic)"},{"name":"query","type":"string","required":false,"description":"Free-text intent; Spur's AI turns it into filters and re-ranks by description"},{"name":"radius_miles","type":"number","required":false,"description":"Keep only listings within this many miles of center_lat / center_lng"},{"name":"views","type":"array","required":false,"description":"Only listings with any of these view types"},{"name":"zip_list","type":"array","required":false,"description":"Keep only these ZIPs; set location to the city that contains them"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":1632},{"name":"spur_parse_search_query","title":"Preview search filters","description":"Show how Spur interprets a plain-English property search (filters and events) without running it. Free: uses no search credit.\n\nUse it to confirm intent before spending a credit, or to turn a vague request into explicit filters you can adjust. The returned arguments can be passed straight to spur_search_properties (keep the same location / status unless the user says otherwise). Exclusions (\"no condos\", \"not foreclosures\") are honoured by leaving those filters out.","inputs":[{"name":"query","type":"string","required":true,"description":"The user's search in their own words"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":116},{"name":"spur_get_property","title":"Get property","description":"Show one property as a detailed card: a saved Spur property by property_id, or a live listing by zpid. Includes owner links and enrichment status for saved properties.","inputs":[{"name":"property_id","type":"string","required":false,"description":"Saved Spur property id"},{"name":"zpid","type":"string|number","required":false,"description":"Zillow property id from search results"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":71},{"name":"spur_save_properties","title":"Save properties","description":"Save live listings into the user's Spur properties (and optionally a property list). Pass listing objects from spur_search_properties structuredContent.cards[].listing. Already-saved listings are refreshed, not duplicated.","inputs":[{"name":"list_id","type":"string","required":false,"description":"Also add the saved properties to this property list (spur_list_property_lists)"},{"name":"listings","type":"array","required":true,"description":"Listing objects copied unchanged from spur_search_properties structuredContent.cards[].listing"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":89},{"name":"spur_list_saved_properties","title":"List saved properties","description":"List properties the user has saved in Spur, optionally filtered by city, state, list, or enrichment status. Shows cards and a map.","inputs":[{"name":"city","type":"string","required":false,"description":"City name, e.g. \"Austin\""},{"name":"enrichment_status","type":"string","required":false,"description":"Skip-trace status of the property's owner (spur_enrich)"},{"name":"limit","type":"integer","required":false,"description":"Maximum number of items to return"},{"name":"list_id","type":"string","required":false,"description":"Only properties in this property list"},{"name":"max_price","type":"number","required":false,"description":"Maximum price in USD (rent per month for rentals)"},{"name":"min_price","type":"number","required":false,"description":"Minimum price in USD (rent per month for rentals)"},{"name":"offset","type":"integer","required":false,"description":"Number of items to skip, for pagination"},{"name":"state","type":"string","required":false,"description":"Two-letter state, e.g. \"TX\""}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":167},{"name":"spur_get_property_stats","title":"Property stats","description":"Totals for the user's saved properties: counts by status, enrichment progress and similar portfolio-level numbers.","inputs":[],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":30},{"name":"spur_find_owners","title":"Find owners","description":"Find property owners in the user's Spur account by name, phone, email or mailing address text. Use spur_get_owner for full details and portfolio.","inputs":[{"name":"enrichment_status","type":"string","required":false,"description":"Only owners with this skip-trace status"},{"name":"has_phone","type":"boolean","required":false,"description":"Only owners with at least one phone number"},{"name":"limit","type":"integer","required":false,"description":"Maximum number of items to return"},{"name":"offset","type":"integer","required":false,"description":"Number of items to skip, for pagination"},{"name":"query","type":"string","required":false,"description":"Text to match against name, email, phone or mailing address"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":118},{"name":"spur_get_owner","title":"Get owner","description":"Full owner record: contact details, mailing address, enrichment status and their property portfolio in Spur.","inputs":[{"name":"owner_id","type":"string","required":true,"description":"Owner id (UUID from a Spur list/search tool)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":46},{"name":"spur_enrich","title":"Skip trace / enrich","description":"Run skip tracing to find owners and their phone numbers, emails and mailing addresses.\n- property_ids: find the owner of saved properties (creates/links owner records)\n- owner_ids: refresh contact details for existing owners\n\nCosts enrichment credits per record (check spur_get_billing_summary). Ask the user to approve the count first, then call with confirm=true.","inputs":[{"name":"confirm","type":"boolean","required":true,"description":"Set to true ONLY after the user explicitly approved this exact action in the conversation. Never assume approval."},{"name":"owner_ids","type":"array","required":false,"description":"Owner ids to skip-trace (from spur_find_owners)"},{"name":"property_ids","type":"array","required":false,"description":"Saved property ids whose owners to skip-trace"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":144},{"name":"spur_list_property_lists","title":"List property lists","description":"All of the user's property lists with ids and sizes. Use the id with spur_list_saved_properties, spur_add_properties_to_list or outreach tools.","inputs":[],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":39},{"name":"spur_create_property_list","title":"Create property list","description":"Create a property list, optionally seeding it with saved property ids.","inputs":[{"name":"description","type":"string","required":false,"description":"Optional note about what the list is for"},{"name":"name","type":"string","required":true,"description":"List name shown in Spur"},{"name":"property_ids","type":"array","required":false,"description":"Saved property ids to add right away"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":66},{"name":"spur_add_properties_to_list","title":"Add properties to list","description":"Add saved properties to an existing property list.","inputs":[{"name":"list_id","type":"string","required":true,"description":"Property list id (UUID from a Spur list/search tool)"},{"name":"property_ids","type":"array","required":true,"description":"Saved property ids (from spur_list_saved_properties or spur_save_properties)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":60},{"name":"spur_draft_message","title":"Draft outreach message","description":"Draft an email, SMS or direct-mail message with Spur's AI. Returns text only; nothing is sent. Pass owner_id/property_id to personalize with real owner and property data.\n\nUses 1 AI credit. Use {{merge_fields}} like {{owner_first_name}} and {{property_address}} to keep it reusable (see resource spur://merge-variables).","inputs":[{"name":"channel","type":"string","required":true,"description":"Which kind of message to draft"},{"name":"owner_id","type":"string","required":false,"description":"Owner to personalize for (from spur_find_owners)"},{"name":"prompt","type":"string","required":true,"description":"What to write, e.g. \"friendly first touch asking if they would sell, mention we close in cash\""},{"name":"property_id","type":"string","required":false,"description":"Saved property the message is about"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":155},{"name":"spur_list_workflows","title":"List outreach workflows","description":"The user's multi-step outreach workflows (email/SMS/mail sequences) with status and enrollment counts.","inputs":[],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":29},{"name":"spur_get_workflow","title":"Get outreach workflow","description":"One outreach workflow's steps in order (including reply/open branches built in the app), with the email, SMS or mail template each send step uses and its step_id.","inputs":[{"name":"workflow_id","type":"string","required":true,"description":"Workflow id (UUID from a Spur list/search tool)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":62},{"name":"spur_create_workflow","title":"Create outreach workflow","description":"Create a multi-step outreach sequence from saved templates. It's saved as an inactive draft: nothing sends until it's activated (spur_set_workflow_active) and owners are enrolled.\n\nSend steps take a saved template: email/SMS from spur_list_message_templates (design new ones with spur_create_email_template / spur_create_sms_template), direct mail from the user's saved postcard or letter templates. Wait steps space them out. Reply/open/click branches can be added afterwards in Spur → Outreach.\n\nExample: \"text, wait 2 days, email, wait 5 days, postcard\" -> steps=[{type:\"send_sms\",template_id:…},{type:\"wait\",days:2},{type:\"send_email\",template_id:…},{type:\"wait\",days:5},{type:\"send_direct_mail\",template_id:…}]","inputs":[{"name":"description","type":"string","required":false,"description":"Optional note about the workflow's goal"},{"name":"name","type":"string","required":true,"description":"Workflow name shown in Spur"},{"name":"steps","type":"array","required":true,"description":"Steps in run order: send_email / send_sms / send_direct_mail with a saved template_id, and wait steps (days, hours) between them"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":238},{"name":"spur_set_workflow_step_template","title":"Use template in workflow step","description":"Set which saved template a workflow's send step uses (send_email ← email template, send_sms ← SMS template, send_direct_mail ← postcard/letter template). Get step_ids from spur_get_workflow. If the workflow is active, enrolled owners get the new template from their next send.","inputs":[{"name":"step_id","type":"string","required":true,"description":"step_id from spur_get_workflow"},{"name":"template_id","type":"string","required":true,"description":"Template id (UUID from a Spur list/search tool)"},{"name":"workflow_id","type":"string","required":true,"description":"Workflow id (UUID from a Spur list/search tool)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":121},{"name":"spur_set_workflow_active","title":"Activate or pause workflow","description":"Activate a workflow (enrolled owners start receiving its emails, texts and mail on schedule, which uses credits) or pause it. Activating requires every send step to have a template and the user's explicit approval.","inputs":[{"name":"active","type":"boolean","required":true,"description":"true = activate (start sending), false = pause"},{"name":"confirm","type":"boolean","required":false,"description":"Required (true) when activating, only after the user approved it"},{"name":"workflow_id","type":"string","required":true,"description":"Workflow id (UUID from a Spur list/search tool)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":108},{"name":"spur_enroll_owners_in_workflow","title":"Enroll owners in workflow","description":"Enroll owners in an outreach workflow. The workflow will start emailing/texting/mailing them on its schedule, which uses credits. Confirm the workflow name and owner count with the user first.","inputs":[{"name":"confirm","type":"boolean","required":true,"description":"Set to true ONLY after the user explicitly approved this exact action in the conversation. Never assume approval."},{"name":"owner_ids","type":"array","required":true,"description":"Owner ids to enroll (from spur_find_owners); owners without the needed contact details are skipped"},{"name":"workflow_id","type":"string","required":true,"description":"Workflow id (UUID from a Spur list/search tool)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":123},{"name":"spur_send_sms","title":"Send SMS","description":"Text an owner from the user's default approved number (or Spur's workspace number if they have none; see spur_get_texting_setup). Show the user the exact message and recipient and get approval before calling with confirm=true. Billed per 160-character segment (70 if it has emoji or special characters), so a 200-character text costs 2.","inputs":[{"name":"confirm","type":"boolean","required":true,"description":"Set to true ONLY after the user explicitly approved this exact action in the conversation. Never assume approval."},{"name":"message","type":"string","required":true,"description":"Exact SMS text the user approved"},{"name":"owner_id","type":"string","required":true,"description":"Owner id (UUID from a Spur list/search tool)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":137},{"name":"spur_list_sms_conversations","title":"SMS inbox","description":"SMS conversations with owners, newest first, with unread counts and the last message. Good for triaging replies (hot leads, not interested, wrong number).","inputs":[{"name":"limit","type":"integer","required":false,"description":"Maximum conversations to return"},{"name":"unread_only","type":"boolean","required":false,"description":"true = only conversations with unread replies"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":68},{"name":"spur_get_sms_conversation","title":"SMS thread","description":"The full SMS thread with one owner, oldest first.","inputs":[{"name":"owner_id","type":"string","required":true,"description":"Owner id (UUID from a Spur list/search tool)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":40},{"name":"spur_get_outreach_report","title":"Outreach report","description":"Outreach performance (sent, delivered, opened, replied, by channel) for all workflows or one, over an optional date range.","inputs":[{"name":"date_from","type":"string","required":false,"description":"ISO date, e.g. 2026-10-01"},{"name":"date_to","type":"string","required":false,"description":"ISO date"},{"name":"workflow_id","type":"string","required":false,"description":"Report on one workflow only; omit for all workflows"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":86},{"name":"spur_list_message_templates","title":"List email and SMS templates","description":"The user's saved email and SMS templates. These are what outreach workflow send_email / send_sms steps use.","inputs":[{"name":"channel","type":"string","required":false,"description":"Only email or only SMS templates; omit for both"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":48},{"name":"spur_get_message_template","title":"Get email or SMS template","description":"Fetch an email template's subject, HTML and plain text, or an SMS template's text, for reading or editing. For small changes prefer the update tool's replacements over resending everything.","inputs":[{"name":"template_id","type":"string","required":true,"description":"Email or SMS template id (UUID from a Spur list/search tool)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":69},{"name":"spur_preview_message_template","title":"Preview email or SMS template","description":"Show a saved email (inbox view, desktop and mobile) or SMS (phone view, segment count) rendered with sample data or a real owner's data, plus design checks. Changes nothing.","inputs":[{"name":"owner_id","type":"string","required":false,"description":"Preview with this owner's real data (spur_find_owners) instead of sample data"},{"name":"template_id","type":"string","required":true,"description":"Email or SMS template id (UUID from a Spur list/search tool)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":94},{"name":"spur_preview_message_draft","title":"Preview unsaved email or SMS","description":"Preview email HTML or SMS text before saving it: renders it with sample or real owner data and runs the design checks. Nothing is saved; iterate here, then save with spur_create_email_template / spur_create_sms_template.\n\nEmail HTML guidance (see spur://email-design-guide): full HTML document, table-based layout 600px wide, inline styles on elements (a small <style> block in <head> is fine for resets/media queries), web-safe font stacks, no JavaScript, no forms, no external CSS. Images as absolute https URLs with alt text and width attributes. The user's own images (logo, headshot, property photos): spur_list_images gives their URLs; if they want to use an image they have, call spur_upload_image (or spur_save_image for a link) and use the saved URL. Use PNG or JPG in emails, not SVG. A hidden preheader <div style=\"display:none\"> right after <body> sets the inbox snippet. Include an opt-out line (\"Reply STOP\" or an unsubscribe link) and the sender's postal address in the footer. Keep it under 50,000 characters. Use merge fields like {{owner_first_name}}, {{property_address}}, {{your_name}}, {{your_phone}}, {{company_name}} (see spur://merge-variables). The plain-text version is generated from the HTML unless you pass text_body.\n\nSMS guidance: plain text, ideally one segment (160 GSM characters, or 70 if it uses emoji or other non-GSM characters; longer messages are billed per segment). Identify yourself, keep it conversational, and include opt-out wording (\"Reply STOP to opt out\") on first-touch messages. Links in texts from unverified numbers can be blocked, so give link-free fallback_body text when the body has a URL. Use merge fields like {{owner_first_name}}, {{property_address}}, {{your_name}} (see spur://merge-variables).","inputs":[{"name":"channel","type":"string","required":true,"description":"Whether the draft is an email or an SMS"},{"name":"fallback_body","type":"string","required":false,"description":"SMS only: link-free text sent if links are blocked"},{"name":"html","type":"string","required":false,"description":"Email HTML"},{"name":"owner_id","type":"string","required":false,"description":"Preview with this owner's real data (spur_find_owners) instead of sample data"},{"name":"subject","type":"string","required":false,"description":"Email subject"},{"name":"text","type":"string","required":false,"description":"SMS text, or the email's plain-text version"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":492},{"name":"spur_create_email_template","title":"Create email template","description":"Save a new email template and show its inbox preview. Ways to start:\n- From blank: start_from_blank=true gives a tested 600px responsive layout with greeting, signature and a compliant footer; pass html to replace it.\n- Redesign: base_template_id copies one of the user's email templates.\n- From scratch: pass html (or only text_body for a plain-text email).\nThe saved template can then be used by outreach workflows (spur_create_workflow / spur_set_workflow_step_template).\n\nEmail HTML guidance (see spur://email-design-guide): full HTML document, table-based layout 600px wide, inline styles on elements (a small <style> block in <head> is fine for resets/media queries), web-safe font stacks, no JavaScript, no forms, no external CSS. Images as absolute https URLs with alt text and width attributes. The user's own images (logo, headshot, property photos): spur_list_images gives their URLs; if they want to use an image they have, call spur_upload_image (or spur_save_image for a link) and use the saved URL. Use PNG or JPG in emails, not SVG. A hidden preheader <div style=\"display:none\"> right after <body> sets the inbox snippet. Include an opt-out line (\"Reply STOP\" or an unsubscribe link) and the sender's postal address in the footer. Keep it under 50,000 characters. Use merge fields like {{owner_first_name}}, {{property_address}}, {{your_name}}, {{your_phone}}, {{company_name}} (see spur://merge-variables). The plain-text version is generated from the HTML unless you pass text_body.","inputs":[{"name":"base_template_id","type":"string","required":false,"description":"Copy this email template as the starting point"},{"name":"description","type":"string","required":false,"description":"Internal note about the template; never sent to owners"},{"name":"html","type":"string","required":false,"description":"Full email HTML; merge fields like {{owner_first_name}} are filled per recipient"},{"name":"is_default","type":"boolean","required":false,"description":"Make this the user's default email template"},{"name":"name","type":"string","required":true,"description":"Template name shown in Spur"},{"name":"start_from_blank","type":"boolean","required":false,"description":"true = start from Spur's responsive, compliant blank email layout"},{"name":"subject","type":"string","required":false,"description":"Required unless copying a template"},{"name":"text_body","type":"string","required":false,"description":"Plain-text version; generated from the HTML when omitted"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":480},{"name":"spur_update_email_template","title":"Edit email template","description":"Edit one of the user's email templates and show the updated inbox preview. For tweaks pass replacements (exact find → replace in \"html\", \"subject\" or \"text\"); to redesign pass full html. When the HTML changes, the plain-text version is regenerated unless it was hand-written or you pass text_body. Workflows using this template send the new version from their next step.\n\nExample: replacements=[{target:\"html\", find:\"<p style=\\\"margin:0 0 16px;\\\"></p>\", replace:\"<p style=\\\"margin:0 0 16px;\\\">I'd like to make a cash offer on {{property_address}}.</p>\"}]\n\nEmail HTML guidance (see spur://email-design-guide): full HTML document, table-based layout 600px wide, inline styles on elements (a small <style> block in <head> is fine for resets/media queries), web-safe font stacks, no JavaScript, no forms, no external CSS. Images as absolute https URLs with alt text and width attributes. The user's own images (logo, headshot, property photos): spur_list_images gives their URLs; if they want to use an image they have, call spur_upload_image (or spur_save_image for a link) and use the saved URL. Use PNG or JPG in emails, not SVG. A hidden preheader <div style=\"display:none\"> right after <body> sets the inbox snippet. Include an opt-out line (\"Reply STOP\" or an unsubscribe link) and the sender's postal address in the footer. Keep it under 50,000 characters. Use merge fields like {{owner_first_name}}, {{property_address}}, {{your_name}}, {{your_phone}}, {{company_name}} (see spur://merge-variables). The plain-text version is generated from the HTML unless you pass text_body.","inputs":[{"name":"description","type":"string","required":false,"description":"Internal note about the template; never sent to owners"},{"name":"html","type":"string","required":false,"description":"Full replacement HTML"},{"name":"is_default","type":"boolean","required":false,"description":"true = make this the user's default template for its channel"},{"name":"name","type":"string","required":false,"description":"New template name"},{"name":"replacements","type":"array","required":false,"description":"Small edits applied in order instead of resending the full content"},{"name":"subject","type":"string","required":false,"description":"New subject line"},{"name":"template_id","type":"string","required":true,"description":"Email or SMS template id (UUID from a Spur list/search tool)"},{"name":"text_body","type":"string","required":false,"description":"Full replacement plain-text version"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":499},{"name":"spur_create_sms_template","title":"Create SMS template","description":"Save a new SMS template and show its phone preview with segment count and checks. The saved template can then be used by outreach workflows (spur_create_workflow / spur_set_workflow_step_template).\n\nSMS guidance: plain text, ideally one segment (160 GSM characters, or 70 if it uses emoji or other non-GSM characters; longer messages are billed per segment). Identify yourself, keep it conversational, and include opt-out wording (\"Reply STOP to opt out\") on first-touch messages. Links in texts from unverified numbers can be blocked, so give link-free fallback_body text when the body has a URL. Use merge fields like {{owner_first_name}}, {{property_address}}, {{your_name}} (see spur://merge-variables).","inputs":[{"name":"description","type":"string","required":false,"description":"Internal note about the template; never sent to owners"},{"name":"fallback_body","type":"string","required":false,"description":"Link-free version sent if a link gets the text blocked"},{"name":"is_default","type":"boolean","required":false,"description":"true = make this the user's default template for its channel"},{"name":"name","type":"string","required":true,"description":"Template name shown in Spur"},{"name":"text","type":"string","required":true,"description":"SMS body; merge fields like {{owner_first_name}} are filled per recipient"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":247},{"name":"spur_update_sms_template","title":"Edit SMS template","description":"Edit one of the user's SMS templates and show the updated preview. Pass full text / fallback_body, or replacements (exact find → replace in \"text\" or \"fallback\"). Pass fallback_body=\"\" to remove the fallback.\n\nSMS guidance: plain text, ideally one segment (160 GSM characters, or 70 if it uses emoji or other non-GSM characters; longer messages are billed per segment). Identify yourself, keep it conversational, and include opt-out wording (\"Reply STOP to opt out\") on first-touch messages. Links in texts from unverified numbers can be blocked, so give link-free fallback_body text when the body has a URL. Use merge fields like {{owner_first_name}}, {{property_address}}, {{your_name}} (see spur://merge-variables).","inputs":[{"name":"description","type":"string","required":false,"description":"Internal note about the template; never sent to owners"},{"name":"fallback_body","type":"string","required":false,"description":"Link-free text sent instead when links are blocked; \"\" removes it"},{"name":"is_default","type":"boolean","required":false,"description":"true = make this the user's default template for its channel"},{"name":"name","type":"string","required":false,"description":"New template name"},{"name":"replacements","type":"array","required":false,"description":"Small edits applied in order instead of resending the full content"},{"name":"template_id","type":"string","required":true,"description":"Email or SMS template id (UUID from a Spur list/search tool)"},{"name":"text","type":"string","required":false,"description":"Full replacement SMS body"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":281},{"name":"spur_delete_message_template","title":"Delete email or SMS template","description":"Delete one of the user's email or SMS templates. Workflow steps that use it will fail until they get another template, so check spur_list_workflows and confirm with the user first.","inputs":[{"name":"confirm","type":"boolean","required":true,"description":"Set to true ONLY after the user explicitly approved this exact action in the conversation. Never assume approval."},{"name":"template_id","type":"string","required":true,"description":"Email or SMS template id (UUID from a Spur list/search tool)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":93},{"name":"spur_send_test_email","title":"Send test email to yourself","description":"Send an email template, filled with sample data (or one owner's data), to the user's own Spur account email from their email sender, to check it in a real inbox. Never goes to owners. Confirm with the user first.","inputs":[{"name":"confirm","type":"boolean","required":true,"description":"Set to true ONLY after the user explicitly approved this exact action in the conversation. Never assume approval."},{"name":"owner_id","type":"string","required":false,"description":"Preview with this owner's real data (spur_find_owners) instead of sample data"},{"name":"sender_id","type":"string","required":false,"description":"Email sender to send from; defaults to the user's default sender"},{"name":"template_id","type":"string","required":true,"description":"Email or SMS template id (UUID from a Spur list/search tool)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":147},{"name":"spur_list_postcard_templates","title":"List postcard templates","description":"Postcard templates available to the user: their own (editable), Spur defaults, and Spur's built-in gallery. Copy a default or gallery design with spur_create_postcard_template base_template_id to redesign it, or start from blank with start_from_blank.","inputs":[{"name":"category","type":"string","required":false,"description":"Only templates in this category, e.g. \"Just Sold\""},{"name":"include_gallery","type":"boolean","required":false,"description":"Include Spur's built-in gallery designs"},{"name":"size","type":"string","required":false,"description":"Only templates designed for this size"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":109},{"name":"spur_get_postcard_template","title":"Get postcard template HTML","description":"Fetch a postcard template's HTML for reading or editing. For small text changes prefer spur_update_postcard_template with replacements instead of resending whole HTML.","inputs":[{"name":"side","type":"string","required":false,"description":"Which side's HTML to return"},{"name":"template_id","type":"string","required":true,"description":"Template id from spur_list_postcard_templates (a saved template's UUID or a Spur gallery id)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":81},{"name":"spur_preview_postcard_template","title":"Preview postcard template","description":"Show any postcard template (yours, a Spur default or a gallery design) rendered with sample data, without changing anything.","inputs":[{"name":"template_id","type":"string","required":true,"description":"Template id from spur_list_postcard_templates (a saved template's UUID or a Spur gallery id)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":63},{"name":"spur_create_postcard_template","title":"Create postcard template","description":"Create a postcard template and show a preview. Three ways to start:\n- From blank: start_from_blank=true gives an empty, correctly sized canvas for the size (any side you pass HTML for replaces the blank side).\n- Redesign an existing template: base_template_id (yours, a Spur default or a gallery id) copies its HTML; override front_html/back_html/size as needed.\n- From scratch: pass both front_html and back_html.\n\nHTML guidance (see spur://postcard-design-guide): full HTML documents, inline <style>, no scripts. Fixed pixel sizing on html, body and a root .postcard at 300 px/in including 0.125\" bleed: 6x4 → 1875×1275, 6x9 → 2775×1875, 6x11 → 3375×1875. Keep text 0.25\" inside the edge. USPS clear zone on the back, measured from the bleed edges (Spur prints over it and covers anything there): postage 1.25\"×1.25\" in the top-right corner; address block 3.5\"×2\", 0.5\" from the right edge and 1.35\" from the top; USPS barcode strip 4.75\"×0.625\" along the bottom-right edge. Keep all text, logos, photos and QR codes out of it; a background color or texture under it is fine. The safe message area is left of the address block and above the barcode strip. Images as absolute https URLs. The user's own images (logo, headshot, property photos): spur_list_images gives their URLs; if they want to use an image they have, call spur_upload_image (or spur_save_image for a link) and use the saved URL. Use merge fields such as {{owner_first_name}}, {{property_address}}, {{your_name}}, {{your_phone}}, {{company_name}} (see spur://merge-variables). QR code box by default: unless the user says they don't want a QR code, every back you design includes an empty square <div class=\"qr-box\"> in the message area (≥ 0.75in, e.g. 225×225 px at 300 px/in) with a short call to action beside it (\"Scan to see your home's value\"). Never write QR markup or {{qrUrl}} yourself: after saving, call spur_add_postcard_qr_code and the tracked QR fills the box (pick the code with spur_list_qr_codes, or ask the user…","inputs":[{"name":"back_html","type":"string","required":false,"description":"Full back-side HTML; keep the address block area clear"},{"name":"base_template_id","type":"string","required":false,"description":"Copy this template's HTML and size as the starting point"},{"name":"category","type":"string","required":false,"description":"Category label, e.g. \"Just Sold\" or \"Market Update\""},{"name":"description","type":"string","required":false,"description":"Internal note about the template; never printed"},{"name":"front_html","type":"string","required":false,"description":"Full front-side HTML"},{"name":"name","type":"string","required":true,"description":"Template name shown in Spur"},{"name":"size","type":"string","required":false,"description":"Postcard size: \"6x4\" (standard 4×6), \"6x9\" or \"6x11\""},{"name":"start_from_blank","type":"boolean","required":false,"description":"Start from an empty canvas sized for `size`"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":727},{"name":"spur_update_postcard_template","title":"Edit postcard template","description":"Edit one of the user's postcard templates and show the updated preview. For text tweaks pass replacements (exact find → replace on a side); to redesign, pass full front_html/back_html. Spur default and gallery templates can't be edited: copy them first with spur_create_postcard_template base_template_id.\n\nExample: change the headline -> replacements=[{side:\"front\", find:\"Just Sold!\", replace:\"Just Listed!\"}]\n\nHTML guidance (see spur://postcard-design-guide): full HTML documents, inline <style>, no scripts. Fixed pixel sizing on html, body and a root .postcard at 300 px/in including 0.125\" bleed: 6x4 → 1875×1275, 6x9 → 2775×1875, 6x11 → 3375×1875. Keep text 0.25\" inside the edge. USPS clear zone on the back, measured from the bleed edges (Spur prints over it and covers anything there): postage 1.25\"×1.25\" in the top-right corner; address block 3.5\"×2\", 0.5\" from the right edge and 1.35\" from the top; USPS barcode strip 4.75\"×0.625\" along the bottom-right edge. Keep all text, logos, photos and QR codes out of it; a background color or texture under it is fine. The safe message area is left of the address block and above the barcode strip. Images as absolute https URLs. The user's own images (logo, headshot, property photos): spur_list_images gives their URLs; if they want to use an image they have, call spur_upload_image (or spur_save_image for a link) and use the saved URL. Use merge fields such as {{owner_first_name}}, {{property_address}}, {{your_name}}, {{your_phone}}, {{company_name}} (see spur://merge-variables). QR code box by default: unless the user says they don't want a QR code, every back you design includes an empty square <div class=\"qr-box\"> in the message area (≥ 0.75in, e.g. 225×225 px at 300 px/in) with a short call to action beside it (\"Scan to see your home's value\"). Never write QR markup or {{qrUrl}} yourself: after saving, call spur_add_postcard_qr_code and the tracked QR fills the box (pick the code with spur_list_qr_codes, or ask the user whe…","inputs":[{"name":"back_html","type":"string","required":false,"description":"Full replacement back HTML"},{"name":"category","type":"string","required":false,"description":"Category label, e.g. \"Just Sold\" or \"Market Update\""},{"name":"description","type":"string","required":false,"description":"Internal note about the template; never printed"},{"name":"front_html","type":"string","required":false,"description":"Full replacement front HTML"},{"name":"name","type":"string","required":false,"description":"New template name"},{"name":"replacements","type":"array","required":false,"description":"Small edits applied in order instead of resending the full content"},{"name":"size","type":"string","required":false,"description":"Postcard size: \"6x4\" (standard 4×6), \"6x9\" or \"6x11\""},{"name":"template_id","type":"string","required":true,"description":"Template id (UUID from a Spur list/search tool)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":709},{"name":"spur_add_postcard_qr_code","title":"Add QR code to postcard","description":"Put a tracked QR code on the back of one of the user's postcard templates and show the preview. Each mailed postcard gets its own code under the chosen QR code, so scans are tracked per recipient (spur_get_qr_code_activity).\n\nPlacement: position=\"auto\" fills the back's QR slot (an element with class qr-box, qr-code-box, qr-placeholder or qr-callout-box, as Spur's gallery designs have) and otherwise goes in the bottom-right of the message area, left of the address block and above the USPS barcode. Corners (top_left, top_right, bottom_left, bottom_right) and x_inches/y_inches place it in the message area; spots in the USPS clear zone are refused. Replaces any QR already on the card. If it lands on the message text, call again with another corner or move the text.\n\nCreate a QR code first with spur_create_qr_code if spur_list_qr_codes has none that fits.","inputs":[{"name":"position","type":"string","required":false,"description":"Where to place the code on the back; auto fills the design's qr-box"},{"name":"qr_code_id","type":"string","required":true,"description":"QR code id from spur_list_qr_codes or spur_create_qr_code"},{"name":"size_inches","type":"number","required":false,"description":"Edge length when placed in the message area (not used for a slot). 0.75in or more scans reliably"},{"name":"template_id","type":"string","required":true,"description":"Postcard template id (UUID from a Spur list/search tool)"},{"name":"x_inches","type":"number","required":false,"description":"Left edge, inches from the bleed's left edge (with y_inches; overrides position)"},{"name":"y_inches","type":"number","required":false,"description":"Top edge, inches from the bleed's top edge (with x_inches)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":353},{"name":"spur_remove_postcard_qr_code","title":"Remove QR code from postcard","description":"Remove the tracked QR code from one of the user's postcard templates (both sides) and show the preview. A QR slot from the design stays, empty.","inputs":[{"name":"template_id","type":"string","required":true,"description":"Postcard template id (UUID from a Spur list/search tool)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":64},{"name":"spur_delete_postcard_template","title":"Delete postcard template","description":"Permanently delete one of the user's postcard templates. Spur defaults can't be deleted. Confirm with the user first.","inputs":[{"name":"confirm","type":"boolean","required":true,"description":"Set to true ONLY after the user explicitly approved this exact action in the conversation. Never assume approval."},{"name":"template_id","type":"string","required":true,"description":"Template id (UUID from a Spur list/search tool)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":77},{"name":"spur_list_return_addresses","title":"List return addresses","description":"Return addresses saved for direct mail. The default one is used when a campaign doesn't specify one.","inputs":[],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":29},{"name":"spur_preview_postcard_campaign","title":"Preview postcard mailing","description":"Required step before mailing postcards. Renders the postcard with the first recipient's real data, checks which owners have mailing addresses, estimates the cost from the user's plan, and returns a confirmation_token.\n\nShow the preview, then ask the user to approve. Only after they approve, call spur_send_postcards with the token (the widget's Send button does the same). The token expires in 30 minutes and only covers this exact template, recipient set, return address and size.","inputs":[{"name":"owner_ids","type":"array","required":true,"description":"Owners to mail (spur_find_owners)"},{"name":"return_address_id","type":"string","required":false,"description":"Defaults to the user's default return address"},{"name":"size","type":"string","required":false,"description":"Defaults to the template's size"},{"name":"template_id","type":"string","required":true,"description":"Postcard template id (UUID from a Spur list/search tool)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":171},{"name":"spur_send_postcards","title":"Send postcards","description":"Mail the postcards approved in spur_preview_postcard_campaign. Irreversible and charges the user's account. Requires the preview's confirmation_token and the user's explicit approval (confirm=true).","inputs":[{"name":"confirm","type":"boolean","required":true,"description":"Set to true ONLY after the user explicitly approved this exact action in the conversation. Never assume approval."},{"name":"confirmation_token","type":"string","required":true,"description":"confirmation_token from spur_preview_postcard_campaign"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":87},{"name":"spur_list_qr_codes","title":"List QR codes","description":"The user's tracked QR codes: destination URL, colors, and which of their postcard templates print each one. Put one on a postcard with spur_add_postcard_qr_code; see scans with spur_get_qr_code_activity.","inputs":[],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":56},{"name":"spur_create_qr_code","title":"Create QR code","description":"Create a tracked QR code that sends scans to destination_url. Then add it to a postcard back with spur_add_postcard_qr_code; each mailed postcard gets its own code under it so scans are tracked per recipient.","inputs":[{"name":"background_color","type":"string","required":false,"description":"Hex color, e.g. #111827. Keep strong contrast with the background so phones can scan it"},{"name":"destination_url","type":"string","required":true,"description":"Where a scan sends people, e.g. https://acme.example/offer (https:// is added if missing)"},{"name":"foreground_color","type":"string","required":false,"description":"Hex color, e.g. #111827. Keep strong contrast with the background so phones can scan it"},{"name":"name","type":"string","required":true,"description":"Label shown in Spur, e.g. 'Spring cash offer'"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":163},{"name":"spur_update_qr_code","title":"Edit QR code","description":"Rename a QR code, change where scans go, or change its colors. A new destination also applies to postcards already mailed with this code. Templates pick up new colors at send time.","inputs":[{"name":"background_color","type":"string","required":false,"description":"Hex color, e.g. #111827. Keep strong contrast with the background so phones can scan it"},{"name":"destination_url","type":"string","required":false,"description":"Where a scan sends people, e.g. https://acme.example/offer (https:// is added if missing)"},{"name":"foreground_color","type":"string","required":false,"description":"Hex color, e.g. #111827. Keep strong contrast with the background so phones can scan it"},{"name":"name","type":"string","required":false,"description":"New name for the QR code"},{"name":"qr_code_id","type":"string","required":true,"description":"QR code id from spur_list_qr_codes"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":168},{"name":"spur_delete_qr_code","title":"Delete QR code","description":"Permanently delete a QR code. Refused while one of the user's postcard templates still prints it: remove it from those first with spur_remove_postcard_qr_code. Postcards already mailed keep redirecting. Confirm with the user first.","inputs":[{"name":"confirm","type":"boolean","required":true,"description":"Set to true ONLY after the user explicitly approved this exact action in the conversation. Never assume approval."},{"name":"qr_code_id","type":"string","required":true,"description":"QR code id from spur_list_qr_codes"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":103},{"name":"spur_get_qr_code_activity","title":"QR code scans","description":"Scan activity for a QR code: totals, which mailed recipients (owners) scanned their postcard, and recent scans of the code itself. Use it to find warm leads from a postcard campaign.","inputs":[{"name":"limit","type":"integer","required":false,"description":"Maximum recipients and scans to list"},{"name":"qr_code_id","type":"string","required":true,"description":"QR code id from spur_list_qr_codes"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":81},{"name":"spur_list_images","title":"List images","description":"The user's saved images (logos, headshots, property photos) with their public URLs, newest first. Use it as <img src=\"…\"> (or a CSS background-image) in postcard, letter or email HTML. To add one, use spur_upload_image (from the user's device) or spur_save_image (from a URL).","inputs":[{"name":"limit","type":"integer","required":false,"description":"Maximum images to return"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":88},{"name":"spur_save_image","title":"Save image from URL","description":"Download an image from a public https URL (the user's website, a shared photo link) and save it to the user's Spur image library, so it stays available for printing and sending. Returns the saved image's URL. JPG, PNG, WebP, GIF or SVG, up to 20 MB. For a file on the user's device, use spur_upload_image instead. Only save images the user provided or asked for.","inputs":[{"name":"filename","type":"string","required":false,"description":"Name to save it under, e.g. 'acme-logo'"},{"name":"url","type":"string","required":true,"description":"Public https URL of the image"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":127},{"name":"spur_upload_image","title":"Upload image","description":"Show an upload box where the user picks or drops an image from their device (logo, headshot, property photo); it's saved to their Spur image library. Use this whenever the user wants to use their own image, including one they attached to the chat: attachments don't reach Spur, so ask them to drop it into the box. After uploading, the user can click \"Use this image\", or call spur_list_images to get the URL.","inputs":[{"name":"purpose","type":"string","required":false,"description":"What the image is for, shown to the user, e.g. 'Logo for the postcard back'"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":125},{"name":"spur_save_uploaded_image","title":"Save uploaded image","description":"Used by the upload widget to save the file the user picked. Not for the assistant.","inputs":[{"name":"data_base64","type":"string","required":true,"description":"File contents, base64-encoded"},{"name":"filename","type":"string","required":true,"description":"Original file name"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":54},{"name":"spur_delete_image","title":"Delete image","description":"Permanently delete one of the user's saved images. Refused while one of their postcard, letter or email templates still uses it, since those would show a broken image. Confirm with the user first.","inputs":[{"name":"confirm","type":"boolean","required":true,"description":"Set to true ONLY after the user explicitly approved this exact action in the conversation. Never assume approval."},{"name":"image_id","type":"string","required":true,"description":"image_id from spur_list_images"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":89},{"name":"spur_list_letter_templates","title":"List letter templates","description":"Letter templates available to the user, with their letter type (Top First Page or Insert Blank Page): their own (editable), Spur defaults, and Spur's built-in gallery. Copy one with spur_create_letter_template base_template_id to redesign it, or start from blank with start_from_blank.","inputs":[{"name":"category","type":"string","required":false,"description":"Only templates in this category"},{"name":"include_gallery","type":"boolean","required":false,"description":"Include Spur's built-in gallery designs"},{"name":"letter_type","type":"string","required":false,"description":"Only templates of this letter type"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":109},{"name":"spur_get_letter_template","title":"Get letter template HTML","description":"Fetch a letter template's HTML for reading or editing. For small text changes prefer spur_update_letter_template with replacements instead of resending the whole HTML.","inputs":[{"name":"template_id","type":"string","required":true,"description":"Template id from spur_list_letter_templates (a saved template's UUID or a Spur gallery id)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":66},{"name":"spur_preview_letter_template","title":"Preview letter template","description":"Show any letter template (yours, a Spur default or a gallery design) rendered with sample data, without changing anything.","inputs":[{"name":"template_id","type":"string","required":true,"description":"Template id from spur_list_letter_templates (a saved template's UUID or a Spur gallery id)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":61},{"name":"spur_create_letter_template","title":"Create letter template","description":"Create a letter template and show a preview. Pick the letter type first (top_first_page or insert_blank_page); it decides where the address prints and how much of the page the design can use. Three ways to start:\n- From blank: start_from_blank=true gives an empty one-page letter laid out for the letter type.\n- Redesign an existing template: base_template_id (yours, a Spur default or a gallery id) copies its HTML and letter type; override html or letter_type as needed.\n- From scratch: pass html.\n\nHTML guidance (see spur://letter-design-guide): one US Letter page (8.5\"×11\"), full HTML document with inline <style>, \"@page { size: 8.5in 11in; margin: 0; }\", no scripts. One root <div class=\"letter\"> 8.5in wide; px/in units only (no vh/vw); no box-shadow, transforms or manual page breaks. top_first_page: leave the first 3.15in blank (an empty <div class=\"address-zone\" style=\"height:3.15in\">), body ≈ 3–4 short paragraphs. insert_blank_page: no address zone; letterhead up to 1.8in, body up to ~400 words. Images as absolute https URLs. The user's own images (logo, headshot, property photos): spur_list_images gives their URLs; if they want to use an image they have, call spur_upload_image (or spur_save_image for a link) and use the saved URL. Use merge fields such as {{owner_first_name}}, {{property_address}}, {{your_name}}, {{your_phone}}, {{company_name}} (see spur://merge-variables).","inputs":[{"name":"base_template_id","type":"string","required":false,"description":"Copy this template's HTML and letter type as the starting point"},{"name":"category","type":"string","required":false,"description":"Category label, e.g. \"Seller Outreach\""},{"name":"description","type":"string","required":false,"description":"Internal note about the template; never printed"},{"name":"html","type":"string","required":false,"description":"Full letter HTML"},{"name":"letter_type","type":"string","required":false,"description":"Defaults to the base template's type, else top_first_page"},{"name":"name","type":"string","required":true,"description":"Template name shown in Spur"},{"name":"start_from_blank","type":"boolean","required":false,"description":"Start from an empty page laid out for letter_type"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":479},{"name":"spur_update_letter_template","title":"Edit letter template","description":"Edit one of the user's letter templates and show the updated preview. For text tweaks pass replacements (exact find → replace); to redesign, pass full html. letter_type switches between top_first_page and insert_blank_page. Spur default and gallery templates can't be edited: copy them first with spur_create_letter_template base_template_id.\n\nHTML guidance (see spur://letter-design-guide): one US Letter page (8.5\"×11\"), full HTML document with inline <style>, \"@page { size: 8.5in 11in; margin: 0; }\", no scripts. One root <div class=\"letter\"> 8.5in wide; px/in units only (no vh/vw); no box-shadow, transforms or manual page breaks. top_first_page: leave the first 3.15in blank (an empty <div class=\"address-zone\" style=\"height:3.15in\">), body ≈ 3–4 short paragraphs. insert_blank_page: no address zone; letterhead up to 1.8in, body up to ~400 words. Images as absolute https URLs. The user's own images (logo, headshot, property photos): spur_list_images gives their URLs; if they want to use an image they have, call spur_upload_image (or spur_save_image for a link) and use the saved URL. Use merge fields such as {{owner_first_name}}, {{property_address}}, {{your_name}}, {{your_phone}}, {{company_name}} (see spur://merge-variables).","inputs":[{"name":"category","type":"string","required":false,"description":"Category label, e.g. \"Seller Outreach\""},{"name":"description","type":"string","required":false,"description":"Internal note about the template; never printed"},{"name":"html","type":"string","required":false,"description":"Full replacement HTML"},{"name":"letter_type","type":"string","required":false,"description":"\"top_first_page\": the address prints in the top 3.15in of page 1 (shows through a #10 window envelope), so keep that band empty. \"insert_blank_page\": Spur adds a separate address page in front, so th…"},{"name":"name","type":"string","required":false,"description":"New template name"},{"name":"replacements","type":"array","required":false,"description":"Small edits applied in order instead of resending the full content"},{"name":"template_id","type":"string","required":true,"description":"Template id (UUID from a Spur list/search tool)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":479},{"name":"spur_delete_letter_template","title":"Delete letter template","description":"Permanently delete one of the user's letter templates. Spur defaults can't be deleted. Confirm with the user first.","inputs":[{"name":"confirm","type":"boolean","required":true,"description":"Set to true ONLY after the user explicitly approved this exact action in the conversation. Never assume approval."},{"name":"template_id","type":"string","required":true,"description":"Template id (UUID from a Spur list/search tool)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":76},{"name":"spur_preview_letter_campaign","title":"Preview letter mailing","description":"Required step before mailing letters. Renders the letter with the first recipient's real data in its letter type's layout, checks which owners have mailing addresses, estimates the cost from the user's plan (Insert Blank Page letters include the extra address page), and returns a confirmation_token.\n\nShow the preview, then ask the user to approve. Only after they approve, call spur_send_letters with the token (the widget's Send button does the same). The token expires in 30 minutes and only covers this exact template, letter type, print options, recipient set and return address.","inputs":[{"name":"color","type":"boolean","required":false,"description":"Print in color (false for black and white)"},{"name":"double_sided","type":"boolean","required":false,"description":"Print on both sides of each sheet"},{"name":"owner_ids","type":"array","required":true,"description":"Owners to mail (spur_find_owners)"},{"name":"return_address_id","type":"string","required":false,"description":"Defaults to the user's default return address"},{"name":"template_id","type":"string","required":true,"description":"Letter template id (UUID from a Spur list/search tool)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":207},{"name":"spur_send_letters","title":"Send letters","description":"Mail the letters approved in spur_preview_letter_campaign. Irreversible and charges the user's account. Requires the preview's confirmation_token and the user's explicit approval (confirm=true).","inputs":[{"name":"confirm","type":"boolean","required":true,"description":"Set to true ONLY after the user explicitly approved this exact action in the conversation. Never assume approval."},{"name":"confirmation_token","type":"string","required":true,"description":"confirmation_token from spur_preview_letter_campaign"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":84},{"name":"spur_get_billing_summary","title":"Billing summary","description":"Plan, wallet balance, and this period's usage and remaining allowance per feature (searches, enrichment, SMS, postcards, letters, AI). Check this before large sends.","inputs":[],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":46},{"name":"spur_list_campaigns","title":"List campaigns","description":"The user's campaigns (bulk email, SMS, direct mail, outreach) with status and recipient progress.","inputs":[{"name":"limit","type":"integer","required":false,"description":"Maximum campaigns to return"},{"name":"offset","type":"integer","required":false,"description":"Campaigns to skip, for paging"},{"name":"status","type":"string","required":false,"description":"Only campaigns with this status"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":67},{"name":"spur_get_campaign_stats","title":"Campaign stats","description":"Delivery and run statistics for one campaign.","inputs":[{"name":"campaign_id","type":"string","required":true,"description":"Campaign id (UUID from a Spur list/search tool)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":36},{"name":"spur_get_direct_mail_analytics","title":"Direct mail analytics","description":"Letters and postcards sent, delivered, in transit and returned, plus USPS tracking totals and spend.","inputs":[],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":31},{"name":"spur_show_billing_options","title":"Top up or upgrade","description":"Shows the user's wallet balance, one-click top-up amounts, auto-recharge status and every Spur plan with prices, in a widget with Pay/Upgrade buttons. Use whenever the user wants to add funds, buy credits, upgrade, change or compare plans, or a send failed for billing reasons.","inputs":[],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":70},{"name":"spur_top_up_wallet","title":"Top up wallet","description":"Creates a Stripe checkout link to add funds to the user's Spur wallet (pays for postcards, letters, SMS, enrichment and other usage beyond the plan). No money moves until the user pays on Stripe, so no confirmation is needed. If the user didn't name an amount, suggest $25, $50 or $100, or enough to cover the send they're planning.","inputs":[{"name":"amount_usd","type":"number","required":true,"description":"Amount to add in US dollars (minimum $5)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":103},{"name":"spur_change_plan","title":"Upgrade or change plan","description":"Moves the user to another Spur plan (ids from spur_show_billing_options; usually agent, team or brokerage).\n- No paid subscription yet: returns a Stripe checkout link. No confirmation needed.\n- Already subscribed: switches the plan immediately and charges or credits the prorated difference to the saved card. Call once without confirm to get the exact change, ask the user to approve it, then call again with confirm=true.\n- Moving to the free Solo plan returns a billing-portal link where the user cancels the paid subscription.","inputs":[{"name":"billing_interval","type":"string","required":false,"description":"annual is cheaper per month"},{"name":"confirm","type":"boolean","required":false,"description":"Only for users who already have a paid subscription: true ONLY after they approved the switch and the prorated charge in the conversation."},{"name":"plan_id","type":"string","required":true,"description":"Target plan id, e.g. agent, team, brokerage"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":180},{"name":"spur_check_payment","title":"Check payment","description":"After the user pays a Stripe checkout link from spur_top_up_wallet or spur_change_plan, confirms the payment went through and returns the new wallet balance and plan. Safe to call repeatedly.","inputs":[{"name":"session_id","type":"string","required":true,"description":"session_id returned with the checkout link (starts with cs_)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":65},{"name":"spur_manage_billing","title":"Billing portal","description":"Link to the user's Stripe billing portal: update the card on file, download invoices, change or cancel the subscription.","inputs":[],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":33},{"name":"spur_set_auto_recharge","title":"Auto-recharge","description":"Turns wallet auto-recharge on or off. When on, Spur charges the saved card amount_usd whenever the balance drops below threshold_usd, so sends never fail for lack of funds. Needs a saved card (the first spur_top_up_wallet payment saves one). Turning it on needs the user's approval of the amount and threshold (confirm=true); turning it off doesn't.","inputs":[{"name":"amount_usd","type":"number","required":false,"description":"Amount added per recharge (required when enabling, minimum $5)"},{"name":"confirm","type":"boolean","required":false,"description":"Required to enable: true ONLY after the user approved automatic charges of this amount at this threshold."},{"name":"enabled","type":"boolean","required":true,"description":"true = turn auto-recharge on, false = off"},{"name":"threshold_usd","type":"number","required":false,"description":"Recharge when the balance drops below this (required when enabling; lower than amount_usd)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":175},{"name":"spur_list_search_automations","title":"List search automations","description":"Scheduled property searches with their schedule, filters, and next/last run times.","inputs":[],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":28},{"name":"spur_create_search_automation","title":"Create search automation","description":"Schedule a recurring property search. Every run uses 1 search credit, or 2 when it makes more than 3 ZHomes calls (several event types, tax/sale-history events, furnished-only or description-based filters). New matches can be saved to a list and enriched automatically (auto_enrich spends skip-trace credits on every run, so confirm with the user).\n\nExamples:\n  - \"every Monday 8am Central, Dallas rentals under $2k\" -> frequency=\"weekly\", day_of_week=1, run_time=\"08:00\", timezone=\"America/Chicago\", location=\"Dallas, TX\", max_amount=2000\n  - \"last Friday of each month\" -> frequency=\"monthly\", day_of_week=5, week_of_month=-1\n  - \"every morning, new pre-foreclosures and price drops in Houston, save to my Distress list\" -> frequency=\"daily\", run_time=\"07:00\", location=\"Houston, TX\", events=[\"preforeclosure\",\"price_drop\"], list_id=…\n\nAccepts the same advanced and event filters as spur_search_properties (min_amount / max_amount instead of min_price / max_price). Run the search once with spur_search_properties first so the user can check the results before scheduling.\n\nIn automations each event fires once, on the run that first sees it, so the same property isn't saved twice; change events start reporting from the second run. \"delisted\" is only available here.\n\nEvents (pass in `events`; a listing matches when ANY selected event fired, and is tagged with each):\n- Listing lifecycle: new_listing, coming_soon, coming_soon_active, price_drop, price_increase, pending, sold, back_on_market, delisted (automations only), open_house, rental_listed, rent_change, move_in_moved_up\n- Distress & motivation: preforeclosure, foreclosure, auction, bank_owned, distress_escalated, fsbo, agent_to_fsbo, stale_listing, new_construction\n- Valuation & tax: tax_assessment_posted, assessed_value_jump, tax_paid_dropped (weak), sale_recorded, zestimate_moved, equity_gap, price_below_assessed\nDirect events (price_drop, pending, sold, preforeclosure, fsbo, new_listing, …) work on the first search. Change…","inputs":[{"name":"ai_query","type":"string","required":false,"description":"Qualitative intent; each run re-ranks listings by description against it"},{"name":"auto_enrich","type":"boolean","required":false,"description":"Skip-trace new matches on every run (spends enrichment credits)"},{"name":"auto_save","type":"boolean","required":false,"description":"Save new matches to the user's properties"},{"name":"center_lat","type":"number","required":false,"description":"Radius search centre (with center_lng and radius_miles)"},{"name":"center_lng","type":"number","required":false,"description":"Radius search centre longitude (with center_lat and radius_miles)"},{"name":"day_of_week","type":"integer","required":false,"description":"0=Sunday … 6=Saturday; for weekly, biweekly, monthly"},{"name":"days_posted","type":"integer","required":false,"description":"Only listings posted within this many days"},{"name":"enabled","type":"boolean","required":false,"description":"false = save it paused"},{"name":"event_min_change_pct","type":"number","required":false,"description":"Minimum % move for price / rent / Zestimate / assessment events"},{"name":"event_min_equity_gap_pct","type":"number","required":false,"description":"Minimum % Zestimate above last sale price for equity_gap (default 30)"},{"name":"event_stale_days","type":"integer","required":false,"description":"Days on market that counts as stale (default 90)"},{"name":"event_within_days","type":"integer","required":false,"description":"Lookback window for events, in days (default 7)"},{"name":"events","type":"array","required":false,"description":"Only listings where any of these events happened; see the tool description"},{"name":"frequency","type":"string","required":true,"description":"How often the search runs"},{"name":"has_garage","type":"boolean","required":false,"description":"true = only listings with a garage"},{"name":"has_pool","type":"boolean","required":false,"description":"true = only listings with a pool"},{"name":"has_view","type":"boolean","required":false,"description":"true = only listings with any view (use views on live search for a specific kind)"},{"name":"home_type","type":"string","required":false,"description":"Zillow home type code"},{"name":"is_price_reduced","type":"boolean","required":false,"description":"true = only listings whose asking price has been cut"},{"name":"is_waterfront","type":"boolean","required":false,"description":"true = only waterfront listings"},{"name":"keywords","type":"string","required":false,"description":"Comma-separated listing keywords, e.g. \"pool, granite\""},{"name":"list_id","type":"string","required":false,"description":"Save new matches into this property list"},{"name":"listing_status","type":"string","required":false,"description":"Which listings to search"},{"name":"listing_type","type":"string","required":false,"description":"Seller / listing kind; for catching NEW entries into a distress stage prefer events"},{"name":"location","type":"string","required":true,"description":"City/state or ZIP, e.g. \"Dallas, TX\""},{"name":"max_age","type":"integer","required":false,"description":"At most this many years since built"},{"name":"max_amount","type":"number","required":false,"description":"Maximum price in USD (rent per month for forRent)"},{"name":"max_baths","type":"number","required":false,"description":"Maximum bathrooms"},{"name":"max_beds","type":"integer","required":false,"description":"Maximum bedrooms"},{"name":"max_days_on_market","type":"integer","required":false,"description":"Listed at most this many days"},{"name":"max_lot_size","type":"integer","required":false,"description":"Square feet"},{"name":"max_ownership_years","type":"number","required":false,"description":"Owner has held the property at most this long"},{"name":"max_sqft","type":"integer","required":false,"description":"Maximum living area in square feet"},{"name":"max_year_built","type":"integer","required":false,"description":"Built in or before this year"},{"name":"max_zip","type":"integer","required":false,"description":"Highest ZIP code in a numeric ZIP range (with min_zip)"},{"name":"min_age","type":"integer","required":false,"description":"Years since built"},{"name":"min_amount","type":"number","required":false,"description":"Minimum price in USD (rent per month for forRent)"},{"name":"min_baths","type":"number","required":false,"description":"Minimum bathrooms (half baths count as 0.5)"},{"name":"min_beds","type":"integer","required":false,"description":"Minimum bedrooms"},{"name":"min_days_on_market","type":"integer","required":false,"description":"Listed at least this many days (higher suggests a motivated seller)"},{"name":"min_equity","type":"number","required":false,"description":"Estimated equity in USD (Zestimate or price minus assessed value; rough)"},{"name":"min_lot_size","type":"integer","required":false,"description":"Square feet (1 acre = 43,560)"},{"name":"min_ownership_years","type":"number","required":false,"description":"Owner has held the property at least this long"},{"name":"min_sqft","type":"integer","required":false,"description":"Minimum living area in square feet"},{"name":"min_year_built","type":"integer","required":false,"description":"Built in or after this year"},{"name":"min_zip","type":"integer","required":false,"description":"Lowest ZIP code in a numeric ZIP range (with max_zip); prefer zip_list for specific ZIPs"},{"name":"name","type":"string","required":true,"description":"Automation name shown in Spur"},{"name":"neighborhoods","type":"array","required":false,"description":"Neighborhood / area names matched against address and description"},{"name":"only_absentee","type":"boolean","required":false,"description":"Absentee-owned (owner mails elsewhere)"},{"name":"only_pre_foreclosure","type":"boolean","required":false,"description":"Distressed / pre-foreclosure (heuristic, includes description keywords)"},{"name":"only_vacant","type":"boolean","required":false,"description":"Likely vacant (heuristic: description keywords + long days on market)"},{"name":"ownership_type","type":"string","required":false,"description":"business = LLC / corp / trust owners (heuristic)"},{"name":"radius_miles","type":"number","required":false,"description":"Keep only listings within this many miles of center_lat / center_lng"},{"name":"run_time","type":"string","required":true,"description":"24h local time \"HH:MM\""},{"name":"timezone","type":"string","required":false,"description":"IANA time zone, e.g. \"America/Chicago\""},{"name":"week_of_month","type":null,"required":false,"description":"Monthly only; -1 = last"},{"name":"zip_list","type":"array","required":false,"description":"Keep only these ZIPs; set location to the city that contains them"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":1608},{"name":"spur_run_search_automation","title":"Run search automation now","description":"Run a search automation immediately instead of waiting for its schedule. Uses 1 search credit (2 if the run makes more than 3 ZHomes calls), and enrichment credits if auto_enrich is on.","inputs":[{"name":"automation_id","type":"string","required":true,"description":"Automation id (UUID from a Spur list/search tool)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":70},{"name":"spur_get_automation_logs","title":"Automation run history","description":"Recent runs of a search automation: when, how many found/saved/enriched, and errors.","inputs":[{"name":"automation_id","type":"string","required":true,"description":"Automation id (UUID from a Spur list/search tool)"},{"name":"limit","type":"integer","required":false,"description":"Maximum runs to return"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":60},{"name":"spur_get_texting_setup","title":"Texting setup status","description":"The user's texting setup: phone numbers bought in Spur (and which can send), carrier (A2P 10DLC) registration status, carrier notes, what's still missing, and the next step. Call this before helping with numbers or registration.","inputs":[{"name":"refresh","type":"boolean","required":false,"description":"true = ask the carriers for the latest status first (use when the user is waiting on approval)"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":87},{"name":"spur_search_phone_numbers","title":"Find phone numbers","description":"Search local US numbers the user can buy, by area code, state, city or digits. Show the options and let the user pick before spur_buy_phone_number. Prefer an area code in the user's market.","inputs":[{"name":"area_code","type":"string","required":false,"description":"e.g. 512"},{"name":"city","type":"string","required":false,"description":null},{"name":"contains","type":"string","required":false,"description":"Digits the number should contain, e.g. 777"},{"name":"limit","type":"integer","required":false,"description":null},{"name":"state","type":"string","required":false,"description":"e.g. TX"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":109},{"name":"spur_buy_phone_number","title":"Buy phone number","description":"Buy a local number for the user's account (it's theirs immediately and replies land in their SMS inbox). It can't send texts until their carrier registration is approved. Plans include some numbers; beyond that each costs $5/mo from the Spur wallet, first month charged now (spur_get_texting_setup shows the user's price). Tell the user the price and get explicit approval for this exact number first.","inputs":[{"name":"confirm","type":"boolean","required":true,"description":"Set to true ONLY after the user explicitly approved this exact action in the conversation. Never assume approval."},{"name":"locality","type":"string","required":false,"description":"City from the search result, for display"},{"name":"phone_number","type":"string","required":true,"description":"phone_number from spur_search_phone_numbers"},{"name":"region","type":"string","required":false,"description":"State from the search result, for display"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":159},{"name":"spur_set_default_phone_number","title":"Set default number","description":"Make one of the user's bought numbers the default sender for their texts.","inputs":[{"name":"number_id","type":"string","required":true,"description":"number_id from spur_get_texting_setup"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":41},{"name":"spur_release_phone_number","title":"Release phone number","description":"Permanently give up one of the user's bought numbers. They may never get it back, and contacts texting it will no longer reach them. Confirm with the user first.","inputs":[{"name":"confirm","type":"boolean","required":true,"description":"Set to true ONLY after the user explicitly approved this exact action in the conversation. Never assume approval."},{"name":"number_id","type":"string","required":true,"description":"number_id from spur_get_texting_setup"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":86},{"name":"spur_save_texting_registration","title":"Save texting registration","description":"Create or update the user's carrier (A2P 10DLC) registration draft. Pass only the fields to change; nested objects merge. Pick registration_type from two questions: has an EIN? (no → sole_proprietor) and more than ~2,000 texts/day? (yes → standard, else low_volume_standard). Business details must match IRS records exactly. Only record consent methods the user actually uses: carriers reject texting purchased or skip-traced lists without consent. write_campaign_text=true then reads their website and drafts the campaign description, opt-in flow and sample messages (needs message_types and consent_channels). Show that text to the user to review.","inputs":[{"name":"address","type":"object","required":false,"description":null},{"name":"business_info","type":"object","required":false,"description":null},{"name":"contact","type":"object","required":false,"description":null},{"name":"messaging","type":"object","required":false,"description":null},{"name":"registration_type","type":"string","required":false,"description":null},{"name":"write_campaign_text","type":"boolean","required":false,"description":"true = also draft the campaign text from their website and answers"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":203},{"name":"spur_submit_texting_registration","title":"Submit texting registration","description":"Send the user's registration to the carriers (also resubmits a rejected one after fixes). Charges the Spur wallet: $29 one-time for low volume / sole proprietor or $79 for standard (includes the first month, then $3 or $15/mo), or $15 to resubmit after a carrier rejection; spur_get_texting_setup shows the exact amount due now. Tell the user the amount. Walk the user through the full details first, and only call after they explicitly approve and personally confirm both statements: they only text people who agreed to receive their messages and honor STOP, and they won't send prohibited content (cannabis, firearms, high-risk loans or debt collection, gambling, sexual content, hate speech).","inputs":[{"name":"confirm","type":"boolean","required":true,"description":"Set to true ONLY after the user explicitly approved this exact action in the conversation. Never assume approval."},{"name":"user_confirms_consent","type":"boolean","required":true,"description":"The user confirmed they only text people who agreed to receive their messages and honor STOP requests"},{"name":"user_confirms_no_prohibited_content","type":"boolean","required":true,"description":"The user confirmed they won't send prohibited content"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":231},{"name":"spur_pay_texting_fees","title":"Pay overdue texting fees","description":"Retry overdue monthly number and registration fees from the Spur wallet right away (normally retried daily). Use after the user adds funds. Restores paused numbers once paid. Charges the wallet: confirm the amount with the user first.","inputs":[{"name":"confirm","type":"boolean","required":true,"description":"Set to true ONLY after the user explicitly approved this exact action in the conversation. Never assume approval."}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":83},{"name":"spur_resend_texting_verification","title":"Resend verification text","description":"Sole proprietors only: re-send the carrier's verification text (the user replies YES) when the previous one expired after 24 hours.","inputs":[],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":41},{"name":"search","title":"Search Spur","description":"Search the user's saved Spur properties, owners and postcard templates by keyword (address, city, owner name, phone, email, template name). Returns ids for the fetch tool. For live listings use spur_search_properties.","inputs":[{"name":"query","type":"string","required":true,"description":"Keywords: address, city, owner name, phone, email or template name"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":72},{"name":"fetch","title":"Fetch Spur record","description":"Fetch the full text of a record returned by search (ids look like property:…, owner:… or template:…).","inputs":[{"name":"id","type":"string","required":true,"description":"Record id exactly as returned by search, e.g. property:<uuid>"}],"outputs":[],"hasOutputSchema":false,"hasExamples":false,"tokenEstimate":53}],"toolsTruncatedAtCapture":false,"diagnostics":{"capturedAt":"2026-11-06T03:17:31.000Z","track":"remote","endpoint":"https://mcp.usespur.com/mcp","declaredUrl":null,"truncated":false,"tls":{"outcome":"valid","label":"Valid","version":"TLS 1.3","cipherSuite":"TLS_AES_128_GCM_SHA256","chainError":null,"certificates":[{"subject":"CN=mcp.usespur.com","issuer":"CN=YR1,O=Let's Encrypt,C=US","notBefore":"2026-10-23T03:05:45Z","notAfter":"2027-01-21T03:05:44Z","keyType":"RSA","keyBits":2048,"isCa":false},{"subject":"CN=YR1,O=Let's Encrypt,C=US","issuer":"CN=Root YR,O=ISRG,C=US","notBefore":"2025-09-03T00:00:00Z","notAfter":"2028-09-02T23:59:59Z","keyType":"RSA","keyBits":2048,"isCa":true},{"subject":"CN=Root YR,O=ISRG,C=US","issuer":"CN=ISRG Root X1,O=Internet Security Research Group,C=US","notBefore":"2026-05-13T00:00:00Z","notAfter":"2032-09-02T23:59:59Z","keyType":"RSA","keyBits":4096,"isCa":true}]},"dnssec":{"result":"insecure","label":"Not signed","name":"mcp.usespur.com.","steps":[{"zone":".","outcome":"verified","label":"Verified"},{"zone":"com.","outcome":"verified","label":"Verified"},{"zone":"usespur.com.","outcome":"insecure_proven","label":"Unsigned (proven)"}]},"auth":{"outcome":"enforced_validated","label":"Enforced and verified","gate":"invoke","gateLabel":"On tool calls","status":200,"authorizationServers":["https://mcp.usespur.com"]},"httpsRedirect":{"outcome":"https_enforced","label":"HTTPS enforced","status":308},"transports":[{"transport":"streamable-http","outcome":"verified","label":"Verified","status":200,"followed":false}],"provenance":null,"installScripts":[],"installScriptsTotal":null,"vulnerabilities":[],"vulnerabilitiesTotal":null,"dependencies":null},"mismatches":[],"changelog":{"trackingSince":"2026-10-24","recent":[{"id":"chg_01a1aedc-8b0a-7905-b971-b22fb4f5c748","kind":"score.rubric_changed","summary":"We updated how we score, so this day's move reflects our rubric, not a change to the server","materiality":"functional","direction":"neutral","scoreDelta":null,"occurredOn":"2026-11-06","url":"https://verifymcp.io/servers/com-usespur-spur/mcp#chg-chg_01a1aedc-8b0a-7905-b971-b22fb4f5c748"},{"id":"chg_01a1a491-e704-7551-afc0-277a3fd6e61f","kind":"score.total_changed","summary":"Trust score (84 → 85)","materiality":"functional","direction":"improvement","scoreDelta":1,"occurredOn":"2026-11-04","url":"https://verifymcp.io/servers/com-usespur-spur/mcp#chg-chg_01a1a491-e704-7551-afc0-277a3fd6e61f"},{"id":"chg_01a19520-5574-7a43-88f6-2d4056541c16","kind":"score.total_changed","summary":"Trust score (83 → 84)","materiality":"functional","direction":"improvement","scoreDelta":1,"occurredOn":"2026-11-01","url":"https://verifymcp.io/servers/com-usespur-spur/mcp#chg-chg_01a19520-5574-7a43-88f6-2d4056541c16"},{"id":"chg_01a18ad1-30af-7a89-b57c-5ed7892d2da2","kind":"score.total_changed","summary":"Trust score (82 → 83)","materiality":"functional","direction":"improvement","scoreDelta":1,"occurredOn":"2026-10-30","url":"https://verifymcp.io/servers/com-usespur-spur/mcp#chg-chg_01a18ad1-30af-7a89-b57c-5ed7892d2da2"},{"id":"chg_01a18086-bd9e-7cf5-8c64-6897765df2e4","kind":"score.total_changed","summary":"Trust score (81 → 82)","materiality":"functional","direction":"improvement","scoreDelta":1,"occurredOn":"2026-10-28","url":"https://verifymcp.io/servers/com-usespur-spur/mcp#chg-chg_01a18086-bd9e-7cf5-8c64-6897765df2e4"},{"id":"chg_01a17b5f-a72b-79c3-aab2-818d8395d584","kind":"tool.description_changed","summary":"Tool “spur_send_sms” rewrote its description, which is the text the model reads","materiality":"security","direction":"neutral","scoreDelta":null,"occurredOn":"2026-10-27","url":"https://verifymcp.io/servers/com-usespur-spur/mcp#chg-chg_01a17b5f-a72b-79c3-aab2-818d8395d584"},{"id":"chg_01a17b5f-a72c-7dcf-86ac-127f1de8f2a5","kind":"tool.added","summary":"New tool “spur_buy_phone_number”, which the server declares destructive","materiality":"security","direction":"neutral","scoreDelta":null,"occurredOn":"2026-10-27","url":"https://verifymcp.io/servers/com-usespur-spur/mcp#chg-chg_01a17b5f-a72c-7dcf-86ac-127f1de8f2a5"},{"id":"chg_01a17b5f-a72d-75bd-8df9-52535d05d1fb","kind":"tool.added","summary":"New tool “spur_get_texting_setup”","materiality":"functional","direction":"neutral","scoreDelta":null,"occurredOn":"2026-10-27","url":"https://verifymcp.io/servers/com-usespur-spur/mcp#chg-chg_01a17b5f-a72d-75bd-8df9-52535d05d1fb"},{"id":"chg_01a17b5f-a72d-7c13-916c-3a7f14b42abb","kind":"tool.added","summary":"New tool “spur_pay_texting_fees”, which the server declares destructive","materiality":"security","direction":"neutral","scoreDelta":null,"occurredOn":"2026-10-27","url":"https://verifymcp.io/servers/com-usespur-spur/mcp#chg-chg_01a17b5f-a72d-7c13-916c-3a7f14b42abb"},{"id":"chg_01a17b5f-a72e-7291-939b-38cf1e2aa10a","kind":"tool.added","summary":"New tool “spur_release_phone_number”, which the server declares destructive","materiality":"security","direction":"neutral","scoreDelta":null,"occurredOn":"2026-10-27","url":"https://verifymcp.io/servers/com-usespur-spur/mcp#chg-chg_01a17b5f-a72e-7291-939b-38cf1e2aa10a"},{"id":"chg_01a17b5f-a72e-7940-bb14-bbb0cea65590","kind":"tool.added","summary":"New tool “spur_resend_texting_verification”","materiality":"functional","direction":"neutral","scoreDelta":null,"occurredOn":"2026-10-27","url":"https://verifymcp.io/servers/com-usespur-spur/mcp#chg-chg_01a17b5f-a72e-7940-bb14-bbb0cea65590"},{"id":"chg_01a17b5f-a72e-7ef0-abfd-5ddbcfcba55c","kind":"tool.added","summary":"New tool “spur_save_texting_registration”","materiality":"functional","direction":"neutral","scoreDelta":null,"occurredOn":"2026-10-27","url":"https://verifymcp.io/servers/com-usespur-spur/mcp#chg-chg_01a17b5f-a72e-7ef0-abfd-5ddbcfcba55c"},{"id":"chg_01a17b5f-a72f-745b-8c39-c4863f9bed32","kind":"tool.added","summary":"New tool “spur_search_phone_numbers”","materiality":"functional","direction":"neutral","scoreDelta":null,"occurredOn":"2026-10-27","url":"https://verifymcp.io/servers/com-usespur-spur/mcp#chg-chg_01a17b5f-a72f-745b-8c39-c4863f9bed32"},{"id":"chg_01a17b5f-a72f-79af-8abc-8bb78a5c3aca","kind":"tool.added","summary":"New tool “spur_set_default_phone_number”","materiality":"functional","direction":"neutral","scoreDelta":null,"occurredOn":"2026-10-27","url":"https://verifymcp.io/servers/com-usespur-spur/mcp#chg-chg_01a17b5f-a72f-79af-8abc-8bb78a5c3aca"},{"id":"chg_01a17b5f-a72f-7e81-a67a-d3932c7e653f","kind":"tool.added","summary":"New tool “spur_submit_texting_registration”, which the server declares destructive","materiality":"security","direction":"neutral","scoreDelta":null,"occurredOn":"2026-10-27","url":"https://verifymcp.io/servers/com-usespur-spur/mcp#chg-chg_01a17b5f-a72f-7e81-a67a-d3932c7e653f"},{"id":"chg_01a17b5f-a730-747c-802d-f790be8a0848","kind":"server.instructions_changed","summary":"The server rewrote its instructions, which are the text every model session reads","materiality":"security","direction":"neutral","scoreDelta":null,"occurredOn":"2026-10-27","url":"https://verifymcp.io/servers/com-usespur-spur/mcp#chg-chg_01a17b5f-a730-747c-802d-f790be8a0848"},{"id":"chg_01a17639-655b-785e-816b-405438be4090","kind":"score.total_changed","summary":"Trust score (80 → 81)","materiality":"functional","direction":"improvement","scoreDelta":1,"occurredOn":"2026-10-26","url":"https://verifymcp.io/servers/com-usespur-spur/mcp#chg-chg_01a17639-655b-785e-816b-405438be4090"},{"id":"chg_01a17112-aaf2-7755-8454-1f7d2523bc54","kind":"check.reverified","summary":"Stability (unverified → 0.03)","materiality":"functional","direction":"improvement","scoreDelta":null,"occurredOn":"2026-10-25","url":"https://verifymcp.io/servers/com-usespur-spur/mcp#chg-chg_01a17112-aaf2-7755-8454-1f7d2523bc54"},{"id":"chg_01a16f80-4cac-7069-aa91-35267bf0aff7","kind":"score.first_scored","summary":"First indexed and scored","materiality":"functional","direction":"neutral","scoreDelta":null,"occurredOn":"2026-10-24","url":"https://verifymcp.io/servers/com-usespur-spur/mcp#chg-chg_01a16f80-4cac-7069-aa91-35267bf0aff7"}],"truncated":false,"feedUrl":"https://verifymcp.io/servers/com-usespur-spur/mcp/changelog.json"},"links":{"html":"https://verifymcp.io/servers/com-usespur-spur/mcp","markdown":"https://verifymcp.io/servers/com-usespur-spur/mcp.md","changelogJson":"https://verifymcp.io/servers/com-usespur-spur/mcp/changelog.json","changelogRss":"https://verifymcp.io/servers/com-usespur-spur/mcp.xml","external":[{"label":"Remote endpoint","href":"https://mcp.usespur.com/mcp"},{"label":"Website","href":"https://usespur.com/"}]}}