{"server":{"name":"euler-mcp","title":"EULER","website":"https://eulerapp.com","privacy_policy_url":"https://eulerapp.com/privacy","terms_of_service_url":"https://eulerapp.com/terms","trust_center_url":"https://trust.eulerapp.com/","support_email":"help@eulerapp.com"},"authentication":{"type":"oauth2.1","flow":"authorization_code_with_pkce","dynamic_client_registration":true,"scopes":["customer","partner"]},"categories":[{"id":"account_management","name":"Account Management","description":"Discover which accounts (customer + partner) the user has access to. Always call list_accounts FIRST before any partner-scope tool — returns the partner_id values you must pass downstream. Avoids guessing which company/partner the AI should act on when the user has multiple roles."},{"id":"partner_directory","name":"Partner Directory","description":"Profiles and listings of the EULER Partner Directory: tiers, status, custom profile data, iframe analytics, partner profile attributes."},{"id":"partners","name":"Partners — general management","description":"Listing, pending approval, and aggregate summary of partners. Use when the user wants a partner list, partners awaiting approval, or a high-level partner snapshot."},{"id":"partner_relationships","name":"Partner Relationships","description":"Artifacts linked to a partner: agreements, affiliate links, deals, referrals/deal registrations. Tools take a `partner_id` and return that partner's items."},{"id":"partner_contacts","name":"Partner Contacts & Portal Invites","description":"Per-partner contact people (the humans inside a partner organisation) + portal-access invitations. List a partner's existing contacts (`list_partner_contacts`); add a new contact by email (`create_partner_contact`, idempotent); invite one or more existing contacts by id to the partner's portal in a single call (`invite_partners_to_portal`, idempotent per contact). All tools take `partner_id`; the worker injects the customer's `company_id` automatically. Customer-side only; tenant-guarded."},{"id":"flows","name":"Flows — onboarding & training lifecycle","description":"Flow lifecycle (create/edit/save), step management (create/edit/deactivate, attaches), and assignment (assign/unassign flows to partners or users). Use when the user manages onboarding/training programs."},{"id":"invoices","name":"Invoices","description":"Invoices at partner level, company level, and summaries. Use for revenue, billing, payment status questions."},{"id":"commissions","name":"Commissions & Payouts","description":"Commission calculations and payout/expense totals. Use for 'how much paid in commissions', 'commission expense', 'commission payout for partner X', 'total commission spend', 'commission owed to <partner>' — at partner level or company-wide aggregate."},{"id":"incentives","name":"Incentives","description":"Incentive program summaries: counts of participants, program metadata."},{"id":"partner_incentives","name":"Partner Incentives","description":"Incentive plan details for the calling partner. Inc 1.0: tier package with commission tiers, payout frequency, and net days. Inc 2.0: current tier, full 3-level commission structure (product group → revenue range → time phase), non-monetary benefits, active boost campaigns, and tier-progression rules with live performance metrics."},{"id":"performance","name":"Performance & Rankings","description":"Partner performance metrics and cross-partner rankings. Use for 'top N partners', 'best performing partners', 'leaderboard', 'partner ranking', 'highest/most deals', 'who's performing best' — plus per-partner and company-wide performance summaries over date ranges."},{"id":"content_search","name":"Content Search","description":"Find EULER knowledge three ways: `euler_help` answers product help / how-to questions from EULER's public help center / support articles ('how does EULER work', 'how do I add a user'); `content_search` semantically searches a partner's privately-shared content (needs partner_id); `content_list` lists/browses the actual files a partner can access (filter by name, with download URLs)."},{"id":"feedback","name":"Feedback","description":"Send feedback to the EULER engineering team about MCP friction — broken tools, ambiguous descriptions, missing features. Use proactively when something didn't work or could be better; the team gets a Slack notification."},{"id":"communications_hub","name":"Communications Hub","description":"Partner email communications. Read with `communications`: list audiences (partner mailing lists), email templates, and sent broadcasts; preview a broadcast's recipients or a template with a sample contact's data. Create + send with `communications_manage`: create an audience or template, add partners to an audience, send a broadcast. Customer-side only; everything is scoped to the user's company automatically."},{"id":"partner_si_projects","name":"SI Partner — Service Projects","description":"SI (System Integrator) partner service projects. Read with `si_service_projects`: list the partner's projects, read one project, read a project's scope form (per-service question fields + checkbox completion sub-items), and browse the catalog of services the partner can add. Create + modify with `si_manage_service_project`: create/update/delete a project, submit scope-form answers, and set checkbox completion percentages. Partner-scoped; every record is tenant-isolated by Bubble privacy rules. SI is an OPT-IN feature — not every partner has it provisioned. When these reads come back empty (especially `list_services`), the partner may simply not have SI enabled, rather than there being no matching data."},{"id":"customer_si_projects","name":"SI Service Projects — Customer Admin","description":"SI (System Integrator) service projects seen from the CUSTOMER ADMIN side — service delivery across ALL of the customer's partners (the company's own Service Projects page). Read with `si_customer_service_projects`: `list` (paginated summary of every project across partners, optionally filtered to one partner), `scope_form` (one project's full scope-form answers), and `export` (a batch that returns a page of projects each already enriched with its full scope form — for pulling large volumes that aren't synced to a CRM). Customer-scoped; the customer company is injected server-side and every record is tenant-isolated by privacy rules."},{"id":"partner_user_management","name":"Partner — Users & Roles","description":"Manage the people and permission roles inside a partner organisation (the partner-admin's own team). Read with `partner_users`: list the partner's users who have an activated portal login (a subset — the full team roster is `list_partner_contacts`), list its roles (custom + system, each with how many contacts are assigned), and read the default roles auto-assigned to new members. Manage with `manage_partner_users`: create/edit a role (admin flag, portal-section visibility), assign or remove roles for a member (by email), and set which roles are the partner's defaults. Partner-scoped; every record is tenant-isolated by Bubble privacy rules. All actions take `partner_id` (from list_accounts)."},{"id":"dashboards","name":"Dashboards — Program-wide Overview","description":"Single-call executive snapshots of the company's partner program. Use when the user wants a quick at-a-glance picture (total partners, active vs pending, deal counts, revenue totals, top performers) instead of drilling into one partner or one metric. Customer-admin only; everything is scoped to the caller's company automatically."}],"tools":[{"name":"list_accounts","title":"List Authorized Accounts","description":"Returns the customer + partner accounts the user approved during the MCP consent screen. Call this first when you need to resolve a partner by name or when the user has multiple roles. Each entry has: id, type ('customer' | 'partner'), name, company_id, partner_id (null for customer), affiliate_company_name (partner only — the customer company hosting the partnership), and dashboard_url (the canonical EULER web-app URL for this account's role — use this when the user needs to do something MCP doesn't expose, like creating an incentive program; customer accounts point to /dashboard, partner accounts to /partners). To resolve a partner by name, match `type === 'partner'` AND `affiliate_company_name === <target>`; use that entry's `partner_id` for referrals / partner_artifacts / performance / commissions. Response also includes `consent_summary` with `hidden_count` — if non-zero, some user accounts were not authorized in this session; ask the user to disconnect/reconnect to include more rather than guessing. Downstream tools auto-resolve partner_id to its company_id, so you only need to pass partner_id. IF the response includes a `backend_data_issue` field (only ever present when the backend returned zero accounts for a provisioned user), this is a backend data anomaly, NOT a consent problem — do NOT suggest disconnect+reconnect; surface the support_email from that field and offer to continue with other tools (partners, performance, commissions) which read directly from the user's company and may still work. When the user asks to do something this MCP doesn't cover (creating incentives, billing/account settings, adding new partners from scratch), surface the matching account's `dashboard_url` instead of guessing a URL.","category":"account_management","scope":"both","operation":"read","actions":null,"parameters":[]},{"name":"partners","title":"Query Partners","description":"Query partners of the user's company (customer-admin view). action='list' searches by name (filter_name='<name>') and returns partner_id values usable in referrals / partner_artifacts / performance / commissions. If the user is asking about a partner where they themselves have an account (they are a partner OF that company), prefer `list_accounts` matched by affiliate_company_name. action='pending' returns partners awaiting approval. action='summary' returns company-wide aggregate metrics broken down by partner status — this is the canonical source for company-level partner-status / partner-count questions like 'how many partners are active?', 'how many active partners do we have?', 'partner status breakdown', 'partner totals'. Generic mentions of partner status refer to the company's partner roster and belong here; route to `partner_directory_search` ONLY when the user explicitly mentions the Partner Directory (which uses a separate profile-status concept). The summary result is stable across a conversation, so call it at most once per conversation when needed.","category":"partners","scope":"customer","operation":"read","actions":["list","pending","summary"],"parameters":[{"name":"action","type":"string","required":true},{"name":"filter_name","type":"string","required":false},{"name":"page","type":"integer","required":false},{"name":"limit","type":"integer","required":false}]},{"name":"partner_directory_search","title":"Search Partner Directory","description":"Searches EULER's Partner Directory — public-facing partner profiles, tiers, status, custom-fields completeness, iframe analytics, saved filters. Returns directory `profile_id` values that are DIFFERENT from the `partner_id` used by referrals / partner_artifacts / performance / commissions; using a profile_id where partner_id is expected produces `partner_not_in_consent`. To resolve a partner_id from a name, prefer `list_accounts` (match by `affiliate_company_name`) or `partners(action: 'list', filter_name: ...)`. entity_key selects mode: 'partner_directory' is THE workhorse search mode — filter by name, status, tier, type, email, featured, incomplete, language, or website, INCLUDING language-coverage queries like 'which partners serve Spanish?'. 'partner_history' returns a single profile's change history (also requires profile_id). 'partner_filter' applies a named saved filter via `version_description` — only used when the user explicitly references a saved filter name. The `status` enum here filters Directory profile status (Active / Under Review / Awaiting Setup / Rejected / Hidden) — that's distinct from the partner roster's active-vs-pending state. Route generic partner-status / partner-count questions ('how many partners are active?', 'how many active partners do we have?', 'partner status breakdown') to `partners(action: 'summary')`; only use this tool's `status` filter when the user is explicitly asking about the Partner Directory. Filterable by name, status, featured, incomplete, tier, profile_id, type, email, language, website.","category":"partner_directory","scope":"customer","operation":"read","actions":null,"parameters":[{"name":"entity_key","type":"string","required":true},{"name":"name","type":"string","required":false},{"name":"status","type":"string","required":false},{"name":"featured","type":"boolean","required":false},{"name":"incomplete","type":"boolean","required":false},{"name":"tier","type":"string","required":false},{"name":"profile_id","type":"string","required":false},{"name":"type","type":"string","required":false},{"name":"email","type":"string","required":false},{"name":"desc","type":"string","required":false},{"name":"language","type":"string","required":false},{"name":"website","type":"string","required":false},{"name":"version_description","type":"string","required":false},{"name":"page","type":"integer","required":false},{"name":"limit","type":"integer","required":false}]},{"name":"partner_artifacts","title":"Get Partner Artifacts","description":"Returns artifacts linked to a specific partner. All actions require partner_id. To resolve partner_id, prefer (a) `list_accounts` (when the user has an account with that partner, match by `affiliate_company_name`) or (b) `partners(action: 'list', filter_name: ...)` (customer admin querying their company's partners). IDs from `partner_directory_search` are profile_ids and will be rejected as `partner_not_in_consent`. action='agreements' returns signed agreements; 'tracking_links' returns affiliate links; 'deals' returns the partner's deals (paginated); 'charges' returns charges assigned to the partner (paginated); 'invoices' returns the partner's invoices (paginated).","category":"partner_relationships","scope":"both","operation":"read","actions":["agreements","tracking_links","deals","charges","invoices"],"parameters":[{"name":"action","type":"string","required":true},{"name":"partner_id","type":"string","required":true},{"name":"page","type":"integer","required":false},{"name":"limit","type":"integer","required":false}]},{"name":"referrals","title":"Query Referrals","description":"Query existing referrals / deal-registrations. action='search' (requires os_referral_type) returns a filtered search across the company's referrals (optional partner_id filter, date range, status). action='for_partner' returns referrals submitted by a specific partner (paginated). action='get_form_for_partner' returns the referral-form question schema a partner must fill in — call this before `submit_referral` to discover form_id + questions[]. Each question carries an `answer_field` ('answer_text' | 'multiselect_options') telling you exactly which field to put its answer in (only true multi-select uses multiselect_options; everything else, dropdown included, uses answer_text). The response also includes `question_count` and a `presentation_guidance` string with formatting tips for displaying the questions cleanly to the user — follow it, especially for long forms. action='get_answers' returns the form answers submitted with a specific referral (requires approval_id — obtain it from a prior `search` (POPs / customer-side) or `for_partner` (PAM / partner-side) call; each referral record carries the identifier). action='rejection_reasons' lists the valid rejection reasons for a referral; call it before rejecting via resolve_referral. Partner_id sources: `list_accounts` (the user's own partner roles) or `partners(action: 'list', filter_name: ...)` (customer admin querying their company's partners). IDs from `partner_directory_search` are profile_ids and will be rejected. Use `submit_referral` to actually send a new referral after collecting answers.","category":"partner_relationships","scope":"both","operation":"read","actions":["search","for_partner","get_form_for_partner","get_answers","rejection_reasons"],"parameters":[{"name":"action","type":"string","required":true},{"name":"partner_id","type":"string","required":false},{"name":"approval_id","type":"string","required":false},{"name":"referral_id","type":"string","required":false},{"name":"start_date","type":"string","required":false},{"name":"end_date","type":"string","required":false},{"name":"filter_status","type":"string","required":false},{"name":"os_referral_type","type":"string","required":false},{"name":"page","type":"integer","required":false},{"name":"limit","type":"integer","required":false}]},{"name":"submit_referral","title":"Submit Referral","description":"Submits a new referral or deal registration on behalf of a partner. TWO caller modes: (A) a PARTNER submitting for one of their OWN partner accounts — resolve partner_id from `list_accounts` (match by `affiliate_company_name`, e.g. 'send a referral to <Partner>' means the user has a partner account with that affiliate_company_name); (B) a CUSTOMER admin submitting on behalf of a partner OF THEIR COMPANY — resolve partner_id from `partners(action: 'list', filter_name: ...)`. Required flow: (1) resolve partner_id (mode A via `list_accounts`, mode B via `partners(action: 'list')`). (2) call `referrals(action: 'get_form_for_partner', partner_id)` to fetch form_id + questions[]. (3) have the user fill in every required question. (4) call this tool with partner_id + form_id + answers (a JSON array of {question_id, answer_text, multiselect_options[]} entries) — place each value in the field its question's `answer_field` indicates (almost always `answer_text`; only true multi-select uses `multiselect_options`). A customer admin may only submit for a partner that belongs to their own company — EULER enforces that scoping server-side (a partner outside your company won't resolve). IDs from `partner_directory_search` are profile_ids, NOT partner_ids — they'll be rejected.","category":"partner_relationships","scope":"both","operation":"write","actions":null,"parameters":[{"name":"partner_id","type":"string","required":true},{"name":"form_id","type":"string","required":true},{"name":"answers","type":"array | string","required":true}]},{"name":"resolve_referral","title":"Approve or Reject Referral","description":"Approve or reject a referral a partner submitted. action='approve' creates the referral in your CRM; action='reject' marks it rejected. Rejecting requires a rejection reason when reasons are configured — list them first with referrals(action:'rejection_reasons'). Optionally email the partner about the decision via notify_partner + notification_details. A referral can only be resolved once.","category":"partner_relationships","scope":"customer","operation":"write","actions":["approve","reject"],"parameters":[{"name":"action","type":"string","required":true},{"name":"referral_id","type":"string","required":true},{"name":"internal_note","type":"string","required":false},{"name":"rejection_reason_id","type":"string","required":false},{"name":"notify_partner","type":"boolean","required":false},{"name":"notification_details","type":"object","required":false}]},{"name":"get_search_deals","title":"Search Deal by Name","description":"Look up a specific deal by name. Returns deals whose name matches `deal_name`. REQUIRED: deal_name. partner_id behaviour depends on caller role: partner-role callers MUST pass partner_id (the search is scoped to that partner's deals); customer-role callers MAY pass partner_id to narrow the search to one partner — when omitted, the search covers all the company's deals. Use this for questions like 'find the Acme deal' or 'show me deals named *renewal*'; for generic listings or aggregates use `performance(action: 'overall')` instead.","category":"partner_relationships","scope":"both","operation":"read","actions":null,"parameters":[{"name":"deal_name","type":"string","required":true},{"name":"partner_id","type":"string","required":false}]},{"name":"list_partner_contacts","title":"List Partner Contacts","description":"Returns the contacts (people) associated with a specific partner — the COMPLETE team roster, including people who never logged into the portal (unlike `partner_users` action='list_users', which returns only the subset with an activated login). Customer-admin view only. Resolve `partner_id` via `partners(action: 'list', filter_name: ...)` or `list_accounts` (match by affiliate_company_name); IDs from `partner_directory_search` are profile_ids and will be rejected. Pair with `create_partner_contact` (to add a new person) and `invite_partners_to_portal` (to grant portal access). Returns `contacts_count` (number) plus `contacts` — a comma-separated string of compact JSON objects, one per contact, each with `id`, `email`, `first_name`, `last_name` (e.g. `{\"id\":\"..\",\"email\":\"..\",\"first_name\":\"..\",\"last_name\":\"..\"},{..}`). Use the `id` values directly as input to `invite_partners_to_portal`. NOTE: the `contacts` string is NOT wrapped in `[ ]` — wrap it yourself if you need a strict JSON array.","category":"partner_contacts","scope":"customer","operation":"read","actions":null,"parameters":[{"name":"partner_id","type":"string","required":true}]},{"name":"create_partner_contact","title":"Create Partner Contact","description":"Creates a `partner_contact` (a person inside a partner organisation) for a given partner. **Idempotent**: if a contact with the same email already exists for that partner, no duplicate is created — the existing one is returned with `was_created: false`. Use this to add a new person BEFORE granting them portal access with `invite_partners_to_portal` (which takes contact ids, not emails). Returns `{ contact_id, was_created }`. Customer-side only. **Two failure modes** (both safe — nothing created): (1) If `partner_id` doesn't exist at the backend (typo / stale id), rejected upstream with HTTP 400 `dispatch_failed` + a `MISSING_DATA` body — the most common failure for a bad id; ask the user to verify the partner. (2) If `partner_id` exists but belongs to a different company than the caller's, the response is `{status: 'success', response: {}}` (empty body) — the tenant guard terminated. Resolve `partner_id` via `partners(action: 'list', filter_name: ...)` or `list_accounts` (match by affiliate_company_name); IDs from `partner_directory_search` are profile_ids and will be rejected.","category":"partner_contacts","scope":"customer","operation":"write","actions":null,"parameters":[{"name":"partner_id","type":"string","required":true},{"name":"email","type":"string","required":true},{"name":"first_name","type":"string","required":false},{"name":"last_name","type":"string","required":false}]},{"name":"invite_partners_to_portal","title":"Invite Partner Contacts to Portal","description":"Invites one or more EXISTING partner_contacts (by their contact ids) to a partner's portal in a single call. Get the ids from `list_partner_contacts` (the `id` in each contact object) or by creating them first with `create_partner_contact`. The backend reuses the partner's single `externally_invited_user_mapping` (or creates it on the first invite), then adds a per-contact invitation and sends the invitation email; if a contact's email already corresponds to a Euler account, portal access is granted immediately, otherwise on accept. **Idempotent per contact**: a contact already invited is not duplicated or re-emailed. Optional `agreement_id`, `restrict_access`, and `tags` mirror the in-app bulk-invite. Returns `{ success, invited_count, mapping_existed }` (`invited_count` = number of contact ids submitted; the actual sends happen asynchronously). Customer-side only. **Failure modes** (all safe): (1) invalid `partner_id` → HTTP 400 `dispatch_failed` + `MISSING_DATA` body. (2) `partner_id` of a different company, OR an EMPTY `partner_contacts` list → `{status: 'success', response: {}}` (empty body) — a guard terminated the workflow before anything was created. Resolve `partner_id` via `partners(action: 'list', filter_name: ...)` or `list_accounts`.","category":"partner_contacts","scope":"customer","operation":"write","actions":null,"parameters":[{"name":"partner_id","type":"string","required":true},{"name":"partner_contacts","type":"array","required":true},{"name":"agreement_id","type":"string","required":false},{"name":"restrict_access","type":"boolean","required":false},{"name":"tags","type":"array","required":false}]},{"name":"company_invoices","title":"Query Company Invoices","description":"Query invoices at the company level. action='list' returns invoices with advanced filters (status, currency, date range, sort, descending); 'summary' returns aggregate totals by status (paid/pending/processing) — use for 'how much was invoiced', 'total revenue', invoice aggregate questions.","category":"invoices","scope":"customer","operation":"read","actions":["list","summary"],"parameters":[{"name":"action","type":"string","required":true},{"name":"partner_id","type":"string","required":false},{"name":"status","type":"string","required":false},{"name":"sort_by","type":"string","required":false},{"name":"descending","type":"string","required":false},{"name":"currency","type":"string","required":false},{"name":"start_date","type":"string","required":false},{"name":"end_date","type":"string","required":false},{"name":"page","type":"integer","required":false},{"name":"limit","type":"integer","required":false}]},{"name":"charges_lookup","title":"Charges Lookup","description":"Inspect specific charges (for partner-scoped charge lists, use `partner_artifacts(action: 'charges')`). action='for_deal' returns all charges linked to a deal_id (e.g. 'show me what was charged on deal X', 'list charges for deal abc'). action='by_id' returns the full detail of a single charge — amount, commission, partner, date, status (e.g. 'inspect charge xyz', 'why was this charge created'). Customer-only.","category":"partner_relationships","scope":"customer","operation":"read","actions":["for_deal","by_id"],"parameters":[{"name":"action","type":"string","required":true},{"name":"deal_id","type":"string","required":false},{"name":"charge_id","type":"string","required":false}]},{"name":"flow_lifecycle","title":"Manage Flow Lifecycle","description":"Create or edit Flow containers (onboarding / training / certification programs). action='create' creates a new flow (requires name, description, type, enable_due_date; optional due_date). action='edit' saves/activates/publishes/archives an existing flow (requires flow_id, flow_status; final_order optional). To inspect an existing flow's details, use `flow_details`.","category":"flows","scope":"customer","operation":"write","actions":["create","edit"],"parameters":[{"name":"action","type":"string","required":true},{"name":"flow_id","type":"string","required":false},{"name":"name","type":"string","required":false},{"name":"description","type":"string","required":false},{"name":"type","type":"string","required":false},{"name":"enable_due_date","type":"boolean","required":false},{"name":"due_date_days","type":"integer","required":false},{"name":"flow_status","type":"string","required":false},{"name":"final_order","type":"array","required":false}]},{"name":"flow_details","title":"Get Flow Details","description":"Returns Flow or Step details. The three drill-down params `name`, `flow_id`, and `flow_step_id` are **mutually exclusive — pass at most one per call**. Intent selected by which drill-down param is set: (1) `name` alone → searches flows by name (returns matching flow IDs); (2) `flow_id` alone → returns that flow's details + its assignments + step IDs; (3) `flow_step_id` alone → returns that step's details. For drill-down, make **separate sequential calls**: search by `name` → call with a returned `flow_id` → call with a returned `flow_step_id`. Never combine multiple drill-down params in the same call — the workflow returns only the first matching intent and silently drops the rest. The response field `can_be_updated_now` reflects whether a flow's content (steps) can be edited — it is \"no\" while the flow is published (Active) — and does not affect assigning the flow to partners (done via `flow_assignment`).","category":"flows","scope":"customer","operation":"read","actions":null,"parameters":[{"name":"flow_id","type":"string","required":false},{"name":"name","type":"string","required":false},{"name":"flow_step_id","type":"string","required":false}]},{"name":"partner_flow_details","title":"Get My Flow Details","description":"Returns Flow and/or Step details for flows assigned to the calling partner. Partner-role only (customer-admin sessions will not see this tool). The three drill-down params `name`, `flow_id`, and `flow_step_id` are mutually exclusive — pass at most one per call. Intent selected by which drill-down param is set: (1) none → the partner's currently assigned flows, grouped by completion rule into three sets (the discovery starting point); (2) `name` alone → searches that assigned-flow list by title; (3) `flow_id` alone → returns that flow's details + its step IDs; (4) `flow_step_id` alone → returns that step's details. The grouped list (no drill-down param) returns three field pairs: `open_matching_flows_details` + `number_of_open_matching_flows` (flows any team member may optionally complete), `needed_flows` + `needed_flows_count` (flows every team member must complete), and `specific_users_flow` + `specific_users_flow_count` (flows assigned to the calling user specifically). Each of the three detail fields is a comma-joined text payload of one or more flow objects with keys flow_name, flow_id, flow_type, flow_description, flow_status, flow_number_of_steps, flow_due_date, completion_rule — parse it before presenting; the count field gives the number of objects. For drill-down, make separate sequential calls: start with no drill-down param (or `name`) → pick a flow_id from the response and call again → pick a flow_step_id from that flow's step list and call again. Only flows the partner has an active assignment to ever surface. `partner_id` is required; resolve from `list_accounts` (entry with `type: 'partner'`).","category":"flows","scope":"partner","operation":"read","actions":null,"parameters":[{"name":"partner_id","type":"string","required":true},{"name":"flow_id","type":"string","required":false},{"name":"name","type":"string","required":false},{"name":"flow_step_id","type":"string","required":false}]},{"name":"partner_flow_progress","title":"Get My Flow Progress","description":"Returns per-flow completion stats for the calling partner: step counts by status (total, done, failed, to_do, overdue), percent_complete, and flow metadata (title, description, type, due date, due_in_days, certification_badge text when earned, plus done/failed/to_do/overdue per-step detail lists). Partner-role only (customer-admin sessions will not see this tool). REQUIRED: partner_id, flow_id. Resolve partner_id via `list_accounts` (entry with type='partner'); resolve flow_id via `partner_flow_details` first (its no-drill-down call returns the partner's assigned flows). Only flows the partner is actively assigned to return populated data — a flow_id outside the partner's assignments returns empty counts and empty detail lists.","category":"flows","scope":"partner","operation":"read","actions":null,"parameters":[{"name":"partner_id","type":"string","required":true},{"name":"flow_id","type":"string","required":true}]},{"name":"flow_assignments","title":"Flow Assignments","description":"Returns the company's flow-assignment roster — which flows are assigned to which partners. Customer-admin only (partner-role sessions will not see this tool). The filters `partner_id` and `flow_id` are optional; with neither, returns the company's full active-assignment roster (paginated, 250 per page by default). `partner_id` narrows to one partner's flows (pivot partner→flows); `flow_id` narrows to one flow's partners (pivot flow→partners). Response fields: `results` (the current page — a comma-joined text payload of assignment objects carrying partner + flow fields), `grouped_by_partners` (the same data grouped per partner — a comma-joined text payload; parse before presenting), `total_count` (all matching assignments), `total_for_this_page`, and `has_more` (pagination signal). Resolve `partner_id` via `partners(action: 'list', filter_name: ...)`; resolve `flow_id` via `flow_details`. Only active assignments within the caller's own company are returned.","category":"flows","scope":"customer","operation":"read","actions":null,"parameters":[{"name":"partner_id","type":"string","required":false},{"name":"flow_id","type":"string","required":false},{"name":"page","type":"integer","required":false},{"name":"limit","type":"integer","required":false}]},{"name":"flow_steps","title":"Manage Flow Steps","description":"Create, edit, or deactivate Steps inside a Flow. action='create_or_edit' creates/edits a step (requires flow_id, name, description, os_flow_step_type, order_temp, entity_key='create'|'edit'). action='deactivate' enables/disables an existing step (requires flow_id, flow_step_id, step_status='Active'|'Inactive', entity_key='step'). To list attachable resources (content/agreement/quiz) before creating a step, use `flow_step_attachments`. Note: `entity_key` overloads two distinct discriminators across the two actions — see its field description.","category":"flows","scope":"customer","operation":"write_destructive","actions":["create_or_edit","deactivate"],"parameters":[{"name":"action","type":"string","required":true},{"name":"flow_id","type":"string","required":false},{"name":"flow_step_id","type":"string","required":false},{"name":"name","type":"string","required":false},{"name":"description","type":"string","required":false},{"name":"os_flow_step_type","type":"string","required":false},{"name":"accepted_file_types","type":"string","required":false},{"name":"due_in_days","type":"integer","required":false},{"name":"order","type":"integer","required":false},{"name":"order_temp","type":"integer","required":false},{"name":"agreement_term_id","type":"string","required":false},{"name":"content_id","type":"string","required":false},{"name":"quiz_from_schema_id","type":"string","required":false},{"name":"scorm_pack_id","type":"string","required":false},{"name":"step_status","type":"string","required":false},{"name":"flow_status","type":"string","required":false},{"name":"entity_key","type":"string","required":false}]},{"name":"flow_attachable_resources","title":"List Flow Attachable Resources","description":"Lists attachable resources (content items, agreements, or quizzes) available to embed inside Flow Steps. Pass entity_key='content' | 'agreement' | 'quiz' to scope which resource type to list. Returned ids feed `flow_steps(action: 'create_or_edit')` via content_id / agreement_term_id / quiz_from_schema_id. Note: 'Task', 'File Upload', 'SCORM', and 'Text' are valid Flow step types but NOT valid entity_key values here — those step types don't have an attachable-resource pool.","category":"flows","scope":"customer","operation":"read","actions":null,"parameters":[{"name":"entity_key","type":"string","required":true},{"name":"file_type","type":"string","required":false}]},{"name":"flow_assignment","title":"Assign / Unassign Flow","description":"Assign or unassign Flows to partners/users. action='assign' assigns a flow (requires flow_id and assign_to_option — exact strings; the completion rule decides which id list to pass: the 'Any user…can complete' and 'All Users…need to complete' rules assign at the partner level and require partners_id_list, while the 'Specific users…need to complete' rule assigns to named users and requires users_id plus partners_id_list holding the single partner whose users are assigned). action='unassign' removes an assignment (requires flow_id, entity_key='all'|'partner'|'user'). Assignment works on published (Active) flows and does not require the flow to be editable or unpublished — a `can_be_updated_now` value of \"no\" returned by flow_details concerns editing a flow's content, not assigning it, and does not block this tool. To list eligible partners/users before assigning, use `flow_assignment_candidates`.","category":"flows","scope":"customer","operation":"write_destructive","actions":["assign","unassign"],"parameters":[{"name":"action","type":"string","required":true},{"name":"flow_id","type":"string","required":false},{"name":"assign_to_option","type":"string","required":false},{"name":"partners_id_list","type":"array","required":false},{"name":"partners_id","type":"array","required":false},{"name":"users_id","type":"array","required":false},{"name":"entity_key","type":"string","required":false}]},{"name":"flow_assignment_candidates","title":"List Flow Assignment Candidates","description":"Returns the partners that can be assigned to a Flow (paginated; each entry has partner_id + name). Requires partner_id. Optionally pass `email` to resolve a single user: when the email matches a user, the response carries that user's unique_id — pass that id as the `users_id` value for `flow_assignment`'s 'Specific users' rule (end users know emails, not ids). Use the returned ids in `flow_assignment(action: 'assign')`.","category":"flows","scope":"customer","operation":"read","actions":null,"parameters":[{"name":"partner_id","type":"string","required":true},{"name":"email","type":"string","required":false},{"name":"page","type":"integer","required":false},{"name":"limit","type":"integer","required":false}]},{"name":"incentives_summary","title":"Incentives Summary","description":"Returns the company's incentives and the count of partners associated with each. Use for incentive program reporting.","category":"incentives","scope":"customer","operation":"read","actions":null,"parameters":[]},{"name":"content_search","title":"Search Shared Partner Content","description":"Semantic (vector) search INSIDE a partner's privately-shared content — the documents/decks/files the customer shared with a specific partner — matches on meaning, not filename. Use for 'find content about <topic>' / 'what do my shared docs say about X' scoped to a partner. Partner-scoped: requires `partner_id` (resolve via `list_accounts` for partner-role, or `partners(action: 'list', filter_name: ...)` for customer-admin). For product HELP / how-to questions about EULER itself ('how do I add a user', 'how does EULER work'), use `euler_help` instead. To LIST or browse a partner's files by name, use `content_list`.","category":"content_search","scope":"both","operation":"read","actions":null,"parameters":[{"name":"query","type":"string","required":true},{"name":"partner_id","type":"string","required":true}]},{"name":"euler_help","title":"EULER Help & Support","description":"EULER product help & support search — THE tool for any question about how EULER itself works or how to do something in the EULER product: 'how do I add a user', 'how do I submit a referral', 'where do I find X in the portal', 'how does EULER work', 'what is <EULER feature>', 'how do I set up Y'. Searches EULER's public help center / support articles (the support.eulerapp.com knowledge base) and returns the matching article text plus a citable `article_url`. ALWAYS call this before telling the user you can't find a procedure or that there's no documentation — do not guess generic SaaS steps. Needs only the question — no partner_id or other context. (To semantically search a partner's privately-shared content instead, use `content_search`; to list a partner's files by name, use `content_list`.)","category":"content_search","scope":"both","operation":"read","actions":null,"parameters":[{"name":"query","type":"string","required":true}]},{"name":"content_list","title":"List Partner Content","description":"Lists the actual content/files a partner has access to in their Partner Portal — PDFs, Word docs, images, uploaded videos, and external links (YouTube, Vimeo, Loom, Google Drive), newest first. Use for 'what content/files do I have', 'list my documents', 'show the PDFs shared with me', 'do I have a deck about X'. Returns each item with name, description, file_type, a ready-to-use URL (file_url for uploaded files OR link for external), cover image, size, and a downloadable flag. Pass `partner_id` (resolve via `list_accounts` for partner-role, or `partners(action: 'list', filter_name: ...)` for customer-admin). Optional `query` filters by a case-insensitive substring of the content NAME — omit it (or pass '') to list everything. This is direct catalog retrieval; for conceptual 'how does X work' / documentation lookups that search INSIDE content, use `content_search` (semantic) instead. IDs from `partner_directory_search` are profile_ids and will be rejected.","category":"content_search","scope":"both","operation":"read","actions":null,"parameters":[{"name":"partner_id","type":"string","required":true},{"name":"query","type":"string","required":false}]},{"name":"commissions","title":"Commissions Calculation","description":"Commission payouts and expense calculations over a date range. action='company' returns the third-person aggregate (e.g. 'how much did we pay', 'our commission expense', 'company-wide payout', 'total spent on partner commissions') — customer-only. action='partner' returns first-person or specific-partner queries (e.g. 'how much commission did I earn this month', 'minha comissão', 'commission owed to <Partner X>', 'partner X earnings') — requires partner_id, callable by partner-role tokens. Partner_id sources for action='partner': `list_accounts` (the user's own partner roles, match by `affiliate_company_name`) or `partners(action: 'list', filter_name: ...)` (customer admin querying their company's partners). IDs from `partner_directory_search` are profile_ids and will be rejected as `partner_not_in_consent`. start_date and end_date are required (no defaults).","category":"commissions","scope":"customer","operation":"read","actions":["partner","company"],"parameters":[{"name":"action","type":"string","required":true},{"name":"partner_id","type":"string","required":false},{"name":"start_date","type":"string","required":true},{"name":"end_date","type":"string","required":true},{"name":"page","type":"integer","required":false},{"name":"limit","type":"integer","required":false}]},{"name":"performance","title":"Partner Performance","description":"Partner performance metrics and rankings over a date range. action='overall' ranks all partners (e.g. 'top 5 partners', 'best performing partners', 'leaderboard', 'highest performers', 'partner ranking') and REQUIRES start_date, end_date, page, limit, and entity_key. Dates use YYYY-MM-DD format (e.g. '2024-04-07') — NEVER include a time component; full ISO 8601 datetimes are silently mis-parsed downstream and return corrupted years. When the user doesn't specify a window, default start_date to today − 90 days and end_date to today. Pagination defaults: page=1, limit=10. entity_key selects the ranking metric — pass 'revenue' for closed-won/invoiced revenue ($) questions (top sellers, biggest accounts, highest revenue contributors) and as the default for generic performance questions; pass 'deals' only when the question is specifically about deal COUNT (most deals closed, most wins). 'overall' is terminal — it returns ranking plus aggregate metrics in one call, considering deals only (no commissions data); subsequent partner_artifacts/commissions calls aren't necessary unless the user asks for more depth. action='partner' returns a single partner's metrics — requires partner_id, callable by partner-role tokens. action='company' returns the user's own company aggregate (sales/deals/charges) — customer-only. Disambiguation when the user names an entity: if the name matches the user's own company (`list_accounts` entry where `type==='customer'` AND `name===<asked>`), use action='company'. If it matches an `affiliate_company_name` in `list_accounts` OR a partner of the user's company, use action='partner' with that partner_id. Partner_id sources for action='partner': `list_accounts` (match by `affiliate_company_name`) or `partners(action: 'list', filter_name: ...)`. IDs from `partner_directory_search` are profile_ids and will be rejected as `partner_not_in_consent`.","category":"performance","scope":"customer","operation":"read","actions":["partner","company","overall"],"parameters":[{"name":"action","type":"string","required":true},{"name":"partner_id","type":"string","required":false},{"name":"start_date","type":"string","required":false},{"name":"end_date","type":"string","required":false},{"name":"status","type":"string","required":false},{"name":"page","type":"integer","required":false},{"name":"limit","type":"integer","required":false},{"name":"entity_key","type":"string","required":false}]},{"name":"influenced_sourced_deals","title":"Deal Attribution — Influenced & Sourced","description":"Returns deal attribution data for a date range: counts and totals for sourced deals (partner-originated), influenced deals (partner-touched), and combined sourced-and-influenced deals — with per-partner breakdowns suitable for ranking. Use for deal-source-attribution questions: counts of sourced/influenced deals, total value of sourced/influenced deals, sourced vs influenced comparisons, and partner rankings by sourced/influenced deal performance. Pass `partner_id` to scope the response to a single partner's attribution (sourced/influenced deals owned by that partner only); omit it for the company-wide view across all partners. For generic partner deal lists without sourced/influenced framing, `partner_artifacts(action: 'deals')` is the right tool; for company-wide aggregates use `performance(action: 'overall')`; for looking up a specific deal by name use `get_search_deals`.","category":"performance","scope":"customer","operation":"read","actions":null,"parameters":[{"name":"partner_id","type":"string","required":false},{"name":"start_date","type":"string","required":false},{"name":"end_date","type":"string","required":false},{"name":"page","type":"integer","required":false},{"name":"limit","type":"integer","required":false}]},{"name":"submit_feedback","title":"Submit MCP Feedback","description":"Sends feedback to the EULER engineering team about the MCP itself — tool errors, ambiguous descriptions, missing capabilities. Use proactively when: (a) type='error' — a tool returned an unrecoverable error and the user couldn't get what they asked for; (b) type='improvement' — a tool's description was ambiguous and led down the wrong path before recovering; (c) type='feature_request' — the user asked for something this MCP doesn't support yet; (d) type='recommendation' — a general suggestion (UX, naming, missing context, etc). Feedback is delivered to a Slack channel monitored by EULER engineering. Do NOT use for routine user-fixable errors (wrong input, missing partner_id, malformed deal_id) or to surface successful operations — only when the MCP itself fell short.","category":"feedback","scope":"both","operation":"write","actions":null,"parameters":[{"name":"type","type":"string","required":true},{"name":"summary","type":"string","required":true},{"name":"details","type":"string","required":true},{"name":"affected_tool","type":"string","required":false},{"name":"user_intent","type":"string","required":false}]},{"name":"communications","title":"Communications Hub (read)","description":"Read-only access to the Communications Hub (partner email program). action='list_audiences' lists the company's audiences with name, active flag, and contact count. action='list_templates' lists email templates (name, description, published). action='list_sent_emails' lists individually-sent emails (recipient, subject, status, opens, clicks). action='emails_by_broadcast' lists the emails sent in one broadcast (requires broadcast_id). action='preview_broadcast' returns how many recipients an audience + template would reach plus a sample of eligible contacts (requires audience_id + template_id) — call this before sending. action='preview_template' renders a template with one contact's data substituted so you can show the final email (requires template_id + sample_contact_id). Everything is scoped to the user's company automatically.","category":"communications_hub","scope":"customer","operation":"read","actions":["list_audiences","list_templates","list_sent_emails","emails_by_broadcast","preview_broadcast","preview_template"],"parameters":[{"name":"action","type":"string","required":true},{"name":"audience_id","type":"string","required":false},{"name":"template_id","type":"string","required":false},{"name":"broadcast_id","type":"string","required":false},{"name":"sample_contact_id","type":"string","required":false},{"name":"page","type":"integer","required":false},{"name":"limit","type":"integer","required":false}]},{"name":"communications_manage","title":"Communications Hub — manage","description":"Create and send within the Communications Hub (partner email program). action='create_audience' creates an empty partner audience (requires name). action='create_template' creates an email template from raw HTML (requires name + html); the ONLY supported merge tokens are [Contact First Name], [Contact Last Name], [Contact Email], and [Company Contact] — any other token (e.g. {{name}}) is rejected before saving. action='add_contacts' adds up to 100 partners to an audience (requires audience_id + partner_ids); idempotent (partners already in the audience are skipped). action='send_broadcast' sends a template to all eligible contacts of an audience (requires audience_id + template_id + subject + confirm) — this emails real partners and cannot be undone, so call communications(action:'preview_broadcast') first and pass confirm=true only after the user explicitly approves. Everything is scoped to the user's company automatically.","category":"communications_hub","scope":"customer","operation":"write_destructive","actions":["create_audience","create_template","add_contacts","send_broadcast"],"parameters":[{"name":"action","type":"string","required":true},{"name":"name","type":"string","required":false},{"name":"html","type":"string","required":false},{"name":"description","type":"string","required":false},{"name":"preview_text","type":"string","required":false},{"name":"audience_id","type":"string","required":false},{"name":"partner_ids","type":"array","required":false},{"name":"template_id","type":"string","required":false},{"name":"subject","type":"string","required":false},{"name":"confirm","type":"boolean","required":false}]},{"name":"si_service_projects","title":"SI Service Projects (read)","description":"Reads SI (System Integrator) service projects for the authenticated partner. action='list' returns the projects for one of your partner accounts (requires partner_id, from list_accounts; paginated via offset/limit) as `items[]` — each item has id and name; call action='get' with the id for status, approval, dates, and full detail. action='get' returns one project's detail (requires project_id). action='scope_form' returns the project's scope form AND its current answers (requires project_id): `questions[]` (each with field_id, field_name, a `type` — Short Text, URL, Email, Phone, Long Text, Dropdown, Currency, Percentage, Number, Date, Multi-select, Tags, or Checkbox — required, order, and the field's current answer: `answer_text`, `answer_number` (string), `answer_date` (string), `answers_list` (array of text); the slot that's populated depends on the field's type — PLUS `options` (array of the field's allowed values for Dropdown / Multi-select / Checkbox; empty for free-text types) so you submit only valid values) and `checkboxes[]` (each with checkbox_id, field_id, name, percentage). To read what a partner has filled in, call scope_form; to change an answer, call si_manage_service_project action='submit_answers' with the same field_id (it overwrites). action='list_services' returns the catalog of services the partner can add to a project (requires partner_id; the customer company is resolved server-side from your partner account, NOT passed in). Everything is scoped to the partner by privacy rules; a project the partner cannot see returns found=false. NOTE: SI is an opt-in feature that not every partner has — an empty `list_services` (or empty project list) can mean this partner does not have SI enabled, not necessarily that nothing matches; don't assume the program has no services without considering that.","category":"partner_si_projects","scope":"partner","operation":"read","actions":["list","get","scope_form","list_services"],"parameters":[{"name":"action","type":"string","required":true},{"name":"project_id","type":"string","required":false},{"name":"partner_id","type":"string","required":false},{"name":"offset","type":"integer","required":false},{"name":"limit","type":"integer","required":false}]},{"name":"si_manage_service_project","title":"Manage SI Service Project","description":"Creates and modifies SI service projects for the authenticated partner. action='create' creates a project (requires project_name, start_date, end_date, partner_id, service_type_ids[]; the customer company is resolved server-side from your partner account, NOT passed in); status and approval are derived server-side from the linked deal and the selected services; returns the new project_id. The project's scope-form fields are created asynchronously by a server-side cascade — allow a few seconds after create before calling scope_form (an immediate read may show a partial field set). action='update' edits a project's name/description/dates/deal — only the fields you pass change (requires project_id + partner_id). action='delete' soft-deletes a project (requires project_id + partner_id; optional deletion_note) — it then stops appearing in reads. action='submit_answers' writes scope-form answers (requires project_id + answers[]); each answer has field_id plus ONE value matching the field's `type` from scope_form: Short Text / URL / Email / Phone / Long Text / Dropdown use answer_text; Currency / Percentage / Number use answer_number; Date uses answer_date (ISO 8601); Multi-select / Tags use answers_list. For Dropdown / Multi-select fields the value(s) must be among the field's `options` (from scope_form): a Dropdown or Multi-select answer with any value outside its options is REJECTED with `invalid_option` (the error lists the field_id, the submitted value(s), and the allowed options); always check `options` from scope_form before submitting. Answer writes process asynchronously (the response status is 'scheduled') — allow a moment before re-reading the answers via scope_form. action='set_progress' sets one checkbox sub-item's completion percentage 0–100 (requires checkbox_id + percentage) using checkbox_id values from scope_form — Checkbox-type fields are updated here, not via submit_answers. The partner_id passed to 'create', 'update', and 'delete' must be one of your authorized partner accounts.","category":"partner_si_projects","scope":"partner","operation":"write_destructive","actions":["create","update","delete","submit_answers","set_progress"],"parameters":[{"name":"action","type":"string","required":true},{"name":"project_id","type":"string","required":false},{"name":"project_name","type":"string","required":false},{"name":"description","type":"string","required":false},{"name":"start_date","type":"string","required":false},{"name":"end_date","type":"string","required":false},{"name":"partner_id","type":"string","required":false},{"name":"deal_id","type":"string","required":false},{"name":"service_type_ids","type":"array","required":false},{"name":"deletion_note","type":"string","required":false},{"name":"answers","type":"array","required":false},{"name":"checkbox_id","type":"string","required":false},{"name":"percentage","type":"number","required":false}]},{"name":"si_customer_service_projects","title":"SI Service Projects (customer admin)","description":"Reads SI (System Integrator) service projects from the CUSTOMER ADMIN side — service delivery across ALL of the company's partners. The customer company is resolved server-side (NOT an AI input). action='list' returns the company's projects (paginated via offset/limit; optionally filtered to one partner via partner_id) as `items[]` — each item has id, name, partner_id, partner_name, status, service_types[], start_date, end_date, completed_date, created_date — PLUS total_count and has_more. action='scope_form' returns ONE project's full scope form (requires project_id): `questions[]` (each with field_id, field_name, type, required, order, and the current answer in answer_text / answer_number / answer_date / answers_list, plus `options` for Dropdown/Multi-select/Checkbox) and `checkboxes[]`. action='export' is a BATCH for pulling large volumes: it returns a page of projects (offset/limit, max 100) where EACH project is already enriched with its full `scope_form` (+ `checkboxes`) — use it to extract all ~N projects' complete data in pages without calling scope_form one-by-one (a project may carry `scope_form_error` if its form couldn't be fetched; the rest of the batch still returns). Everything is tenant-isolated by privacy rules (a project outside your company is never returned). NOTE: 'In Progress' here is the project lifecycle status; a scope-form field literally named 'Project Status' (e.g. 'Scoping') is a different value that lives inside the scope form.","category":"customer_si_projects","scope":"customer","operation":"read","actions":["list","scope_form","export"],"parameters":[{"name":"action","type":"string","required":true},{"name":"project_id","type":"string","required":false},{"name":"partner_id","type":"string","required":false},{"name":"offset","type":"integer","required":false},{"name":"limit","type":"integer","required":false}]},{"name":"partner_users","title":"Partner Users & Roles (read)","description":"Reads the users and permission roles of a partner organisation. Works for BOTH a partner-admin reading their OWN team AND a customer-admin reading one of their company's partners. action='list_users' returns ONLY the partner's people who have an activated EULER portal login, as `users[]` (each: id, email, first_name, last_name, roles); requires partner_id. ⚠️ This is NOT the partner's full team — most partner people are contacts who were never invited or never logged in and do NOT appear here, so a partner with hundreds of contacts may legitimately return only a handful of users (or zero); that is correct, not a truncation. For the COMPLETE team roster (everyone, with or without a login) use `list_partner_contacts`. action='list_roles' returns the partner's permission roles as `roles[]` (each: id, role_title, category, have_admin_permissions, all_sections_enabled, default_sections_enabled, custom_sections_enabled, qty_users_assigned); requires partner_id. action='get_default_roles' returns the role(s) auto-assigned to new members (same role shape); requires partner_id. Everything is tenant-isolated by privacy rules — you only ever see your own partner account (partner-admin) or a partner of your own company (customer-admin). partner_id comes from list_accounts. NOTE: `list_roles`'s `qty_users_assigned` counts how many of the partner's CONTACTS hold each role, so its total is typically far larger than `list_users` (which is only logged-in users) — they are different populations and are NOT expected to match.","category":"partner_user_management","scope":"both","operation":"read","actions":["list_users","list_roles","get_default_roles"],"parameters":[{"name":"action","type":"string","required":true},{"name":"partner_id","type":"string","required":false}]},{"name":"manage_partner_users","title":"Manage Partner Users & Roles","description":"Creates and modifies a partner organisation's permission roles, and assigns/removes them for members. Works for BOTH a partner-admin managing their OWN team AND a customer-admin managing a partner of their company. ⚠️ AS A CUSTOMER-ADMIN, these writes change the roles and portal access of users in ANOTHER company (your partner's organisation) — they are your partner, but still a separate company. BE CERTAIN before you create/edit a role or assign/remove roles, and confirm the intent with the user first. action='create_role' creates a role (requires partner_id, role_title, category); optional: have_admin_permissions ('yes'/'no'), enable_all_sections ('yes'/'no'), default_sections[] (portal sections the role may see), custom_sections[] (custom partner-portal section ids). action='edit_role' edits a role — only the fields you pass change (requires role_id; same optional fields as create). action='assign_roles' assigns one or more roles to one or more members BY EMAIL (requires partner_id, emails[], role_ids[]); keep_old_roles (boolean): true ADDS to the members' current roles, false REPLACES them. **The emails must be people already in this partner's team — find them via `list_partner_contacts` (the full team roster), NOT via `partner_users` action='list_users' (which returns only the few people who have an activated portal login). An email that isn't one of the partner's contacts is silently skipped.** action='remove_roles' removes ONE role from ONE member (requires partner_id, email, role_id); remove_all_roles (boolean) strips ALL of that member's roles instead. action='update_default_roles' sets which roles new members are auto-assigned (requires partner_id, role_ids[]). Get role ids from partner_users action='list_roles'; `category` is one of the partner role categories (e.g. Partnerships, Sales, Marketing, Finance, Support). partner_id comes from list_accounts.","category":"partner_user_management","scope":"both","operation":"write_destructive","actions":["create_role","edit_role","assign_roles","remove_roles","update_default_roles"],"parameters":[{"name":"action","type":"string","required":true},{"name":"partner_id","type":"string","required":false},{"name":"role_id","type":"string","required":false},{"name":"role_ids","type":"array","required":false},{"name":"role_title","type":"string","required":false},{"name":"category","type":"string","required":false},{"name":"have_admin_permissions","type":"string","required":false},{"name":"enable_all_sections","type":"string","required":false},{"name":"default_sections","type":"array","required":false},{"name":"custom_sections","type":"array","required":false},{"name":"emails","type":"array","required":false},{"name":"email","type":"string","required":false},{"name":"keep_old_roles","type":"boolean","required":false},{"name":"remove_all_roles","type":"boolean","required":false}]},{"name":"partner_inc1_incentives","title":"Partner Incentives — Inc 1.0","description":"Get a partner's active Inc 1.0 incentive plan. Requires partner_id — the data is scoped to THAT partner, not the logged-in user. action='status' returns: package name, payout frequency, net days post payment, and the commission tiers as `tiers[]` — each tier has name, min/max revenue range, commission_pct (%), flat_payout (fixed fee), and net_days. Use when a partner asks about their commission structure, tier package, or payout terms. Returns found=false if this partner has no Inc 1.0 plan.","category":"partner_incentives","scope":"partner","operation":"read","actions":["status"],"parameters":[{"name":"action","type":"string","required":true},{"name":"partner_id","type":"string","required":true}]},{"name":"partner_inc2_incentives","title":"Partner Incentives — Inc 2.0","description":"Get a partner's Inc 2.0 incentive program data. Requires partner_id — the data is scoped to THAT partner, not the logged-in user. action='status' returns the current tier (`tier_name`, `tier_badge`, `tier_badge_color`, `tier_order`), `program_name`/`program_status`/`progression_type`, `payout_frequency`, plus three flat lists: `commission_structure[]` — one row per product-group × revenue-range × time-period, each row with `group_name`, `all_products`, `range_min`, `range_max`, `is_max_infinite`, `fixed_payout`, `demo_payout`, `net_days`, `period_months`, `commission_pct`; `benefits[]` — the current tier's non-monetary benefits (`property_name`, `type`, `answer_text`/`answer_bool`/`answer_number`/`answer_select`); and `active_campaign[]` — 0 or 1 active temporary boost/override campaign (`name`, `type`, `date_start`, `date_end`, `boost_pct`, `boost_fixed`, `override_tier_name`). action='progression' returns `current_tier_name`/`current_tier_order`, `program_tiers[]` — every tier in the program (`tier_name`, `tier_order`, `tier_badge`, `badge_color`), `at_highest_tier`, `next_tier_name`/`next_tier_order` (the next tier above the partner's current one), `all_tiers[]` — one row per advancement rule across the program's tiers (`tier_name`, `tier_order`, `logic`, `metric`, `operator`, `value`, `period`) — and `live_metrics[]` — the partner's live progress, EACH ROW already carrying its rule + which tier it belongs to: `tier_name`, `tier_order`, `logic`, `metric`, `operator`, `goal` (the rule's threshold), `calculated` (current value), `passing`, `period`. Returns found=false if no Inc 2.0 assignment exists. To explain what's needed to reach the next tier, filter `live_metrics[]` by `tier_name === next_tier_name` — each row is a requirement with its target (`goal`), current progress (`calculated`), and whether it's met (`passing`). `all_tiers[]` is the full rule catalog per tier (use it for tiers that have no live metric yet); `live_metrics[]` is the one that carries progress.","category":"partner_incentives","scope":"partner","operation":"read","actions":["status","progression"],"parameters":[{"name":"action","type":"string","required":true},{"name":"partner_id","type":"string","required":true}]},{"name":"get_partner_overall_stats","title":"Partner Program — Overall Stats","description":"Single-call executive snapshot of the company's partner program. Returns total partner count (all), active partner count, pending partner count, total deal count, won deal count, total revenue (sum), and the top 100 partners ranked by revenue. Use when the user wants a quick at-a-glance picture of the program (e.g. 'give me a program overview', 'how is our partner program doing overall', 'top performers'). Customer-side only; scoped to the caller's company automatically. For ranking by date range / pagination, use `performance(action: 'overall')` instead — this tool is unfiltered and lifetime-to-date.","category":"dashboards","scope":"customer","operation":"read","actions":null,"parameters":[]},{"name":"manage_deals","title":"Create & Update Deals","description":"Create or update deals attributed to your partners. action='create'/'update' handle one deal; action='create_batch'/'update_batch' handle many at once. Batch operations are scheduled and run in the background — the response confirms how many were queued (schedule_amount) and does not return the rows; confirm results later with get_search_deals rather than resubmitting. Every deal validates its partner against your company; amount must be greater than 0; close_date must be after created_date. Dates are YYYY-MM-DD.","category":"partner_relationships","scope":"customer","operation":"write","actions":["create","update","create_batch","update_batch"],"parameters":[{"name":"action","type":"string","required":true},{"name":"deal_id","type":"string","required":false},{"name":"partner_id","type":"string","required":false},{"name":"deal_name","type":"string","required":false},{"name":"crm_id","type":"string","required":false},{"name":"amount","type":"number","required":false},{"name":"stage","type":"string","required":false},{"name":"created_date","type":"string","required":false},{"name":"close_date","type":"string","required":false},{"name":"deals","type":"array","required":false}]}]}