Skip to main content
Tools Reference

ScalePad Tools

Written By Christopher Scaminaci

Last updated 7 days ago

ScalePad Tools

scalepad_ · 384 tools · Free 182 · Pro 202 Five separately-subscribed ScalePad products behind one personal API key: Core, Lifecycle Manager, ControlMap, Quoter and Backup Radar. The region is stored as a code - US, EU, Canada or Australia. ControlMap is served in all four and Backup Radar in the US and EU, while Core, Lifecycle Manager and Quoter are US-only and stay on the US gateway whatever region is stored. Paging is uniform: a page size of 1 to 200 with an opaque cursor. Filters use the vendor's field, operator and value grammar and are passed as one filters argument, because Core alone documents around 130 filter keys. The rate limit is 50 requests per 5 seconds per key. A 402 means that product is not subscribed - Lifecycle Manager needs LM Pro or higher and Backup Radar its own subscription - and a 401 can mean the key's owning user was deactivated or lost Administrator permission. Lifecycle Manager exports return a short-lived link to a PDF, CSV or spreadsheet.

All connector tools · ScalePad setup guide

ScalePad tool groups

Backup Radar

ToolPlanAccessSummary
scalepad_br_get_client_healthFreeRead-onlyGet the daily backup-health history for ONE ScalePad client.
scalepad_br_list_clients_devicesFreeRead-onlyList monitored backup devices with their most recent result and daily SLA history.
scalepad_br_list_clients_healthFreeRead-onlyList ScalePad clients with their daily backup-health history from Backup Radar.

[ScalePad] Get the daily backup-health history for ONE ScalePad client. Returns {client:{name, client_id}, data:[...]} — note the array is named 'data' here, whereas scalepad_br_list_clients_health names the same per-day array 'history'. Each entry carries date and a compliance object (pending_count, at_risk_count, in_compliance_count, out_of_compliance_count, out_of_compliance_no_results_count). Pass the ScalePad client_id returned by scalepad_br_list_clients_health or scalepad_core_list_clients — NOT a Backup Radar company identifier. This operation documents no 404: an unknown, inaccessible, or missing client surfaces as a 400 alongside genuine validation failures, so read the vendor error detail instead of inferring not-found from the status. Requires an ACTIVE Backup Radar product subscription of its own (HTTP 402 PRODUCT_SUBSCRIPTION_REQUIRED otherwise), and is served only from the US and EU ScalePad gateways.

ParamTypeRequiredDefaultDescription
historyDaysintegernonullDays of backup history to return, 1-365 (out-of-range values are clamped). This operation documents a default of 7 days when omitted.
idstringyesThe ScalePad client id whose backup health to read (client.client_id from scalepad_br_list_clients_health, or id from scalepad_core_list_clients).

[ScalePad] List monitored backup devices with their most recent result and daily SLA history. Cursor-paginated: data[], total_count, next_cursor. Each device carries br_device_id (the encrypted URL-safe Backup Radar identity — always present, and the reliable key to deduplicate on), br_job_name, br_device_name, br_backup_classification (Primary Backup | Primary Verification | Primary Boot Verification | Replication | System Alert), client (ScalePad name/client_id plus br_company_name, all nullable), last_result[] ({status: success|warning|error, date} — an empty array means no result has arrived yet, and several entries can share a timestamp when one result raised multiple flags), and history[] (most-recent-first, each {date, sla_status} where sla_status is Pending | At Risk | In Compliance | Out Of Compliance | Out Of Compliance - No Results; history is null when there is no linked backup plan or the row is a system alert). Devices are returned whether or not their Backup Radar company is mapped to a ScalePad company — the ScalePad-side fields device_id and device_name are NULL on unmapped rows. Requires an ACTIVE Backup Radar product subscription of its own (HTTP 402 PRODUCT_SUBSCRIPTION_REQUIRED otherwise), and is served only from the US and EU ScalePad gateways.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; stop when next_cursor is null or absent.
filtersJsonstringnonullJSON object of field -> "operator:value" filters. This operation documents TWO filters, both applied to the MAPPED ScalePad values: device_name and device_id, e.g. {"device_name":"eq:SRV-01"} or {"device_id":"in:d1,d2"}. Operators are eq (the default when omitted) and in. Because both fields are null on unmapped devices, using either filter excludes every unmapped device from the result.
historyDaysintegernonullDays of SLA history to return per device, 1-365 (out-of-range values are clamped). This operation declares no default — pass an explicit value when the window matters.
pageSizeintegernonullMaximum records per page, 1-200 (default 100). Use with cursor to page.
sortstringnonullSingle-field sort. Sortable fields are device_name and device_id: e.g. 'device_name', '+device_id', '-device_name' ('-' prefix descends).

[ScalePad] List ScalePad clients with their daily backup-health history from Backup Radar. Cursor-paginated: returns data[] (each entry is {client:{name, client_id}, history:[...]}), total_count, and next_cursor (null on the last page). Each history entry carries date (yyyy-MM-ddTHH:mm:ssZ) and a compliance object of five integer counts — pending_count, at_risk_count, in_compliance_count, out_of_compliance_count, out_of_compliance_no_results_count (the last two are distinct states; do not collapse them). History ends on the tenant's current LOCAL date, not the caller's timezone. Pass a returned client.client_id to scalepad_br_get_client_health for one client's detail. Requires an ACTIVE Backup Radar product subscription of its own — any other ScalePad subscription is not enough and the call returns HTTP 402 (PRODUCT_SUBSCRIPTION_REQUIRED). Backup Radar is served only from the US and EU ScalePad gateways.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; stop when next_cursor is null.
filtersJsonstringnonullJSON object of field -> "operator:value" filters. This operation documents ONE filter: client.name, e.g. {"client.name":"eq:Acme Inc"} or {"client.name":"in:Acme,Globex"}. Operators here are eq (the default when the operator is omitted) and in.
historyDaysintegernonullDays of backup history to return, 1-365 (out-of-range values are clamped). ScalePad's prose describes about 7 days when omitted, but this operation declares no schema default — pass an explicit value when the window matters.
pageSizeintegernonullMaximum records per page, 1-200 (default 100). Use with cursor to page.
sortstringnonullSingle-field sort. Only client.name is sortable: 'client.name' or '+client.name' ascending, '-client.name' descending.

Backup Radar (classic)

ToolPlanAccessSummary
scalepad_br_get_backupFreeRead-onlyGet one Backup Radar backup's detail from the classic API.
scalepad_br_get_backup_resultsFreeRead-onlyGet the individual backup RESULTS (the parsed status emails) for one Backup Radar backup.
scalepad_br_get_backups_overviewFreeRead-onlyGet Backup Radar's overview counts — the tenant-wide totals behind the portal's header.
scalepad_br_list_backup_filtersFreeRead-onlyList the filter values this Backup Radar tenant actually has — the companies, tags, backup methods, device types, statuses and policy types that scalepad_br_list_backups accepts in its filtersJson…
scalepad_br_list_backupsFreeRead-onlyList monitored backups from Backup Radar's classic API.
scalepad_br_list_inactive_backupsFreeRead-onlyList INACTIVE backups — jobs Backup Radar has stopped receiving results for.
scalepad_br_list_retired_backupsFreeRead-onlyList RETIRED backups — jobs someone deliberately retired in Backup Radar, as opposed to jobs that merely went quiet (scalepad_br_list_inactive_backups).

[ScalePad] Get one Backup Radar backup's detail from the classic API. Returns a single object with the same shape as an entry of scalepad_br_list_backups (backupId, companyName, deviceName, deviceType, jobName, methodName, backupType, status, daysInStatus, isVerified, lastResult, lastSuccess, ticketCount, failureThreshold, treatWarningAsSuccess, note, dayStartHour, tags, standalone, ticketingCompany, history). This operation documents no 404 — an unknown or inaccessible backupId surfaces as a 400 alongside genuine validation failures, so read the vendor error detail rather than inferring not-found from the status. Requires an active Backup Radar subscription, and is served from the US and EU only.

ParamTypeRequiredDefaultDescription
backupIdintegeryesThe backup's numeric id (backupId from scalepad_br_list_backups).
datestringnonullThe date to read the backup as of. The vendor types this a date-time and gives 2018-02-14 as its example; the value is sent exactly as written, never reformatted. Omit for the current state.

[ScalePad] Get the individual backup RESULTS (the parsed status emails) for one Backup Radar backup. Page/size shaped like the lists: {Total, Page, PageSize, TotalPages, Results:[...]}, where each result carries dateTime, resultId, and the four independent flags success, warning, failure and manual — they are separate booleans, not one status field, and more than one can be true on the same result. Use scalepad_br_get_backup for the backup's rolled-up status instead. Requires an active Backup Radar subscription, and is served from the US and EU only.

ParamTypeRequiredDefaultDescription
backupIdintegeryesThe backup's numeric id (backupId from scalepad_br_list_backups).
datestringnonullThe date whose results to read. The vendor types this a date-time and gives 2018-02-14 as its example; the value is sent exactly as written, never reformatted. Omit for the default window.

[ScalePad] Get Backup Radar's overview counts — the tenant-wide totals behind the portal's header. Takes no arguments. Returns six integers: backups, office365, workstations, activePolicies, inactivePolicies, retiredPolicies. The cheapest call on this API, so it is the natural first read to confirm the Backup Radar key and region are right before paging any list. Requires an active Backup Radar subscription, and is served from the US and EU only.

[ScalePad] List the filter values this Backup Radar tenant actually has — the companies, tags, backup methods, device types, statuses and policy types that scalepad_br_list_backups accepts in its filtersJson list filters. Takes no arguments. Call this FIRST when filtering by any of those: the vendor ignores a value it does not recognize, so a guessed company or status name comes back as a full unfiltered page rather than an error. Requires an active Backup Radar subscription, and is served from the US and EU only.

[ScalePad] List monitored backups from Backup Radar's classic API. Page/size paginated: returns {Total, Page, PageSize, TotalPages, Results:[...]}. Each result carries backupId (int64 — pass it to scalepad_br_get_backup and scalepad_br_get_backup_results), companyName, deviceName, deviceType, jobName, methodName, backupType {id,name}, status {id,name}, daysInStatus, isVerified, lastResult and lastSuccess timestamps, ticketCount, failureThreshold, treatWarningAsSuccess, note, dayStartHour, tags[], standalone, ticketingCompany, and history[] when HistoryDays was requested (each entry: date, status, lastResultDate, isScheduled, daysInStatus, countSuccess/countWarning/countFailure/countNoResult, daysSinceLastResult, daysSinceLastGoodResult, resultsCount). This is Backup Radar's OWN API, not ScalePad's Backup Radar surface — for client-level backup-health rollups use scalepad_br_list_clients_health instead. Requires an active Backup Radar subscription, and is served from the US and EU only.

ParamTypeRequiredDefaultDescription
filtersJsonstringnonullJSON object of this operation's own filters, e.g. {"SearchByCompanyName":"Acme","statuses":["Failed"],"HistoryDays":7}. Accepted names (case-insensitive, sent in the vendor's casing): SearchByCompanyName, SearchByDeviceName, SearchByJobName, SearchByBackupMethod, SearchByTooltip (backup note), SearchByTag, DaysWithoutSuccess, HistoryDays (0-31, clamped; 0 = no history), FilterScheduled, date (a from-date, vendor example 2018-02-14), searchString, and the list filters companies, tags, excludeTags, backupMethods, deviceTypes, excludeDeviceTypes, statuses, policyIds, excludeBackupMethods, policyTypes (each takes a JSON array). Any other name is refused rather than sent, because Backup Radar ignores unknown parameters and would answer a typo with an unfiltered page. Call scalepad_br_list_backup_filters for the values this tenant actually has.
pageintegernonullPage number, 1-based. Default 1 when omitted.
sizeintegernonullRecords per page, 1-200 (values above 200 are clamped; Backup Radar itself allows up to 1000, 200 is StackJack's cap). Default 50 when omitted.

[ScalePad] List INACTIVE backups — jobs Backup Radar has stopped receiving results for. Page/size paginated: {Total, Page, PageSize, TotalPages, Results:[...]}, each carrying backupId, companyName, deviceName, deviceType, jobName, methodName, backupType {id,name}, emailFrom (the address the status emails arrived from) and lastReceived. Note the narrower shape: an inactive row carries NO status, history, tags or ticket counts — read scalepad_br_get_backup with its backupId for those. Inactive is not retired; retired jobs are a separate list (scalepad_br_list_retired_backups). Requires an active Backup Radar subscription, and is served from the US and EU only.

ParamTypeRequiredDefaultDescription
filtersJsonstringnonullJSON object of this operation's own filters, e.g. {"SearchByCompanyName":"Acme"}. Accepted names (case-insensitive): SearchByCompanyName, SearchByDeviceName, SearchByJobName, SearchByBackupMethod, SearchByEmailFrom, SearchByBackupType. This is a SHORTER set than scalepad_br_list_backups accepts — no list filters and no HistoryDays — and any other name is refused rather than sent, because Backup Radar ignores unknown parameters and would answer a typo with an unfiltered page.
pageintegernonullPage number, 1-based. Default 1 when omitted.
sizeintegernonullRecords per page, 1-200 (values above 200 are clamped; Backup Radar itself allows up to 1000, 200 is StackJack's cap). Default 50 when omitted.

[ScalePad] List RETIRED backups — jobs someone deliberately retired in Backup Radar, as opposed to jobs that merely went quiet (scalepad_br_list_inactive_backups). Page/size paginated: {Total, Page, PageSize, TotalPages, Results:[...]} with the same narrow row shape as the inactive list (backupId, companyName, deviceName, deviceType, jobName, methodName, backupType, emailFrom, lastReceived). Requires an active Backup Radar subscription, and is served from the US and EU only.

ParamTypeRequiredDefaultDescription
filtersJsonstringnonullJSON object of this operation's own filters, e.g. {"SearchByRetiredBy":"jane","SearchByRetiredDateStart":"2026-01-01"}. Accepted names (case-insensitive): SearchByCompanyName, SearchByDeviceName, SearchByJobName, SearchByBackupMethod, SearchByEmailFrom, SearchByRetireMessage, SearchByRetiredBy, SearchByRetiredDateStart, SearchByRetiredDateEnd. The last four are documented ONLY on this operation. Any other name is refused rather than sent, because Backup Radar ignores unknown parameters and would answer a typo with an unfiltered page.
pageintegernonullPage number, 1-based. Default 1 when omitted.
sizeintegernonullRecords per page, 1-200 (values above 200 are clamped; Backup Radar itself allows up to 1000, 200 is StackJack's cap). Default 50 when omitted.

ControlMap Action Items

ToolPlanAccessSummary
scalepad_cm_create_client_action_itemProWriteCreate a remediation action item for a client.
scalepad_cm_create_client_action_item_documentProWriteAttach ONE document you already hold to an action item, uploading it through ScalePad in a single call (vendor multipart/form-data operation, one binary 'file' part).
scalepad_cm_create_client_action_item_document_signed_urlProWriteRegister one or more documents against an action item and get back vendor-minted pre-signed UPLOAD urls — an ordinary JSON POST, not a file transfer.
scalepad_cm_delete_client_action_itemProDestructivePERMANENTLY delete an action item.
scalepad_cm_get_client_action_itemFreeRead-onlyGet one action item in full for a client.
scalepad_cm_list_clients_action_items_summaryFreeRead-onlyAcross EVERY client of the MSP — takes NO client id.
scalepad_cm_map_client_action_itemProWriteLINK an action item to the compliance entities it remediates.
scalepad_cm_search_client_action_itemsFreeRead-onlyFor ONE client, by client id: search that client's remediation action items.
scalepad_cm_unmap_client_action_itemProDestructiveUNLINK an action item from objectives, assessment questions, risks, controls, assets or asset types.
scalepad_cm_update_client_action_itemProWritePartially update an action item (HTTP PATCH).

[ScalePad] Create a remediation action item for a client. Returns 201 with the created record (id, code, parent_entity_id, status, weakness_name, weakness_description, corrective_action, responsible_person, responsible_department, roadmap, dates, currency, priority, cost, milestones, change_in_milestone). Only three properties are schema-required: weakness_name, priority and currency. ScalePad resolves responsible_person and responsible_department against existing users/departments and returns INVALID_RESPONSIBLE_PERSON / INVALID_DEPARTMENT when they do not exist. There is no documented idempotency key, so never blind-retry this call — re-check with scalepad_cm_search_client_action_items first. Map the new item to objectives, controls, risks or questions afterwards with scalepad_cm_map_client_action_item; the create body takes no mappings.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. Required: weakness_name (non-empty title — one vendor 400 example confusingly calls this field 'title'), priority (High | Medium | Low | Critical; default High) and currency (USD | EUR | AUD | CAD | SEK | NZD | SGD | INR; default USD). Optional: weakness_description, status (Not Started | In Progress | Review | Completed | Not Applicable; default Not Started), corrective_action, responsible_person (an existing user's EMAIL), responsible_department (an existing department name), efforts_in_hours (integer, minimum 0 and MAXIMUM 8 — the 201 response echoes it as 'efforts'), roadmap (3 months | 6 months | 12 months), planned_start_date, planned_end_date, actual_start_date, actual_end_date (date strings, e.g. 2026-03-20), cost (integer, zero or positive), milestones and change_in_milestone (free text). Example: {"weakness_name":"Implement security patch","priority":"High","currency":"USD","responsible_person":"john.doe@example.com","efforts_in_hours":8}.
clientIdstringyesThe ControlMap client id to create the action item under (client.id from scalepad_cm_list_clients_health).

[ScalePad] Attach ONE document you already hold to an action item, uploading it through ScalePad in a single call (vendor multipart/form-data operation, one binary 'file' part). Returns the vendor's declared 200 with {id, code, documents[{file_name, document_id}]}. The vendor caps each upload at 10 MB; because MCP has no binary parameter type the content is passed here as base64 and decoded into a real binary part before sending, so the base64 text is roughly a third larger than the file itself. Use THIS tool for a single modest file whose bytes you have. Use scalepad_cm_create_client_action_item_document_signed_url instead when you have several files, a large file, or a file the end user will upload themselves — that tool returns pre-signed URLs and the bytes never pass through StackJack.

ParamTypeRequiredDefaultDescription
actionItemIdstringyesThe action item id to attach the document to (action_items.data[].id from scalepad_cm_search_client_action_items).
clientIdstringyesThe ControlMap client id owning the action item (client.id from scalepad_cm_list_clients_health).
fileContentBase64stringyesThe complete file content, base64-encoded. Decoded size must be greater than zero (an empty file returns 400 'File size must be greater than 0') and no more than the vendor's 10 MB limit.
fileContentTypestringnonullOptional MIME type for the part, e.g. "application/pdf". ScalePad publishes no allowed-MIME list or restriction; omit it to let the client choose a default.
fileNamestringyesFile name to store, including its extension (e.g. "User_Access_Review_Q1_2026.pdf"). ScalePad does not publish a file-name length limit or an allowed-extension list.

[ScalePad] Register one or more documents against an action item and get back vendor-minted pre-signed UPLOAD urls — an ordinary JSON POST, not a file transfer. Returns 201 with {id, code, documents[{file_name, document_id, signed_url, expires_in_seconds}]} (300 seconds in the vendor example). You then send each file's bytes yourself to its signed_url using the method the response names, before it expires; that URL is already authorized, so do NOT attach the ScalePad x-api-key to the storage request. ScalePad does not publish required storage headers, checksums, a maximum batch size or a completion callback — honor whatever the runtime response returns and invent nothing. Choose this over scalepad_cm_create_client_action_item_document for batches, for files near or above the 10 MB direct-upload cap, or when a human will do the uploading.

ParamTypeRequiredDefaultDescription
actionItemIdstringyesThe action item id the documents belong to (action_items.data[].id from scalepad_cm_search_client_action_items). The signed URLs attach to this existing item — no new action item is created.
bodyJsonstringyesA top-level JSON ARRAY (not an object) of file-metadata entries, one per file. Each entry has file_name and file_size_bytes; neither appears in the schema's required list, but the vendor description defines both and its validation examples reject a bad size, so always send both. Example: [{"file_name":"Action_Item_Evidence_Q1_2026.pdf","file_size_bytes":245760},{"file_name":"Incident_Snapshot.png","file_size_bytes":102400}].
clientIdstringyesThe ControlMap client id owning the action item (client.id from scalepad_cm_list_clients_health).

[ScalePad] PERMANENTLY delete an action item. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the item is not found. ScalePad's own changelog states that action-item deletion removes the record AND all associated data — its documents, mappings and history go with it — and documents no soft delete, restore or undo. Confirm the exact client and the exact item (read it back with scalepad_cm_get_client_action_item first) before calling, and prefer setting status to Not Applicable via scalepad_cm_update_client_action_item when the goal is only to take an item out of the active workload.

ParamTypeRequiredDefaultDescription
actionItemIdstringyesThe action item id to delete (action_items.data[].id from scalepad_cm_search_client_action_items). Resolve and confirm this with the user before calling — the deletion is irreversible.
clientIdstringyesThe ControlMap client id owning the action item (client.id from scalepad_cm_list_clients_health).

[ScalePad] Get one action item in full for a client. Adds to the search-row fields the complete relationship set: documents[] {id, file_name}, objectives[] {id, name, code, program_name}, controls[] {id, name, code}, risks[] {id, name, code} and assessment_questions[] {id, name, code} — the codes in those arrays are exactly the values scalepad_cm_map_client_action_item and scalepad_cm_unmap_client_action_item accept. Dates come back as planned_start_date / planned_end_date / actual_start_date / actual_end_date here (the search rows use the *_completion_date spelling for the same two fields).

ParamTypeRequiredDefaultDescription
actionItemIdstringyesThe action item id to read — the action_items.data[].id value from scalepad_cm_search_client_action_items. This is the numeric id, NOT the AI-nn business code; the vendor path type is integer, so pass it as a string (e.g. "75").
clientIdstringyesThe ControlMap client id owning the action item (client.id from scalepad_cm_list_clients_health).

[ScalePad] Across EVERY client of the MSP — takes NO client id. Cursor-paginated remediation roll-up for the whole client base: returns {data[], total_count, next_cursor} where each row is {client {id, name, tenant_id}, action_summary {completed, review, in_progress, not_started, not_applicable, total, completion_percentage}}. Keep paging until next_cursor is null. This tool returns COUNTS ONLY — it never lists individual action items; use scalepad_cm_search_client_action_items with a client id for the item rows.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque Base64 next_cursor from the previous page. Omit for the first page; never decode or manufacture one. Cursor scans are not atomic, so deduplicate by client.id.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together and sent as filter[field] query keys, e.g. {"client.id":"in:id-1,id-2"}. Documented filters here are exactly client.id, client.tenant_id and client.name, with vendor examples using eq and in (an omitted operator means eq). An unsupported field returns 400.
pageSizeintegernonullMaximum clients per page, 1-200 (vendor default 50). Values above 200 are clamped rather than rejected; the vendor itself returns 400 for an over-limit page_size.
sortstringnonullComma-separated sort expression; '-' prefix descends, '+' or no prefix ascends. This endpoint accepts multi-sort. Sortable: client.id, client.tenant_id, client.name. Example: '-client.name,+client.tenant_id'.

[ScalePad] LINK an action item to the compliance entities it remediates. For an action item the linkable entities are framework objectives (requirements), assessment questions, risks, controls, assets and asset types — each identified by its BUSINESS CODE (or, for asset types, by name), never by numeric id. Succeeds with HTTP 204 and no body (this tool returns ). Only the non-empty arrays you send are applied; empty or omitted arrays are ignored, so a body with no codes is accepted but does nothing — send at least one populated array. Mapping is additive: it never removes existing links (use scalepad_cm_unmap_client_action_item for that). Read current links from the objectives[] / assessment_questions[] / risks[] / controls[] arrays of scalepad_cm_get_client_action_item.

ParamTypeRequiredDefaultDescription
actionItemIdstringyesThe action item id to link from (action_items.data[].id from scalepad_cm_search_client_action_items).
bodyJsonstringyesJSON object body of code arrays; include only the ones you want to link. Properties: objective_codes[] (framework requirement codes such as "6.2" or "A.5.1", from scalepad_cm_search_client_framework_objectives), question_codes[] (assessment question codes such as "Q-GOV-01"), risk_codes[] (e.g. "RSK-001"), control_codes[] (control display codes such as "HRM-1", from scalepad_cm_search_client_controls), asset_codes[] (e.g. "AST-001") and asset_type_names[] (asset type NAMES, e.g. "Cloud Infrastructure"). Example: {"control_codes":["HRM-1","ACC-2"],"objective_codes":["6.2"]}.
clientIdstringyesThe ControlMap client id owning the action item (client.id from scalepad_cm_list_clients_health).

[ScalePad] For ONE client, by client id: search that client's remediation action items. This is a READ despite using HTTP POST — the verb only carries the query body and nothing is persisted (vendor operationId a_actionitem); it replaced the retired GET /action-items list route. Returns client {id, name, tenant_id}, action_summary {completed, review, in_progress, not_started, not_applicable, total, completion_percentage}, and action_items {total_count, next_cursor, data[]}. Each row carries id, code (e.g. AI-9), weakness_name, weakness_description, status, priority, created_by and responsible_person (each {id, name, email}), responsible_department, corrective_action, milestones, changes_to_milestones, source_of_weakness, currency, cost, roadmap, effort_in_hours, requirements[] and timestamps. Beware one vendor inconsistency: search rows spell the end dates planned_completion_date / actual_completion_date while scalepad_cm_get_client_action_item returns planned_end_date / actual_end_date for the same values.

ParamTypeRequiredDefaultDescription
bodyJsonstringnonullOptional JSON query body; the vendor requires a body but accepts and every property is optional, so omitting this returns the first unfiltered page. Properties: filter (object of field -> "operator:value"; documented fields are status, code, title and responsible_person.email — the vendor examples use eq and the schema is open-ended about other operators, with status values Not Started, In Progress, Review, Completed and Not Applicable), page_size (1-200, vendor default 50), cursor (opaque Base64 next_cursor), and sort (ONE of id, code, title or status; '-' prefix descends). Example: {"filter":{"status":"eq:In Progress","responsible_person.email":"john.doe@example.com"},"page_size":50,"sort":"+title"}. Paging and filtering live in this body — this operation has no query-string parameters.
clientIdstringyesThe ControlMap client id whose action items to search (client.id from scalepad_cm_list_clients_health).

[ScalePad] UNLINK an action item from objectives, assessment questions, risks, controls, assets or asset types. The codes you send identify only the LINKS to remove — neither the action item nor the mapped entities are deleted — but the relationship removal is a real state change with no undo, so echo the exact action item and every code back to the user before calling. Succeeds with HTTP 204 and no body (this tool returns ); empty or omitted arrays make no changes. Despite the HTTP POST verb this is a removal: the vendor path is .../mappings/bulk-delete.

ParamTypeRequiredDefaultDescription
actionItemIdstringyesThe action item id to unlink from (action_items.data[].id from scalepad_cm_search_client_action_items).
bodyJsonstringyesJSON object body of code arrays naming the links to REMOVE — same shape as the map tool: objective_codes[], question_codes[], risk_codes[], control_codes[], asset_codes[], asset_type_names[]. Include only the codes to unlink; empty or omitted arrays result in no changes. Example: {"control_codes":["HRM-1"]}.
clientIdstringyesThe ControlMap client id owning the action item (client.id from scalepad_cm_list_clients_health).

[ScalePad] Partially update an action item (HTTP PATCH). Only the properties you send change; everything else is left alone. Returns 200 with the patched record. Every property is optional here — including the three the create call requires — but weakness_name, if present, must not be empty. This is the tool for status transitions and for recording actual start/end dates as remediation progresses. To change an item's LINKS to objectives, controls, risks or questions use scalepad_cm_map_client_action_item / scalepad_cm_unmap_client_action_item instead; relationships are not patchable here.

ParamTypeRequiredDefaultDescription
actionItemIdstringyesThe action item id to update (action_items.data[].id from scalepad_cm_search_client_action_items — the numeric id, not the AI-nn code).
bodyJsonstringyesJSON object body; every property is optional, send only what changes. Accepts weakness_name (must not be empty if sent), weakness_description, status (Not Started | In Progress | Review | Completed | Not Applicable), corrective_action, responsible_person (existing user's email), responsible_department, efforts_in_hours (integer 0-8), roadmap (3 months | 6 months | 12 months), priority (High | Medium | Low | Critical), planned_start_date, planned_end_date, actual_start_date, actual_end_date, currency (USD | EUR | AUD | CAD | SEK | NZD | SGD | INR), cost (zero or positive), milestones, change_in_milestone and notes. Example: {"status":"In Progress","actual_start_date":"2026-03-25"}.
clientIdstringyesThe ControlMap client id owning the action item (client.id from scalepad_cm_list_clients_health).

ControlMap Assessments

ToolPlanAccessSummary
scalepad_cm_create_client_assessment_question_responseProWriteAdd a RESPONSE — a free-text entry on an assessment question's note thread.
scalepad_cm_delete_client_assessment_question_answerProDestructiveCLEAR the saved ANSWER on an assessment question, returning it to unanswered.
scalepad_cm_delete_client_assessment_question_responseProDestructivePERMANENTLY delete ONE response (note) from an assessment question's thread.
scalepad_cm_get_client_assessment_questionFreeRead-onlyGet one assessment question in full, addressed by its question CODE (not its numeric id).
scalepad_cm_get_client_assessment_summaryFreeRead-onlyFor ONE client, by client id: that client's assessment completion summary.
scalepad_cm_list_clients_assessment_summaryFreeRead-onlyAcross EVERY client of the MSP — takes NO client id.
scalepad_cm_map_client_assessment_questionProWriteLINK an assessment question to the records that substantiate its answer.
scalepad_cm_search_client_assessment_questionsFreeRead-onlyFor ONE client, by client id: search that client's common-assessment questions.
scalepad_cm_unmap_client_assessment_questionProDestructiveUNLINK an assessment question from evidence records, action items, policies or procedures.
scalepad_cm_update_client_assessment_question_answerProWriteSave the ANSWER to an assessment question — the questionnaire selection itself, not a note.
scalepad_cm_update_client_assessment_question_responsesProWriteEdit an existing RESPONSE (a note on an assessment question's thread) — again, not the question's answer.

[ScalePad] Add a RESPONSE — a free-text entry on an assessment question's note thread. This does NOT set or change the question's answer (use scalepad_cm_update_client_assessment_question_answer for that). Returns 201 with {id, question_code, response, provided_by, created_at, updated_at, created_by {id, full_name}}; keep the returned id, because editing the note later requires it in the request body. A question can carry many responses — each call appends a new one rather than replacing the last.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. Required: response (the note text). Optional: provided_by (who supplied it — defaults to the authenticated API-key user's display name when omitted). Example: {"response":"Evidence reviewed and accepted.","provided_by":"Jane Auditor"}.
clientIdstringyesThe ControlMap client id owning the assessment (client.id from scalepad_cm_list_clients_health).
questionCodestringyesThe question CODE to attach the note to (e.g. "Q-GOV-01"), from scalepad_cm_search_client_assessment_questions.

[ScalePad] CLEAR the saved ANSWER on an assessment question, returning it to unanswered. Succeeds with HTTP 204 and no body (this tool returns ). Scope is deliberately narrow and worth stating to the user: this removes only the answer selection — the question itself, its note-thread responses, and its mappings to evidence/action items/policies/procedures all survive. It is still a real data loss (the previous selection and its answered_by/answered_at attribution are gone) and it lowers the client's assessment completion figures. To DELETE a note instead, use scalepad_cm_delete_client_assessment_question_response.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id owning the assessment (client.id from scalepad_cm_list_clients_health).
questionCodestringyesThe question CODE whose answer to clear (e.g. "Q-GOV-01"). Confirm the exact question with the user first — there is no undo.

[ScalePad] PERMANENTLY delete ONE response (note) from an assessment question's thread. Succeeds with HTTP 204 and no body (this tool returns ). Here the response id is a PATH parameter, unlike the edit tool which carries it in the body. The question, its answer and its other responses are untouched, but the deleted note and its author attribution are unrecoverable — ScalePad documents no soft delete or restore. Confirm the exact note (read responses[] from scalepad_cm_search_client_assessment_questions) before calling. Do not use this to clear the question's ANSWER — that is scalepad_cm_delete_client_assessment_question_answer.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id owning the assessment (client.id from scalepad_cm_list_clients_health).
questionCodestringyesThe question CODE the note belongs to (e.g. "Q-GOV-01").
responseIdstringyesThe response (note) id to delete — the responses[].id value from scalepad_cm_search_client_assessment_questions, or the id returned when the note was created. Vendor path type is integer, so pass the number as a string (e.g. "88").

[ScalePad] Get one assessment question in full, addressed by its question CODE (not its numeric id). Returns id, code, question, owner {id, full_name}, status, auditor_status, answer_options[] {id, answer, order} — the authoritative list of values the answer write will accept for THIS question — plus the linked action_items[] {id, title, code, description, due_at}, objectives[] {id, code, name, program_name}, delegated_to and assessment_status. Read answer_options before calling scalepad_cm_update_client_assessment_question_answer. This fragment declares no 400, so a bad code may surface as 404.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id owning the assessment (client.id from scalepad_cm_list_clients_health).
questionCodestringyesThe question CODE — the stable string code from scalepad_cm_search_client_assessment_questions, e.g. "Q-GOV-01". This is NOT the numeric id that also appears on the row; the vendor path type here is string.

[ScalePad] For ONE client, by client id: that client's assessment completion summary. Returns answering_progress {total_questions, answered_questions, answered_questions_percentage, yes, no, partially, na}, assessment_score_percentage, and question_group_progress[] {name, count, answered, not_answered, yes, no, partially, na}. Setting includeFrameworkAssessmentStats adds frameworks_count and frameworks[] {framework_id, framework_name, answering_progress, assessment_score_percentage}. There is no pagination envelope. Field-name caveat: the vendor's prose describes the default payload as assessment_question_groups / grades / assessment_stats while its schema and examples use the names listed above — trust the returned JSON. For every client at once use scalepad_cm_list_clients_assessment_summary.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id whose assessment summary to read (client.id from scalepad_cm_list_clients_health).
includeFrameworkAssessmentStatsbooleannonullSet true to also return per-framework statistics (frameworks_count plus a frameworks[] breakdown). The vendor default is false; this tool sends the parameter only when you set it explicitly.

[ScalePad] Across EVERY client of the MSP — takes NO client id. Cursor-paginated assessment completeness for the whole client base, for partner-level dashboards: returns {data[], total_count, next_cursor} where each row is {client {id, name, tenant_id}, assessment_summary {yes, no, partially, not_answered, not_applicable, answering_percentage, total}}. Keep paging until next_cursor is null. This roll-up is counts only and has no per-framework breakdown — call scalepad_cm_get_client_assessment_summary with includeFrameworkAssessmentStats for that, one client at a time.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque Base64 next_cursor from the previous page. Omit for the first page; never decode or manufacture one. Cursor scans are not atomic, so deduplicate by client.id.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, sent as filter[field] query keys, e.g. {"client.name":"eq:Joel Tech"}. Documented filters here are exactly client.tenant_id and client.name, each supporting eq and in (an omitted operator means eq). There is no client.id filter on this endpoint.
pageSizeintegernonullMaximum clients per page, 1-200 (vendor default 50). Values above 200 are clamped rather than rejected; the vendor itself answers an over-limit page_size with 400.
sortstringnonullSort expression; '-' prefix descends, '+' or no prefix ascends. This endpoint documents exactly ONE sortable field per request — client.name or client.tenant_id — and does NOT accept a comma-separated list. An unsupported field returns 400.

[ScalePad] LINK an assessment question to the records that substantiate its answer. For a question the linkable entities are evidence records, action items, policies and procedures — each by BUSINESS CODE (evidence and action-item codes resolve against their task definitions; policy and procedure codes against document codes). Note this set is narrower than a control's or an action item's: there are no risk, objective or asset arrays here. Succeeds with HTTP 204 and no body (this tool returns ). Only non-empty arrays are applied and empty/omitted ones are ignored, so send at least one populated array. Mapping is additive; it never removes an existing link.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body of code arrays; include only what you want to link. Properties: evidence_codes[] (e.g. "EV-100"), action_item_codes[] (e.g. "AI-1001", from scalepad_cm_search_client_action_items), policy_codes[] (e.g. "POL-1") and procedure_codes[] (e.g. "PRO-1"). Example: {"evidence_codes":["FG-001","EV-100"],"action_item_codes":["AI-1001"]}.
clientIdstringyesThe ControlMap client id owning the assessment (client.id from scalepad_cm_list_clients_health).
questionCodestringyesThe question CODE to link from (e.g. "Q-GOV-01"), from scalepad_cm_search_client_assessment_questions.

[ScalePad] For ONE client, by client id: search that client's common-assessment questions. This is a READ despite using HTTP POST — the verb only carries the query body and nothing is persisted (vendor operationId assessmentQuestionsSearchByClient). Note the path is .../assessments/common/questions with NO /search suffix; an older changelog documents a /search path that the current contract does not have. Returns {data[], total_count, next_cursor} where each question carries id, code (the question_code every other tool here takes), question, guidance, group_name, question_area, answer, past_answer, answered_at, frameworks[{id, name}], responses[{id, response, created_at, updated_at, created_by {id, full_name, email}}] and timestamps.

ParamTypeRequiredDefaultDescription
bodyJsonstringnonullOptional JSON query body; the vendor requires a body but every property is optional, so omitting this returns the first unfiltered page. Properties: filter (object of field -> "operator:value"; documented fields are frameworks.name, group_name and answer, supporting eq — the default when the operator is omitted — plus in and like), page_size (1-200, vendor default 50), cursor (opaque Base64 next_cursor), sort (use the public labels code or group_name; the fragment's own text also names internal-looking aliases question_id, programid and external_id, which are best avoided), and rules[] — an alternative filter form of {field, operator, value, values[]} limited to the same three fields. IMPORTANT: a NON-EMPTY rules array makes the vendor IGNORE filter entirely, so send one or the other, not both. Example: {"filter":{"frameworks.name":"eq:ISO 27001","answer":"Yes"},"page_size":50}.
clientIdstringyesThe ControlMap client id whose assessment questions to search (client.id from scalepad_cm_list_clients_health).

[ScalePad] UNLINK an assessment question from evidence records, action items, policies or procedures. This is ControlMap's ONLY unmap that uses the HTTP DELETE verb rather than a .../mappings/bulk-delete POST, and consequently the ONLY operation in the whole ScalePad connector whose DELETE carries a request body — the codes to unlink go in the BODY, not the URL. Succeeds with HTTP 204 and no body (this tool returns ); empty lists make no changes. Removal is limited to the relationships: neither the question nor any mapped record is deleted. It still destroys compliance traceability with no undo, so echo the exact question code and every code being detached back to the user first.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body — sent as the DELETE request's BODY — naming the links to REMOVE: evidence_codes[], action_item_codes[], policy_codes[], procedure_codes[]. Include only the codes to unlink; empty or omitted arrays result in no changes. Example: {"evidence_codes":["EV-100"],"policy_codes":["POL-1"]}.
clientIdstringyesThe ControlMap client id owning the assessment (client.id from scalepad_cm_list_clients_health).
questionCodestringyesThe question CODE to unlink from (e.g. "Q-GOV-01").

[ScalePad] Save the ANSWER to an assessment question — the questionnaire selection itself, not a note. Upsert semantics (HTTP PUT): it creates the answer if there is none and overwrites it if there is, with no read-modify-write step and no previous value returned other than through the question's past_answer field. Returns 200 with {question_code, answer, answered_by {id, name}, reset_answer, answered_at}. The value must be one of the question's valid options — Yes, No, Partially or NA — so check answer_options via scalepad_cm_get_client_assessment_question if unsure; an invalid value returns 400. To add commentary instead of changing the selection use scalepad_cm_create_client_assessment_question_response.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. Required: answer — exactly one of "Yes", "No", "Partially" or "NA" (and it must appear in that question's answer_options). Example: {"answer":"Yes"}.
clientIdstringyesThe ControlMap client id owning the assessment (client.id from scalepad_cm_list_clients_health).
questionCodestringyesThe question CODE to answer (e.g. "Q-GOV-01"), from scalepad_cm_search_client_assessment_questions — not the numeric id.

[ScalePad] Edit an existing RESPONSE (a note on an assessment question's thread) — again, not the question's answer. The vendor's HTTP PATCH targets the responses COLLECTION, so the note being edited is identified by an id INSIDE the request body, not in the URL. Returns 200 with the updated note. One documented contradiction: the operation text says you may update either or both of response and provided_by, while the schema marks id and response as required — so always send id and response, and add provided_by only when it changes. This edits one note per call; it never rewrites the whole thread.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body identifying the note IN THE BODY. Required: id (the response id from the create call or from the responses[] array on scalepad_cm_search_client_assessment_questions) and response (the new text). Optional: provided_by. Example: {"id":88,"response":"Updated after follow-up call.","provided_by":"Jane Auditor"}.
clientIdstringyesThe ControlMap client id owning the assessment (client.id from scalepad_cm_list_clients_health).
questionCodestringyesThe question CODE whose note thread is being edited (e.g. "Q-GOV-01").

ControlMap Controls

ToolPlanAccessSummary
scalepad_cm_create_client_controlProWriteCreate a control for a client.
scalepad_cm_delete_client_controlProDestructivePERMANENTLY delete a control.
scalepad_cm_get_client_controlFreeRead-onlyGet one control in full for a client.
scalepad_cm_get_client_controls_summaryFreeRead-onlyFor ONE client, by client id: that client's control implementation roll-up.
scalepad_cm_list_client_control_familiesFreeRead-onlyList the control FAMILIES available to one client — the groupings inside a control set (e.g. "Human Resources Management" with code HRM, "Common Criteria Related").
scalepad_cm_list_client_control_setsFreeRead-onlyList the control SETS available to one client — the control libraries themselves (e.g. "Controlmap Baseline Controls" with code CBC, "SOC 2").
scalepad_cm_list_clients_controls_summaryFreeRead-onlyAcross EVERY client of the MSP — takes NO client id.
scalepad_cm_map_client_controlProWriteLINK a control to the compliance entities that evidence or depend on it.
scalepad_cm_search_client_controlsFreeRead-onlyFor ONE client, by client id: search that client's controls.
scalepad_cm_unmap_client_controlProDestructiveUNLINK a control from objectives, evidence, policies, procedures, governance documents, risks or action items.
scalepad_cm_update_client_controlProWritePartially update a control (HTTP PATCH).

[ScalePad] Create a control for a client. Returns 201 with the created record (id, the ScalePad-assigned code, name, description, implementation_notes, control_set_name, control_family_name, owner, created_by, timestamps). The authenticated API-key user becomes the control's owner and creator unless owner_email names someone else. Two contract details matter: UNKNOWN JSON properties are REJECTED, so send only the documented fields; and the fragment publishes no formal required array even though type, name, description, control_set_name and control_family_name all carry minLength 1 — send a real value for each rather than an empty string. No idempotency key is documented, so never blind-retry; re-check with scalepad_cm_search_client_controls. Relationships are not part of the create body — use scalepad_cm_map_client_control afterwards.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. Documented properties, none in a formal required array: type (Preventive | Corrective | Detective), name, description, tag, contributors, owner_email (an existing user's email; omitted or empty means the authenticated user), control_set_name (an existing set name from scalepad_cm_list_client_control_sets) and control_family_name (an existing family name from scalepad_cm_list_client_control_families). Unknown properties are rejected. Example: {"type":"Preventive","name":"Password Policy Enforcement","description":"Enforce complexity and rotation.","control_set_name":"SOC 2","control_family_name":"Common Criteria Related"}.
clientIdstringyesThe ControlMap client id to create the control under (client.id from scalepad_cm_list_clients_health).

[ScalePad] PERMANENTLY delete a control. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the control is not found. ScalePad documents no soft delete, restore, undo, or dependency-conflict status for control deletion, and a control is the hub every objective, policy, procedure, evidence record, risk and action item maps to — read scalepad_cm_get_client_control first, show the user the policies/procedures/evidences/objectives/risks/action_items arrays that will lose their link, and confirm the exact client and control before calling. When the intent is only to stop tracking, patch status instead.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id owning the control (client.id from scalepad_cm_list_clients_health).
controlIdstringyesThe control id to delete (controls.data[].id from scalepad_cm_search_client_controls). Resolve and confirm this with the user first — the deletion is irreversible.

[ScalePad] Get one control in full for a client. Adds to the search-row fields (id, code, name, description, implementation_notes, status, compliant, control_set, control_family, owner) the audit trail — created_by, created_at, updated_at — and the complete relationship set: policies[], procedures[], evidences[], action_items[], objectives[], risks[], documents[] and audit_tests[]. Those arrays are the authoritative view of what is currently mapped; their codes are what scalepad_cm_map_client_control and scalepad_cm_unmap_client_control accept.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id owning the control (client.id from scalepad_cm_list_clients_health).
controlIdstringyesThe control id to read — the controls.data[].id value from scalepad_cm_search_client_controls. This is the numeric id, NOT the display code (e.g. "3", not "CC6.1"); the vendor path type is integer, so pass it as a string.

[ScalePad] For ONE client, by client id: that client's control implementation roll-up. Takes no query parameters. The vendor returns the same paginated envelope as the cross-client tool but with exactly one row — {data:[{client {id, name, tenant_id}, control_summary {completed, review, in_progress, not_started, not_applicable, total, completion_percentage}}], total_count: 1, next_cursor: null} — so read data[0], and do not try to page it. Use scalepad_cm_list_clients_controls_summary instead for the same roll-up across every client.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id whose control roll-up to read (client.id from scalepad_cm_list_clients_health).

[ScalePad] List the control FAMILIES available to one client — the groupings inside a control set (e.g. "Human Resources Management" with code HRM, "Common Criteria Related"). Cursor-paginated: returns {data[], total_count, next_cursor}. Each row is deliberately thin: id, name, code, the creating user and created_at only — no controls are included. Use the family NAME as control_family_name when creating or patching a control; a family name is not necessarily unique across sets, so send control_set_name alongside it when disambiguating. This endpoint accepts no sort parameter.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id whose control families to list (client.id from scalepad_cm_list_clients_health).
cursorstringnonullOpaque Base64 next_cursor from the previous page. Omit for the first page; never decode or manufacture one.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, sent as filter[field] query keys. Documented fields: id (eq, ne, gt, gte, lt, lte, in), name and code (eq, ne, in, cont, like). Example: {"code":"eq:HRM"} or {"name":"cont:resources"}. An unsupported field or operator returns 400 INVALID_FILTER.
pageSizeintegernonullMaximum families per page, 1-200 (vendor default 50). Values above 200 are clamped rather than rejected.

[ScalePad] List the control SETS available to one client — the control libraries themselves (e.g. "Controlmap Baseline Controls" with code CBC, "SOC 2"). Cursor-paginated: returns {data[], total_count, next_cursor} with the same thin row shape as control families — id, name, code, creating user and created_at only. Use the set NAME as control_set_name when creating or patching a control; when a patch changes both catalog fields, ScalePad resolves the control SET first and then the family within it. This endpoint accepts no sort parameter.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id whose control sets to list (client.id from scalepad_cm_list_clients_health).
cursorstringnonullOpaque Base64 next_cursor from the previous page. Omit for the first page; never decode or manufacture one.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, sent as filter[field] query keys. Same documented fields as control families: id (eq, ne, gt, gte, lt, lte, in), name and code (eq, ne, in, cont, like). Example: {"code":"eq:CBC"}.
pageSizeintegernonullMaximum sets per page, 1-200 (vendor default 50). Values above 200 are clamped rather than rejected.

[ScalePad] Across EVERY client of the MSP — takes NO client id. Cursor-paginated control implementation roll-up for the whole client base: returns {data[], total_count, next_cursor} where each row is {client {id, name, tenant_id}, control_summary {completed, review, in_progress, not_started, not_applicable, total, completion_percentage}}. Keep paging until next_cursor is null. Counts only — for the control rows themselves call scalepad_cm_search_client_controls with a client id.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque Base64 next_cursor from the previous page. Omit for the first page; never decode or manufacture one. Cursor scans are not atomic, so deduplicate by client.id.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, sent as filter[field] query keys, e.g. {"client.id":"in:cl_1,cl_2"}. This fragment exposes exactly ONE documented filter field — client.id, supporting eq and in — even though the release changelog loosely mentions filter[client.*]; anything else returns 400 INVALID_FILTER.
pageSizeintegernonullMaximum clients per page, 1-200 (vendor default 50). Values above 200 are clamped rather than rejected; the vendor itself answers an over-limit page_size with 400 INVALID_PAGE_SIZE.
sortstringnonullSort expression; '-' prefix descends, '+' or no prefix ascends. Sortable: client.id, client.name, client.tenant_id. An unsupported field returns 400 INVALID_SORT.

[ScalePad] LINK a control to the compliance entities that evidence or depend on it. For a control the linkable entities are framework objectives (requirements), evidence records, policies, procedures, governance documents, risks and action items. Objectives are special: they use an objectives[] array of {program_name, codes[]} because a requirement code is only unique WITHIN its compliance program; everything else uses a flat array of business codes. Succeeds with HTTP 204 and no body (this tool returns ). Only non-empty arrays are applied and empty/omitted ones are ignored, so send at least one populated array. Mapping is additive and never removes an existing link — use scalepad_cm_unmap_client_control for that.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body; include only the arrays you want to link. Properties: objectives[] — each {program_name, codes[]} where program_name is the compliance program (e.g. "ISO-27001:2022") and codes are that program's requirement codes (e.g. "4.1"); evidence_codes[] (e.g. "EV-1"), policy_codes[] (e.g. "POL-1"), risk_codes[] (e.g. "RSK-1"), action_item_codes[] (e.g. "AI-1"), procedure_codes[] (e.g. "PROC-1") and governance_codes[] (e.g. "GOV-1") — governance and procedure codes both resolve against document codes. Example: {"objectives":[{"program_name":"ISO-27001:2022","codes":["4.1"]}],"policy_codes":["POL-1","POL-2"]}.
clientIdstringyesThe ControlMap client id owning the control (client.id from scalepad_cm_list_clients_health).
controlIdstringyesThe control id to link from (controls.data[].id from scalepad_cm_search_client_controls).

[ScalePad] For ONE client, by client id: search that client's controls. This is a READ despite using HTTP POST — the verb only carries the query body and nothing is persisted (vendor operationId openControlsSearch). Returns client {id, tenant_id, name}, control_summary {completed, review, in_progress, not_started, not_applicable, total, completion_percentage}, and controls {total_count, next_cursor, data[]}. Each control row carries id, code (the display code, e.g. CC6.1), name, description, implementation_notes, status, compliant (boolean), control_set {id, name}, control_family {id, name} and owner {id, name}. Use the row id with scalepad_cm_get_client_control for full relationships, and the row code as a control_codes value in every mapping body across ControlMap.

ParamTypeRequiredDefaultDescription
bodyJsonstringnonullOptional JSON query body; the vendor requires a body but accepts and every property is optional, so omitting this returns the first unfiltered page. Properties: filter (object of field -> "operator:value"; documented fields are code, control_id, name, status, owner, control_set, control_set_name and control_family, each supporting eq and in, with status values Not Implemented, Partially Implemented, Implemented, Alternative implementation and 'In progress / Planned'), page_size (1-200, vendor default 50), cursor (opaque Base64 next_cursor), and sort (ONE of id, name, control_id, code or status; '-' prefix descends). Example: {"filter":{"code":"eq:CC6.1","status":"in:Implemented,Not Implemented"},"page_size":50,"sort":"+code"}. Paging and filtering live in this body — this operation has no query-string parameters.
clientIdstringyesThe ControlMap client id whose controls to search (client.id from scalepad_cm_list_clients_health).

[ScalePad] UNLINK a control from objectives, evidence, policies, procedures, governance documents, risks or action items. The codes you send identify only the LINKS to remove — neither the control nor the mapped records are deleted — but this breaks compliance traceability with no undo, so echo the exact control and every code back to the user before calling. Succeeds with HTTP 204 and no body (this tool returns ); empty or omitted arrays make no changes. Despite the HTTP POST verb this is a removal: the vendor path is .../mappings/bulk-delete.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body naming the links to REMOVE — same shape as the map tool: objectives[] of {program_name, codes[]}, plus evidence_codes[], policy_codes[], risk_codes[], action_item_codes[], procedure_codes[] and governance_codes[]. Include only what should be unlinked; empty or omitted arrays result in no changes. Example: {"policy_codes":["POL-1"]}.
clientIdstringyesThe ControlMap client id owning the control (client.id from scalepad_cm_list_clients_health).
controlIdstringyesThe control id to unlink from (controls.data[].id from scalepad_cm_search_client_controls).

[ScalePad] Partially update a control (HTTP PATCH). Only the properties you send change. Returns 200 with the updated record plus a message naming exactly which fields were applied (e.g. "Successfully patched: name, status."), which is the field to check rather than assuming the whole body took effect. Ordering is defined by the vendor: status is applied AFTER the other scalar changes, and when control_set_name and control_family_name are sent together the set is resolved before the family. Two replacement semantics to be careful with: tags and contributors REPLACE the current lists rather than appending, and an empty array clears them. Note the response echoes priority as a NUMBER even though the request takes the string enum.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body; every property is optional, send only what changes. Accepts name, description, code (the business/display code, e.g. HRM-1), status (Not Implemented | Partially Implemented | Implemented | Alternative implementation | 'In progress / Planned'), owner (a user email or display name), type (Preventive | Corrective | Detective), frequency (Weekly | Monthly | Quarterly | Bi-Annual | Annual), team, implementation_notes, control_family_name and control_set_name (send both when the family name is ambiguous), priority (Not Set | Low | Medium | High | Critical), effectiveness (Not Performed | Performed Informally | Planned & Tracked | Automated | Quantitatively Measured), tags[] and contributors[] (both REPLACE the existing list; [] clears it). Example: {"status":"Implemented","frequency":"Annual"}.
clientIdstringyesThe ControlMap client id owning the control (client.id from scalepad_cm_list_clients_health).
controlIdstringyesThe control id to update (controls.data[].id from scalepad_cm_search_client_controls — the numeric id, not the display code).

ControlMap Documents

ToolPlanAccessSummary
scalepad_cm_create_client_evidence_document_signed_urlProWriteRegister one or more documents against an EVIDENCE DEFINITION and get back vendor-minted pre-signed UPLOAD urls — an ordinary JSON POST, not a file transfer.
scalepad_cm_create_client_evidence_request_document_signed_urlProWriteRegister one or more documents against an EXISTING evidence request and get back vendor-minted pre-signed UPLOAD urls — an ordinary JSON POST, not a file transfer.
scalepad_cm_delete_client_documentProDestructivePERMANENTLY delete ONE stored document from a client's compliance record — the file itself, wherever it was attached (an evidence request, a policy, a procedure, a governance document or an action…
scalepad_cm_get_client_document_signed_urlFreeRead-onlyGet a short-lived DOWNLOAD url for ONE stored document belonging to ONE client.

[ScalePad] Register one or more documents against an EVIDENCE DEFINITION and get back vendor-minted pre-signed UPLOAD urls — an ordinary JSON POST, not a file transfer. SIDE EFFECT: because the target is the evidence rather than a specific request, ScalePad ALSO CREATES A NEW EVIDENCE REQUEST to hold the documents; the response's evidence_request_id is that new request. Returns 201 with {evidence_request_id, documents[{file_name, document_id, method, signed_url, expires_in_seconds}]} (300 seconds in the vendor example — note the relative expires_in_seconds here, versus the absolute expires_at on the DOWNLOAD tool). You then send each file's bytes yourself to its signed_url using the method the response names, before it expires; that URL is already authorized, so do NOT attach the ScalePad x-api-key to the storage request. ScalePad does not publish required storage headers, MIME restrictions, checksums, a maximum batch size or a completion callback — honor whatever the runtime response returns and invent nothing. Choose this over scalepad_cm_create_client_evidence_document for batches, for files near or above the 10 MB direct-upload cap, or when a human will do the uploading. To register against an EXISTING request instead, use scalepad_cm_create_client_evidence_request_document_signed_url.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesA top-level JSON ARRAY (not an object) of file-metadata entries, one per file. Each entry has file_name and file_size_bytes; neither appears in the schema's required list, but the vendor description defines both and its validation examples reject a bad size (400 "File size must be greater than 0"), so always send both. Example: [{"file_name":"User_Access_Review_Q1_2026.pdf","file_size_bytes":245760},{"file_name":"SSO_Login_Success_2026-02-12.png","file_size_bytes":102400}].
clientIdstringyesThe ControlMap client id owning the evidence (client.id from scalepad_cm_list_clients_health).
evidenceIdstringyesThe evidence id to register the documents under (evidences.data[].id from scalepad_cm_search_client_evidences — the numeric id, not the EV-nn code). A new evidence request is created against this evidence to hold them.

[ScalePad] Register one or more documents against an EXISTING evidence request and get back vendor-minted pre-signed UPLOAD urls — an ordinary JSON POST, not a file transfer. Unlike the evidence-level variant this creates NO new request; the documents attach to the request you name. Returns 201 with {evidence_request_id, evidence_request_code, documents[{file_name, document_id, method, signed_url, expires_in_seconds}]} (300 seconds in the vendor example — a relative expiry, versus the absolute expires_at on the DOWNLOAD tool). You then send each file's bytes yourself to its signed_url using the method the response names, before it expires; that URL is already authorized, so do NOT attach the ScalePad x-api-key to the storage request. ScalePad does not publish required storage headers, MIME restrictions, checksums, a maximum batch size or a completion callback — honor whatever the runtime response returns and invent nothing. Choose this over scalepad_cm_create_client_evidence_request_document for batches, for files near or above the 10 MB direct-upload cap, or when a human will do the uploading.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesA top-level JSON ARRAY (not an object) of file-metadata entries, one per file — the same shape the evidence-level signed-url tool takes. Each entry has file_name and file_size_bytes; neither appears in the schema's required list, but the vendor description defines both and its validation examples reject a bad size (400 "File size must be greater than 0"), so always send both. Example: [{"file_name":"User_Access_Review_Q1_2026.pdf","file_size_bytes":245760}].
clientIdstringyesThe ControlMap client id owning the evidence request (client.id from scalepad_cm_list_clients_health).
evidenceRequestIdstringyesThe evidence request id to register the documents against (a data[].id value from scalepad_cm_list_client_evidence_requests, or the evidence_request_id returned by scalepad_cm_create_client_evidence_request). The vendor path type is integer, so pass it as a string (e.g. "101").

[ScalePad] PERMANENTLY delete ONE stored document from a client's compliance record — the file itself, wherever it was attached (an evidence request, a policy, a procedure, a governance document or an action item). Succeeds with HTTP 204 and no body (this tool returns ); 404 if the document is not found. The parent record survives with the document removed from its documents[] array; ScalePad documents no soft delete, restore or undo, and this is the only ControlMap operation that removes a file without removing its parent. Confirm the exact client and the exact document with the user before calling — resolve the file NAME first, either from the parent record's documents[] array or from scalepad_cm_get_client_document_signed_url, so the user is confirming a filename rather than a bare integer.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id owning the document (client.id from scalepad_cm_list_clients_health).
documentIdstringyesThe document id to delete — a documents[].id / document_id value from the record it is attached to (e.g. scalepad_cm_list_client_evidence_requests or scalepad_cm_get_client_policy). Resolve and confirm the corresponding file_name with the user before calling; the deletion is irreversible.

[ScalePad] Get a short-lived DOWNLOAD url for ONE stored document belonging to ONE client. Returns the DocumentResponse as JSON — {document_id, file_name, signed_url, expires_at} — not the file bytes, and no StackJack blob storage is involved: signed_url points at ScalePad's own storage and expires_at is the vendor's absolute expiry timestamp (note the field name is expires_at here, whereas the UPLOAD signed-url tools return a relative expires_in_seconds). Fetch the file from signed_url before that time; the URL is already authorized, so do NOT attach the ScalePad x-api-key to the storage request. Document ids come from the documents[] arrays on the compliance records — scalepad_cm_list_client_evidence_requests, scalepad_cm_get_client_policy, scalepad_cm_get_client_procedure, scalepad_cm_get_client_governance and scalepad_cm_get_client_action_item all carry them.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id owning the document (client.id from scalepad_cm_list_clients_health).
documentIdstringyesThe document id to mint a download url for — a documents[].id / document_id value from any record that carries documents (e.g. scalepad_cm_list_client_evidence_requests or scalepad_cm_get_client_policy). The vendor path type is integer, so pass it as a string (e.g. "1024"); an unknown id returns 404 ("The requested document was not found").

ControlMap Evidence

ToolPlanAccessSummary
scalepad_cm_create_client_evidenceProWriteCreate an evidence definition for ONE client.
scalepad_cm_create_client_evidence_documentProWriteAttach ONE document you already hold to an EVIDENCE DEFINITION, uploading it through ScalePad in a single call (vendor multipart/form-data operation, one binary 'file' part).
scalepad_cm_create_client_evidence_requestProWriteOpen a NEW evidence request against an existing evidence definition — an out-of-cycle ask for proof, on top of whatever the recurrence schedule generates.
scalepad_cm_delete_client_evidenceProDestructivePERMANENTLY delete an evidence definition.
scalepad_cm_delete_client_evidence_scheduleProDestructiveRemove the RECURRING COLLECTION SCHEDULE from an evidence record — the evidence itself survives, but its recurrence configuration (frequency, interval, days, start/end) is gone and ScalePad will raise…
scalepad_cm_get_client_evidenceFreeRead-onlyGet ONE evidence definition in full for ONE client.
scalepad_cm_list_client_evidence_requestsFreeRead-onlyList every evidence REQUEST raised against ONE evidence definition for ONE client — the individual collection cycles behind an EV-nn record.
scalepad_cm_list_clients_evidences_summaryFreeRead-onlyAcross EVERY client of the MSP — takes NO client id.
scalepad_cm_map_client_evidenceProWriteLINK an evidence definition to the compliance entities it proves.
scalepad_cm_refresh_client_evidence_mappingsProDestructiveRe-evaluate ALL automated evidence mappings for ONE client — a command, not a query, and it acts on the whole client at once rather than a single evidence record.
scalepad_cm_search_client_evidencesFreeRead-onlyFor ONE client, by client id: search that client's evidence definitions.
scalepad_cm_unmap_client_evidenceProDestructiveUNLINK an evidence definition from objectives, controls or assessment questions.
scalepad_cm_update_client_evidenceProWritePartially update an evidence definition (HTTP PATCH).

[ScalePad] Create an evidence definition for ONE client. Returns 201 with EvidenceCreatedIdsResponse — just {id, evidence_request_id} — because creating an evidence also opens its first evidence request; read the full record back with scalepad_cm_get_client_evidence. An evidence can have only ONE schedule, and the PATCH tool cannot modify an existing one (it returns 409), so get the schedule right here or plan to delete and re-add it. There is no documented idempotency key, so never blind-retry this call — re-check with scalepad_cm_search_client_evidences first.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. The schema marks no top-level property formally required, though title has minLength 1 so send it. Properties: title, description, owner_email, assignee_email, repeat_type (once | recurring, default once), mappings (an EvidenceMappingRequest — the same {objective_codes[], control_codes[], assessment_question_codes[]} shape scalepad_cm_map_client_evidence takes) and schedule. The nested schedule object requires frequency (WEEKLY | MONTHLY | YEARLY), start_date (a date) and end_type (on_date | after_occurrences, default after_occurrences), and optionally takes interval (integer >= 1, default 1), days_of_week[] (lowercase weekday names such as "monday"; used when frequency is WEEKLY, defaulting to the schedule-creation day when omitted), day_of_month (1-31, default 1; used when frequency is MONTHLY), end_date (required in practice when end_type is on_date) and occurrences (integer >= 1, default 1; required in practice when end_type is after_occurrences). Note a vendor documentation slip: the schema field is days_of_week but the vendor's own create EXAMPLE writes it as days — send days_of_week, which is what the schema defines. Example: {"title":"SOC 2 Access Review Evidence","description":"Quarterly user access review evidence.","owner_email":"joel@example.com","assignee_email":"joel@example.com","repeat_type":"recurring","schedule":{"frequency":"WEEKLY","interval":2,"days_of_week":["monday"],"start_date":"2025-12-16","end_type":"on_date","end_date":"2026-01-16"}}.
clientIdstringyesThe ControlMap client id to create the evidence under (client.id from scalepad_cm_list_clients_health).

[ScalePad] Attach ONE document you already hold to an EVIDENCE DEFINITION, uploading it through ScalePad in a single call (vendor multipart/form-data operation, one binary 'file' part). SIDE EFFECT: because the target is the evidence rather than a specific request, ScalePad CREATES A NEW EVIDENCE REQUEST and attaches the document to it — returns 201 with {evidence_request_id, documents[{file_name, document_id}]}. To attach to an EXISTING request instead, use scalepad_cm_create_client_evidence_request_document. The vendor caps each upload at 10 MB; because MCP has no binary parameter type the content is passed here as base64 and decoded into a real binary part before sending, so the base64 text is roughly a third larger than the file itself. Use THIS tool for a single modest file whose bytes you have; use scalepad_cm_create_client_evidence_document_signed_url instead for several files, a large file, or a file the end user will upload themselves — that tool returns pre-signed URLs and the bytes never pass through StackJack.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id owning the evidence (client.id from scalepad_cm_list_clients_health).
evidenceIdstringyesThe evidence id to attach the document under (evidences.data[].id from scalepad_cm_search_client_evidences). A new evidence request is created against this evidence to hold the document.
fileContentBase64stringyesThe complete file content, base64-encoded. Decoded size must be greater than zero (an empty file returns 400 "File size must be greater than 0") and no more than the vendor's 10 MB limit.
fileContentTypestringnonullOptional MIME type for the part, e.g. "application/pdf". ScalePad publishes no allowed-MIME list or restriction; omit it to let the client choose a default.
fileNamestringyesFile name to store, including its extension (e.g. "User_Access_Review_Q1_2026.pdf"). ScalePad does not publish a file-name length limit or an allowed-extension list.

[ScalePad] Open a NEW evidence request against an existing evidence definition — an out-of-cycle ask for proof, on top of whatever the recurrence schedule generates. This operation takes NO request body at all: the client id and evidence id in the path are the entire input. Returns 201 with the created request's id (EvidenceResponse, e.g. {"id":101}); read the row back with scalepad_cm_list_client_evidence_requests for its code, status, owner and due date, then set assignee/status/due_date/notes with scalepad_cm_update_client_evidence_request. There is no documented idempotency key and nothing distinguishes two identical calls, so never blind-retry — a repeat creates a SECOND request.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id owning the evidence (client.id from scalepad_cm_list_clients_health).
evidenceIdstringyesThe evidence id to raise the request against (evidences.data[].id from scalepad_cm_search_client_evidences — the numeric id, not the EV-nn code).

[ScalePad] PERMANENTLY delete an evidence definition. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the evidence is not found. This removes the standing definition together with everything hanging off it — its recurrence schedule, every evidence request raised against it, and its mappings to objectives, controls and assessment questions — and ScalePad documents no soft delete, restore or undo. Confirm the exact client and the exact evidence (read it back with scalepad_cm_get_client_evidence, and check how many requests exist with scalepad_cm_list_client_evidence_requests) before calling. If the goal is only to stop FUTURE collection, use scalepad_cm_delete_client_evidence_schedule instead, which keeps the evidence record.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id owning the evidence (client.id from scalepad_cm_list_clients_health).
evidenceIdstringyesThe evidence id to delete (evidences.data[].id from scalepad_cm_search_client_evidences). Resolve and confirm this with the user before calling — the deletion is irreversible and takes the evidence's requests and mappings with it.

[ScalePad] Remove the RECURRING COLLECTION SCHEDULE from an evidence record — the evidence itself survives, but its recurrence configuration (frequency, interval, days, start/end) is gone and ScalePad will raise no further evidence requests from it. Succeeds with HTTP 204 and no body (this tool returns ). The required scheduleAction argument decides how far the deletion reaches, and one of its two values ALSO DELETES existing incomplete evidence requests, taking any partial collection work with them — always read the exact value back to the user before calling. There is no restore: re-establishing recurrence means PATCHing a fresh schedule with scalepad_cm_update_client_evidence (which is also the only way to CHANGE a schedule, since PATCH returns 409 while one still exists).

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id owning the evidence (client.id from scalepad_cm_list_clients_health).
evidenceIdstringyesThe evidence id whose schedule to remove (evidences.data[].id from scalepad_cm_search_client_evidences — the numeric id, not the EV-nn code).
scheduleActionstringyesREQUIRED by the vendor contract — exactly one of two literal strings, sent as the schedule_action query parameter. "Disable future evidence requests" removes only the recurrence, leaving every existing request (including incomplete ones) untouched. "Disable future evidence requests and delete existing requests" ALSO DELETES the existing incomplete requests, discarding whatever collection progress and notes they held — treat this as the stronger, doubly-confirmed choice. Any other value returns 400 ("schedule_action must be one of: ...").

[ScalePad] Get ONE evidence definition in full for ONE client. Returns the EvidenceDetailResponse shape: id, code (e.g. EV-1), title, description, repeats, created_by and owner (each {id, name}), schedule, refresh_status, created_at/updated_at, plus the complete relationship set — controls[{id, code, title}], assessments[{id, code, question}] and objectives[{id, code, name, program_name}]. The codes in those arrays are exactly the values scalepad_cm_map_client_evidence and scalepad_cm_unmap_client_evidence accept (objective_codes, control_codes and assessment_question_codes respectively). To see the collection cycles instead of the definition, call scalepad_cm_list_client_evidence_requests.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id owning the evidence (client.id from scalepad_cm_list_clients_health).
evidenceIdstringyesThe evidence id to read — the evidences.data[].id value from scalepad_cm_search_client_evidences. This is the numeric id, NOT the EV-nn business code; the vendor path type is integer and it validates it (400 "The evidence id in the request path must be a valid positive integer"), so pass a positive integer as a string (e.g. "1").

[ScalePad] List every evidence REQUEST raised against ONE evidence definition for ONE client — the individual collection cycles behind an EV-nn record. Returns {client {id, name, tenant_id}, evidence_id, data[]} where each row is {id, code (e.g. EV-2-1), status, created_by, owner, documents[{id, file_name}], due_date, implementation_notes, created_at, updated_at}. This endpoint takes NO paging, sort or filter parameters at all — the full set for that evidence comes back in one response. The row id is the evidence_request_id the ControlMap Evidence Requests tools and the evidence-request signed-URL tool take.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id owning the evidence (client.id from scalepad_cm_list_clients_health).
evidenceIdstringyesThe evidence id whose requests to list (evidences.data[].id from scalepad_cm_search_client_evidences — the numeric id, not the EV-nn code).

[ScalePad] Across EVERY client of the MSP — takes NO client id. Cursor-paginated evidence-collection roll-up for the whole client base: returns {data[], total_count, next_cursor} where each row is {client {id, name, tenant_id}, evidence_summary {completed, review, in_progress, not_started, not_applicable, total, completion_percentage}}. Keep paging until next_cursor is null. This tool returns COUNTS ONLY — it never lists individual evidence records; use scalepad_cm_search_client_evidences with a client id for the evidence rows.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque Base64 next_cursor from the previous page. Omit for the first page; never decode or manufacture one. Cursor scans are not atomic, so deduplicate by client.id.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together and sent as filter[field] query keys. Documented filters here are client.id, client.tenant_id and client.name, with operators eq and in; an omitted operator means eq. This is the ONLY *-summary endpoint in ControlMap that exposes a client.id filter — the governance, policies, procedures and risks summaries accept only client.tenant_id and client.name. Example: {"client.id":"in:365bcf1e-a26b-4abc-ad9a-8607e2f43910,365bcf1e-a26b-4abc-ad9a-8607e2f43911"}. An unsupported field returns 400.
pageSizeintegernonullMaximum clients per page, 1-200 (vendor default 50). Values above 200 are clamped rather than rejected; the vendor itself returns 400 for an over-limit page_size.
sortstringnonullSort expression; '-' prefix descends, '+' or no prefix ascends. Exactly ONE field is accepted — either client.tenant_id or client.name (no comma-separated multi-sort here). Example: '-client.name'.

[ScalePad] LINK an evidence definition to the compliance entities it proves. For evidence the linkable entities are exactly three kinds: framework objectives (requirements), controls and assessment questions — each identified by its BUSINESS CODE, never by numeric id, and resolved server-side (objective_codes to requirement.reqid, control_codes to control.display_code, assessment_question_codes to the assessment question's external id). Succeeds with HTTP 204 and no body (this tool returns ). Only the non-empty arrays you send are applied; empty or omitted arrays are ignored, so a body with no codes is accepted but does nothing — send at least one populated array. Mapping is additive: it never removes existing links (use scalepad_cm_unmap_client_evidence for that). Read current links from the objectives[], controls[] and assessments[] arrays of scalepad_cm_get_client_evidence. This tool sets EXPLICIT links; scalepad_cm_refresh_client_evidence_mappings re-derives the AUTOMATED ones.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body of three optional flat STRING arrays; include only the ones you want to link. objective_codes[] takes framework requirement codes such as "6.2" or "A.5.1" (from scalepad_cm_search_client_framework_objectives), control_codes[] takes control DISPLAY codes such as "HRM-1" or "ACC-2" (from scalepad_cm_search_client_controls), and assessment_question_codes[] takes assessment question external identifiers such as "Q-AST-01.2" (from scalepad_cm_search_client_assessment_questions). Note that although the vendor page title mentions only objectives and controls, the current schema explicitly includes assessment_question_codes. Example: {"objective_codes":["6.2","A.5.1"],"control_codes":["HRM-1","ACC-2"]}.
clientIdstringyesThe ControlMap client id owning the evidence (client.id from scalepad_cm_list_clients_health).
evidenceIdstringyesThe evidence id to link from (evidences.data[].id from scalepad_cm_search_client_evidences).

[ScalePad] Re-evaluate ALL automated evidence mappings for ONE client — a command, not a query, and it acts on the whole client at once rather than a single evidence record. It takes NO request body: the client id in the path is the entire input. ScalePad uses it to re-sync mappings after configuration changes, pull updated mapping definitions and recompute mapping relationships for compliance workflows. Returns 200 with {status, code, clientId, message}, e.g. {"status":"SUCCESS","code":"EVIDENCE_MAPPINGS_REFRESHED","clientId":"...","message":"Evidence mappings refreshed successfully."}; note the response spells the client key clientId in camelCase, unlike the client_id path parameter. It recomputes DERIVED mappings — it does not add or remove the explicit links you set with scalepad_cm_map_client_evidence, and it is not needed after a manual map/unmap. Unusually for this connector the operation declares no 404, so a bad client id surfaces as 400 INVALID_CLIENT.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id whose automated evidence mappings to recompute (client.id from scalepad_cm_list_clients_health). Every evidence record for this client is re-evaluated — there is no per-evidence variant.

[ScalePad] For ONE client, by client id: search that client's evidence definitions. This is a READ despite using HTTP POST — the verb only carries the query body and nothing is persisted; it replaced the retired GET /evidences list route. Returns client {id, name, tenant_id}, evidence_summary {completed, review, in_progress, not_started, not_applicable, total, completion_percentage}, and evidences {total_count, next_cursor, data[]}. Each row carries id, code (e.g. EV-1), title, description, repeats, created_by and owner, schedule, refresh_status (e.g. Current, Somewhat Current), created_date/updated_date, an evidence_request_summary keyed by status, and — when the evidence_request flag is on — an evidence_requests[] array of {id, code, title, description, status, created_by, owner, documents[], due_date, implementation_notes, timestamps}. For the whole client base use scalepad_cm_list_clients_evidences_summary instead, which takes NO client id.

ParamTypeRequiredDefaultDescription
bodyJsonstringnonullOptional JSON query body; the vendor requires a body but accepts and every property is optional, so omitting this returns the first unfiltered page. Properties: filter (object of field -> "operator:value"; the ONLY filterable fields here are code, title and owner.email, with operators eq and in — an omitted operator means eq), sort (exactly ONE of created_at or updated_at; '-' prefix descends, '+' or no prefix ascends, and no comma-separated multi-sort), fields (comma-separated sparse-field selector), page_size (1-200, vendor default 50), cursor (opaque Base64 next_cursor), and two flags: fetch_items (true includes the evidence item rows, false returns the summary only; documented default true) and evidence_request (includes evidence-request metadata on each row). The vendor documents evidence_request's default INCONSISTENTLY — its prose says false while its schema says true — so ALWAYS send it explicitly rather than relying on either. Example: {"filter":{"code":"in:EV-30,EV-31"},"fetch_items":true,"evidence_request":true,"sort":"-created_at","page_size":50}. Paging, filtering and sorting live ONLY in this body — this operation has no query-string parameters.
clientIdstringyesThe ControlMap client id whose evidence to search (client.id from scalepad_cm_list_clients_health).

[ScalePad] UNLINK an evidence definition from objectives, controls or assessment questions. The codes you send identify only the LINKS to remove — neither the evidence nor the mapped entities are deleted — but the relationship removal is a real state change with no undo, and dropping a link can leave a requirement showing as unevidenced, so echo the exact evidence and every code back to the user before calling. Succeeds with HTTP 204 and no body (this tool returns ); empty or omitted arrays make no changes, so reject an all-empty request rather than sending it. Despite the HTTP POST verb this is a removal: the vendor path is .../mappings/bulk-delete.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body naming the links to REMOVE — same three flat string arrays as the map tool: objective_codes[], control_codes[] and assessment_question_codes[]. Include only the codes to unlink; empty or omitted arrays result in no changes. Example: {"control_codes":["HRM-1"]}.
clientIdstringyesThe ControlMap client id owning the evidence (client.id from scalepad_cm_list_clients_health).
evidenceIdstringyesThe evidence id to unlink from (evidences.data[].id from scalepad_cm_search_client_evidences).

[ScalePad] Partially update an evidence definition (HTTP PATCH). Only the properties you send change; everything else is left alone. Unusually for a PATCH this returns HTTP 204 with NO body (this tool returns ) — read the result back with scalepad_cm_get_client_evidence if you need the updated record. IMPORTANT: an evidence can have only ONE schedule and this call CANNOT modify an existing one — sending a schedule for an evidence that already has one returns the declared 409 conflict. To change a recurrence, first remove the old schedule with scalepad_cm_delete_client_evidence_schedule and then PATCH the new one. To change the evidence's LINKS to objectives, controls or assessment questions use scalepad_cm_map_client_evidence / scalepad_cm_unmap_client_evidence; relationships are not patchable here.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body; every property is optional, send only what changes. Accepts title, description, owner (the owning user's EMAIL — note this is spelled owner here, not owner_email as on create) and schedule (the same EvidenceSchedule object create takes: frequency WEEKLY | MONTHLY | YEARLY, interval, days_of_week[], day_of_month, start_date, end_type on_date | after_occurrences, end_date, occurrences). Sending schedule when one already exists returns 409. Example: {"title":"SOC 2 Access Review Evidence (Revised)","owner":"joel@example.com","schedule":{"frequency":"WEEKLY","interval":1,"days_of_week":["monday","friday"],"start_date":"2025-11-01","end_type":"after_occurrences","occurrences":4}}.
clientIdstringyesThe ControlMap client id owning the evidence (client.id from scalepad_cm_list_clients_health).
evidenceIdstringyesThe evidence id to update (evidences.data[].id from scalepad_cm_search_client_evidences — the numeric id, not the EV-nn code).

ControlMap Evidence Requests

ToolPlanAccessSummary
scalepad_cm_archive_client_evidence_requestProDestructiveARCHIVE ONE evidence request — a ONE-WAY transition that removes it from the active collection workflow.
scalepad_cm_create_client_evidence_request_documentProWriteAttach ONE document you already hold to an EXISTING evidence request, uploading it through ScalePad in a single call (vendor multipart/form-data operation, one binary 'file' part).
scalepad_cm_create_client_evidence_requests_linksProWriteAttach a HYPERLINK to an evidence request instead of uploading a file — for proof that lives elsewhere (a shared drive folder, a dashboard, a ticket).
scalepad_cm_delete_client_evidence_requestProDestructivePERMANENTLY delete ONE evidence request.
scalepad_cm_update_client_evidence_requestProWritePartially update ONE evidence request for ONE client (HTTP PATCH).

[ScalePad] ARCHIVE ONE evidence request — a ONE-WAY transition that removes it from the active collection workflow. ScalePad publishes no unarchive or restore operation on this API, so the request stops appearing as outstanding work and cannot be returned to the active queue through the API; that irreversibility is why this is treated as destructive even though nothing is deleted (the request, its documents and its history remain readable). This operation takes NO request body: the client id and request id in the path are the entire input. Returns 200 with {id, code, status, message}, e.g. {"id":102,"code":1,"status":true,"message":"Evidence request archived successfully."}. Archiving an already-archived request is a no-op in effect, though the vendor still records audit metadata for the call — so it is safe to retry, unlike the create. Prefer setting status to Completed or Not Applicable with scalepad_cm_update_client_evidence_request when the request should stay in the workflow.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id owning the evidence request (client.id from scalepad_cm_list_clients_health).
evidenceRequestIdstringyesThe evidence request id to archive (a data[].id value from scalepad_cm_list_client_evidence_requests). Confirm it with the user before calling — there is no API operation to unarchive it.

[ScalePad] Attach ONE document you already hold to an EXISTING evidence request, uploading it through ScalePad in a single call (vendor multipart/form-data operation, one binary 'file' part). Unlike the evidence-level upload this creates NO new request — the document lands on the request you name. Returns 201 with {evidence_request_id, evidence_request_code, documents[{file_name, document_id}]}. The vendor caps each upload at 10 MB; because MCP has no binary parameter type the content is passed here as base64 and decoded into a real binary part before sending, so the base64 text is roughly a third larger than the file itself. Use THIS tool for a single modest file whose bytes you have. Use scalepad_cm_create_client_evidence_request_document_signed_url instead when you have several files, a large file, or a file the end user will upload themselves — that tool returns pre-signed URLs and the bytes never pass through StackJack. For a link rather than a file, use scalepad_cm_create_client_evidence_requests_links.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id owning the evidence request (client.id from scalepad_cm_list_clients_health).
evidenceRequestIdstringyesThe evidence request id to attach the document to (a data[].id value from scalepad_cm_list_client_evidence_requests). The document attaches to this existing request — no new request is created.
fileContentBase64stringyesThe complete file content, base64-encoded. Decoded size must be greater than zero (an empty file returns 400 "File size must be greater than 0") and no more than the vendor's 10 MB limit.
fileContentTypestringnonullOptional MIME type for the part, e.g. "application/pdf". ScalePad publishes no allowed-MIME list or restriction; omit it to let the client choose a default.
fileNamestringyesFile name to store, including its extension (e.g. "User_Access_Review_Q1_2026.pdf"). ScalePad does not publish a file-name length limit or an allowed-extension list.

[ScalePad] PERMANENTLY delete ONE evidence request. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the request is not found. The parent evidence definition survives, but this request's status, due date, reviewer notes, uploaded documents and hyperlinks go with it, and ScalePad documents no soft delete, restore or undo. Confirm the exact client and the exact request (read the row back with scalepad_cm_list_client_evidence_requests, including its documents[] array) before calling. If the goal is only to take a request out of the active workflow while keeping its history, use scalepad_cm_archive_client_evidence_request instead; to mark it as not required, set status to Not Applicable with scalepad_cm_update_client_evidence_request.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id owning the evidence request (client.id from scalepad_cm_list_clients_health).
evidenceRequestIdstringyesThe evidence request id to delete (a data[].id value from scalepad_cm_list_client_evidence_requests). Resolve and confirm this with the user before calling — the deletion is irreversible and takes the request's uploaded documents with it.

[ScalePad] Partially update ONE evidence request for ONE client (HTTP PATCH). Only the properties you send change; everything else is left alone. Unusually for a PATCH this returns HTTP 204 with NO body (this tool returns ) — read the result back with scalepad_cm_list_client_evidence_requests if you need the updated row. This is the tool for reassigning a request, moving it through its status workflow, shifting its due date and recording reviewer notes. It does NOT attach evidence: use scalepad_cm_create_client_evidence_request_document (or the signed-URL sibling) for a file, or scalepad_cm_create_client_evidence_requests_links for a hyperlink.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body; every property is optional, send only what changes. Accepts assigned_to (the assignee's EMAIL), status (exactly one of Not Started, In Progress, Review, Completed, Not Applicable), due_date (a DATE-TIME, e.g. 2026-01-05T00:00:00Z — not a bare date) and notes (reviewer/implementation notes as free text). Example: {"assigned_to":"joel@example.com","status":"In Progress","due_date":"2026-01-05T00:00:00Z","notes":"Reviewed by admin. Waiting for supporting documents."}.
clientIdstringyesThe ControlMap client id owning the evidence request (client.id from scalepad_cm_list_clients_health).
evidenceRequestIdstringyesThe evidence request id to update — a data[].id value from scalepad_cm_list_client_evidence_requests (or the evidence_request_id returned by scalepad_cm_create_client_evidence_request). This is the numeric id, NOT the EV-nn-n code; the vendor path type is integer, so pass it as a string (e.g. "102").

ControlMap Frameworks

ToolPlanAccessSummary
scalepad_cm_get_client_framework_objectiveFreeRead-onlyGet one framework objective (requirement) in full, for a single client and framework.
scalepad_cm_get_client_framework_objectives_summaryFreeRead-onlyFor ONE client, by client id: the objective compliance roll-up for every framework that client has enabled.
scalepad_cm_list_clients_framework_objectives_summaryFreeRead-onlyAcross EVERY client of the MSP — takes NO client id.
scalepad_cm_search_client_framework_objectivesFreeRead-onlyFor ONE client and ONE framework: search that framework's objectives (requirements).

[ScalePad] Get one framework objective (requirement) in full, for a single client and framework. Returns id, code, name, description (HTML), status, level1_code/level1_name and level2_code/level2_name (the requirement's place in the framework hierarchy), in_scope, type (clauses or controls), implementation_details (HTML), automated_test and automated_tested_on, current_maturity and target_maturity (each {id, name, description, score}), assessment_questions[] {id, code, title, answer}, audit_tests[] (each audit_name plus its evidence_requests[] with audit_result and a detailed_evaluation breakdown), plus created_at/updated_at. This fragment does not declare a 400, so an unknown id may surface as 404.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id (client.id from scalepad_cm_list_clients_health).
frameworkIdstringyesThe framework id the objective belongs to (framework_stats[].id from scalepad_cm_get_client_framework_objectives_summary). Vendor type is integer — pass it as a string, e.g. "3".
objectiveIdstringyesThe objective id to read — the objectives.data[].id value from scalepad_cm_search_client_framework_objectives. This is the numeric id, NOT the business code (e.g. "393", not "4.1"). Vendor type is integer.

[ScalePad] For ONE client, by client id: the objective compliance roll-up for every framework that client has enabled. Returns client {id, name, tenant_id} and framework_stats[] where each entry is {id, name, objective_summary {compliant, not_compliant, partially_compliant, not_assessed, in_review, not_applicable, total, complaint_percentage}} — complaint_percentage is the vendor's own misspelling of compliant. There is no pagination envelope and no query surface. This is the tool that gives you the framework ids for scalepad_cm_search_client_framework_objectives. Use scalepad_cm_list_clients_framework_objectives_summary instead when you want the same roll-up for every client at once.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id whose framework roll-up to read (client.id from scalepad_cm_list_clients_health).

[ScalePad] Across EVERY client of the MSP — takes NO client id. Cursor-paginated objective roll-up for the whole client base, for partner-level compliance dashboards. Returns {data[], total_count, next_cursor} where each row is {client {id, name, tenant_id}, framework_stats[] {id, name, objective_summary {compliant, not_compliant, partially_compliant, not_assessed, in_review, not_applicable, total, complaint_percentage}}}. Keep paging until next_cursor is null. For one client only, call scalepad_cm_get_client_framework_objectives_summary — it needs no paging.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque Base64 next_cursor from the previous page. Omit for the first page; never decode or manufacture one. Cursor scans are not atomic, so deduplicate by client.id.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together and sent as filter[field] query keys, e.g. {"client.name":"eq:Joel Tech"}. Documented filters here are exactly client.tenant_id and client.name, each supporting eq and in (an omitted operator means eq). Unlike the health list there is NO client.id filter on this endpoint.
pageSizeintegernonullMaximum clients per page, 1-200 (vendor default 50). Values above 200 are clamped rather than rejected.
sortstringnonullSort expression; '-' prefix descends, '+' or no prefix ascends. This endpoint documents exactly ONE sortable field per request: client.name or client.tenant_id. Example: '-client.name'.

[ScalePad] For ONE client and ONE framework: search that framework's objectives (requirements). This is a READ despite using HTTP POST — the verb only carries the query body and nothing is persisted (vendor operationId 01_searchClientObjectives). Returns client {id, name, tenant_id}, framework {id, name}, objective_summary {compliant, not_compliant, partially_compliant, not_assessed, in_review, not_applicable, total, complaint_percentage — the misspelling is the vendor's own field name}, and objectives {total_count, next_cursor, data[]}. Each objective row carries id, code, name, description (HTML), status, level1_code/level1_name, level2_code/level2_name, in_scope, type (clauses or controls), created_at, updated_at and the linked documents/evidences. Use the row id with scalepad_cm_get_client_framework_objective for the full detail, and the row code as an objective_codes / objectives.codes value when mapping controls, policies, procedures, risks or action items.

ParamTypeRequiredDefaultDescription
bodyJsonstringnonullOptional JSON query body; the vendor requires a body but accepts and every property is optional, so omitting this returns the first unfiltered page. Properties: filter (object of field -> "operator:value"; documented fields are status and in_scope, each supporting eq and in, with status values Not Assessed, Not Compliant, Partially Compliant, In Review, Compliant and Not Applicable), page_size (1-200, vendor default 50), cursor (opaque Base64 next_cursor), and sort (ONE field only; the vendor prose documents name and req_id while the schema regex also permits id and sort_order — prefer name or req_id, '-' prefix descends). Example: {"filter":{"status":"in:Not Assessed,In Review","in_scope":"eq:true"},"page_size":50,"sort":"+name"}.
clientIdstringyesThe ControlMap client id (client.id from scalepad_cm_list_clients_health).
frameworkIdstringyesThe compliance framework id to search within — the framework_stats[].id value from scalepad_cm_get_client_framework_objectives_summary (also the frameworks[].framework_id in the health snapshot). Declared as an integer by the vendor, so pass the numeric id as a string (e.g. "3").

ControlMap Governance

ToolPlanAccessSummary
scalepad_cm_create_client_governanceProWriteCreate a governance document for ONE client.
scalepad_cm_delete_client_governanceProDestructivePERMANENTLY delete a governance document.
scalepad_cm_get_client_governanceFreeRead-onlyGet ONE governance document in full for ONE client.
scalepad_cm_list_clients_governance_summaryFreeRead-onlyAcross EVERY client of the MSP — takes NO client id.
scalepad_cm_map_client_governanceProWriteLINK a governance document to the compliance entities it governs.
scalepad_cm_search_client_governanceFreeRead-onlyFor ONE client, by client id: search that client's governance documents.
scalepad_cm_unmap_client_governanceProDestructiveUNLINK a governance document from objectives, policies or controls.
scalepad_cm_update_client_governanceProWritePartially update a governance document (HTTP PATCH).

[ScalePad] Create a governance document for ONE client. Returns 201 with the full ProcedureDetailResponse. ScalePad assigns the authenticated API user as both owner and creator, starts the document in status In Review, and sets the default review date one week after creation — you cannot override those in the create body; adjust them afterwards with scalepad_cm_update_client_governance. There is no documented idempotency key, so never blind-retry this call — re-check with scalepad_cm_search_client_governance first. The create body takes no mappings you can rely on; link objectives, policies and controls afterwards with scalepad_cm_map_client_governance.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. Send ONLY title and description: title is the document name (minLength 1) and description is its body, which the vendor documents as "HTML or plain description" (e.g. "<p>Defines scope and controls.</p>"). The vendor schema contradicts its own prose here — the ProcedureCreateRequest schema exposes exactly title and description with NO formal required array, while the schema's own description text says "Required: name" and mentions an optional nested mappings object; neither name nor mappings is an actual property. Do not send name, source or mappings until ScalePad resolves the contradiction. Example: {"title":"Management review governance","description":"<p>Defines scope and controls.</p>"}.
clientIdstringyesThe ControlMap client id to create the governance document under (client.id from scalepad_cm_list_clients_health).

[ScalePad] PERMANENTLY delete a governance document. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the document is not found. ScalePad documents no soft delete, restore or undo for compliance documents, and the document's HTML body, attached files, hyperlinks, tags and its mappings to objectives/policies/controls go with it. Confirm the exact client and the exact document (read it back with scalepad_cm_get_client_governance first) before calling, and prefer moving it to a Draft status via scalepad_cm_update_client_governance when the goal is only to take it out of the active review workflow.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id owning the governance document (client.id from scalepad_cm_list_clients_health).
governanceIdstringyesThe governance document id to delete (governance.data[].id from scalepad_cm_search_client_governance). Resolve and confirm this with the user before calling — the deletion is irreversible.

[ScalePad] Get ONE governance document in full for ONE client. Returns the ProcedureDetailResponse shape: id, code, title, description (the document's HTML body), source, status, frequency, owner and last_approved_by (each {id, name}), contributors[], data_classification, review_date, last_approved_date, created_at/updated_at, documents[{id, filename, signed_url, expires_at}], hyperlinks[{name, hyperlink}], tags[], controls[] and objectives[{id, title, code, program_name}]. The codes in controls[] and objectives[] are exactly the values scalepad_cm_map_client_governance and scalepad_cm_unmap_client_governance accept. Unlike policies there is NO sections[] array — a governance document's body is the single description field.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id owning the governance document (client.id from scalepad_cm_list_clients_health).
governanceIdstringyesThe governance document id to read — the governance.data[].id value from scalepad_cm_search_client_governance. This is the numeric id, NOT the GOV-nn business code; the vendor path type is integer, so pass it as a string (e.g. "24").

[ScalePad] Across EVERY client of the MSP — takes NO client id. Cursor-paginated governance-document roll-up for the whole client base: returns {data[], total_count, next_cursor} where each row is {client {id, name, tenant_id}, documents_summary {approved, in_review, ready_for_approval, in_progress, draft, approved_percentage, total}}. Keep paging until next_cursor is null. This tool returns COUNTS ONLY — it never lists individual governance documents; use scalepad_cm_search_client_governance with a client id for the document rows.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque Base64 next_cursor from the previous page. Omit for the first page; never decode or manufacture one. Cursor scans are not atomic, so deduplicate by client.id.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together and sent as filter[field] query keys. Documented filters here are exactly client.tenant_id and client.name (there is NO client.id filter on this endpoint — only the evidences summary has one), with operators eq and in; an omitted operator means eq. Example: {"client.tenant_id":"in:joel1,joel2"}. An unsupported field returns 400 ("Filtering is not supported for the field ...").
pageSizeintegernonullMaximum clients per page, 1-200 (vendor default 50). Values above 200 are clamped rather than rejected; the vendor itself returns 400 ("page size must be less than or equal to 200") for an over-limit page_size.
sortstringnonullSort expression; '-' prefix descends, '+' or no prefix ascends. Exactly ONE field is accepted — either client.tenant_id or client.name (a comma-separated multi-sort is NOT supported here, unlike the health and action-item summaries). Example: '-client.name'.

[ScalePad] LINK a governance document to the compliance entities it governs. For a governance document the linkable entities are framework objectives (requirements, scoped by compliance program), policies and controls — each identified by its BUSINESS CODE, never by numeric id, and resolved server-side. Succeeds with HTTP 204 and no body (this tool returns ). Only the non-empty arrays you send are applied; empty or omitted arrays are ignored, so a body with no codes is accepted but does nothing — send at least one populated array. Mapping is additive: it never removes existing links (use scalepad_cm_unmap_client_governance for that). Read current links from the controls[] and objectives[] arrays of scalepad_cm_get_client_governance.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with three optional arrays; include only the ones you want to link. objectives[] is an array of OBJECTS, not strings — each is {"program_name": "<compliance program display name>", "codes": ["<requirement business code>", ...]}, e.g. {"program_name":"ISO-27001:2022","codes":["4.1"]}. policy_codes[] takes policy business codes (e.g. "POL-001", from scalepad_cm_search_client_policies) and control_codes[] takes control DISPLAY codes (e.g. "AC-1", from scalepad_cm_search_client_controls). Governance and procedure documents share the same document-code space. Example: {"objectives":[{"program_name":"ISO-27001:2022","codes":["4.1","7.4"]}],"control_codes":["AC-1"]}.
clientIdstringyesThe ControlMap client id owning the governance document (client.id from scalepad_cm_list_clients_health).
governanceIdstringyesThe governance document id to link from (governance.data[].id from scalepad_cm_search_client_governance).

[ScalePad] For ONE client, by client id: search that client's governance documents. This is a READ despite using HTTP POST — the verb only carries the query body and nothing is persisted. Returns client {id, name, tenant_id}, documents_summary {approved, in_review, ready_for_approval, in_progress, draft, approved_percentage, total}, and governance {total_count, next_cursor, data[]}. Each row carries id, code (e.g. GOV-1), title, owner and created_by (each {id, name, email}), status, source (e.g. "html"), frequency, contributors[], data_classification, tags[], controls[{id, title, code}] and objectives[{id, title, code, program_name}]. For the whole client base use scalepad_cm_list_clients_governance_summary instead, which takes NO client id.

ParamTypeRequiredDefaultDescription
bodyJsonstringnonullOptional JSON query body; the vendor requires a body but accepts and every property is optional, so omitting this returns the first unfiltered page. Properties: filter (object of field -> "operator:value"; the ONLY filterable fields here are code, title and status, with operators eq and in — an omitted operator means eq), sort (exactly ONE of code or title; '-' prefix descends, '+' or no prefix ascends — this endpoint does NOT accept a comma-separated multi-sort), fields (comma-separated sparse-field selector), page_size (1-200, vendor default 50) and cursor (opaque Base64 next_cursor). Document status values are Draft, In Progress, In Review, Ready For Approval and Approved. Example: {"filter":{"status":"in:Draft,In Review"},"sort":"code","page_size":50}. Paging, filtering and sorting live ONLY in this body — this operation has no query-string parameters.
clientIdstringyesThe ControlMap client id whose governance documents to search (client.id from scalepad_cm_list_clients_health).

[ScalePad] UNLINK a governance document from objectives, policies or controls. The codes you send identify only the LINKS to remove — neither the governance document nor the mapped entities are deleted — but the relationship removal is a real state change with no undo, so echo the exact document and every code back to the user before calling. Succeeds with HTTP 204 and no body (this tool returns ); empty or omitted arrays make no changes, so reject an all-empty request rather than sending it. Despite the HTTP POST verb this is a removal: the vendor path is .../mappings/bulk-delete.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body naming the links to REMOVE — same shape as the map tool: objectives[] as objects {"program_name", "codes":[...]}, plus policy_codes[] and control_codes[] as plain business-code strings. Include only the codes to unlink; empty or omitted arrays result in no changes. Example: {"control_codes":["AC-1"]}.
clientIdstringyesThe ControlMap client id owning the governance document (client.id from scalepad_cm_list_clients_health).
governanceIdstringyesThe governance document id to unlink from (governance.data[].id from scalepad_cm_search_client_governance).

[ScalePad] Partially update a governance document (HTTP PATCH). Only the properties you send change; everything else is left alone. Returns 200 with the patched record (id, code, name, description, status, source, data_classification, frequency, owner and the rest of the document metadata). At least one field is required. When status is included the vendor applies it AFTER the other fields in the same request. tags and contributors REPLACE the current lists rather than appending — send an empty array to clear one. This is the tool for status transitions and for editing the HTML body. To change the document's LINKS to objectives, policies or controls use scalepad_cm_map_client_governance / scalepad_cm_unmap_client_governance instead; relationships are not patchable here.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body; every property is optional but at least one must be present. Accepts title (document title), description (document body / HTML), code (business code), status (exactly one of Draft, In Progress, In Review, Ready For Approval, Approved), data_classification (label), review_date (ISO-8601 instant, e.g. 2026-05-04T10:15:00.000Z), owner and approver (each a user EMAIL or an exact display name, matched case-insensitively), team (team name), tags[] (tag names — REPLACES the current tag set) and contributors[] (user emails — REPLACES the current contributor list). Example: {"status":"Approved","approver":"john.doe@example.com","review_date":"2027-01-30T09:19:42.000Z"}.
clientIdstringyesThe ControlMap client id owning the governance document (client.id from scalepad_cm_list_clients_health).
governanceIdstringyesThe governance document id to update (governance.data[].id from scalepad_cm_search_client_governance — the numeric id, not the GOV-nn code).

ControlMap Health

ToolPlanAccessSummary
scalepad_cm_get_client_healthFreeRead-onlyFor ONE client, by client id: the full ControlMap compliance health snapshot.
scalepad_cm_list_clients_healthFreeRead-onlyAcross EVERY client of the MSP — takes NO client id.

[ScalePad] For ONE client, by client id: the full ControlMap compliance health snapshot. Returns a single object (no pagination envelope) carrying client {id, name, tenant_id}, compliance_score {overall_score, score_label, updated_at, trend {last_30_days, last_60_days, last_90_days}}, risk_score {overall_score, risk_level, updated_at, risk_breakdown {severe, high, medium, low}}, frameworks[] (each with framework_id, framework_name, compliance_score, score_label, compliance_breakdown {compliant, in_review, not_applicable, not_assessed, not_compliant, partially_compliant, compliance_achieved_percentage, total} and assessment_breakdown {yes, no, partially, not_answered, not_applicable, answering_percentage, total}), and work_progress {evidence {completed, review, in_progress, not_started, not_applicable, completion_percentage, total}, updated_at}. All scores reflect the most recently computed values, not a live recalculation. Use this when you need one client's complete snapshot; for several clients at once, scalepad_cm_list_clients_health with its fields parameter set returns the same components in a single paginated pass.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id (the client.id value returned by scalepad_cm_list_clients_health; a GUID-like string in the vendor examples). An unknown id returns 400 INVALID_CLIENT rather than 404.

[ScalePad] Across EVERY client of the MSP — takes NO client id. Cursor-paginated compliance health for the whole client base: returns {data[], total_count, next_cursor} where next_cursor is null on the final page (keep paging until it is null, not until a page looks short). Each row carries client {id, name, tenant_id} plus that client's health metrics, and the client.id values are exactly what you pass to scalepad_cm_get_client_health and to every other per-client ControlMap tool. IMPORTANT: by default a row carries the client's OVERALL compliance health ONLY — the richer compliance_score, risk_score, frameworks and work_progress components are additive and must be requested through the fields parameter. Use fields to pull the metrics you need for the whole client base in one pass, rather than calling scalepad_cm_get_client_health once per client.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque Base64 next_cursor from the previous page. Omit for the first page; never decode, edit or manufacture one. Cursor scans are not atomic — concurrent changes can skip or repeat rows, so deduplicate by client.id.
fieldsstringnonullComma-separated list of ADDITIONAL health components to include in every row — this parameter EXPANDS the response, it does not narrow it. Documented components: compliance_score, risk_score, frameworks, work_progress. Example: 'compliance_score,risk_score'. Omit it and each row carries the client's overall compliance health only. The vendor's endpoint description also mentions 'history', but the parameter's own documentation lists only the four above, so treat history as unsupported until ScalePad resolves that discrepancy.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together and sent as filter[field] query keys, e.g. {"client.name":"eq:ABC Corporation"} or {"client.id":"in:id-1,id-2"}. Documented filters here are exactly client.id, client.tenant_id and client.name, each supporting eq and in only (an omitted operator means eq). Any other field returns 400.
pageSizeintegernonullMaximum clients per page, 1-200 (vendor default 50). Values above 200 are clamped rather than rejected.
sortstringnonullComma-separated sort expression; '-' prefix descends, '+' or no prefix ascends. This endpoint accepts multi-sort. Sortable: client.name, client.tenant_id, client.id. Example: '+client.name,-client.tenant_id'. An unsupported field returns 400.

ControlMap Policies

ToolPlanAccessSummary
scalepad_cm_create_client_policyProWriteCreate a policy for ONE client, optionally with its initial sections in order.
scalepad_cm_delete_client_policyProDestructivePERMANENTLY delete an entire policy, including ALL of its sections.
scalepad_cm_delete_client_policy_sectionProDestructivePERMANENTLY delete ONE section of a policy — the section's title and its entire HTML body are lost.
scalepad_cm_get_client_policyFreeRead-onlyGet ONE policy in full for ONE client.
scalepad_cm_list_clients_policies_summaryFreeRead-onlyAcross EVERY client of the MSP — takes NO client id.
scalepad_cm_map_client_policyProWriteLINK a policy to the compliance entities it satisfies.
scalepad_cm_search_client_policiesFreeRead-onlyFor ONE client, by client id: search that client's policies.
scalepad_cm_unmap_client_policyProDestructiveUNLINK a policy from objectives or controls.
scalepad_cm_update_client_policyProWritePartially update a policy's METADATA (HTTP PATCH).
scalepad_cm_update_client_policy_sectionsProWriteCreate OR update a single section of a policy — an upsert (HTTP PUT on the sections collection), and the ONLY way to edit a policy's prose, because the policy PATCH schema has no description field.

[ScalePad] Create a policy for ONE client, optionally with its initial sections in order. Returns 201 with the full PolicyDetailResponse. ScalePad assigns the authenticated API user as both owner and creator, starts the policy in status In Review, and sets the default review date one week after creation — you cannot override those in the create body; adjust them afterwards with scalepad_cm_update_client_policy. There is no documented idempotency key, so never blind-retry this call — re-check with scalepad_cm_search_client_policies first. Link objectives and controls afterwards with scalepad_cm_map_client_policy; the create body takes no mappings.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. The schema exposes exactly three properties and marks none formally required, though title has minLength 1 so send it: title (the policy name), policy_template_name (the ScalePad policy template to base it on, e.g. "User policy") and sections[] (the initial body sections, PERSISTED IN THE ORDER GIVEN, each {title, description} where description is the section body and HTML is allowed). Pass HTML as a plain string — ControlMap policies use an HTML source, not a rich-text JSON document. Example: {"title":"Acceptable Use Policy","policy_template_name":"User policy","sections":[{"title":"Purpose","description":"<p>Define intent.</p>"},{"title":"Scope","description":"<p>All employees.</p>"}]}.
clientIdstringyesThe ControlMap client id to create the policy under (client.id from scalepad_cm_list_clients_health).

[ScalePad] PERMANENTLY delete an entire policy, including ALL of its sections. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the policy is not found. ScalePad documents no soft delete, restore or undo for compliance documents, and the policy's sections, version history, attached files, hyperlinks, tags and its mappings to objectives/controls go with it. Confirm the exact client and the exact policy (read it back with scalepad_cm_get_client_policy first) before calling. If you only need to remove one part of the body use scalepad_cm_delete_client_policy_section instead; if you only need it out of the active review workflow, move it to a Draft status with scalepad_cm_update_client_policy.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id owning the policy (client.id from scalepad_cm_list_clients_health).
policyIdstringyesThe policy id to delete (policies.data[].id from scalepad_cm_search_client_policies). Resolve and confirm this with the user before calling — the deletion is irreversible and takes every section with it.

[ScalePad] PERMANENTLY delete ONE section of a policy — the section's title and its entire HTML body are lost. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the policy or section is not found. The policy itself and its other sections survive; ScalePad documents no restore or undo for a deleted section, so read the section back with scalepad_cm_get_client_policy and echo its title (and, if the user may want it later, its description) before calling. To reword a section instead of losing it, update it in place with scalepad_cm_update_client_policy_sections.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id owning the policy (client.id from scalepad_cm_list_clients_health).
policyIdstringyesThe policy id that owns the section (policies.data[].id from scalepad_cm_search_client_policies).
sectionIdstringyesThe section id to delete — the sections[].id value from scalepad_cm_get_client_policy (e.g. "9001"). This is NOT the section_order; confirm you have the id of the section the user means before calling, because the deletion is irreversible.

[ScalePad] Get ONE policy in full for ONE client. Returns the PolicyDetailResponse shape: id, code, title, status, source, frequency, owner and last_approved_by (each {id, name}), policy_contributors[], data_classification, review_date, last_approved_date, created_at/updated_at, documents[{id, filename, signed_url, expires_at}], hyperlinks[{name, hyperlink}], tags[], versions[{major_version, minor_version}] plus the current major_version/minor_version, is_published, controls[{id, title, code}], objectives[{id, title, code, program_name}] and — uniquely for policies — sections[{id, title, description, section_order, created_at, updated_at}] holding the HTML body in order. sections[].id is the value scalepad_cm_update_client_policy_sections uses to update a section and scalepad_cm_delete_client_policy_section uses to remove one; the control and objective codes are what the mapping tools accept.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id owning the policy (client.id from scalepad_cm_list_clients_health).
policyIdstringyesThe policy id to read — the policies.data[].id value from scalepad_cm_search_client_policies. This is the numeric id, NOT the POL-nn business code; the vendor path type is integer, so pass it as a string (e.g. "1").

[ScalePad] Across EVERY client of the MSP — takes NO client id. Cursor-paginated policy roll-up for the whole client base: returns {data[], total_count, next_cursor} where each row is {client {id, name, tenant_id}, documents_summary {approved, in_review, ready_for_approval, in_progress, draft, approved_percentage, total}}. Keep paging until next_cursor is null. This tool returns COUNTS ONLY — it never lists individual policies; use scalepad_cm_search_client_policies with a client id for the document rows.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque Base64 next_cursor from the previous page. Omit for the first page; never decode or manufacture one. Cursor scans are not atomic, so deduplicate by client.id.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together and sent as filter[field] query keys. Documented filters here are exactly client.tenant_id and client.name (there is NO client.id filter on this endpoint — only the evidences summary has one), with operators eq and in; an omitted operator means eq. Example: {"client.tenant_id":"in:joel1,joel2"}. An unsupported field returns 400 ("Filtering is not supported for the field ...").
pageSizeintegernonullMaximum clients per page, 1-200 (vendor default 50). Values above 200 are clamped rather than rejected; the vendor itself returns 400 ("page size must be less than or equal to 200") for an over-limit page_size.
sortstringnonullSort expression; '-' prefix descends, '+' or no prefix ascends. Exactly ONE field is accepted — either client.tenant_id or client.name (no comma-separated multi-sort here, unlike the health and action-item summaries). Example: '-client.name'.

[ScalePad] LINK a policy to the compliance entities it satisfies. For a policy the linkable entities are exactly TWO kinds: framework objectives (requirements, scoped by compliance program) and controls — identified by BUSINESS CODE, never by numeric id, and resolved server-side. There is deliberately no policy_codes array here (policies do not map to other policies) even though the procedure and governance mapping bodies have one. Succeeds with HTTP 204 and no body (this tool returns ). Only the non-empty arrays you send are applied; empty or omitted arrays are ignored, so a body with no codes is accepted but does nothing — send at least one populated array. Mapping is additive: it never removes existing links (use scalepad_cm_unmap_client_policy for that). Read current links from the controls[] and objectives[] arrays of scalepad_cm_get_client_policy.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with two optional arrays. objectives[] is an array of OBJECTS, not strings — each is {"program_name": "<compliance program display name>", "codes": ["<requirement business code>", ...]}, e.g. {"program_name":"ISO-27001:2022","codes":["4.1"]}. control_codes[] takes control DISPLAY codes (e.g. "AC-1", "CC-2", from scalepad_cm_search_client_controls). Example: {"objectives":[{"program_name":"ISO-27001:2022","codes":["A8.1.3"]}],"control_codes":["AC-1","CC-2"]}.
clientIdstringyesThe ControlMap client id owning the policy (client.id from scalepad_cm_list_clients_health).
policyIdstringyesThe policy id to link from (policies.data[].id from scalepad_cm_search_client_policies).

[ScalePad] For ONE client, by client id: search that client's policies. This is a READ despite using HTTP POST — the verb only carries the query body and nothing is persisted. Returns client {id, name, tenant_id}, documents_summary {approved, in_review, ready_for_approval, in_progress, draft, approved_percentage, total}, and policies {total_count, next_cursor, data[]}. Each row carries id, code (e.g. POL-1), title, owner and created_by (each {id, name, email}), status, source (e.g. "html"), frequency, contributors[], data_classification, tags[], controls[{id, title, code}] and objectives[{id, title, code, program_name}]. The search rows do NOT include sections — fetch a single policy with scalepad_cm_get_client_policy for its section bodies. For the whole client base use scalepad_cm_list_clients_policies_summary instead, which takes NO client id.

ParamTypeRequiredDefaultDescription
bodyJsonstringnonullOptional JSON query body; the vendor requires a body but accepts and every property is optional, so omitting this returns the first unfiltered page. Properties: filter (object of field -> "operator:value"; the ONLY filterable fields here are code, owner.name and status — note owner.NAME, not owner.email as on the evidence and risk searches — with operators eq and in, an omitted operator meaning eq), sort (exactly ONE of code or created_date — note created_DATE here, not created_at; '-' prefix descends, '+' or no prefix ascends, and no comma-separated multi-sort), fields (comma-separated sparse-field selector), page_size (1-200, vendor default 50) and cursor (opaque Base64 next_cursor). Document status values are Draft, In Progress, In Review, Ready For Approval and Approved. Example: {"filter":{"status":"eq:Draft","owner.name":"Joel King"},"sort":"-created_date"}. Paging, filtering and sorting live ONLY in this body — this operation has no query-string parameters.
clientIdstringyesThe ControlMap client id whose policies to search (client.id from scalepad_cm_list_clients_health).

[ScalePad] UNLINK a policy from objectives or controls. The codes you send identify only the LINKS to remove — neither the policy nor the mapped objectives/controls are deleted — but the relationship removal is a real state change with no undo, so echo the exact policy and every code back to the user before calling. Succeeds with HTTP 204 and no body (this tool returns ); empty or omitted arrays make no changes, so reject an all-empty request rather than sending it. Despite the HTTP POST verb this is a removal: the vendor path is .../mappings/bulk-delete.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body naming the links to REMOVE — same shape as the map tool: objectives[] as objects {"program_name", "codes":[...]} and control_codes[] as plain control display codes. Include only the codes to unlink; empty or omitted arrays result in no changes. Example: {"control_codes":["AC-1"]}.
clientIdstringyesThe ControlMap client id owning the policy (client.id from scalepad_cm_list_clients_health).
policyIdstringyesThe policy id to unlink from (policies.data[].id from scalepad_cm_search_client_policies).

[ScalePad] Partially update a policy's METADATA (HTTP PATCH). Only the properties you send change; everything else is left alone. Returns 200 with the patched record (id, code, name, status, source, data_classification, frequency, owner and the rest of the document metadata). When status is included the vendor applies it AFTER the other fields in the same request. tags and contributors REPLACE the current lists rather than appending — send an empty array to clear one. IMPORTANT: unlike procedures and governance documents the policy PATCH schema has NO description field — a policy's prose lives in its sections, so use scalepad_cm_update_client_policy_sections to change the body. Relationships are likewise not patchable here; use scalepad_cm_map_client_policy / scalepad_cm_unmap_client_policy.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body; every property is optional, send only what changes. Accepts title, code (business code), status (exactly one of Draft, In Progress, In Review, Ready For Approval, Approved), data_classification, review_date (ISO-8601 instant, e.g. 2026-05-04T10:15:00.000Z), owner and approver (each a user EMAIL or an exact display name, matched case-insensitively), team, tags[] (tag names — REPLACES the current tag set) and contributors[] (user emails — REPLACES the current contributor list). There is deliberately no description property. Example: {"status":"Approved","approver":"john.doe@example.com","tags":["SOC2","ISO27001"]}.
clientIdstringyesThe ControlMap client id owning the policy (client.id from scalepad_cm_list_clients_health).
policyIdstringyesThe policy id to update (policies.data[].id from scalepad_cm_search_client_policies — the numeric id, not the POL-nn code).

[ScalePad] Create OR update a single section of a policy — an upsert (HTTP PUT on the sections collection), and the ONLY way to edit a policy's prose, because the policy PATCH schema has no description field. Which operation happens is decided by whether the body carries an id: omit id to CREATE a new section (returns 201) and include id to UPDATE an existing one (returns 200). Either way the response is the PolicySectionDetail for that one section (id, title, description, section_order, created_at, updated_at). Sections are the only sectioned record type in ControlMap — procedures and governance documents have none. This tool handles ONE section per call; loop for several. Read the current sections, their ids and their section_order from scalepad_cm_get_client_policy.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with snake_case fields id (optional), title and description. The validation is CONDITIONAL, which is why the schema carries no static required array: to CREATE, omit id — then title is required and description is optional; to UPDATE, send the existing section's id — then at least one of title or description must be present. description is the section body and HTML is allowed; pass it as a plain HTML string (ControlMap policies use an HTML source, not a rich-text JSON document). Create example: {"title":"Purpose","description":"<p>Define intent.</p>"}. Update example: {"id":9001,"title":"Purpose and scope","description":"<p>Updated body.</p>"}.
clientIdstringyesThe ControlMap client id owning the policy (client.id from scalepad_cm_list_clients_health).
policyIdstringyesThe policy id whose section is being created or updated (policies.data[].id from scalepad_cm_search_client_policies).

ControlMap Procedures

ToolPlanAccessSummary
scalepad_cm_create_client_procedureProWriteCreate a procedure for ONE client.
scalepad_cm_delete_client_procedureProDestructivePERMANENTLY delete a procedure.
scalepad_cm_get_client_procedureFreeRead-onlyGet ONE procedure in full for ONE client.
scalepad_cm_list_clients_procedures_summaryFreeRead-onlyAcross EVERY client of the MSP — takes NO client id.
scalepad_cm_map_client_procedureProWriteLINK a procedure to the compliance entities it implements.
scalepad_cm_search_client_proceduresFreeRead-onlyFor ONE client, by client id: search that client's procedures.
scalepad_cm_unmap_client_procedureProDestructiveUNLINK a procedure from objectives, policies or controls.
scalepad_cm_update_client_procedureProWritePartially update a procedure (HTTP PATCH).

[ScalePad] Create a procedure for ONE client. Returns 201 with the full ProcedureDetailResponse. ScalePad assigns the authenticated API user as both owner and creator, starts the document in status In Review, and sets the default review date one week after creation — you cannot override those in the create body; adjust them afterwards with scalepad_cm_update_client_procedure. There is no documented idempotency key, so never blind-retry this call — re-check with scalepad_cm_search_client_procedures first. Link objectives, policies and controls afterwards with scalepad_cm_map_client_procedure rather than trying to embed them here.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. Send ONLY title and description: title is the procedure name (minLength 1) and description is its body, documented as "HTML or plain description" (e.g. "<p>Defines scope and controls.</p>"). The vendor schema contradicts its own prose here — the ProcedureCreateRequest schema exposes exactly title and description with NO formal required array, while the schema's own description text says "Required: name" and refers to optional source and nested mappings; none of name, source or mappings is an actual property, so do not send them until ScalePad resolves the contradiction. Example: {"title":"Procedure for control of documented information","description":"<p>Steps for versioning and retention.</p>"}.
clientIdstringyesThe ControlMap client id to create the procedure under (client.id from scalepad_cm_list_clients_health).

[ScalePad] PERMANENTLY delete a procedure. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the procedure is not found. ScalePad documents no soft delete, restore or undo for compliance documents, and the procedure's HTML body, attached files, hyperlinks, tags and its mappings to objectives/policies/controls go with it. Confirm the exact client and the exact procedure (read it back with scalepad_cm_get_client_procedure first) before calling, and prefer moving it to a Draft status via scalepad_cm_update_client_procedure when the goal is only to take it out of the active review workflow.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id owning the procedure (client.id from scalepad_cm_list_clients_health).
procedureIdstringyesThe procedure id to delete (procedures.data[].id from scalepad_cm_search_client_procedures). Resolve and confirm this with the user before calling — the deletion is irreversible.

[ScalePad] Get ONE procedure in full for ONE client. Returns the ProcedureDetailResponse shape: id, code, title, description (the procedure's HTML body), source, status, frequency, owner and last_approved_by (each {id, name}), contributors[], data_classification, review_date, last_approved_date, created_at/updated_at, documents[{id, filename, signed_url, expires_at}], hyperlinks[{name, hyperlink}], tags[], controls[] and objectives[{id, title, code, program_name}]. The codes in controls[] and objectives[] are exactly the values scalepad_cm_map_client_procedure and scalepad_cm_unmap_client_procedure accept. Unlike policies there is NO sections[] array — a procedure's body is the single description field.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id owning the procedure (client.id from scalepad_cm_list_clients_health).
procedureIdstringyesThe procedure id to read — the procedures.data[].id value from scalepad_cm_search_client_procedures. This is the numeric id, NOT the PRO-nn business code; the vendor path type is integer, so pass it as a string (e.g. "23").

[ScalePad] Across EVERY client of the MSP — takes NO client id. Cursor-paginated procedure roll-up for the whole client base: returns {data[], total_count, next_cursor} where each row is {client {id, name, tenant_id}, documents_summary {approved, in_review, ready_for_approval, in_progress, draft, approved_percentage, total}}. Keep paging until next_cursor is null. This tool returns COUNTS ONLY — it never lists individual procedures; use scalepad_cm_search_client_procedures with a client id for the document rows.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque Base64 next_cursor from the previous page. Omit for the first page; never decode or manufacture one. Cursor scans are not atomic, so deduplicate by client.id.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together and sent as filter[field] query keys. Documented filters here are exactly client.tenant_id and client.name (there is NO client.id filter on this endpoint — only the evidences summary has one), with operators eq and in; an omitted operator means eq. Example: {"client.tenant_id":"in:joel1,joel2"}. An unsupported field returns 400 ("Filtering is not supported for the field ...").
pageSizeintegernonullMaximum clients per page, 1-200 (vendor default 50). Values above 200 are clamped rather than rejected; the vendor itself returns 400 ("page size must be less than or equal to 200") for an over-limit page_size.
sortstringnonullSort expression; '-' prefix descends, '+' or no prefix ascends. Exactly ONE field is accepted — either client.tenant_id or client.name (no comma-separated multi-sort here, unlike the health and action-item summaries). Example: '-client.name'.

[ScalePad] LINK a procedure to the compliance entities it implements. For a procedure the linkable entities are framework objectives (requirements, scoped by compliance program), policies and controls — each identified by its BUSINESS CODE, never by numeric id, and resolved server-side. Succeeds with HTTP 204 and no body (this tool returns ). Only the non-empty arrays you send are applied; empty or omitted arrays are ignored, so a body with no codes is accepted but does nothing — send at least one populated array. Mapping is additive: it never removes existing links (use scalepad_cm_unmap_client_procedure for that). Read current links from the controls[] and objectives[] arrays of scalepad_cm_get_client_procedure.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with three optional arrays; include only the ones you want to link. objectives[] is an array of OBJECTS, not strings — each is {"program_name": "<compliance program display name>", "codes": ["<requirement business code>", ...]}, e.g. {"program_name":"ISO-27001:2022","codes":["4.1"]}. policy_codes[] takes policy business codes (e.g. "POL-001", from scalepad_cm_search_client_policies) and control_codes[] takes control DISPLAY codes (e.g. "AC-1", from scalepad_cm_search_client_controls). Example: {"objectives":[{"program_name":"ISO-27001:2022","codes":["4.1"]}],"policy_codes":["POL-001"],"control_codes":["AC-1"]}.
clientIdstringyesThe ControlMap client id owning the procedure (client.id from scalepad_cm_list_clients_health).
procedureIdstringyesThe procedure id to link from (procedures.data[].id from scalepad_cm_search_client_procedures).

[ScalePad] For ONE client, by client id: search that client's procedures. This is a READ despite using HTTP POST — the verb only carries the query body and nothing is persisted. Returns client {id, name, tenant_id}, documents_summary {approved, in_review, ready_for_approval, in_progress, draft, approved_percentage, total}, and procedures {total_count, next_cursor, data[]}. Each row carries id, code (e.g. PRO-1), title, owner and created_by (each {id, name, email}), status, source (e.g. "html"), frequency, contributors[], data_classification, tags[], controls[{id, title, code}] and objectives[{id, title, code, program_name}]. For the whole client base use scalepad_cm_list_clients_procedures_summary instead, which takes NO client id.

ParamTypeRequiredDefaultDescription
bodyJsonstringnonullOptional JSON query body; the vendor requires a body but accepts and every property is optional, so omitting this returns the first unfiltered page. Properties: filter (object of field -> "operator:value"; the ONLY filterable fields here are code, title and status, with operators eq and in — an omitted operator means eq), sort (exactly ONE of code or title; '-' prefix descends, '+' or no prefix ascends — no comma-separated multi-sort), fields (comma-separated sparse-field selector), page_size (1-200, vendor default 50) and cursor (opaque Base64 next_cursor). Document status values are Draft, In Progress, In Review, Ready For Approval and Approved. Example: {"filter":{"code":"in:PRO-1,PRO-2"},"sort":"-title","page_size":50}. Paging, filtering and sorting live ONLY in this body — this operation has no query-string parameters.
clientIdstringyesThe ControlMap client id whose procedures to search (client.id from scalepad_cm_list_clients_health).

[ScalePad] UNLINK a procedure from objectives, policies or controls. The codes you send identify only the LINKS to remove — neither the procedure nor the mapped entities are deleted — but the relationship removal is a real state change with no undo, so echo the exact procedure and every code back to the user before calling. Succeeds with HTTP 204 and no body (this tool returns ); empty or omitted arrays make no changes, so reject an all-empty request rather than sending it. Despite the HTTP POST verb this is a removal: the vendor path is .../mappings/bulk-delete.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body naming the links to REMOVE — same shape as the map tool: objectives[] as objects {"program_name", "codes":[...]}, plus policy_codes[] and control_codes[] as plain business-code strings. Include only the codes to unlink; empty or omitted arrays result in no changes. Example: {"policy_codes":["POL-001"]}.
clientIdstringyesThe ControlMap client id owning the procedure (client.id from scalepad_cm_list_clients_health).
procedureIdstringyesThe procedure id to unlink from (procedures.data[].id from scalepad_cm_search_client_procedures).

[ScalePad] Partially update a procedure (HTTP PATCH). Only the properties you send change; everything else is left alone. Returns 200 with the patched record (id, code, name, description, status, source, data_classification, frequency, owner and the rest of the document metadata). When status is included the vendor applies it AFTER the other fields in the same request. tags and contributors REPLACE the current lists rather than appending — send an empty array to clear one. This is the tool for status transitions and for editing the HTML body. To change the procedure's LINKS to objectives, policies or controls use scalepad_cm_map_client_procedure / scalepad_cm_unmap_client_procedure instead; relationships are not patchable here.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body; every property is optional, send only what changes. Accepts title, description (document body / HTML), code (business code), status (exactly one of Draft, In Progress, In Review, Ready For Approval, Approved), data_classification, review_date (ISO-8601 instant, e.g. 2026-05-04T10:15:00.000Z), owner and approver (each a user EMAIL or an exact display name, matched case-insensitively), team, tags[] (tag names — REPLACES the current tag set) and contributors[] (user emails — REPLACES the current contributor list). Example: {"status":"Ready For Approval","approver":"john.doe@example.com"}.
clientIdstringyesThe ControlMap client id owning the procedure (client.id from scalepad_cm_list_clients_health).
procedureIdstringyesThe procedure id to update (procedures.data[].id from scalepad_cm_search_client_procedures — the numeric id, not the PRO-nn code).

ControlMap Reports

ToolPlanAccessSummary
scalepad_cm_get_client_report_signed_urlFreeRead-onlyMint a short-lived DOWNLOAD url for one finished ControlMap report.
scalepad_cm_list_client_reportsFreeRead-onlyFor ONE client, by client id: list that client's generated ControlMap reports.

[ScalePad] Mint a short-lived DOWNLOAD url for one finished ControlMap report. Returns JSON only — {id, name, signed_url, expires_in_seconds} (3600 in the vendor example) — never the file bytes, and no StackJack blob storage is involved anywhere in ControlMap. Fetch signed_url directly before expires_in_seconds elapses; it is already authorized, so do NOT attach the ScalePad x-api-key to that request. The URL is single-purpose and time-limited: re-run this tool for a fresh one rather than caching it. A report still in Progress or Fail status has no downloadable document.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id owning the report (client.id from scalepad_cm_list_clients_health).
reportIdstringyesThe report id to download — the reports.data[].id value from scalepad_cm_list_client_reports. Declared as an integer by the vendor path schema, so pass the numeric id as a string (e.g. "1234").

[ScalePad] For ONE client, by client id: list that client's generated ControlMap reports. This is a READ despite using HTTP POST — the verb only exists to carry the filter/paging body, and nothing is created (vendor operationId 01_listClientReports). Returns client {id, name, tenant_id}, report_summary {total, progress, completed, failed}, and reports {total_count, next_cursor, data[]} where each row carries id, report_name, program, created_by {id, name, email}, created_at and status. The row id is the report_id you pass to scalepad_cm_get_client_report_signed_url to obtain a download link.

ParamTypeRequiredDefaultDescription
bodyJsonstringnonullOptional JSON query body; the vendor requires a body but accepts and every property is optional, so omitting this sends an empty object and returns the first unfiltered page. Properties: filter (object of field -> "operator:value"; documented fields report_name, status and owner.email, each supporting eq and in, with status values Progress, Completed and Fail), page_size (1-200, vendor default 50), cursor (opaque Base64 next_cursor from the previous response), and sort (exactly ONE of created_at, report_name or status; '-' prefix descends). Example: {"filter":{"status":"eq:Completed"},"page_size":50,"sort":"-created_at"}. Paging and filtering live in this body — there are no query-string parameters on this operation.
clientIdstringyesThe ControlMap client id whose reports to list (client.id from scalepad_cm_list_clients_health).

ControlMap Risks

ToolPlanAccessSummary
scalepad_cm_create_client_riskProWriteCreate a risk in ONE client's register.
scalepad_cm_delete_client_riskProDestructivePERMANENTLY delete a risk from a client's register.
scalepad_cm_get_client_riskFreeRead-onlyGet ONE risk in full for ONE client.
scalepad_cm_get_client_risk_categoryFreeRead-onlyGet ONE risk category by id for ONE client.
scalepad_cm_list_client_risks_departmentsFreeRead-onlyFor ONE client, by client id: list the departments available for risk assignment.
scalepad_cm_list_clients_risks_summaryFreeRead-onlyAcross EVERY client of the MSP — takes NO client id.
scalepad_cm_map_client_riskProWriteLINK a risk to the entities that expose it or mitigate it.
scalepad_cm_search_client_risksFreeRead-onlyFor ONE client, by client id: search that client's risk register.
scalepad_cm_unmap_client_riskProDestructiveUNLINK a risk from assets, asset types, threats, vulnerabilities, vendors, objectives, controls or action items.
scalepad_cm_update_client_riskProWritePartially update a risk (HTTP PATCH).

[ScalePad] Create a risk in ONE client's register. Returns 201 with the full RiskResponse, including the code ScalePad assigns (RSK-nn) and the inherent/current/target scores it derives from impact and likelihood. name is the ONLY schema-required property. Watch the field vocabulary: create uses name and risk_category, whereas scalepad_cm_update_client_risk uses title and category for the same two values — do not carry names across. There is no documented idempotency key, so never blind-retry this call — re-check with scalepad_cm_search_client_risks first. Link assets, threats, vulnerabilities, vendors, objectives, controls or action items afterwards with scalepad_cm_map_client_risk; the create body takes no mappings.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. Required: name (the risk title, minLength 1, cannot be blank or null). Optional: description (context/detail), status (one of Not Assessed, Assessment in progress, Assessed, Remediation in progress, Remediated, Closed; default Not Assessed), department (an existing department NAME — get the exact string from scalepad_cm_list_client_risks_departments), risk_category (a category name, e.g. "Asset Management"), owner_email (the owning user's email), business_impact (free text describing impact on the business), impact (integer 1-5, default 1) and likelihood (integer 1-5, default 1). treatment is NOT settable at create — it appears on reads and in the search filter only. Example: {"name":"Data Breach Risk","description":"Potential exposure of sensitive data due to system vulnerabilities.","status":"Not Assessed","department":"IT","risk_category":"Asset Management","owner_email":"john.doe@example.com","impact":4,"likelihood":5}.
clientIdstringyesThe ControlMap client id to create the risk under (client.id from scalepad_cm_list_clients_health).

[ScalePad] PERMANENTLY delete a risk from a client's register. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the risk is not found. ScalePad's own changelog states that risk deletion removes the record AND all associated data — its mappings to assets, threats, vulnerabilities, vendors, objectives, controls and action items go with it — and documents no soft delete, restore or undo. Confirm the exact client and the exact risk (read it back with scalepad_cm_get_client_risk first) before calling, and prefer setting status to Closed via scalepad_cm_update_client_risk when the goal is only to retire the risk while keeping its history.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id owning the risk (client.id from scalepad_cm_list_clients_health).
riskIdstringyesThe risk id to delete (risks.data[].id from scalepad_cm_search_client_risks). Resolve and confirm this with the user before calling — the deletion is irreversible.

[ScalePad] Get ONE risk in full for ONE client. Returns the RiskResponse shape: id, code (e.g. RSK-1), name, description, owner and created_by (each {id, name}), status, department, risk_category, treatment, business_impact, inherent_risk_score and inherent_risk_label, current_risk_score and current_risk_label, target_risk_score and target_risk_label, created_at and updated_at. The score labels are ControlMap's own bands (e.g. Severe, High, Medium, Low) derived from impact × likelihood — they are computed, not settable directly.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id owning the risk (client.id from scalepad_cm_list_clients_health).
riskIdstringyesThe risk id to read — the risks.data[].id value from scalepad_cm_search_client_risks. This is the numeric id, NOT the RSK-nn business code; the vendor path type is integer, so pass it as a string (e.g. "1").

[ScalePad] Get ONE risk category by id for ONE client. Returns a minimal RiskCategoryResponse — exactly {id, name}, e.g. {"id":35,"name":"Access Control"} — and 404 NOT_FOUND ("No risk category exists with ID ...") when the id is unknown. ScalePad publishes NO list-categories endpoint on this API, so the only way to discover a category id is from a risk that already uses it: the risk_category NAME appears on every row of scalepad_cm_search_client_risks. Use this tool to resolve a category id you already hold back to its display name.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id the category belongs to (client.id from scalepad_cm_list_clients_health).
riskCategoryIdstringyesThe risk category id to read (the vendor path type is integer, so pass it as a string, e.g. "35"). There is no documented endpoint that lists category ids — see the tool description.

[ScalePad] For ONE client, by client id: list the departments available for risk assignment. Returns a bare JSON ARRAY of {id, name} objects (e.g. [{"id":1,"name":"IT"},{"id":2,"name":"Sales"}]) — not a paginated envelope, and this endpoint takes no page_size, cursor, sort or filter parameters at all. Call this first to get the exact department NAME string to send in the department field of scalepad_cm_create_client_risk or scalepad_cm_update_client_risk; ScalePad resolves that field by name, so a typo is rejected rather than created.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ControlMap client id whose risk departments to list (client.id from scalepad_cm_list_clients_health).

[ScalePad] Across EVERY client of the MSP — takes NO client id. Cursor-paginated risk-posture roll-up for the whole client base: returns {data[], total_count, next_cursor} where each row is {client {id, name, tenant_id}, risk_summary {overall_score, risk_level, updated_at, risk_breakdown {severe, high, medium, low}}}. Keep paging until next_cursor is null. This tool returns SCORES AND COUNTS ONLY — it never lists individual risks; use scalepad_cm_search_client_risks with a client id for the risk rows.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque Base64 next_cursor from the previous page. Omit for the first page; never decode or manufacture one — a non-Base64 value returns 400 ("The 'cursor' parameter must be a valid Base64-encoded string"). Cursor scans are not atomic, so deduplicate by client.id.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together and sent as filter[field] query keys. Documented filters here are exactly client.tenant_id and client.name (there is NO client.id filter on this endpoint — only the evidences summary has one), with operators eq and in; an omitted operator means eq. Example: {"client.tenant_id":"in:joel1,joel2"}. An unsupported field returns 400.
pageSizeintegernonullMaximum clients per page, 1-200 (vendor default 50). Values above 200 are clamped rather than rejected; the vendor itself returns 400 for an over-limit page_size.
sortstringnonullSort expression; '-' prefix descends, '+' or no prefix ascends. Exactly ONE field is accepted — either client.name or client.tenant_id (no comma-separated multi-sort here). Example: '-client.name'.

[ScalePad] LINK a risk to the entities that expose it or mitigate it. A risk has the widest set of linkable entities in ControlMap — eight arrays: assets and asset types, threats, vulnerabilities, vendors, framework objectives (requirements), controls and action items — each identified by its BUSINESS CODE (asset types by NAME), never by numeric id, and resolved server-side. Succeeds with HTTP 204 and no body (this tool returns ). Only the non-empty arrays you send are applied; empty or omitted arrays are ignored, so a body with no codes is accepted but does nothing — send at least one populated array. Mapping is additive: it never removes existing links (use scalepad_cm_unmap_client_risk for that).

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body of code arrays; include only the ones you want to link. All eight are flat arrays of STRINGS (unlike the policy/procedure/governance mapping bodies, whose objectives are objects): asset_codes[] (asset codes), asset_type_names[] (asset type NAMES, not codes), threat_codes[], vulnerability_codes[], vendor_codes[], objective_codes[] (framework requirement codes such as "6.2" or "A.5.1", from scalepad_cm_search_client_framework_objectives), control_codes[] (control display codes such as "HRM-1", from scalepad_cm_search_client_controls) and action_item_codes[] (action item codes such as "AI-9", from scalepad_cm_search_client_action_items). Example: {"control_codes":["HRM-1"],"objective_codes":["6.2"],"vendor_codes":["VEN-004"]}.
clientIdstringyesThe ControlMap client id owning the risk (client.id from scalepad_cm_list_clients_health).
riskIdstringyesThe risk id to link from (risks.data[].id from scalepad_cm_search_client_risks).

[ScalePad] For ONE client, by client id: search that client's risk register. This is a READ despite using HTTP POST — the verb only carries the query body and nothing is persisted; it replaced the retired GET /risks list route. Returns client {id, name, tenant_id}, an aggregate risk_summary {overall_score, risk_level, updated_at, risk_breakdown {severe, high, medium, low}}, and risks {total_count, next_cursor, data[]}. Each row carries id, code (e.g. RSK-1), name, description, owner and created_by (each {id, name, email}), status, department, risk_category, treatment, business_impact, inherent_risk_score/label, current_risk_score/label, target_risk_score/label and timestamps. For the whole client base use scalepad_cm_list_clients_risks_summary instead, which takes NO client id.

ParamTypeRequiredDefaultDescription
bodyJsonstringnonullOptional JSON query body; the vendor requires a body but accepts and every property is optional, so omitting this returns the first unfiltered page. Properties: filter (object of field -> "operator:value"; the ONLY filterable fields here are status, treatment and owner.email, with operators eq and in — an omitted operator means eq), sort (exactly ONE of id, name, current_risk or owner.name; '-' prefix descends, '+' or no prefix ascends, and no comma-separated multi-sort), fields (comma-separated sparse-field selector), page_size (1-200, vendor default 50) and cursor (opaque Base64 next_cursor). Status values are Not Assessed, Assessment in progress, Assessed, Remediation in progress, Remediated and Closed; treatment values are Avoid, Reduce, Transfer, Share and Accept. Example: {"filter":{"status":"in:Assessed,Remediation in progress","treatment":"eq:Reduce"},"sort":"-current_risk"}. Paging, filtering and sorting live ONLY in this body — this operation has no query-string parameters.
clientIdstringyesThe ControlMap client id whose risks to search (client.id from scalepad_cm_list_clients_health).

[ScalePad] UNLINK a risk from assets, asset types, threats, vulnerabilities, vendors, objectives, controls or action items. The codes you send identify only the LINKS to remove — neither the risk nor the mapped entities are deleted — but the relationship removal is a real state change with no undo, so echo the exact risk and every code back to the user before calling. Succeeds with HTTP 204 and no body (this tool returns ); empty or omitted arrays make no changes, so reject an all-empty request rather than sending it. Despite the HTTP POST verb this is a removal: the vendor path is .../mappings/bulk-delete.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body of code arrays naming the links to REMOVE — same eight flat string arrays as the map tool: asset_codes[], asset_type_names[], threat_codes[], vulnerability_codes[], vendor_codes[], objective_codes[], control_codes[], action_item_codes[]. Include only the codes to unlink; empty or omitted arrays result in no changes. Example: {"control_codes":["HRM-1"]}.
clientIdstringyesThe ControlMap client id owning the risk (client.id from scalepad_cm_list_clients_health).
riskIdstringyesThe risk id to unlink from (risks.data[].id from scalepad_cm_search_client_risks).

[ScalePad] Partially update a risk (HTTP PATCH). Only the properties you send change; everything else is left alone. Returns 200 with the patched RiskResponse. Every property is optional here — including name/title, which create requires. Watch the field vocabulary: PATCH spells the risk title title and the category category, whereas scalepad_cm_create_client_risk spells the same two values name and risk_category. The PATCH schema does NOT expose impact, likelihood, treatment or business_impact, so the derived risk scores cannot be changed through this tool. To change a risk's LINKS to assets, threats, vulnerabilities, vendors, objectives, controls or action items use scalepad_cm_map_client_risk / scalepad_cm_unmap_client_risk instead; relationships are not patchable here.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body; every property is optional, send only what changes. Accepts code (business code), title (the risk name — spelled name on create), description, status (one of Not Assessed, Assessment in progress, Assessed, Remediation in progress, Remediated, Closed), owner_email, team, department (an existing department name from scalepad_cm_list_client_risks_departments) and category (the risk category — spelled risk_category on create). Example: {"title":"Data Breach Risk","status":"Assessment in progress","department":"IT","category":"Access Control","owner_email":"john.doe@example.com"}.
clientIdstringyesThe ControlMap client id owning the risk (client.id from scalepad_cm_list_clients_health).
riskIdstringyesThe risk id to update (risks.data[].id from scalepad_cm_search_client_risks — the numeric id, not the RSK-nn code).

Core Clients

ToolPlanAccessSummary
scalepad_core_get_clientFreeRead-onlyGet one client organization by its ScalePad id (from scalepad_core_list_clients).
scalepad_core_list_clientsFreeRead-onlyList the MSP's active client organizations.

[ScalePad] Get one client organization by its ScalePad id (from scalepad_core_list_clients). Returns id, name, lifecycle, primary_domain, num_contacts, num_hardware_assets, address (ISO-standardized country/state plus geo-spatial coordinates where available), record_lineage[], record_created_at, and record_updated_at. A 404 means the normalized record is missing or inaccessible — it does not by itself distinguish a source-system deletion from integration sync lag.

ParamTypeRequiredDefaultDescription
idstringyesThe ScalePad client id to read (the id field from scalepad_core_list_clients).

[ScalePad] List the MSP's active client organizations. Cursor-paginated: returns data[], total_count, and next_cursor (present and null on the final page — keep paging until it is null, not until a page is short). Each client carries id, name, lifecycle, primary_domain (derived from its contacts), num_contacts, num_hardware_assets, address, record_lineage[] (the source PSA/RMM integration and source_record_id behind the normalized record), record_created_at, and record_updated_at. The returned id is the value you pass to scalepad_core_get_client and to filter[client.id] on nearly every other Core list tool (hardware assets, SaaS assets, contacts, tickets, contracts, sites, opportunities).

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page. Cursor scans are not atomic — records changed mid-scan can be skipped or repeated, so deduplicate by id.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together, e.g. {"name":"cont:acme","num_hardware_assets":"gte:25"}. Operators: eq (the default when the operator is omitted), cont (partial match, minimum 3 characters), in (comma-separated list), lt, lte, gt, gte. Most useful here: id, name, lifecycle, num_contacts, num_hardware_assets, record_updated_at (for incremental polling), and the lineage filters record_lineage.source_record_id / record_lineage.integration_configuration.id / .vendor.id / .vendor.brand_name. Quote values containing a comma, colon, or space.
pageSizeintegernonullMaximum records per page, 1-200 — clamped to ScalePad's 200 platform cap. This operation's schema declares NO default, so do not assume a page size; set it explicitly when it matters. Use with cursor to page.
sortstringnonullComma-separated sort expression; '-' prefix descends ('+' or no prefix ascends), e.g. 'name' or '-num_hardware_assets,name'. Sortable: name, lifecycle, num_contacts, num_hardware_assets, record_created_at, record_updated_at.

Core Contacts & Members

ToolPlanAccessSummary
scalepad_core_get_contactFreeRead-onlyGet one client contact by its ScalePad id (from scalepad_core_list_contacts).
scalepad_core_get_memberFreeRead-onlyGet one MSP member by its ScalePad id (from scalepad_core_list_members).
scalepad_core_list_contactsFreeRead-onlySearch the active contacts (individuals) at the MSP's client organizations.
scalepad_core_list_membersFreeRead-onlySearch the MSP's own active members — employees, contractors, downstream IT.

[ScalePad] Get one client contact by its ScalePad id (from scalepad_core_list_contacts). Returns id, client (the owning client organization), title, contact_info (primary communication details), name, record_lineage[], record_created_at, and record_updated_at. A 404 means the normalized record is missing or inaccessible.

ParamTypeRequiredDefaultDescription
idstringyesThe ScalePad contact id to read (the id field from scalepad_core_list_contacts).

[ScalePad] Get one MSP member by its ScalePad id (from scalepad_core_list_members). Returns id, name, hired_at, title, is_scalepad_user, contact_info, reports_to_member (including their email), work_roles[], hourly_cost, daily_capacity, address, record_lineage[], record_created_at, and record_updated_at. A 404 means the normalized record is missing or inaccessible.

ParamTypeRequiredDefaultDescription
idstringyesThe ScalePad member id to read (the id field from scalepad_core_list_members).

[ScalePad] Search the active contacts (individuals) at the MSP's client organizations. The HTTP verb is POST, but this is a SEARCH and changes nothing — ScalePad uses a request body only so the PII-sensitive filters stay out of the query string. Cursor-paginated: returns data[], total_count, and next_cursor (null on the final page). Each contact carries id, client, title, contact_info, name, record_lineage[], record_created_at, and record_updated_at. Pass a returned id to scalepad_core_get_contact, or use it as filter[contact.id] on hardware assets, SaaS users, tickets, contracts, and opportunities.

ParamTypeRequiredDefaultDescription
bodyJsonstringnonullOptional JSON request body carrying the BODY-ONLY PII filters, shaped {"filter":{"<field>":"<operator>:<value>"}}. Only two fields are accepted here and they CANNOT be sent as query filters: contact_info.email (operators eq, in) and name.full (operators eq, in, cont). Example: {"filter":{"name.full":"cont:smith","contact_info.email":"eq:ada@acme.test"}}. Wrap values containing a space or comma in quotes (eq:"John Smith") and escape embedded quotes. Omit entirely when you only need query filters.
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; deduplicate by id because cursor scans are not atomic.
filtersJsonstringnonullJSON object of NON-PII field -> "operator:value" query filters, ANDed together, e.g. {"client.id":"eq:2220324","title":"cont:manager"}. Operators: eq (default when omitted), cont (minimum 3 characters), in, lt, lte, gt, gte. Documented here: id, client.id, client.name, title, record_created_at, record_updated_at, and the lineage filters record_lineage.source_record_id / record_lineage.integration_configuration.id / .vendor.id / .vendor.brand_name. Email and full name are NOT valid here — put them in bodyJson.
pageSizeintegernonullMaximum records per page, 1-200 — clamped to ScalePad's 200 platform cap. This operation's schema declares NO default, so do not assume a page size; set it explicitly when it matters. Use with cursor to page.
sortstringnonullComma-separated sort expression; '-' prefix descends. Sortable: client.id, client.name, title, contact_info.email, record_created_at, record_updated_at.

[ScalePad] Search the MSP's own active members — employees, contractors, downstream IT. The HTTP verb is POST, but this is a SEARCH and changes nothing: ScalePad uses a request body only so the PII-sensitive filters (email, phone, full name, manager email) stay out of the query string. Cursor-paginated: returns data[], total_count, and next_cursor. Each member carries id, name, hired_at, title, is_scalepad_user (whether they are a ScalePad platform user), contact_info, reports_to_member, work_roles[], hourly_cost (the member's default cost per hour to the MSP), daily_capacity (available working hours per day), address, record_lineage[], record_created_at, and record_updated_at. Pass a returned id to scalepad_core_get_member, or use it as filter[owner_member.id] / filter[responsible_member.id] on tickets and opportunities.

ParamTypeRequiredDefaultDescription
bodyJsonstringnonullOptional JSON request body carrying the BODY-ONLY PII filters, shaped {"filter":{"<field>":"<operator>:<value>"}}. Four fields are accepted here and NONE of them can be sent as query filters: contact_info.email (eq, in), contact_info.phone (eq, in), name.full (eq, in, cont), reports_to_member.email (eq, in). Example: {"filter":{"name.full":"cont:smith","reports_to_member.email":"eq:lead@msp.test"}}. Wrap values containing a space or comma in quotes and escape embedded quotes. Omit entirely when you only need query filters.
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; deduplicate by id because cursor scans are not atomic.
filtersJsonstringnonullJSON object of NON-PII field -> "operator:value" query filters, ANDed together, e.g. {"is_scalepad_user":"eq:true","daily_capacity":"gte:8"}. Operators: eq (default when omitted), cont (minimum 3 characters), in, lt, lte, gt, gte. Documented here: id, hired_at, title, is_scalepad_user, reports_to_member.id, hourly_cost.amount, daily_capacity, record_created_at, record_updated_at, and the lineage filters record_lineage.source_record_id / record_lineage.integration_configuration.id / .vendor.id / .vendor.brand_name. Email, phone, full name and manager email are NOT valid here — put them in bodyJson.
pageSizeintegernonullMaximum records per page, 1-200 — clamped to ScalePad's 200 platform cap. This operation's schema declares NO default, so do not assume a page size; set it explicitly when it matters. Use with cursor to page.
sortstringnonullComma-separated sort expression; '-' prefix descends. Sortable: hired_at, title, is_scalepad_user, hourly_cost.amount, daily_capacity, record_created_at, record_updated_at.

Core Hardware Assets

ToolPlanAccessSummary
scalepad_core_get_hardware_assetFreeRead-onlyGet one hardware asset by its ScalePad id (from scalepad_core_list_hardware_assets).
scalepad_core_list_hardware_assetsFreeRead-onlyList active hardware assets across the MSP's clients.

[ScalePad] Get one hardware asset by its ScalePad id (from scalepad_core_list_hardware_assets). Returns the full record: name, client, contact, manufacturer, model, serial_number, type, last_login_user, location_name, mac_addresses[], configuration (cpu / ram_bytes / disks), software (operating_system, antivirus_info, office_suite_info), address (ISO-standardized country/state plus geo-spatial coordinates where available), record_lineage[], record_created_at, and record_updated_at. A 404 means the normalized record is missing or inaccessible.

ParamTypeRequiredDefaultDescription
idstringyesThe ScalePad hardware asset id to read (the id field from scalepad_core_list_hardware_assets).

[ScalePad] List active hardware assets across the MSP's clients. Cursor-paginated: returns data[], total_count, and next_cursor (null on the final page). Each asset carries id, name, client, contact (the assigned user), manufacturer (ScalePad-standardized), model, serial_number, type (WORKSTATION | IMAGING | SERVER | NETWORK | MOBILE | VIRTUAL), last_login_user, location_name, mac_addresses[], configuration (cpu name/manufacturer, ram_bytes, disks total_bytes/used_bytes), software (operating_system, antivirus_info status + definition_status, office_suite_info name/version), address, record_lineage[], record_created_at, and record_updated_at. Use filter[client.id] with an id from scalepad_core_list_clients to scope to one client; pass a returned asset id to scalepad_core_get_hardware_asset.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; deduplicate by id because cursor scans are not atomic.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together, e.g. {"type":"eq:WORKSTATION","configuration.ram_bytes":"lte:8000000000"}. Operators: eq (default when omitted), cont (minimum 3 characters), in, lt, lte, gt, gte. The filters that matter most here: client.id, type (WORKSTATION | IMAGING | SERVER | NETWORK | MOBILE | VIRTUAL), serial_number, configuration.ram_bytes, software.operating_system, software.antivirus_info.status (UNKNOWN | DISABLED | RUNNING), software.antivirus_info.definition_status (OUTOFDATE | UNKNOWN | UPTODATE), and record_updated_at (for incremental polling). Also documented: id, name, client.name, contact.id, manufacturer.id, manufacturer.name, model.number, location_name, configuration.cpu., configuration.disks.total_bytes, configuration.disks.used_bytes, software.office_suite_info., record_created_at, and the record_lineage.* filters. Quote values containing a comma, colon, or space.
pageSizeintegernonullMaximum records per page, 1-200 — clamped to ScalePad's 200 platform cap. This operation's schema declares NO default, so do not assume a page size; set it explicitly when it matters. Use with cursor to page.
sortstringnonullComma-separated sort expression; '-' prefix descends, e.g. '-configuration.ram_bytes' or 'client.name,type'. Sortable: id, name, client.id, client.name, contact.id, manufacturer.id, manufacturer.name, model.number, type, location_name, configuration.cpu.name, configuration.cpu.manufacturer_name, configuration.cpu.manufacturer_id, configuration.ram_bytes, software.operating_system, software.antivirus_info.status, software.antivirus_info.definition_status, software.office_suite_info.name, software.office_suite_info.version, record_created_at, record_updated_at.

Core Integrations

ToolPlanAccessSummary
scalepad_core_list_integration_configurationsFreeRead-onlyList the MSP's configured integration instances — one entry per connected PSA/RMM/SaaS setup.
scalepad_core_list_integration_vendorsFreeRead-onlyList the catalogue of third-party vendors and platforms ScalePad can integrate with — the lookup behind record_lineage.integration_configuration.vendor on every Core record.

[ScalePad] List the MSP's configured integration instances — one entry per connected PSA/RMM/SaaS setup. Each carries id, vendor (the integration vendor it belongs to), nickname (the operator-facing label that distinguishes multiple instances of the same vendor), and primary[] (the datatypes for which this instance is the primary integration; empty when it is not primary for anything). The id here is what appears as record_lineage.integration_configuration.id on client, contact, asset, ticket, contract and site records, and is the value to pass as filter[record_lineage.integration_configuration.id] on those list tools. ScalePad documents NO parameters on this operation — no pagination, filtering or sorting — so it returns the full set in one response.

[ScalePad] List the catalogue of third-party vendors and platforms ScalePad can integrate with — the lookup behind record_lineage.integration_configuration.vendor on every Core record. Unlike the other Core list operations this one returns a BARE JSON ARRAY of vendors rather than the {data, total_count, next_cursor} envelope, so there is no cursor to follow even though page_size and cursor are accepted. Vendors are grouped by category: PSA, RMM, SaaS, Network, Documentation, Backup, Customer Satisfaction, Cybersecurity, PSA & RMM. Note that ScalePad removed filter[data_types_supported] in the 2026-07-21 Core release while keeping the response field — filter on it and expect a 400/422. This operation can return 422 for a semantically invalid filter in addition to the usual 400.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque cursor. Accepted by the operation, but the array response carries no next_cursor, so there is no documented way to obtain a subsequent cursor here.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together, e.g. {"category":"eq:PSA"} or {"name":"cont:connect"}. Documented filters: name (operators cont, eq, in), vendor_id (eq, in), and category (cont, eq, in; values PSA, RMM, SaaS, Network, Documentation, Backup, Customer Satisfaction, Cybersecurity, PSA & RMM). An omitted operator means eq; cont needs at least 3 characters. Quote values containing a comma, colon, or space — e.g. {"category":"eq:"PSA & RMM""}.
pageSizeintegernonullMaximum records requested, 1-200 — clamped to ScalePad's 200 platform cap. This operation's schema declares NO default, so do not assume a page size. Accepted by the operation, but the response is an unenveloped array with no next_cursor to follow.

Core Opportunities

ToolPlanAccessSummary
scalepad_core_get_opportunityFreeRead-onlyGet one sales opportunity by its ScalePad id (from scalepad_core_list_opportunities).
scalepad_core_list_opportunitiesFreeRead-onlyList sales opportunities for the MSP's active clients.

[ScalePad] Get one sales opportunity by its ScalePad id (from scalepad_core_list_opportunities). Returns id, title, description, source_status, source_stage, is_active, probability (0-100), client, contact, responsible_member, record_lineage[], record_created_at, and record_updated_at. A 404 means the normalized record is missing or inaccessible.

ParamTypeRequiredDefaultDescription
idstringyesThe ScalePad opportunity id to read (the id field from scalepad_core_list_opportunities).

[ScalePad] List sales opportunities for the MSP's active clients. Cursor-paginated: returns data[], total_count, and next_cursor (null on the final page). Each opportunity carries id, title, description, source_status and source_stage (the ORIGINAL values from the source system, before any ScalePad standardization — so the valid values depend on the connected PSA/CRM), is_active, probability (percent 0-100 that it closes won), client, contact, responsible_member (the MSP member accountable for it), record_lineage[], record_created_at, and record_updated_at. Use filter[client.id] with an id from scalepad_core_list_clients, or filter[responsible_member.id] with an id from scalepad_core_list_members.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; deduplicate by id because cursor scans are not atomic.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together, e.g. {"is_active":"eq:true","probability":"gte:70"}. Operators: eq (default when omitted), cont (minimum 3 characters), in, lt, lte, gt, gte. The filters that matter most here: client.id, is_active, probability, source_status, source_stage, responsible_member.id, and record_updated_at (for incremental polling). Also documented: id, title, client.name, contact.id, record_created_at, and the record_lineage.* filters. Because source_status/source_stage are raw source-system strings, discover the real values from a first unfiltered page rather than assuming an enum. Quote values containing a comma, colon, or space.
pageSizeintegernonullMaximum records per page, 1-200 — clamped to ScalePad's 200 platform cap. This operation's schema declares NO default, so do not assume a page size; set it explicitly when it matters. Use with cursor to page.
sortstringnonullComma-separated sort expression; '-' prefix descends, e.g. '-probability' or 'client.name,title'. Sortable: title, source_status, source_stage, is_active, probability, client.id, client.name, contact.id, responsible_member.id, record_created_at, record_updated_at.

Core Product Catalog

ToolPlanAccessSummary
scalepad_core_get_product_catalog_itemFreeRead-onlyGet one product catalog record by its ScalePad id (from scalepad_core_list_product_catalog).
scalepad_core_list_product_catalogFreeRead-onlyList the product catalog records exported from the MSP's PSA integrations.

[ScalePad] Get one product catalog record by its ScalePad id (from scalepad_core_list_product_catalog). Returns id, source_system, source_product_id, source_product_identifier, name, description, category, subcategory, product_type, product_class, manufacturer_name, unit_cost, unit_price, is_active, updated_at, record_lineage[], record_created_at, and record_updated_at. Every field except id and source_system is nullable — PSAs expose different subsets. A 404 means the normalized record is missing or inaccessible.

ParamTypeRequiredDefaultDescription
idstringyesThe ScalePad product catalog record id to read (the id field from scalepad_core_list_product_catalog).

[ScalePad] List the product catalog records exported from the MSP's PSA integrations. Cursor-paginated: returns data[], total_count, and next_cursor (null on the final page). Each record carries id, source_system (the PSA that supplied it), source_product_id, source_product_identifier (the human-facing product code/SKU, used to match related source records such as PSA contract additions), name, description, category, subcategory, product_type, product_class, manufacturer_name, unit_cost, unit_price, is_active, updated_at (the SOURCE product's last-updated timestamp, distinct from ScalePad's own record_updated_at), record_lineage[], record_created_at, and record_updated_at. The id is what appears as contract_pricings.items.product.id on contracts — see scalepad_core_list_contracts.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; deduplicate by id because cursor scans are not atomic.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together, e.g. {"is_active":"eq:true","category":"eq:Hardware"}. Operators: eq (default when omitted), cont (minimum 3 characters), in, lt, lte, gt, gte. The filters that matter most here: is_active, name, category, subcategory, product_type, product_class, manufacturer_name, source_system, source_product_identifier, and updated_at / record_updated_at (for incremental polling). Also documented: id, source_product_id and the record_lineage.* filters. Category/type/class values are raw source-PSA strings, not a ScalePad enum. Quote values containing a comma, colon, or space.
pageSizeintegernonullMaximum records per page, 1-200 — clamped to ScalePad's 200 platform cap. This operation's schema declares NO default, so do not assume a page size; set it explicitly when it matters. Use with cursor to page.
sortstringnonullComma-separated sort expression; '-' prefix descends, e.g. '-unit_price' or 'category,name'. Sortable: source_system, source_product_id, source_product_identifier, name, category, subcategory, product_type, product_class, manufacturer_name, unit_cost, unit_price, is_active, updated_at, record_created_at, record_updated_at. Note unit_cost and unit_price are sortable but NOT filterable.

Core SaaS Assets

ToolPlanAccessSummary
scalepad_core_get_saas_assetFreeRead-onlyGet one SaaS asset by its ScalePad id (from scalepad_core_list_saas_assets).
scalepad_core_get_saas_userFreeRead-onlyGet one SaaS user by its ScalePad id (from scalepad_core_list_saas_users).
scalepad_core_list_saas_assetsFreeRead-onlyList the SaaS assets (cloud subscriptions and licence pools) tracked for the MSP's clients.
scalepad_core_list_saas_usersFreeRead-onlyList the SaaS users — the individuals at client organizations who hold an assigned SaaS licence.

[ScalePad] Get one SaaS asset by its ScalePad id (from scalepad_core_list_saas_assets). Returns id, client, product, status, tenant_domain, term, subscriptions[], pool (type / capacity / utilized / suspended / grace_period_warning), record_lineage[], record_created_at, and record_updated_at. A 404 means the normalized record is missing or inaccessible.

ParamTypeRequiredDefaultDescription
idstringyesThe ScalePad SaaS asset id to read (the id field from scalepad_core_list_saas_assets).

[ScalePad] Get one SaaS user by its ScalePad id (from scalepad_core_list_saas_users). Returns id, client, contact, asset, product, term, subscription, activity (last_active_at, is_active), authentication (is_mfa_enabled, default_mfa_method), is_admin, record_lineage[], record_created_at, and record_updated_at. Activity fields are only populated when the SaaS vendor supplies them. A 404 means the normalized record is missing or inaccessible.

ParamTypeRequiredDefaultDescription
idstringyesThe ScalePad SaaS user id to read (the id field from scalepad_core_list_saas_users).

[ScalePad] List the SaaS assets (cloud subscriptions and licence pools) tracked for the MSP's clients. Cursor-paginated: returns data[], total_count, and next_cursor (null on the final page). Each asset carries id, client, product (manufacturer, name, category, manufacturer_sku), status (the raw source lifecycle/availability value), tenant_domain (the Microsoft 365 tenant's default verified domain), term (starts_at / ends_at / is_auto_renewed), subscriptions[] (per-subscription id, commerce_subscription_id, status, type, license_count, billing_cycle_name, friendly_name, provider_name, partner_tier, csp_tier and its own term), pool (type SEAT, capacity, utilized, suspended, grace_period_warning), record_lineage[], record_created_at, and record_updated_at. Note pool.active was REMOVED from the response, filters and sorting in the 2026-07-21 Core release. Pass a returned id to scalepad_core_get_saas_asset, or use it as filter[asset.id] on scalepad_core_list_saas_users to see who holds the licences.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; deduplicate by id because cursor scans are not atomic.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together, e.g. {"client.id":"eq:2220324","term.ends_at":"lte:2026-12-31"}. Operators: eq (default when omitted), cont (minimum 3 characters), in, lt, lte, gt, gte. The filters that matter most here: client.id, product.name, product.category, status, tenant_domain, term.ends_at and term.is_auto_renewed (renewal exposure), pool.capacity / pool.utilized / pool.suspended / pool.grace_period_warning (licence waste), subscriptions.status, subscriptions.license_count, and record_updated_at (for incremental polling). Also documented: id, client.name, product.id, product.manufacturer., product.manufacturer_sku., pool.type (SEAT), term.starts_at, the remaining subscriptions.* filters, record_created_at, and the record_lineage.* filters. Quote values containing a comma, colon, or space.
pageSizeintegernonullMaximum records per page, 1-200 — clamped to ScalePad's 200 platform cap. This operation's schema declares NO default, so do not assume a page size; set it explicitly when it matters. Use with cursor to page.
sortstringnonullComma-separated sort expression; '-' prefix descends, e.g. 'term.ends_at' or '-pool.utilized'. Sortable: id, client.id, client.name, product.manufacturer.id, product.manufacturer.name, product.id, product.name, product.category, product.manufacturer_sku.id, product.manufacturer_sku.name, status, tenant_domain, term.starts_at, term.ends_at, term.is_auto_renewed, pool.type, pool.capacity, pool.utilized, pool.suspended, pool.grace_period_warning, record_created_at, record_updated_at.

[ScalePad] List the SaaS users — the individuals at client organizations who hold an assigned SaaS licence. Cursor-paginated: returns data[], total_count, and next_cursor (null on the final page). Each user carries id, client, contact (the Core contact they map to), asset (id + status of the SaaS asset the licence belongs to), product, term, subscription (the selected Microsoft subscription for this SKU row), activity (last_active_at, is_active — only when the vendor supplies it), authentication (is_mfa_enabled, default_mfa_method), is_admin, record_lineage[], record_created_at, and record_updated_at. This is the surface for licence-waste and MFA-gap reporting: combine filter[activity.is_active]=eq:false with filter[asset.id], or filter[authentication.is_mfa_enabled]=eq:false with filter[is_admin]=eq:true.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; deduplicate by id because cursor scans are not atomic.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together, e.g. {"authentication.is_mfa_enabled":"eq:false","is_admin":"eq:true"}. Operators: eq (default when omitted), cont (minimum 3 characters), in, lt, lte, gt, gte. The filters that matter most here: client.id, asset.id, contact.id, activity.is_active and activity.last_active_at (dormant licences), authentication.is_mfa_enabled and authentication.default_mfa_method (MFA gaps), is_admin, product.name, subscription.status, and record_updated_at (for incremental polling). Also documented: id, client.name, asset.status, product.id, product.category, product.manufacturer., product.manufacturer_sku., term.starts_at, term.ends_at, subscription.id, subscription.commerce_subscription_id, subscription.license_count, subscription.billing_cycle_name, subscription.is_auto_renewed, record_created_at, and the record_lineage.* filters. Quote values containing a comma, colon, or space.
pageSizeintegernonullMaximum records per page, 1-200 — clamped to ScalePad's 200 platform cap. This operation's schema declares NO default, so do not assume a page size; set it explicitly when it matters. Use with cursor to page.
sortstringnonullComma-separated sort expression; '-' prefix descends, e.g. 'activity.last_active_at' or '-is_admin'. Sortable: id, client.id, client.name, contact.id, asset.id, asset.status, product.manufacturer.id, product.manufacturer.name, product.id, product.name, product.category, product.manufacturer_sku.id, product.manufacturer_sku.name, term.starts_at, term.ends_at, activity.last_active_at, activity.is_active, authentication.is_mfa_enabled, authentication.default_mfa_method, is_admin, record_created_at, record_updated_at.

Core Service Contracts & Tickets

ToolPlanAccessSummary
scalepad_core_get_contractFreeRead-onlyGet one service contract by its ScalePad id (from scalepad_core_list_contracts).
scalepad_core_get_ticketFreeRead-onlyGet one service ticket by its ScalePad id (from scalepad_core_list_tickets).
scalepad_core_list_contractsFreeRead-onlyList the service contracts between the MSP and its active clients.
scalepad_core_list_ticketsFreeRead-onlyList normalized service tickets across the MSP's clients.

[ScalePad] Get one service contract by its ScalePad id (from scalepad_core_list_contracts). Returns the full record including term, status, type, total_price / total_cost, pricing_item_total_price / pricing_item_total_cost, and contract_pricings[] with each line's product, term, unit_cost, unit_price, total_cost and total_price — ACTIVE pricing lines only. total_cost and is_addendum are populated only for ConnectWise-sourced contracts. A 404 means the normalized record is missing or inaccessible.

ParamTypeRequiredDefaultDescription
idstringyesThe ScalePad contract id to read (the id field from scalepad_core_list_contracts).

[ScalePad] Get one service ticket by its ScalePad id (from scalepad_core_list_tickets). Returns the full record including owner_member, responsible_member, client, contact, contract, board, summary, source, category, is_child_ticket, is_long_ticket, timeline, duration (per-stage minutes, excluding time-off / non-working days / wait time), sla, status (current plus transition history), priority (current plus change history with timestamps), impact, severity, num_notes, record_lineage[], record_created_at, and record_updated_at. Ticket note bodies are not part of this response — only num_notes. A 404 means the normalized record is missing or inaccessible.

ParamTypeRequiredDefaultDescription
idstringyesThe ScalePad ticket id to read (the id field from scalepad_core_list_tickets).

[ScalePad] List the service contracts between the MSP and its active clients. Cursor-paginated: returns data[], total_count, and next_cursor (null on the final page). Each contract carries id, name, description, client, contact, is_recurring, type (MANAGED_SERVICES | BLOCK | 3RD_PARTY | UNASSIGNED), term (starts_at, ends_at, is_auto_renew, billing_period: NOT_BILLED | ONE_TIME | ANNUAL | SEMI_ANNUAL | QUARTERLY | BI_MONTHLY | MONTHLY | BI_WEEKLY | WEEKLY | OTHER), source_type (the raw pre-standardization type), is_addendum, parent_contract, status (SUSPENDED | EXPIRED | ACTIVE | DRAFT | SENT | SIGNED | NOT_EXECUTED | CANCELLED), total_price, total_cost, is_billable, pricing_item_total_price, pricing_item_total_cost, contract_pricings[] (ACTIVE pricing lines only — expired lines are not returned), record_lineage[], record_created_at, and record_updated_at. Vendor-specific caveats: for ConnectWise total_price is the Bill Amount plus Additions, and total_cost / is_addendum are supported ONLY by ConnectWise (null for every other source system). Pass a returned id to scalepad_core_get_contract, or use it as filter[contract.id] on scalepad_core_list_tickets.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; deduplicate by id because cursor scans are not atomic.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together, e.g. {"status":"eq:ACTIVE","term.ends_at":"lte:2026-12-31"}. Operators: eq (default when omitted), cont (minimum 3 characters), in, lt, lte, gt, gte. The filters that matter most here: client.id, status, type, term.ends_at and term.is_auto_renew (note: is_auto_renew, NOT is_auto_renewed — the SaaS surface spells it differently), term.billing_period, is_recurring, is_billable, contract_pricings.items.product.id (which contracts sell a given catalog product), and record_updated_at (for incremental polling). Also documented: id, name, client.name, contact.id, source_type, is_addendum, parent_contract.id, parent_contract.name, term.starts_at, contract_pricings.is_billable, contract_pricings.pricing_type (UNKNOWN | UNIT | ALLOCATION), contract_pricings.items.product.name, record_created_at, and the record_lineage.* filters. Quote values containing a comma, colon, or space.
pageSizeintegernonullMaximum records per page, 1-200 — clamped to ScalePad's 200 platform cap. This operation's schema declares NO default, so do not assume a page size; set it explicitly when it matters. Use with cursor to page.
sortstringnonullComma-separated sort expression; '-' prefix descends, e.g. 'term.ends_at' or '-status,name'. Sortable: name, client.id, client.name, contact.id, is_recurring, type, term.starts_at, term.ends_at, term.is_auto_renew, term.billing_period, source_type, is_addendum, parent_contract.name, status, is_billable, record_created_at, record_updated_at.

[ScalePad] List normalized service tickets across the MSP's clients. Cursor-paginated: returns data[], total_count, and next_cursor (null on the final page). Each ticket carries id, owner_member (accountable) and responsible_member (who resolved it), client, contact, contract, board (the workflow queue), summary, source (the channel it came in on), category (a large ScalePad-standardized enum spanning the INCIDENT and SERVICE_REQUEST families plus IT_MANAGEMENT, CYBERSECURITY_MANAGEMENT, DISASTER_RECOVERY, DOCUMENTATION_TRAINING, USER_MANAGEMENT, AUDIT and GENERAL_MANAGEMENT groups, falling back to the MSP's own unmapped categories and UNASSIGNED), is_child_ticket, is_long_ticket (resolved in more than one standard deviation above the account/category baseline), timeline (created_at, updated_at, responded_at, planned_at, resolved_at, closed_at), duration (per-stage metrics in MINUTES, already adjusted to exclude account time-off, non-working days and wait time), sla (is_response_in_sla, is_plan_in_sla, is_resolution_in_sla), status (current plus a full transition history), priority (current plus history), impact, severity, num_notes, record_lineage[], record_created_at, and record_updated_at.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; deduplicate by id because cursor scans are not atomic.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together, e.g. {"status.current":"in:NEW,IN_PROGRESS","timeline.created_at":"gte:2026-07-01"}. Operators: eq (default when omitted), cont (minimum 3 characters), in, lt, lte, gt, gte. The filters that matter most here: client.id, status.current (NEW | ASSIGNED | SCHEDULED | TRIAGE | IN_PROGRESS | WAITING_CLIENT | WAITING_VENDOR | CANCELLED | COMPLETED | CLOSED | REOPENED | ESCALATED | CLIENT_RESPONDED | UNASSIGNED), priority.current (1 | 2 | 3 | 4 | 5 | UNASSIGNED), category, timeline.created_at / timeline.resolved_at / timeline.closed_at, the three sla.* booleans (SLA-breach reporting), owner_member.id and responsible_member.id, board.id / board.name, contract.id, is_long_ticket, and record_updated_at (for incremental polling). Also documented: id, client.name, contact.id, contract.name, is_child_ticket, timeline.updated_at, timeline.responded_at, timeline.planned_at, impact, severity, record_created_at, and the record_lineage.* filters. Quote values containing a comma, colon, or space.
pageSizeintegernonullMaximum records per page, 1-200 — clamped to ScalePad's 200 platform cap. This operation's schema declares NO default, so do not assume a page size; set it explicitly when it matters. Use with cursor to page.
sortstringnonullComma-separated sort expression; '-' prefix descends, e.g. '-timeline.created_at'. Sortable: id, client.name, is_child_ticket, is_long_ticket, timeline.created_at, timeline.updated_at, timeline.responded_at, timeline.planned_at, timeline.resolved_at, timeline.closed_at, sla.is_response_in_sla, sla.is_plan_in_sla, sla.is_resolution_in_sla, status.current, priority.current, impact, severity, record_created_at, record_updated_at. Note client.id and the member ids are filterable but NOT sortable.

Core Sites

ToolPlanAccessSummary
scalepad_core_get_siteFreeRead-onlyGet one client site by its ScalePad id (from scalepad_core_list_sites).
scalepad_core_list_sitesFreeRead-onlyList client sites materialized from the source PSA's location data.

[ScalePad] Get one client site by its ScalePad id (from scalepad_core_list_sites). Returns id, name, client, address, and record_lineage[] — a site record carries no created/updated timestamps. A 404 means the normalized record is missing or inaccessible.

ParamTypeRequiredDefaultDescription
idstringyesThe ScalePad site id to read (the id field from scalepad_core_list_sites).

[ScalePad] List client sites materialized from the source PSA's location data. Cursor-paginated: returns data[], total_count, and next_cursor (null on the final page). Each site carries only id, name, client (the owning client organization), address (the site's postal address), and record_lineage[] — unlike every other Core resource there are NO record_created_at / record_updated_at timestamps on a site, so incremental polling by timestamp is not possible here. Use filter[client.id] with an id from scalepad_core_list_clients to return one client's sites; pass a returned site id to scalepad_core_get_site. This operation documents no sort parameter.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; deduplicate by id because cursor scans are not atomic.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together, e.g. {"client.id":"eq:2220324"}. This resource documents only FOUR filter families, all with operators eq (the default when omitted) and in: id, client.id, and the lineage filters record_lineage.source_record_id / record_lineage.integration_configuration.id / record_lineage.integration_configuration.vendor.id / record_lineage.integration_configuration.vendor.brand_name. There are no name, address or timestamp filters on sites. Quote values containing a comma, colon, or space.
pageSizeintegernonullMaximum records per page, 1-200 — clamped to ScalePad's 200 platform cap. This operation's schema declares NO default, so do not assume a page size; set it explicitly when it matters. Use with cursor to page.

LM Account & Insights

ToolPlanAccessSummary
scalepad_lm_get_user_identityFreeRead-onlyGet the AUTHENTICATED user behind the API key, together with that user's feature access and permissions — the practical way to find out what this credential can actually do before attempting a write.
scalepad_lm_get_user_ui_stateFreeRead-onlyRead back one blob of persisted UI preferences for the AUTHENTICATED user, scoped to the current account, under a caller-chosen state key.
scalepad_lm_get_warranty_pricingFreeRead-onlyList ONE client's renewable warranty assets and the monthly pricing options available for each.
scalepad_lm_list_active_usersFreeRead-onlyList the users belonging to the authenticated ScalePad account — the MSP's own staff, not client contacts (for those see scalepad_lm_list_contacts).
scalepad_lm_list_insightsFreeRead-onlyList the whole insights board for the account — every insight applicable to the caller, FLATTENED across its categories (the vendor names High-risk, Hardware modernization, Software modernization,…
scalepad_lm_set_user_ui_stateProWritePersist one blob of UI preferences for the AUTHENTICATED user under a state key, scoped to the current account.

[ScalePad] Get the AUTHENTICATED user behind the API key, together with that user's feature access and permissions — the practical way to find out what this credential can actually do before attempting a write. Takes no parameters. Returns {user, feature_access[], feature_permissions[]}: user is {id, label, email, is_deleted}; each feature_access entry is a discriminated union on its type field — NoAccess and FullAccess and LimitedAccess — carrying feature_key, is_feature_released, plus licensed_clients[] {id, label} on FullAccess or feature_access_client, number_of_unlocks, max_number_of_unlocks and action_needed_for_full_access on LimitedAccess. feature_permissions[] mirrors that union but renames the two leading fields: feature_permission_key and has_permission instead of feature_key and is_feature_released — read each array with its own field names. The public schema does not name granular roles, so treat a runtime 403 as authoritative rather than inferring permissions from UI role names.

[ScalePad] Read back one blob of persisted UI preferences for the AUTHENTICATED user, scoped to the current account, under a caller-chosen state key. This is opaque per-user interface state — column widths, collapsed groups, a saved roadmap layout — not business data and not shared with anyone else: it tells you nothing about clients, assets or entitlements. Returns {payload, updated_at}, where payload is the JSON document previously stored as a STRING. Alone among the responses in this pack this schema declares NO required fields, so treat both payload and updated_at as possibly absent; the vendor does not document how an unset key behaves, so handle an empty response and a 404 as equally plausible rather than assuming one. Written by scalepad_lm_set_user_ui_state.

ParamTypeRequiredDefaultDescription
stateKeystringyesThe state key to read — a stable, caller-chosen key naming the UI surface whose layout was persisted; the vendor's example is initiatives.roadmap.v1. Keys are per user and per account, and ScalePad publishes no way to enumerate them, so you must already know the key.

[ScalePad] List ONE client's renewable warranty assets and the monthly pricing options available for each. Cursor-paginated: returns {data[], total_count, next_cursor}. Each row is {id, client {id, label}, hardware_asset_id, name, warranty_type, device_type, manufacturer, model, serial_number, is_warranty_information_blurred, warranty_expiration_date, age_in_years, renewal_start_date, is_continuous_renewal_policy_applied, pricing_options[]}, and each pricing option is {id, name, monthly_unit_price_usd_subunits, dynamic_monthly_unit_prices_by_total_coverage_years, is_preferred}. Two things to get right when quoting these numbers: prices are in US-dollar SUBUNITS, so 825 means $8.25 per month, and dynamic_monthly_unit_prices_by_total_coverage_years is a map from total coverage years to that same subunit price (e.g. {"1":825,"2":742,"3":658}); and for assets whose device_type is Server the vendor states the price is an ESTIMATE, not a firm quote. is_preferred marks the option matching the client's preferred service level for that device type, and is_warranty_information_blurred means the date and age fields are withheld for that asset.

ParamTypeRequiredDefaultDescription
clientIdstringyesREQUIRED. The client whose warranty pricing to return — and specifically the CORE client UUID, which ScalePad also calls the client ExternalId. This is a different id space from the Lifecycle Manager client id used by filter[client.id] on the action-item list, so resolve it from the Core clients surface rather than reusing an LM id.
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; never decode or manufacture one. Cursor scans are not atomic, so deduplicate by id.
pageSizeintegernonullMaximum records per page, 1-200 (clamped to ScalePad's documented 200 platform cap). This operation's schema declares no default and no minimum, so omit it to take the server's own page size.
sortstringnonullSort expression; '-' prefix descends, '+' or no prefix ascends. This operation publishes NO field allow-list — the schema constrains only the expression's shape — so confirm a field against a live response instead of assuming, and expect a 400 for an unsupported one.
warrantyTypestringnonullOptional warranty product family to narrow to. The vendor documents exactly two supported values: InfrastructureProtection and WorkstationAssurance. Omit to return every family.

[ScalePad] List the users belonging to the authenticated ScalePad account — the MSP's own staff, not client contacts (for those see scalepad_lm_list_contacts). Takes no parameters and returns {data[]} only: no paging envelope, no filters, no sort. Each user is {user_id, first_name, last_name, email, is_email_bouncing, status}, where the schema's documented status values are Confirmed, Invited, SignedUp, Disabled, Deleted and Canceled — note the vendor calls this the ACTIVE users list yet declares those inactive states on the field, so read status rather than assuming every row is usable. This is the lookup for any user_id a write needs: notably evaluate_user_id on scalepad_lm_create_assessment and scalepad_lm_update_assessment, and the assignee ids/emails on scalepad_lm_create_action_item and scalepad_lm_update_action_item.

[ScalePad] List the whole insights board for the account — every insight applicable to the caller, FLATTENED across its categories (the vendor names High-risk, Hardware modernization, Software modernization, Warranty coverage, Backup monitoring, Windows 11 upgrades, Security, and user-defined). Takes no parameters and returns {data[]} only: no total_count, no next_cursor, no filters, no sort. Each entry is {insight_id, title, description, affected_count, trend_value, risk_level, category, category_label, asset_scope, state, is_pro_insight}. affected_count is the current broken-asset count and trend_value the 30-day change in it — trend_value is NULL when trend data is unavailable OR the caller's plan does not include trend access, so a null there is not necessarily 'no change'. risk_level and category are documented only by example (risk_level e.g. High or Low; category e.g. HighRisk or HardwareModernization, with category_label the display form), as is asset_scope (e.g. Hardware, Software, Contact, DeviceBackup); state is explicitly one of normal, success, prerequisite or initializing. is_pro_insight flags an insight that requires a Pro plan. Do not treat the example values as a closed enum.

[ScalePad] Persist one blob of UI preferences for the AUTHENTICATED user under a state key, scoped to the current account. This stores opaque per-user interface state only — the vendor's example is a saved roadmap layout — and changes no business data whatsoever: it does not affect clients, assets, initiatives, permissions or what any other user sees. The PUT REPLACES any prior payload for the same key; it is not a merge, so read the current value with scalepad_lm_get_user_ui_state and resend the parts that should survive. Succeeds with HTTP 204 and no body (this tool returns ). ScalePad publishes no way to list or delete keys, so a key written with a typo is effectively permanent clutter — reuse a key you have already read back.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with the single required property payload. Note the vendor types payload as a STRING that itself contains the JSON document describing the UI configuration — so the document is JSON-encoded into that string value, not nested as an object. Example: {"payload":"{"columns":["title","due_at"],"collapsed":true}"}.
stateKeystringyesThe state key to write — a stable, caller-chosen key naming the UI surface whose layout is being persisted; the vendor's example is initiatives.roadmap.v1. Reuse the exact key you intend to read back later; there is no key-listing operation.

LM Action Items

ToolPlanAccessSummary
scalepad_lm_attach_goal_action_itemProWriteLINK an existing ACTION ITEM to a GOAL, so the task counts toward that objective's progress.
scalepad_lm_attach_initiative_action_itemProWriteLINK an existing ACTION ITEM to an INITIATIVE, aligning the task with that implementation effort.
scalepad_lm_attach_meeting_action_itemProWriteLINK an existing ACTION ITEM to a MEETING, so the task appears as a discussion topic or tracked deliverable of that meeting.
scalepad_lm_create_action_itemProWriteCreate a new Lifecycle Manager action item for a client.
scalepad_lm_delete_action_itemProDestructivePERMANENTLY delete an action item that is no longer relevant.
scalepad_lm_detach_goal_action_itemProDestructiveUNLINK an ACTION ITEM from a GOAL.
scalepad_lm_detach_initiative_action_itemProDestructiveUNLINK an ACTION ITEM from an INITIATIVE.
scalepad_lm_detach_meeting_action_itemProDestructiveUNLINK an ACTION ITEM from a MEETING.
scalepad_lm_get_action_itemFreeRead-onlyGet ONE Lifecycle Manager action item in full by its id, including its linked entities.
scalepad_lm_list_action_itemsFreeRead-onlyList Lifecycle Manager action items across every client the caller can access.
scalepad_lm_list_goal_action_itemsFreeRead-onlyFor ONE GOAL, list the ACTION ITEMS linked to it — the tasks that contribute toward achieving that objective.
scalepad_lm_list_initiative_action_itemsFreeRead-onlyFor ONE INITIATIVE, list the ACTION ITEMS aligned to it — the tasks required to carry that implementation effort out.
scalepad_lm_list_meeting_action_itemsFreeRead-onlyFor ONE MEETING, list the ACTION ITEMS created during or linked to it — the follow-ups arising from that client discussion.
scalepad_lm_pin_action_itemProWritePin or unpin ONE action item so it sorts above the other open action items in task lists.
scalepad_lm_reposition_action_itemProWriteMove ONE action item within the account's MANUAL sort order (the sort_rank field, and the sort=sort_rank ordering on scalepad_lm_list_action_items).
scalepad_lm_update_action_itemProWriteUpdate an existing action item's core fields — description, rich text, due date and assignees.
scalepad_lm_update_action_item_completion_statusProWriteMark ONE action item complete or incomplete.

[ScalePad] LINK an existing ACTION ITEM to a GOAL, so the task counts toward that objective's progress. Parent = the goal; child = the action item. Both must already exist — this creates no records — and the action item MUST belong to the SAME CLIENT as the goal or the call is rejected. Succeeds with HTTP 200 and no body (this tool returns ). Confirm the resulting link with scalepad_lm_list_goal_action_items, and see the action item's own goal_links[] via scalepad_lm_get_action_item. This is the GOAL attach: use scalepad_lm_attach_initiative_action_item for an initiative and scalepad_lm_attach_meeting_action_item for a meeting.

ParamTypeRequiredDefaultDescription
actionItemIdstringyesThe ACTION ITEM id to attach — the child (engagement_action_id from scalepad_lm_list_action_items). It must belong to the same client as the goal.
goalIdstringyesThe GOAL id to attach TO — the parent, and the first path segment.

[ScalePad] LINK an existing ACTION ITEM to an INITIATIVE, aligning the task with that implementation effort. Parent = the initiative; child = the action item. Both must already exist, and the action item should belong to the SAME CLIENT as the initiative. Succeeds with HTTP 200 and no body (this tool returns ). Verify with scalepad_lm_list_initiative_action_items; the action item's own view of the link is its initiative_links field on scalepad_lm_get_action_item — note that field is a single nullable object, not an array. This is the INITIATIVE attach: use scalepad_lm_attach_goal_action_item for a goal and scalepad_lm_attach_meeting_action_item for a meeting.

ParamTypeRequiredDefaultDescription
actionItemIdstringyesThe ACTION ITEM id to attach — the child (engagement_action_id from scalepad_lm_list_action_items). It should belong to the same client as the initiative.
initiativeIdstringyesThe INITIATIVE id to attach TO — the parent, and the first path segment.

[ScalePad] LINK an existing ACTION ITEM to a MEETING, so the task appears as a discussion topic or tracked deliverable of that meeting. Parent = the meeting; child = the action item. Both must already exist, and the action item MUST belong to the SAME CLIENT as the meeting. Succeeds with HTTP 200 and no body (this tool returns ). Verify with scalepad_lm_list_meeting_action_items, or read the action item's meeting_links[] via scalepad_lm_get_action_item. This is the MEETING attach: use scalepad_lm_attach_goal_action_item for a goal and scalepad_lm_attach_initiative_action_item for an initiative.

ParamTypeRequiredDefaultDescription
actionItemIdstringyesThe ACTION ITEM id to attach — the child (engagement_action_id from scalepad_lm_list_action_items). It must belong to the same client as the meeting.
meetingIdstringyesThe MEETING id to attach TO — the parent, and the first path segment.

[ScalePad] Create a new Lifecycle Manager action item for a client. Returns HTTP 200 with — that id is the same value the read tools expose as engagement_action_id. The create body takes NO link fields: to tie the new task to a goal, an initiative or a meeting, call scalepad_lm_attach_goal_action_item / _attach_initiative_action_item / _attach_meeting_action_item afterwards. ScalePad documents no idempotency-key header for this operation, so never blind-retry it — re-check with scalepad_lm_list_action_items filtered on the client first.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. Required: client_key (the owning client, as {"id":"..."} or, when the id is unavailable, {"name":"..."} using the client's unique name) and assigned_user_ids — despite the name, an ARRAY OF OBJECTS, each {"id":"..."} or {"email":"..."} (one of the two must be present; the read response reuses this same field name for an array of plain id strings). Optional: description_json (the rich-text description as a ProseMirror JSON document, root {"type":"doc","content":[]}, serialized to a STRING for this field — pass structured JSON, never HTML or Markdown), description (plain text, marked DEPRECATED by the vendor; send description_json for new work — sending only the plain field can drop formatting, and when omitted the server derives the plain text from description_json) and due_at (date-time). Example: {"client_key":{"id":"0d3e7d0k-241a-461r-av15-758a90d70283"},"assigned_user_ids":[{"email":"tech@example.com"}],"description_json":"{"type":"doc","content":[]}","due_at":"2026-09-30T17:00:00Z"}.

[ScalePad] PERMANENTLY delete an action item that is no longer relevant. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the item is missing or inaccessible. ScalePad documents no soft delete, restore or undo for this operation, so resolve and echo the exact item back to the user first — read it with scalepad_lm_get_action_item. When the intent is only to take a task out of the active workload, prefer scalepad_lm_update_action_item_completion_status with is_completed true, which keeps the record and its history. To break a task's link to a goal, initiative or meeting WITHOUT destroying the task, use the matching detach tool instead — those leave the action item intact.

ParamTypeRequiredDefaultDescription
idstringyesThe action item id to delete (engagement_action_id from scalepad_lm_list_action_items). Confirm this with the user before calling — the deletion is irreversible.

[ScalePad] UNLINK an ACTION ITEM from a GOAL. This removes only the LINK — neither the goal nor the action item is deleted, and the action item stays in the system (it simply no longer appears under that goal). There is no undo for the relationship removal, so echo the exact goal and action item back to the user first; read the current links with scalepad_lm_list_goal_action_items. Succeeds with HTTP 204 and no body (this tool returns ). If the goal is that the TASK should cease to exist, that is scalepad_lm_delete_action_item, not this tool.

ParamTypeRequiredDefaultDescription
actionItemIdstringyesThe ACTION ITEM id to unlink — the child (an id from scalepad_lm_list_goal_action_items). The action item itself survives.
goalIdstringyesThe GOAL id to detach FROM — the parent, and the first path segment.

[ScalePad] UNLINK an ACTION ITEM from an INITIATIVE. This removes only the LINK — neither the initiative nor the action item is deleted, and the action item remains in the system, just no longer aligned to that initiative. The relationship removal has no undo, so echo the exact initiative and action item back to the user first; read the current links with scalepad_lm_list_initiative_action_items. Succeeds with HTTP 204 and no body (this tool returns ). To destroy the TASK itself use scalepad_lm_delete_action_item instead.

ParamTypeRequiredDefaultDescription
actionItemIdstringyesThe ACTION ITEM id to unlink — the child (an id from scalepad_lm_list_initiative_action_items). The action item itself survives.
initiativeIdstringyesThe INITIATIVE id to detach FROM — the parent, and the first path segment.

[ScalePad] UNLINK an ACTION ITEM from a MEETING. This removes only the LINK — neither the meeting nor the action item is deleted; the task continues to exist independently of the meeting. The relationship removal has no undo, so echo the exact meeting and action item back to the user first; read the current links with scalepad_lm_list_meeting_action_items. Succeeds with HTTP 204 and no body (this tool returns ). To destroy the TASK itself use scalepad_lm_delete_action_item; to remove a PERSON from the meeting use scalepad_lm_delete_meeting_attendee_contacts.

ParamTypeRequiredDefaultDescription
actionItemIdstringyesThe ACTION ITEM id to unlink — the child (an id from scalepad_lm_list_meeting_action_items). The action item itself survives.
meetingIdstringyesThe MEETING id to detach FROM — the parent, and the first path segment.

[ScalePad] Get ONE Lifecycle Manager action item in full by its id, including its linked entities. Returns the same record shape as scalepad_lm_list_action_items: client {id, label}, engagement_action_id, description, description_json, due_at, is_completed, is_pinned, created_at, completed_at, assigned_user_ids (user-id strings), ticket_link_state ({ticket_link_id, type} where type is Created | Pending | Error, plus ticket_link {ticket_number, external_url} once Created), initiative_links (one nullable object, not an array), meeting_links[] {meeting_link_id, meeting_id, meeting_title}, goal_links[] {link_id, goal_id, title, period, status}, created_by_user_id and sort_rank. Use this to hydrate the bare ids returned by scalepad_lm_list_goal_action_items, scalepad_lm_list_initiative_action_items and scalepad_lm_list_meeting_action_items.

ParamTypeRequiredDefaultDescription
idstringyesThe action item id to retrieve — the engagement_action_id value from scalepad_lm_list_action_items, or any id returned in the action_item_ids array of the goal/initiative/meeting action-item list tools.

[ScalePad] List Lifecycle Manager action items across every client the caller can access. Cursor-paginated: returns {data[], total_count, next_cursor} — keep paging until next_cursor is null, not until a page is short. Each row carries client {id, label}, engagement_action_id, description, description_json (the ProseMirror rich-text document), due_at, is_completed, is_pinned, created_at, completed_at, assigned_user_ids, ticket_link_state, initiative_links, meeting_links[], goal_links[], created_by_user_id and sort_rank. Two vendor shapes to expect: assigned_user_ids is an array of plain user-id STRINGS on read (the create body reuses that name for an array of {id|email} objects), and initiative_links is a SINGLE nullable OBJECT {initiative_link_id, initiative_id, initiative_name} despite its plural name, while meeting_links[] and goal_links[] are real arrays. The id to pass to scalepad_lm_get_action_item / update / delete is engagement_action_id (the create call, confusingly, returns it as ). This is the only action-item read that returns full records: the goal/initiative/meeting list tools return ids only.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; never decode or manufacture one. Cursor scans are not atomic, so deduplicate by engagement_action_id.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together and sent as filter[field] query keys. The documented fields here are exactly client.id (eq|in — note the DOT, unlike the contacts list which spells it client_id), is_completed (eq only), is_overdue (eq only), assigned_user_ids (eq|in), created_by_user_id (eq|in) and is_unassigned (eq only). An omitted operator means eq. Example: {"client.id":"eq:0d3e7d0k-241a-461r-av15-758a90d70283","is_completed":"eq:false","is_overdue":"eq:true"}. Do not use an operator a field does not list, and do not borrow filters from another resource.
pageSizeintegernonullMaximum records per page, 1-200 (clamped to ScalePad's documented 200 platform cap). This operation's schema declares no default and no minimum, so omit it to take the server's own page size.
sortstringnonullSort expression; '-' prefix descends, '+' or no prefix ascends. Only two fields are documented for this endpoint: due_at and sort_rank (sort_rank is the account's manual drag order, set by scalepad_lm_reposition_action_item).

[ScalePad] For ONE GOAL, list the ACTION ITEMS linked to it — the tasks that contribute toward achieving that objective. Takes a GOAL id, not an initiative or meeting id. Returns {action_item_ids: [...]} — bare ids ONLY, with no titles, assignees, due dates or paging envelope; hydrate each one with scalepad_lm_get_action_item, or list full records for the whole client with scalepad_lm_list_action_items. This is the goal family: the initiative and meeting equivalents are scalepad_lm_list_initiative_action_items and scalepad_lm_list_meeting_action_items.

ParamTypeRequiredDefaultDescription
goalIdstringyesThe GOAL id whose linked action items to list (the parent — from the Lifecycle Manager goals list).

[ScalePad] For ONE INITIATIVE, list the ACTION ITEMS aligned to it — the tasks required to carry that implementation effort out. Takes an INITIATIVE id, not a goal or meeting id. Returns {action_item_ids: [...]} — bare ids ONLY, no records and no paging envelope; hydrate each with scalepad_lm_get_action_item. This is the initiative family: the goal and meeting equivalents are scalepad_lm_list_goal_action_items and scalepad_lm_list_meeting_action_items.

ParamTypeRequiredDefaultDescription
initiativeIdstringyesThe INITIATIVE id whose aligned action items to list (the parent — from the Lifecycle Manager initiatives list).

[ScalePad] For ONE MEETING, list the ACTION ITEMS created during or linked to it — the follow-ups arising from that client discussion. Takes a MEETING id, not a goal or initiative id. Returns {action_item_ids: [...]} — bare ids ONLY, no records and no paging envelope; hydrate each with scalepad_lm_get_action_item. This is the meeting family: the goal and initiative equivalents are scalepad_lm_list_goal_action_items and scalepad_lm_list_initiative_action_items. (Meeting ATTENDEES are a different resource — see scalepad_lm_add_meeting_attendee_contacts.)

ParamTypeRequiredDefaultDescription
meetingIdstringyesThe MEETING id whose linked action items to list (the parent — from the Lifecycle Manager meetings list).

[ScalePad] Pin or unpin ONE action item so it sorts above the other open action items in task lists. Purely a presentation/priority flag — it does not complete, reschedule or reassign anything, and it is independent of the manual drag order managed by scalepad_lm_reposition_action_item. The current state is the is_pinned field on scalepad_lm_get_action_item. Succeeds with HTTP 204 and no body (this tool returns ).

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with the single required boolean is_pinned: true pins the action item above other open items, false unpins it. Example: {"is_pinned":true}.
idstringyesThe action item id to pin or unpin (engagement_action_id from scalepad_lm_list_action_items).

[ScalePad] Move ONE action item within the account's MANUAL sort order (the sort_rank field, and the sort=sort_rank ordering on scalepad_lm_list_action_items). Position is expressed by naming the dragged item's new neighbours, and the vendor's convention is easy to invert — reproduce it exactly: before_id is the action item the dragged item should land BEFORE, i.e. the neighbour that ends up immediately BELOW it in the final list; after_id is the one it should land AFTER, i.e. the neighbour that ends up immediately ABOVE it. Succeeds with HTTP 201 and no body (this tool returns ). This changes only ordering — nothing about assignment, dates or completion.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body naming the new neighbours. Both properties are individually optional and nullable, but AT LEAST ONE must be supplied. before_id: the id of the action item the dragged item should be placed BEFORE (the neighbour immediately below it once the move lands) — omit when dropping at the END of the list, since nothing follows. after_id: the id of the action item the dragged item should be placed AFTER (the neighbour immediately above it) — omit when dropping at the START of the list. Example: {"after_id":"eng_act_11","before_id":"eng_act_12"}.
idstringyesThe action item id being moved — the dragged item (engagement_action_id from scalepad_lm_list_action_items).

[ScalePad] Update an existing action item's core fields — description, rich text, due date and assignees. The vendor calls the payload "replacement action item fields", so treat it as a REPLACEMENT rather than a merge: read the current record with scalepad_lm_get_action_item first and resend what should survive, in particular the nullable due_at. One documented exception to that rule: when description_json is omitted the server DERIVES the rich-text document from the plain description rather than clearing it. Succeeds with HTTP 204 and no body (this tool returns ) — call scalepad_lm_get_action_item to see the result. Completion, pin state and sort position are NOT editable here: use scalepad_lm_update_action_item_completion_status, scalepad_lm_pin_action_item and scalepad_lm_reposition_action_item. Links are not editable here either — use the attach/detach tools.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with a single required update_payload object. Inside it the vendor marks BOTH description and assigned_user_keys required — and description is simultaneously marked DEPRECATED (writing it overwrites any existing rich text), which is the vendor's own inconsistency, not a transcription error: send description_json alongside it to preserve formatting. Note the field is assigned_user_keys HERE while the create call spells the same concept assigned_user_ids. Payload fields: description (required, plain text, deprecated), assigned_user_keys (required array of {"id"} or {"email"} objects), description_json (optional/nullable, ProseMirror JSON document serialized to a string; when omitted the server derives it from the plain description) and due_at (optional/nullable date-time). Example: {"update_payload":{"description":"Replace the aging firewall","description_json":"{"type":"doc","content":[]}","assigned_user_keys":[{"email":"tech@example.com"}],"due_at":"2026-10-15T17:00:00Z"}}.
idstringyesThe action item id to update (engagement_action_id from scalepad_lm_list_action_items).

[ScalePad] Mark ONE action item complete or incomplete. This is the only way to move an action item's is_completed flag — scalepad_lm_update_action_item cannot touch it. Setting is_completed true is what populates the record's completed_at timestamp; sending false reopens the task. Succeeds with HTTP 204 and no body (this tool returns ). Not to be confused with the assessment-level scalepad_lm_update_assessment_completion_status, which is a different resource entirely.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with the single required boolean is_completed: true marks the action item completed, false marks it incomplete. Example: {"is_completed":true}.
idstringyesThe action item id whose completion state changes (engagement_action_id from scalepad_lm_list_action_items).

LM Assessment Templates

ToolPlanAccessSummary
scalepad_lm_create_assessment_templateProWriteCreate a new account-owned assessment template with its own categories, questions and criteria.
scalepad_lm_delete_assessment_templateProDestructivePERMANENTLY remove an assessment template that is no longer needed, along with its categories, questions and criteria.
scalepad_lm_get_assessment_templateFreeRead-onlyGet ONE assessment template in full — every category, question and criterion.
scalepad_lm_list_assessment_templatesFreeRead-onlyList every assessment template OVERVIEW visible to the account.
scalepad_lm_update_assessment_templateProWriteModify an existing assessment template, including its structure — categories, questions and criteria.

[ScalePad] Create a new account-owned assessment template with its own categories, questions and criteria. Returns HTTP 200 with — note the vendor names the key assessment_template_id here, while scalepad_lm_create_assessment returns a bare . The whole structure goes in one body; there is no per-category or per-question create endpoint. ScalePad documents no idempotency-key header, so never blind-retry — re-check with scalepad_lm_list_assessment_templates first. Instantiate the finished template for a client with scalepad_lm_create_assessment.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with a single required assessment_template object. Required on it: title, description, categories (array) and is_category_weight_evenly_distributed (boolean). Each category requires title, questions (array) and is_question_weight_evenly_distributed, and optionally takes description and weight_in_percentage (a double, used only when weights are not evenly distributed). Each question requires title, description, criteria (array) and tag_ids (array of assessment item tag ids), and optionally takes remediation_tips, scoring_instructions, criterion_label_type_enum (nullable; documented only by the example "RiskBased" — no value list is published, and it may be null when the question has no criteria) and weight_in_percentage. Each criterion requires label_enum (documented only by the example "Satisfactory") and description. Example: {"assessment_template":{"title":"Security Assessment","description":"Network security posture","is_category_weight_evenly_distributed":true,"categories":[{"title":"Perimeter","is_question_weight_evenly_distributed":true,"questions":[{"title":"Firewall Configuration","description":"Assess firewall rules","criterion_label_type_enum":"RiskBased","criteria":[{"label_enum":"Satisfactory","description":"Controls implemented"}],"tag_ids":[]}]}]}}.

[ScalePad] PERMANENTLY remove an assessment template that is no longer needed, along with its categories, questions and criteria. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the template is missing or inaccessible. ScalePad documents no soft delete, restore or undo — read the template with scalepad_lm_get_assessment_template first, check its is_in_use flag, and echo the exact title and id back to the user before calling. This deletes the reusable BLUEPRINT, not the client assessments created from it; to delete one filled-in assessment use scalepad_lm_delete_assessment. The vendor does not document what happens to existing assessments that reference a deleted template, so do not assume they are unaffected.

ParamTypeRequiredDefaultDescription
assessmentTemplateIdstringyesThe assessment template id to delete (assessment_template_id from scalepad_lm_list_assessment_templates). Confirm this with the user before calling — the deletion is irreversible.

[ScalePad] Get ONE assessment template in full — every category, question and criterion. Returns {assessment_template, should_promote_control_map} (the second flag is a vendor upsell hint for ScalePad ControlMap, not part of the template). The template carries assessment_template_id, scope, title, description, created_at, updated_at, is_in_use, is_category_weight_evenly_distributed and categories[]. Each category is {assessment_template_category_id, title, description, is_question_weight_evenly_distributed, weight_in_percentage, questions[]}; each question is {assessment_template_question_id, title, description, remediation_tips, scoring_instructions, criterion_label_type_enum, weight_in_percentage, criteria[]}; each criterion is {assessment_template_criterion_id, label_enum, description}. Note two asymmetries: the read does NOT return the per-question tag_ids that create and update require, and the criterion is label_enum here while a filled-in assessment's criterion exposes label_key plus display_label. Check is_in_use before deleting, and use this response as the starting point for scalepad_lm_update_assessment_template, which replaces the whole structure.

ParamTypeRequiredDefaultDescription
assessmentTemplateIdstringyesThe assessment template id to retrieve (assessment_template_id from scalepad_lm_list_assessment_templates).

[ScalePad] List every assessment template OVERVIEW visible to the account. Unusually for this API the operation declares NO parameters at all — no page_size, no cursor, no sort, no filters — and returns {data[]} with no total_count or next_cursor, i.e. the complete set in one unpaginated response. Each row is {assessment_template_id, scope, title, description, created_at, updated_at}; the vendor documents scope only by example ("Application") and publishes no value list, so treat it as an opaque string. Overviews carry no categories, questions or criteria — call scalepad_lm_get_assessment_template for the structure. The assessment_template_id here is what scalepad_lm_create_assessment instantiates and what filter[assessment_template_id] on scalepad_lm_list_assessments accepts.

[ScalePad] Modify an existing assessment template, including its structure — categories, questions and criteria. Treat it as a STRUCTURAL REPLACEMENT rather than a patch: the body carries the entire template, and each category/question you want to keep must be resent WITH its existing id (an id updates that child, null creates a new one), so always start from scalepad_lm_get_assessment_template and edit that structure. The vendor does not document what happens to an existing category or question you leave out of the body, so never rely on omission to remove one — and never omit a child you intend to keep. Succeeds with HTTP 204 and no body (this tool returns ); read the result back with scalepad_lm_get_assessment_template. Changing a template that is already in use (is_in_use on the read) alters the blueprint for future assessments — it is not a way to retro-edit assessments already created from it.

ParamTypeRequiredDefaultDescription
assessmentTemplateIdstringyesThe assessment template id to update (assessment_template_id from scalepad_lm_list_assessment_templates).
bodyJsonstringyesJSON object body with a single required assessment_template object — the same shape the create call takes, plus two nullable id fields that decide create-versus-update per child: assessment_template_category_id on a category and assessment_template_question_id on a question, where the existing id UPDATES that child and null CREATES a new one. Required on the template: title, description, categories and is_category_weight_evenly_distributed. Each category requires title, questions and is_question_weight_evenly_distributed (optional: description, weight_in_percentage). Each question requires title, description, criteria and tag_ids (optional: remediation_tips, scoring_instructions, criterion_label_type_enum, weight_in_percentage). Example: {"assessment_template":{"title":"Security Assessment 2026","description":"Network security posture","is_category_weight_evenly_distributed":true,"categories":[{"assessment_template_category_id":"cat_1","title":"Perimeter","is_question_weight_evenly_distributed":true,"questions":[{"assessment_template_question_id":null,"title":"New question","description":"...","criteria":[{"label_enum":"Satisfactory","description":"..."}],"tag_ids":[]}]}]}}.

LM Assessments

ToolPlanAccessSummary
scalepad_lm_create_assessmentProWriteCreate a new assessment for a client FROM an existing assessment template — the template supplies the categories, questions and criteria, so there is no way to create a free-form assessment here.
scalepad_lm_delete_assessmentProDestructivePERMANENTLY delete an assessment that is no longer relevant.
scalepad_lm_evaluate_assessmentProWriteANSWER an assessment: submit the criterion selected for each question, which is what makes ScalePad RECOMPUTE the assessment's scores (overall_score and each category_score).
scalepad_lm_get_assessmentFreeRead-onlyGet ONE assessment in full, including its evaluation.
scalepad_lm_list_assessmentsFreeRead-onlyList assessment OVERVIEWS across the account.
scalepad_lm_update_assessmentProWriteUpdate an assessment's header fields — title, evaluating user and evaluation date-time.
scalepad_lm_update_assessment_completion_statusProWriteToggle ONE assessment between complete and incomplete.
scalepad_lm_upsert_assessment_internal_commentProWriteCreate or replace the ASSESSMENT-LEVEL INTERNAL comment — the vendor states these are private notes visible ONLY to the MSP, never to the client.
scalepad_lm_upsert_assessment_question_comment_internalProWriteCreate or replace the INTERNAL comment on ONE QUESTION of an assessment — staff-only commentary that is NOT client-visible (vendor path .../questions//comment/internal).
scalepad_lm_upsert_assessment_question_comment_publicProDestructiveCreate or replace the PUBLIC comment on ONE QUESTION of an assessment (vendor path .../questions//comment/public).

[ScalePad] Create a new assessment for a client FROM an existing assessment template — the template supplies the categories, questions and criteria, so there is no way to create a free-form assessment here. Returns HTTP 200 with . Answer the questions afterwards with scalepad_lm_evaluate_assessment. Pick the template with scalepad_lm_list_assessment_templates and the evaluating user with scalepad_lm_list_active_users. ScalePad documents no idempotency-key header, so never blind-retry — re-check with scalepad_lm_list_assessments filtered on the client and template first.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body; all five properties are required. client_key: the owning client, as {"id":"..."} or, when the id is unavailable, {"name":"..."} using the client's unique name. title: the assessment's title. assessment_template_id: the template to instantiate (assessment_template_id from scalepad_lm_list_assessment_templates). evaluate_user_id: the id of the user who performed the evaluation (user_id from scalepad_lm_list_active_users). evaluate_at: the date-time the evaluation was performed. Example: {"client_key":{"id":"cl_1"},"title":"2026 Security Review","assessment_template_id":"dhka7gwd","evaluate_user_id":"usr_9","evaluate_at":"2026-07-30T15:00:00Z"}.

[ScalePad] PERMANENTLY delete an assessment that is no longer relevant. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the assessment is missing or inaccessible. ScalePad documents no soft delete, restore or undo, and the assessment carries its whole evaluation — answers, scores, and both the internal and public commentary — so resolve and echo the exact assessment back to the user first with scalepad_lm_get_assessment. This deletes ONE completed/in-progress assessment, not the reusable template it came from: for that, see scalepad_lm_delete_assessment_template.

ParamTypeRequiredDefaultDescription
idstringyesThe assessment id to delete (the id field from scalepad_lm_list_assessments). Confirm this with the user before calling — the deletion is irreversible.

[ScalePad] ANSWER an assessment: submit the criterion selected for each question, which is what makes ScalePad RECOMPUTE the assessment's scores (overall_score and each category_score). Send one entry per question you are answering — the batch is the unit of work. This is NOT the same as scalepad_lm_update_assessment_completion_status, which only flips the complete/incomplete flag and recomputes nothing. Succeeds with HTTP 204 and no body (this tool returns ); read the new scores back with scalepad_lm_get_assessment. Note the response field is_previously_selected on each criterion, which shows what the prior answer was.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with the single required array question_evaluations. Each entry has both properties required: question_id (an assessment_question_id from the category_list -> question_list of scalepad_lm_get_assessment) and selected_criteria_id — note the vendor's singular "id" on a plural "criteria" — which is the assessment_criterion_id of the chosen answer from that same question's criteria_list. Example: {"question_evaluations":[{"question_id":"lfja7gwd","selected_criteria_id":"crit_2"},{"question_id":"lfja7gwe","selected_criteria_id":"crit_7"}]}.
idstringyesThe assessment id to evaluate (the id field from scalepad_lm_list_assessments).

[ScalePad] Get ONE assessment in full, including its evaluation. Returns where the assessment carries id, assessment_template_id, client {id, label}, title, description, internal_comment (the MSP-private assessment-level note), evaluate_user_id, record_created_at, evaluated_at, updated_at, status, overall_score and category_list[]. Each category is {assessment_category_id, assessment_template_category_id, title, description, category_score, question_list[]}; each question is {assessment_question_id, assessment_template_question_id, title, description, remediation_tips, scoring_instructions, criteria_list[], public_comment, internal_comments}; each criterion is {assessment_criterion_id, assessment_template_criterion_id, label_key, display_label, description, is_selected, is_previously_selected}. Two vendor shapes to expect: public_comment is SINGULAR while internal_comments is PLURAL, yet both are a single comment object of the same schema {assessment_question_comment_id, assessment_question_id, comment_plain_text, comment_json, record_created_at, updated_user_id, updated_at}; and comment_json arrives here as a nested ProseMirror OBJECT even though the write tools take it as a JSON-encoded STRING. The assessment_question_id values in question_list are exactly what scalepad_lm_evaluate_assessment and the two question-comment tools accept, and the assessment_criterion_id values are what evaluate selects.

ParamTypeRequiredDefaultDescription
idstringyesThe assessment id to retrieve (the id field from scalepad_lm_list_assessments).

[ScalePad] List assessment OVERVIEWS across the account. Cursor-paginated: returns {data[], total_count, next_cursor} — keep paging until next_cursor is null, not until a page is short. Each overview row carries id, assessment_template_id, client {id, label}, title, description, evaluate_user_id, record_created_at, evaluated_at, updated_at, status, overall_score, question_count and question_answered_count. Overviews do NOT include the questions, criteria or comments — call scalepad_lm_get_assessment for the full nested structure. This endpoint documents page_size and cursor but NO sort parameter, so results come back in the server's own order.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; never decode or manufacture one. Cursor scans are not atomic, so deduplicate by id.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together and sent as filter[field] query keys. This endpoint documents exactly three fields, with tighter operators than its neighbours: client.id (eq ONLY — note the DOT spelling), status (eq or in; the only documented values are Completed and InProgress) and assessment_template_id (eq ONLY). An omitted operator means eq. Example: {"status":"in:Completed,InProgress","client.id":"eq:cl_1"}. Do not use in on client.id or assessment_template_id here, and do not borrow filters from the action-item list.
pageSizeintegernonullMaximum records per page, 1-200 (clamped to ScalePad's documented 200 platform cap). This operation's schema declares no default and no minimum, so omit it to take the server's own page size.

[ScalePad] Update an assessment's header fields — title, evaluating user and evaluation date-time. All three are required by the schema, so this is a replacement of that trio rather than a patch: read the current values with scalepad_lm_get_assessment and resend the ones that should not change. Succeeds with HTTP 204 and no body (this tool returns ). This tool cannot change answers (that is scalepad_lm_evaluate_assessment), the completion flag (scalepad_lm_update_assessment_completion_status), the internal comment (scalepad_lm_upsert_assessment_internal_comment), or the client and template the assessment was created from.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body; all three properties are required. title: the assessment's title. evaluate_user_id: the id of the user who performed the evaluation (user_id from scalepad_lm_list_active_users). evaluate_at: the date-time the evaluation was performed. Example: {"title":"2026 Security Review (final)","evaluate_user_id":"usr_9","evaluate_at":"2026-07-30T15:00:00Z"}.
idstringyesThe assessment id to update (the id field from scalepad_lm_list_assessments).

[ScalePad] Toggle ONE assessment between complete and incomplete. This changes only the completion flag — it does NOT score anything, does not answer any question, and does not validate that every question has been answered; scoring is scalepad_lm_evaluate_assessment. Succeeds with HTTP 204 and no body (this tool returns ); the resulting state shows up as the status field on scalepad_lm_list_assessments / scalepad_lm_get_assessment (documented values Completed and InProgress). Distinct from scalepad_lm_update_action_item_completion_status, which is a different resource.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with the single required boolean is_completed: true marks the assessment completed, false marks it incomplete. Example: {"is_completed":true}.
idstringyesThe assessment id whose completion state changes (the id field from scalepad_lm_list_assessments).

[ScalePad] Create or replace the ASSESSMENT-LEVEL INTERNAL comment — the vendor states these are private notes visible ONLY to the MSP, never to the client. Scope and format both differ from the question-level tools: this one covers the whole assessment (not a single question) and takes a required PLAIN-TEXT string, with no ProseMirror rich-text field at all. It is an upsert, so the value REPLACES any previous internal comment; read the current text from the assessment's internal_comment field via scalepad_lm_get_assessment before overwriting. Succeeds with HTTP 204 and no body (this tool returns ). For a client-visible note use scalepad_lm_upsert_assessment_question_comment_public — never this tool.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with the single required string internal_comment — private notes visible only to the MSP. Plain text: this endpoint declares no rich-text/ProseMirror field. Example: {"internal_comment":"Client pushed back on the firewall finding; revisit at the next QBR."}.
idstringyesThe assessment id whose internal comment is being set (the id field from scalepad_lm_list_assessments).

[ScalePad] Create or replace the INTERNAL comment on ONE QUESTION of an assessment — staff-only commentary that is NOT client-visible (vendor path .../questions//comment/internal). This is the per-question sibling of the assessment-wide scalepad_lm_upsert_assessment_internal_comment, and the internal counterpart of scalepad_lm_upsert_assessment_question_comment_public: the two question tools take identical bodies and differ ONLY in the final path segment, so confirm you want internal before calling — sending staff notes to the public endpoint exposes them to the client. Upsert semantics: the value REPLACES the question's existing internal comment. Succeeds with HTTP 204 and no body (this tool returns ); the stored result appears as internal_comments (the vendor's plural name for a single object) on the question in scalepad_lm_get_assessment.

ParamTypeRequiredDefaultDescription
assessmentIdstringyesThe assessment id owning the question (the id field from scalepad_lm_list_assessments).
bodyJsonstringyesJSON object body; both properties are optional and nullable, but send at least one. comment_json: the rich-text comment as a ProseMirror JSON document (root {"type":"doc","content":[]}) serialized to a STRING for this field — use it for formatting-preserving writes; ScalePad REJECTS unsupported node or mark types outright, so stay within doc/paragraph/heading/bulletList/orderedList/listItem/hardBreak and bold/italic/underline/strike/link. comment_plain_text: the plain-text representation, which MUST match the text extracted from comment_json when both are sent; pass an empty string to CLEAR the existing comment. Example: {"comment_plain_text":"Firewall rules were last reviewed in 2024.","comment_json":"{"type":"doc","content":[]}"}.
questionIdstringyesThe question id to comment on — an assessment_question_id from the category_list -> question_list of scalepad_lm_get_assessment (NOT the assessment_template_question_id).

[ScalePad] Create or replace the PUBLIC comment on ONE QUESTION of an assessment (vendor path .../questions//comment/public). This is the ONLY comment tool in this group whose content is CLIENT-VISIBLE — it is the narrative the client reads next to that question, so write it for that audience and never put staff-only notes here. The internal counterpart is scalepad_lm_upsert_assessment_question_comment_internal, which takes an identical body and differs only in the final path segment. Upsert semantics: the value REPLACES the question's existing public comment. Succeeds with HTTP 204 and no body (this tool returns ); the stored result appears as public_comment on the question in scalepad_lm_get_assessment.

ParamTypeRequiredDefaultDescription
assessmentIdstringyesThe assessment id owning the question (the id field from scalepad_lm_list_assessments).
bodyJsonstringyesJSON object body; both properties are optional and nullable, but send at least one. Remember this content is shown to the CLIENT. comment_json: the rich-text comment as a ProseMirror JSON document (root {"type":"doc","content":[]}) serialized to a STRING for this field; ScalePad REJECTS unsupported node or mark types outright, so stay within doc/paragraph/heading/bulletList/orderedList/listItem/hardBreak and bold/italic/underline/strike/link. comment_plain_text: the plain-text representation, which MUST match the text extracted from comment_json when both are sent; pass an empty string to CLEAR the existing comment. Example: {"comment_plain_text":"We recommend replacing the perimeter firewall in Q4.","comment_json":"{"type":"doc","content":[]}"}.
questionIdstringyesThe question id to comment on — an assessment_question_id from the category_list -> question_list of scalepad_lm_get_assessment (NOT the assessment_template_question_id).

LM Budget & Forecast

ToolPlanAccessSummary
scalepad_lm_download_budget_forecast_csvFreeRead-onlyExport ONE client's budget forecast as a CSV file.
scalepad_lm_download_budget_forecast_detail_pdfFreeRead-onlyExport ONE client's budget forecast as the DETAILED PDF — the line-item document behind the overview.
scalepad_lm_download_budget_forecast_pdfFreeRead-onlyExport ONE client's budget forecast as the OVERVIEW PDF — the client-facing summary document.
scalepad_lm_get_budget_summaryFreeRead-onlyGet the AGGREGATE per-type totals for one client's budget forecast across the requested period window.
scalepad_lm_list_budget_availabilitiesFreeRead-onlyGet the OPTION LISTS used to construct valid Budget API queries for ONE client — returns .
scalepad_lm_list_budget_contractsFreeRead-onlyList ONE client's contracts with their PER-PERIOD forecast amounts — the contract rows behind the totals in scalepad_lm_get_budget_summary.
scalepad_lm_list_budget_initiativesFreeRead-onlyList ONE client's Lifecycle Manager initiatives with their PER-PERIOD forecast amounts — the budgeting view of the roadmap, as opposed to scalepad_lm_list_initiatives_v2, which returns the same…
scalepad_lm_list_budget_it_debtFreeRead-onlyList ONE client's IT DEBT assets with their PER-PERIOD forecast amounts — the aging hardware whose replacement cost the forecast is carrying, and the row-level detail behind the IT_DEBT totals in…

[ScalePad] Export ONE client's budget forecast as a CSV file. Binary and text/csv cannot cross MCP's JSON tool surface, so this tool does NOT return the file contents: StackJack downloads the CSV, stores it, and returns a JSON envelope with a short-lived READ-ONLY download URL — {SasUrl, ContentType, SuggestedFilename, SizeBytes, ExpiresAt}. The URL is valid for about 30 minutes and then stops working, so fetch it or hand it off promptly; re-run this tool to mint a fresh one. If you want the numbers IN the conversation rather than a file, use scalepad_lm_get_budget_summary (totals) or scalepad_lm_list_budget_contracts / scalepad_lm_list_budget_it_debt (rows) instead. Unlike the two PDF exports, this endpoint documents no include_chart option.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe unique Core identifier of the client whose budget forecast to export (resolve a client name with scalepad_lm_search_clients).
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together and sent as filter[field] query keys. The documented fields, with their ONLY permitted operators: "type" (eq|in), "status" (eq|in), "asset_type.id" (eq|in, DOTTED), "is_third_party" (eq only), "name" (cont|eq). An omitted operator means eq. Example: {"type":"in:INITIATIVE,CONTRACT"}.
optionsJsonstringnonullJSON object of forecast WINDOW options. Recognized keys on this endpoint — anything else is SILENTLY DROPPED rather than rejected: "frequency" (Quarter | Year), "period_count" (integer 1-12), "starting_date" (date-time anchor for the first period; midnight UTC for a calendar-day anchor), "include_overdue" (boolean, DEFAULTS TO TRUE), "include_not_scheduled" (boolean, DEFAULTS TO TRUE). Note the export option set differs from the JSON reads': include_chart and group are NOT documented for the CSV, and it_debt_group_by_asset_type is not forwarded on any export. Example: {"frequency":"Quarter","period_count":4}.

[ScalePad] Export ONE client's budget forecast as the DETAILED PDF — the line-item document behind the overview. Binary cannot cross MCP's JSON tool surface, so this tool does NOT return the file bytes: StackJack downloads the PDF, stores it, and returns a JSON envelope with a short-lived READ-ONLY download URL — {SasUrl, ContentType, SuggestedFilename, SizeBytes, ExpiresAt}, valid for about 30 minutes. This is the only export that accepts a group option, and ScalePad currently implements exactly one value for it: "Period". For the shorter client-facing summary use scalepad_lm_download_budget_forecast_pdf. Generating an export changes nothing in ScalePad.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe unique Core identifier of the client whose detailed budget forecast to export (resolve a client name with scalepad_lm_search_clients).
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together and sent as filter[field] query keys. The documented fields, with their ONLY permitted operators: "type" (eq|in), "status" (eq|in), "asset_type.id" (eq|in, DOTTED), "is_third_party" (eq only), "name" (cont|eq). An omitted operator means eq. Example: {"status":"in:New,InProgress"}.
optionsJsonstringnonullJSON object of forecast WINDOW options. Recognized keys on this endpoint — anything else is SILENTLY DROPPED: "frequency" (Quarter | Year), "period_count" (integer 1-12), "starting_date" (date-time anchor), "include_overdue" (boolean, DEFAULTS TO TRUE), "include_not_scheduled" (boolean, DEFAULTS TO TRUE), "include_chart" (boolean — summary chart on the first page), and "group" (detail grouping; ScalePad documents "Period" as the ONLY supported value today, so send that or omit the key). it_debt_group_by_asset_type is NOT forwarded on any export. Example: {"frequency":"Quarter","period_count":8,"include_chart":true,"group":"Period"}.

[ScalePad] Export ONE client's budget forecast as the OVERVIEW PDF — the client-facing summary document. Binary cannot cross MCP's JSON tool surface, so this tool does NOT return the file bytes: StackJack downloads the PDF, stores it, and returns a JSON envelope with a short-lived READ-ONLY download URL — {SasUrl, ContentType, SuggestedFilename, SizeBytes, ExpiresAt}. The URL is valid for about 30 minutes and then stops working; re-run this tool to mint a fresh one. This is the OVERVIEW; for the per-period breakdown document use scalepad_lm_download_budget_forecast_detail_pdf, which additionally supports a group option. Generating an export changes nothing in ScalePad.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe unique Core identifier of the client whose budget forecast to export (resolve a client name with scalepad_lm_search_clients).
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together and sent as filter[field] query keys. The documented fields, with their ONLY permitted operators: "type" (eq|in), "status" (eq|in), "asset_type.id" (eq|in, DOTTED), "is_third_party" (eq only), "name" (cont|eq). An omitted operator means eq. Example: {"is_third_party":"eq:false"}.
optionsJsonstringnonullJSON object of forecast WINDOW options. Recognized keys on this endpoint — anything else is SILENTLY DROPPED: "frequency" (Quarter | Year), "period_count" (integer 1-12), "starting_date" (date-time anchor), "include_overdue" (boolean, DEFAULTS TO TRUE), "include_not_scheduled" (boolean, DEFAULTS TO TRUE), and "include_chart" (boolean — whether the summary chart appears on the first page; documented on the two PDF exports only, never on the CSV or the JSON reads). it_debt_group_by_asset_type is NOT forwarded on any export. Example: {"frequency":"Year","period_count":3,"include_chart":true}.

[ScalePad] Get the AGGREGATE per-type totals for one client's budget forecast across the requested period window. Returns {data, column_keys, currency_code} — read column_keys to know which period each figure in a data row belongs to, and currency_code to label the amounts. NOT paginated: this endpoint documents no page_size and no cursor, so one call returns the whole summary. It accepts the WIDEST filter set in the budget family (type, status, asset_type.id, is_third_party, name) plus the it_debt_group_by_asset_type option, which the vendor documents only here — the per-row lists accept narrower sets, so do not copy filters between them. For the underlying rows behind these totals, use scalepad_lm_list_budget_contracts and scalepad_lm_list_budget_it_debt.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe unique Core identifier of the client whose budget summary to retrieve (resolve a client name with scalepad_lm_search_clients).
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together and sent as filter[field] query keys. The documented fields, with their ONLY permitted operators: "type" (eq|in — budget type, e.g. INITIATIVE or CONTRACT), "status" (eq|in — initiative status), "asset_type.id" (eq|in, DOTTED — scopes IT_DEBT rows; the vendor's prose mentions only the in: operator while the schema permits eq: as well), "is_third_party" (eq only, e.g. eq:true), "name" (cont|eq). An omitted operator means eq. This is the widest filter set in the budget family — the contracts and it-debt lists accept only subsets. Example: {"type":"in:INITIATIVE,CONTRACT","is_third_party":"eq:false"}.
optionsJsonstringnonullJSON object of forecast WINDOW options. Recognized keys — anything else is SILENTLY DROPPED rather than rejected, so check spelling: "frequency" (Quarter | Year — the reporting cadence), "period_count" (integer 1-12 inclusive), "starting_date" (date-time anchor for the first period; use midnight UTC for a calendar-day anchor), "include_overdue" (boolean, DEFAULTS TO TRUE — overdue items are counted unless you pass false), "include_not_scheduled" (boolean, DEFAULTS TO TRUE), and "it_debt_group_by_asset_type" (boolean; when true, IT_DEBT totals split into one row per asset type with a non-null asset_type discriminator — the vendor documents this key on THIS endpoint only). Example: {"frequency":"Quarter","period_count":4,"starting_date":"2026-10-01T00:00:00Z","include_overdue":false}.

[ScalePad] Get the OPTION LISTS used to construct valid Budget API queries for ONE client — returns . Call this FIRST when building any budget query: it is how you discover the real asset-type ids, statuses and other values that the filters on scalepad_lm_get_budget_summary, scalepad_lm_list_budget_contracts and scalepad_lm_list_budget_it_debt will accept, rather than guessing them. It takes no window options and no filters of its own — client_id is its only parameter — and returns no paging envelope.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe unique Core identifier of the client whose budget query options to retrieve (resolve a client name with scalepad_lm_search_clients).

[ScalePad] List ONE client's contracts with their PER-PERIOD forecast amounts — the contract rows behind the totals in scalepad_lm_get_budget_summary. Cursor-paginated: returns {data[], total_count, next_cursor} — page until next_cursor is null. Only TWO filters are documented here: is_third_party (eq only) and name (cont|eq); the summary's type, status and asset_type.id filters do NOT apply to this endpoint, so do not copy a filter set across. Note this is the BUDGET view of contracts (forecast amounts per period), not the contract records themselves — for titles, billing cycles and costs use scalepad_lm_list_contracts, whose filters are different again (filter[client.id] and filter[expiry_status]).

ParamTypeRequiredDefaultDescription
clientIdstringyesThe unique Core identifier of the client whose budget contract rows to list (resolve a client name with scalepad_lm_search_clients).
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; never decode or manufacture one.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together and sent as filter[field] query keys. Only TWO fields are documented on this endpoint: "is_third_party" (eq only, e.g. eq:true) and "name" (cont|eq — the contract name). An omitted operator means eq. The summary endpoint's type / status / asset_type.id filters are NOT available here. Example: {"name":"cont:Firewall","is_third_party":"eq:false"}.
optionsJsonstringnonullJSON object of forecast WINDOW options. Recognized keys — anything else is SILENTLY DROPPED rather than rejected: "frequency" (Quarter | Year), "period_count" (integer 1-12), "starting_date" (date-time anchor for the first period; midnight UTC for a calendar-day anchor), "include_overdue" (boolean, DEFAULTS TO TRUE), "include_not_scheduled" (boolean, DEFAULTS TO TRUE). Example: {"frequency":"Year","period_count":3}.
pageSizeintegernonullMaximum records per page, 1-200 (clamped to ScalePad's documented 200 platform cap). No default is declared; omit to take the server's own page size.
sortstringnonullSort expression: one or more lowercase snake_case fields, comma-separated, each optionally prefixed '+' (ascending) or '-' (descending). ScalePad publishes NO field allow-list beyond that syntax for this endpoint.

[ScalePad] List ONE client's Lifecycle Manager initiatives with their PER-PERIOD forecast amounts — the budgeting view of the roadmap, as opposed to scalepad_lm_list_initiatives_v2, which returns the same records with their roadmap detail and no forecast figures. Requires a client id in the path, so it is always single-client. Cursor-paginated: returns {data[], total_count, next_cursor} — keep paging until next_cursor is null, not until a page is short, and deduplicate because cursor scans are not atomic. Note this endpoint's filter vocabulary is NOT the initiative list's: only status and name are documented here (no client.id filter — the client is in the path — and no priority, scheduled or date filters). Amounts follow the connector's convention of integer subunits in the account's currency.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe client id whose initiative forecast to list — the client's unique CORE identifier, and a required path segment. This endpoint is single-client by construction; there is no cross-client form and no client.id filter.
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; never decode or manufacture one.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together and sent as filter[field] query keys. This endpoint documents exactly TWO fields, with different operator sets: status (eq|in — the initiative status; values New, Proposed, Approved, InProgress, OnHold, Declined, Completed) and name (cont|eq — the initiative name, where cont is a substring match). Do NOT reuse the richer filter set from scalepad_lm_list_initiatives_v2: client.id, priority, scheduled, scheduled_period, assigned_user_id, created_at and updated_at are NOT documented here. Example: {"status":"in:New,InProgress","name":"cont:firewall"}.
optionsJsonstringnonullJSON object of the shared budget FORECAST options, which shape the projection rather than filtering the rows. Documented keys for this endpoint: frequency (Quarter | Year — the reporting cadence), period_count (integer 1-12 INCLUSIVE — how many periods to project), starting_date (ISO-8601 date-time anchoring the first period; the vendor says to use midnight UTC when anchoring on a calendar day), include_overdue (boolean, DEFAULTS TO TRUE — include items whose status is overdue) and include_not_scheduled (boolean, DEFAULTS TO TRUE — include items with no fiscal quarter set, i.e. those left unscheduled by scalepad_lm_update_initiative_schedule). Both include_* flags default to true, so pass false explicitly to narrow the forecast. Example: {"frequency":"Quarter","period_count":4,"starting_date":"2027-01-01T00:00:00Z","include_not_scheduled":false}.
pageSizeintegernonullMaximum records per page, 1-200 (clamped to ScalePad's documented 200 platform cap). The vendor declares no default and no minimum here, so omit it to take the server's own page size.
sortstringnonullSort expression; '-' prefix descends, '+' or no prefix ascends, and comma-separated multi-sort is accepted. The vendor publishes only the sort SYNTAX for this endpoint (lower-case field names, optional dotted paths) and explicitly "does not publish a field allow-list beyond its syntax/schema", so a field name it does not recognize is rejected rather than ignored.

[ScalePad] List ONE client's IT DEBT assets with their PER-PERIOD forecast amounts — the aging hardware whose replacement cost the forecast is carrying, and the row-level detail behind the IT_DEBT totals in scalepad_lm_get_budget_summary. Cursor-paginated: returns {data[], total_count, next_cursor} — page until next_cursor is null. Only TWO filters are documented here: asset_type.id (eq|in, DOTTED) and name (cont|eq); the summary's type, status and is_third_party filters do NOT apply. Resolve real asset-type ids with scalepad_lm_list_budget_availabilities rather than guessing them. Note that it_debt_group_by_asset_type is a SUMMARY option and is not documented for this endpoint — these rows are already per asset.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe unique Core identifier of the client whose IT-debt rows to list (resolve a client name with scalepad_lm_search_clients).
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; never decode or manufacture one.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together and sent as filter[field] query keys. Only TWO fields are documented on this endpoint: "asset_type.id" (eq|in, DOTTED — the report-asset-type id, e.g. eq:01HK9X2M4N or in:01HK9X2M4N,01HKA2ZQ7V; discover valid ids with scalepad_lm_list_budget_availabilities) and "name" (cont|eq — the asset name). An omitted operator means eq. The summary endpoint's type / status / is_third_party filters are NOT available here. Example: {"asset_type.id":"in:01HK9X2M4N,01HKA2ZQ7V","name":"cont:OptiPlex"}.
optionsJsonstringnonullJSON object of forecast WINDOW options. Recognized keys — anything else is SILENTLY DROPPED rather than rejected: "frequency" (Quarter | Year), "period_count" (integer 1-12), "starting_date" (date-time anchor for the first period; midnight UTC for a calendar-day anchor), "include_overdue" (boolean, DEFAULTS TO TRUE), "include_not_scheduled" (boolean, DEFAULTS TO TRUE). Example: {"frequency":"Quarter","period_count":8,"include_not_scheduled":false}.
pageSizeintegernonullMaximum records per page, 1-200 (clamped to ScalePad's documented 200 platform cap). No default is declared; omit to take the server's own page size.
sortstringnonullSort expression: one or more lowercase snake_case fields, comma-separated, each optionally prefixed '+' (ascending) or '-' (descending). ScalePad publishes NO field allow-list beyond that syntax for this endpoint.

LM Client Groups

ToolPlanAccessSummary
scalepad_lm_assign_client_group_assignmentsProDestructiveASSIGN clients and/or users to a client group.
scalepad_lm_get_client_groupFreeRead-onlyGet ONE client group in full, including its assigned users and clients.
scalepad_lm_list_client_groupsFreeRead-onlyList every client group on the authenticated partner account.
scalepad_lm_search_clients_client_groupsFreeRead-onlyList the client groups that ONE client is assigned to.
scalepad_lm_unassign_client_group_assignmentsProDestructiveUNASSIGN clients and/or users from a client group — a relationship REMOVAL, and the reason this tool is destructive even though the HTTP verb is POST: the vendor expresses the removal as POST to the…

[ScalePad] ASSIGN clients and/or users to a client group. Additive: it adds the members named in the body and leaves the group's existing membership alone, so this is not a replace-the-roster call. Because client groups gate visibility, assigning a USER GRANTS that user access to every client in the group — treat it as a permission change and confirm the intended people, not just the intended clients. Succeeds with HTTP 204 and no body (this tool returns ); verify with scalepad_lm_get_client_group. The exact reverse is scalepad_lm_unassign_client_group_assignments, which takes an identical body — the only difference between the two calls is the tool you pick, so re-read the name before sending.

ParamTypeRequiredDefaultDescription
bodyJsonstringnonullJSON object body with two arrays, BOTH optional and nullable — supply client_keys alone to move only clients, user_keys alone to move only users, or both. client_keys: array of {"id"} or {"name"} objects (client unique id preferred, unique client name when the id is unknown). user_keys: array of {"id"} or {"email"} objects (one of the two must be present per entry). Example: {"client_keys":[{"id":"0d3e7d0k-241a-461r-av15-758a90d70283"}],"user_keys":[{"email":"tech@example.com"}]}.
clientGroupIdstringyesThe client group id that will RECEIVE the members (from scalepad_lm_list_client_groups).

[ScalePad] Get ONE client group in full, including its assigned users and clients. The record is wrapped in a single-key envelope — {client_group: } — not returned bare, so read through client_group rather than expecting the fields at the top level. This is the read to run BEFORE scalepad_lm_unassign_client_group_assignments so the exact membership being removed can be echoed back to the user, and AFTER an assign to confirm the change landed. It answers 'who is in this group'; for 'which groups is this client in', call scalepad_lm_search_clients_client_groups.

ParamTypeRequiredDefaultDescription
clientGroupIdstringyesThe client group id to retrieve (from scalepad_lm_list_client_groups).

[ScalePad] List every client group on the authenticated partner account. Returns {data[]} — a bare array with NO paging envelope: this endpoint declares no page_size, no cursor, no sort and no filters at all, so one call returns the complete set and there is nothing to page through. Use it to resolve a group NAME into the client_group_id that the get/assign/unassign tools require. For the membership of a single group (its assigned clients and users) call scalepad_lm_get_client_group; to ask the inverse question — which groups a given client belongs to — call scalepad_lm_search_clients_client_groups.

[ScalePad] List the client groups that ONE client is assigned to. This is a POST that only READS — it takes a body instead of a query string (the client key is PII-ish, so ScalePad moved it out of the URL) and changes nothing, despite the vendor's own OpenAPI labelling the operation 'write — update/state change'. That label is a vendor misclassification, recorded rather than followed; the operationId is ApiPublicV1ClientsClientGroupsList and the documented purpose is 'List the client groups assigned to a specific client'. Returns {data[]} with no paging envelope. This is the inverse of scalepad_lm_get_client_group, which lists the members OF a group.

ParamTypeRequiredDefaultDescription
bodyJsonstringnonullJSON object body with a single required client_key object identifying the client whose group memberships to list. Inside client_key, BOTH properties are individually optional but one must be present: "id" (the client's unique identifier — preferred) or "name" (the client's unique name, for when the id is not known). Example: {"client_key":{"id":"0d3e7d0k-241a-461r-av15-758a90d70283"}} or {"client_key":{"name":"Contoso Ltd"}}.

[ScalePad] UNASSIGN clients and/or users from a client group — a relationship REMOVAL, and the reason this tool is destructive even though the HTTP verb is POST: the vendor expresses the removal as POST to the '/assignments/unassign' path rather than as a DELETE, so the verb alone understates what it does. Nothing is deleted (neither the group, the clients, nor the users) but visibility IS revoked: unassigning a user removes that user's access to the group's clients, which can hide data from someone mid-work. There is no undo and no partial report, so read the current membership with scalepad_lm_get_client_group first and echo the exact clients and users back to the user before calling. Succeeds with HTTP 200 and NO response body (note: 200, not the 204 its assign counterpart returns) — this tool returns either way. The additive counterpart is scalepad_lm_assign_client_group_assignments, whose body is identical.

ParamTypeRequiredDefaultDescription
bodyJsonstringnonullJSON object body with two arrays, BOTH optional and nullable — supply client_keys alone to remove only clients, user_keys alone to remove only users, or both. client_keys: array of {"id"} or {"name"} objects (the clients to REMOVE from the group). user_keys: array of {"id"} or {"email"} objects (the users to REMOVE, each losing access to the group's clients). Sending an empty or omitted pair removes nothing. Example: {"client_keys":[{"id":"0d3e7d0k-241a-461r-av15-758a90d70283"}],"user_keys":[{"email":"tech@example.com"}]}.
clientGroupIdstringyesThe client group id that currently CONTAINS the members being removed (from scalepad_lm_list_client_groups). Confirm the resolved group and member set with the user before calling.

LM Clients

ToolPlanAccessSummary
scalepad_lm_list_clientsFreeRead-onlyList the Lifecycle Manager clients the caller can access, as full client-console rows.
scalepad_lm_search_client_contactsFreeRead-onlySearch the PSA CONTACTS (people at the client) belonging to ONE client, by name.
scalepad_lm_search_client_membersFreeRead-onlySearch the PSA MEMBERS (staff and resources, not client-side people) associated with ONE client, by name.
scalepad_lm_search_clientsFreeRead-onlySearch clients by name for a PICKER, returning lightweight {id, label} pairs scoped to the caller's client-group access.

[ScalePad] List the Lifecycle Manager clients the caller can access, as full client-console rows. Cursor-paginated: returns {data[], total_count, next_cursor} — keep paging until next_cursor is null, not until a page looks short, and deduplicate by client id because cursor scans are not atomic. This is the roster read: prefer it over scalepad_lm_search_clients when enumerating an account, because that tool returns only {id, label} pairs and silently narrows to the caller's most recently visited clients when no search term is supplied. ScalePad documents NO filter parameters on this endpoint — only free-text search, sort, page_size and cursor — so narrowing to one client is done with search, not with a filter[...] key.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; never decode or manufacture one.
pageSizeintegernonullMaximum records per page, 1-200 (clamped to ScalePad's documented 200 platform cap). This operation declares no default, so omit it to take the server's own page size.
searchstringnonullFree-text search applied to the client display name. Partial matches are supported. Omit to return every accessible client.
sortstringnonullSort expression: one or more lowercase snake_case fields, comma-separated, each optionally prefixed '+' (ascending, the default) or '-' (descending). ScalePad publishes NO field allow-list for this endpoint beyond that syntax, so an unsupported field is rejected as a 400 rather than ignored.

[ScalePad] Search the PSA CONTACTS (people at the client) belonging to ONE client, by name. Returns {data[]} of id/name/email tuples — no paging envelope, no cursor, no total_count. Intended for resolving a person into the reference a Lifecycle Manager opportunity or meeting attendee expects. Two things to respect: the search term must be at least TWO characters (a shorter or empty term returns NO results rather than everything, so this cannot be used to enumerate a client's contacts), and this reads from the PSA rather than from Lifecycle Manager's own contact records — for those, including hidden status, use scalepad_lm_list_contacts. The sibling scalepad_lm_search_client_members searches PSA STAFF instead of client people.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe client id whose PSA contacts to search (resolve a name to an id with scalepad_lm_search_clients).
searchstringnonullFree-text search applied to PSA contact names. Must be at least TWO characters when supplied; an empty or one-character term returns no results, so there is no 'list them all' call here.

[ScalePad] Search the PSA MEMBERS (staff and resources, not client-side people) associated with ONE client, by name. Returns {data[]} of id/name/email tuples — no paging envelope and no cursor. Used to resolve the internal person an opportunity or assignment should reference. As with the contacts lookup, the search term must be at least TWO characters and a shorter or empty term returns NO results, so this is a resolver rather than a way to enumerate members. For the client's own people use scalepad_lm_search_client_contacts.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe client id whose PSA members to search (resolve a name to an id with scalepad_lm_search_clients).
searchstringnonullFree-text search applied to PSA member names. Must be at least TWO characters when supplied; an empty or one-character term returns no results.

[ScalePad] Search clients by name for a PICKER, returning lightweight {id, label} pairs scoped to the caller's client-group access. Cursor-paginated as {data[], total_count, next_cursor}. Use this to resolve a client NAME the user typed into the client id that every other Lifecycle Manager tool wants. One behavior that makes it unsuitable for inventory work: when search is OMITTED this endpoint returns the caller's MOST RECENTLY VISITED clients, not all clients — so an empty-search call that returns five rows does not mean the account has five clients. Use scalepad_lm_list_clients for the full roster and for the complete client record.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page.
pageSizeintegernonullMaximum records per page, 1-200 (clamped to ScalePad's documented 200 platform cap). No default is declared; omit to take the server's own page size.
searchstringnonullFree-text search applied to the client display name; partial and prefix matches are supported. OMITTING this does not return all clients — it returns the caller's most recently visited clients.
sortstringnonullSort expression: lowercase snake_case field(s), comma-separated, '-' prefix for descending. No field allow-list is published for this endpoint beyond that syntax.

LM Contacts

ToolPlanAccessSummary
scalepad_lm_add_meeting_attendee_contactsProWriteADD one or more CLIENT CONTACTS as attendees of a meeting.
scalepad_lm_delete_meeting_attendee_contactsProDestructiveREMOVE client contacts from a meeting's attendee list — plainly, it takes those people off the meeting.
scalepad_lm_get_contactFreeRead-onlyGet ONE Lifecycle Manager contact by its public id.
scalepad_lm_list_contactsFreeRead-onlyList Lifecycle Manager contacts for every client the caller can access.
scalepad_lm_update_contact_hidden_statusProWriteHide or unhide ONE Lifecycle Manager contact.

[ScalePad] ADD one or more CLIENT CONTACTS as attendees of a meeting. The path id is the MEETING, and the body names the contacts. Returns HTTP 201 with — the ids that are now attendees. This tool handles the client side only: the MSP's own user attendees are a separate meetings-surface resource, so an internal user cannot be added here. Remove attendees again with scalepad_lm_delete_meeting_attendee_contacts.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with the single required array contact_keys, one entry per contact to add. Each entry identifies a contact by EITHER id — {"id":"..."} — OR email plus the owning client: {"email":"...","client_key":{"id":"..."}} (the client_key itself takes id, or name when the id is unavailable). The vendor's rule is that id must be supplied when email is missing, and client_key must accompany email when id is missing. Example: {"contact_keys":[{"id":"cont_1"},{"email":"cfo@example.com","client_key":{"id":"cl_1"}}]}.
idstringyesThe MEETING id to add attendees to (from the Lifecycle Manager meetings list) — not a contact id.

[ScalePad] REMOVE client contacts from a meeting's attendee list — plainly, it takes those people off the meeting. Despite the HTTP POST verb this is a removal: the vendor expresses it as a POST to .../attendees/contacts/delete (the older DELETE-on-collection form is retired and no longer available). It removes only the ATTENDANCE record; the contacts themselves and the meeting both survive, but the removal is a real state change with no undo, so echo the exact meeting and every contact back to the user before calling. Succeeds with HTTP 200 and no body (this tool returns ). This is the client-side attendee list only — MSP user attendees are removed through the separate meetings-surface user-attendee operation. To detach a TASK rather than a person, use scalepad_lm_detach_meeting_action_item.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with the single required array contact_keys, one entry per contact to REMOVE from the meeting. Each entry identifies a contact by EITHER id — {"id":"..."} — OR email plus the owning client: {"email":"...","client_key":{"id":"..."}} (client_key takes id, or name when the id is unavailable). Only the contacts you list are removed. Example: {"contact_keys":[{"id":"cont_1"}]}.
idstringyesThe MEETING id to remove attendees from (from the Lifecycle Manager meetings list) — not a contact id.

[ScalePad] Get ONE Lifecycle Manager contact by its public id. Returns with the same record shape as scalepad_lm_list_contacts: contact_id, client_id, client_name, first_name, last_name, display_name, email_address, title, is_hidden, can_unhide, is_manually_created, record_lineage[], licenses[], posture and sat. Unlike the list — which hides hidden contacts unless you ask for them — this read resolves HIDDEN contacts too, and it accepts EITHER the Core API contact id OR the temporary Lifecycle Manager contact id.

ParamTypeRequiredDefaultDescription
contactIdstringyesThe contact id to retrieve — the contact_id from scalepad_lm_list_contacts. Either the Core API contact id or the temporary Lifecycle Manager contact id is accepted, and hidden contacts resolve here.

[ScalePad] List Lifecycle Manager contacts for every client the caller can access. Cursor-paginated: returns {data[], total_count, next_cursor} — keep paging until next_cursor is null, not until a page is short. Each row carries contact_id, client_id, client_name, first_name, last_name, display_name, email_address, title, is_hidden, can_unhide, is_manually_created, record_lineage[] (source_record_id plus the integration_configuration and vendor behind the synced record), licenses[] (SaaS license assignments: assignment_id, asset_id, sku_id, sku_name, category, status, is_trial_free — an empty array when none are linked), posture (is_active, is_mfa_enabled, mfa_methods[], last_active_at, derived from SaaS data) and sat (security-awareness training: training_completed_count, training_available_count, phishing_emails_sent_count, phishing_emails_failed_count). contact_id is the Core API contact id when available, otherwise a TEMPORARY Lifecycle Manager id while the contact is still processing. IMPORTANT default: with no filter[is_hidden] the response contains VISIBLE contacts only.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; never decode or manufacture one. Cursor scans are not atomic, so deduplicate by contact_id.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together and sent as filter[field] query keys. This endpoint documents exactly two fields, both eq ONLY: client_id — note the UNDERSCORE, not the dotted client.id spelling used by the action-item and assessment lists — which limits results to one Lifecycle Manager client, and is_hidden, which controls whether hidden contact records are included (omit it and only visible contacts come back). Example: {"client_id":"eq:cl_1","is_hidden":"eq:true"}. An omitted operator means eq.
pageSizeintegernonullMaximum records per page, 1-200 (clamped to ScalePad's documented 200 platform cap). This operation's schema declares no default and no minimum, so omit it to take the server's own page size.
sortstringnonullSort expression; '-' prefix descends, '+' or no prefix ascends, and comma-separated multi-sort matches the syntax pattern. This operation publishes NO field allow-list — the schema constrains only the expression's shape — so use a field you have confirmed against a live response rather than assuming, and expect a 400 for an unsupported one.

[ScalePad] Hide or unhide ONE Lifecycle Manager contact. Hiding removes the contact from standard contact views (and from scalepad_lm_list_contacts unless filter[is_hidden] asks for hidden records) — it does NOT delete the contact, and ScalePad's Lifecycle Manager contacts surface publishes only this toggle plus the two reads, no create/update/delete. Before unhiding, check the contact's can_unhide flag on scalepad_lm_get_contact: the vendor states a contact deleted in the SOURCE system cannot be restored from Lifecycle Manager. Succeeds with HTTP 204 and no body (this tool returns ).

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with the single required boolean is_hidden: true hides the contact from standard contact views, false makes a hidden contact visible again. Example: {"is_hidden":true}.
contactIdstringyesThe contact id whose visibility changes (contact_id from scalepad_lm_list_contacts).

LM Contracts

ToolPlanAccessSummary
scalepad_lm_attach_contract_assetsProWriteATTACH one or more hardware assets to an agreement (contract), by hardware identifier.
scalepad_lm_bulk_delete_contract_assetsProDestructiveDETACH one or more hardware assets from an agreement (contract) in bulk.
scalepad_lm_create_contractProWriteCreate a new contract (agreement) for a client.
scalepad_lm_delete_contractProDestructivePERMANENTLY delete a contract that is no longer relevant to business operations.
scalepad_lm_get_contractFreeRead-onlyGet ONE contract in full by its id.
scalepad_lm_list_contractsFreeRead-onlyList contracts (agreements) as overview rows, across every client the caller can access.
scalepad_lm_update_contractProDestructiveUpdate an existing contract with FULL details.

[ScalePad] ATTACH one or more hardware assets to an agreement (contract), by hardware identifier. Additive — it adds the named assets and leaves any already-attached hardware alone; nothing is created or deleted. Sent as a PUT (its detach counterpart is a POST), and succeeds with HTTP 204 and no body (this tool returns ). Verify the result from the asset side with scalepad_lm_search_hardware_attached_agreements, which returns {contract_ids: [...]} for a given hardware key. The exact reverse is scalepad_lm_bulk_delete_contract_assets, which takes an identical hardware_keys body.

ParamTypeRequiredDefaultDescription
bodyJsonstringnonullJSON object body with a single required hardware_keys ARRAY. Each entry identifies one hardware asset EITHER by "id" (its Core API identifier) OR by "unique_key" — never both in the same entry. When using unique_key, all THREE of its properties are required: serial_number, model and manufacturer. Example: {"hardware_keys":[{"id":"a9b3f47d-7b39-4d1e-b320-fdd9b72b8ab5"},{"unique_key":{"serial_number":"ABCDEF123456","model":"OptiPlex 7090","manufacturer":"Dell"}}]}.
contractIdstringyesThe contract (agreement) id the assets are attached TO — the parent, and the first path segment.

[ScalePad] DETACH one or more hardware assets from an agreement (contract) in bulk. This is destructive despite the POST verb and the create-like operationId: the vendor expresses the removal as POST to the '/assets/delete' path, so neither the method nor the name signals what it does. It removes only the LINK — neither the contract nor the hardware assets are deleted, and both continue to exist independently — but the agreement stops covering that hardware, which changes warranty and coverage reporting. There is no undo and no partial report, so echo the exact contract and asset set back to the user first; read the current links from the asset side with scalepad_lm_search_hardware_attached_agreements. Succeeds with HTTP 204 and no body (this tool returns ). To destroy the AGREEMENT itself use scalepad_lm_delete_contract; the additive counterpart is scalepad_lm_attach_contract_assets.

ParamTypeRequiredDefaultDescription
bodyJsonstringnonullJSON object body with a single required hardware_keys ARRAY naming the assets to DETACH. Each entry identifies one hardware asset EITHER by "id" (its Core API identifier) OR by "unique_key" — never both in the same entry. When using unique_key, all THREE of its properties are required: serial_number, model and manufacturer. The assets themselves survive. Example: {"hardware_keys":[{"unique_key":{"serial_number":"ABCDEF123456","model":"OptiPlex 7090","manufacturer":"Dell"}}]}.
contractIdstringyesThe contract (agreement) id the assets are detached FROM — the parent, and the first path segment. Confirm the resolved agreement with the user before calling.

[ScalePad] Create a new contract (agreement) for a client. Returns HTTP 200 with . Costs are integer currency SUBUNITS — $100.00 USD is 10000 — so a major-unit value understates the contract 100-fold and corrupts the client's budget forecast. The body carries NO asset links: to attach hardware, call scalepad_lm_attach_contract_assets afterwards. ScalePad documents no idempotency-key header for this operation, so never blind-retry it — re-check with scalepad_lm_list_contracts filtered on filter[client.id] first. Note the create body names the client as a client_key OBJECT while the update body names it as a flat client_id STRING; that asymmetry is the vendor's.

ParamTypeRequiredDefaultDescription
bodyJsonstringnonullJSON object body with two required members: client_key and create_payload. client_key identifies the owning client as {"id":"..."} or, when the id is unknown, {"name":"..."} using the client's unique name. create_payload REQUIRES title (string), impact (Unspecified | Low | Medium | High), status (Active | Inactive), billing_cycle (Monthly | Quarterly | Annually | SemiAnnual | EveryTwoYears | EveryThreeYears | EveryFourYears | EveryFiveYears | EveryTenYears | OneTime | BiMonthly | Unknown | BiWeekly), billing_cost_subunits (INTEGER currency subunits — 10000 means $100.00), billing_cost_type (Actual | Estimate), billing_start_at (date-time, the first billing date) and should_budget_past_end_date (boolean). Optional/nullable: category, location, description, is_third_party, billing_per_seat_cost_subunits (also subunits), billing_number_of_seats, billing_is_auto_renew, end_at (date-time), notify_days_before_end_date (defaults to 30 when unset) and is_billable. Example: {"client_key":{"id":"0d3e7d0k-241a-461r-av15-758a90d70283"},"create_payload":{"title":"Managed Firewall","impact":"Medium","status":"Active","billing_cycle":"Monthly","billing_cost_subunits":10000,"billing_cost_type":"Actual","billing_start_at":"2026-09-01T00:00:00Z","should_budget_past_end_date":false}}.

[ScalePad] PERMANENTLY delete a contract that is no longer relevant to business operations. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the contract is missing or inaccessible. ScalePad documents no soft delete, restore or undo, and deleting an agreement REMOVES ITS COST FROM THE CLIENT'S BUDGET FORECAST — so resolve and echo the exact contract back to the user first with scalepad_lm_get_contract. When the intent is only to stop an agreement going forward, prefer scalepad_lm_update_contract with status Inactive (or an end_at date), which keeps the record and its forecast history. To remove only the HARDWARE attached to an agreement, use scalepad_lm_bulk_delete_contract_assets — that leaves the contract intact.

ParamTypeRequiredDefaultDescription
idstringyesThe contract id to delete (from scalepad_lm_list_contracts). Confirm this with the user before calling — the deletion is irreversible and changes the client's budget forecast.

[ScalePad] Get ONE contract in full by its id. The record is wrapped in a single-key envelope — {contract: } — not returned bare, so read through contract rather than expecting fields at the top level. Run this BEFORE scalepad_lm_update_contract: that call replaces the whole payload rather than merging, so the current values are what you need in order to resend everything that should survive. Run it before scalepad_lm_delete_contract too, so the exact agreement can be echoed back to the user. Costs come back in integer currency SUBUNITS (10000 = $100.00).

ParamTypeRequiredDefaultDescription
idstringyesThe contract id to retrieve (from scalepad_lm_list_contracts, or a contract_ids entry returned by scalepad_lm_search_hardware_attached_agreements).

[ScalePad] List contracts (agreements) as overview rows, across every client the caller can access. Cursor-paginated: returns {data[], total_count, next_cursor} — keep paging until next_cursor is null, not until a page looks short, and deduplicate by contract id because cursor scans are not atomic. Exactly TWO filter fields are documented here and each accepts ONLY the eq operator: client.id (note the DOT — this endpoint spells it client.id, whereas the hardware dashboard and hardware lifecycles endpoints spell the same concept with an UNDERSCORE as client_id, and the hardware asset list takes client_id as a plain query parameter rather than a filter; a wrong spelling is silently ignored and returns unfiltered or empty results rather than an error) and expiry_status, whose only documented values are Expires90Days and Expired. No sort parameter is documented for this endpoint.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; never decode or manufacture one.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together and sent as filter[field] query keys. Only two fields are documented, both eq-only: "client.id" (DOTTED — not client_id) and "expiry_status" (values Expires90Days | Expired). An omitted operator means eq. Example: {"client.id":"eq:0d3e7d0k-241a-461r-av15-758a90d70283","expiry_status":"eq:Expires90Days"}. Do not borrow filter names from the hardware endpoints — they use different spellings.
pageSizeintegernonullMaximum records per page, 1-200 (clamped to ScalePad's documented 200 platform cap). No default is declared; omit to take the server's own page size.

[ScalePad] Update an existing contract with FULL details. The payload is a REPLACEMENT, not a merge — read the current record with scalepad_lm_get_contract and resend every field that should survive, or an omitted nullable (end_at, description, per-seat cost, seat count) is cleared. Costs are integer currency SUBUNITS (10000 = $100.00); a major-unit value silently rewrites the client's forecast. Succeeds with HTTP 204 and no body (this tool returns ) — re-read with scalepad_lm_get_contract to see the result. Attached hardware is NOT editable here: use scalepad_lm_attach_contract_assets and scalepad_lm_bulk_delete_contract_assets. Note the update body names the client as a flat client_id STRING while the create body uses a client_key OBJECT — the vendor's own asymmetry, so do not carry the create shape over.

ParamTypeRequiredDefaultDescription
bodyJsonstringnonullJSON object body with two required members: client_id (a flat STRING — the client identifier, NOT the client_key object that the create call takes) and update_payload. update_payload REQUIRES title, impact (Unspecified | Low | Medium | High), status (Active | Inactive), billing_cycle (Monthly | Quarterly | Annually | SemiAnnual | EveryTwoYears | EveryThreeYears | EveryFourYears | EveryFiveYears | EveryTenYears | OneTime | BiMonthly | Unknown | BiWeekly), billing_cost_subunits (INTEGER subunits — 10000 means $100.00), billing_cost_type (Actual | Estimate), billing_start_at (date-time) and should_budget_past_end_date (boolean). Optional/nullable, and CLEARED if omitted because this is a replacement: category, location, description, is_third_party, billing_per_seat_cost_subunits, billing_number_of_seats, billing_is_auto_renew, end_at, notify_days_before_end_date and is_billable. Example: {"client_id":"0d3e7d0k-241a-461r-av15-758a90d70283","update_payload":{"title":"Managed Firewall","impact":"High","status":"Active","billing_cycle":"Monthly","billing_cost_subunits":12500,"billing_cost_type":"Actual","billing_start_at":"2026-09-01T00:00:00Z","should_budget_past_end_date":false}}.
idstringyesThe contract id to update (from scalepad_lm_list_contracts).

LM Deliverable Templates

ToolPlanAccessSummary
scalepad_lm_create_deliverable_templateProWriteCreate a new deliverable template for the ACCOUNT from scratch, defining its sections and components yourself.
scalepad_lm_create_deliverable_template_from_deliverableProWritePROMOTE an existing client DELIVERABLE into a new reusable account template, extracting its section and component structure.
scalepad_lm_create_deliverable_template_from_templateProWriteCOPY an existing template into a new account template — template in, template out.
scalepad_lm_delete_deliverable_templateProDestructivePERMANENTLY delete a deliverable template, along with its sections and components.
scalepad_lm_delete_deliverable_template_sectionProDestructivePERMANENTLY delete ONE section from a deliverable template, along with the components inside it.
scalepad_lm_delete_deliverable_template_section_componentProDestructivePERMANENTLY delete ONE component from one section of a deliverable template — the narrowest deletion in the template family.
scalepad_lm_get_deliverable_templateFreeRead-onlyGet ONE deliverable template in full by its id, including all of its sections and the components inside them.
scalepad_lm_list_deliverable_template_catalog_componentsFreeRead-onlyList the deliverable catalog components available for use in TEMPLATES, account-wide.
scalepad_lm_list_deliverable_templatesFreeRead-onlyList every deliverable template available to the account — BOTH the ScalePad-provided templates that ship to all accounts AND the custom templates this account owns, in one combined response.
scalepad_lm_update_deliverable_templateProWritePartially update a deliverable template — rename it, and upsert its sections and components.

[ScalePad] Create a new deliverable template for the ACCOUNT from scratch, defining its sections and components yourself. Returns HTTP 201 with , the full new template. This is the only one of the three template-creating tools that accepts a body — the other two copy a source and derive the name. Two template-vs-deliverable differences to get right in the body: a section's summary is PLAIN TEXT here (the deliverable equivalent, summary_json, takes a ProseMirror document), and template sections have NO include_cover_page or include_exec_summary fields, because those PDF controls exist only on deliverables. Call scalepad_lm_list_deliverable_template_catalog_components first for the legal component values. ScalePad documents no idempotency-key header, so never blind-retry — re-check with scalepad_lm_list_deliverable_templates first.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. Optional: name (the template's name). REQUIRED: sections — an array, present even for a one-section template. Each section requires section_key (the section type's business key, e.g. 'Risk', 'Budget', 'Roadmap'), display_order (integer; lower appears first) and components (a required array). Optional per section: name (custom display name; omit for the section type's default) and summary — PLAIN TEXT here, an ordinary executive-summary string, NOT the ProseMirror summary_json that deliverable sections take. Each component requires component_key (e.g. 'Risk', 'Budget', 'Roadmap') and component_type_configuration (the LifecycleManager, Vendor or Custom variant), and may carry configuration, whose nested data object is required when configuration is present. Do NOT send include_cover_page or include_exec_summary — templates have no such fields. Example: {"name":"Standard QBR","sections":[{"section_key":"Budget","display_order":1,"summary":"Budget outlook for the coming quarter","components":[{"component_key":"Budget","component_type_configuration":{"type":"LifecycleManager"}}]}]}.

[ScalePad] PROMOTE an existing client DELIVERABLE into a new reusable account template, extracting its section and component structure. Takes a DELIVERABLE id, not a template id — this is the tool that turns a document you liked into a pattern, and it is the natural thing to run BEFORE deleting a good deliverable so its structure survives. Returns HTTP 201 with . There is NO request body, so you cannot name the new template in this call — rename it afterwards with scalepad_lm_update_deliverable_template. Expect a lossy conversion at the rich-text boundary: deliverable sections carry ProseMirror summary_json while template sections carry a plain-text summary, so formatting in section summaries can be flattened. The source deliverable is unchanged. To copy a template rather than a deliverable, use scalepad_lm_create_deliverable_template_from_template.

ParamTypeRequiredDefaultDescription
deliverableIdstringyesThe source DELIVERABLE id whose structure becomes the new template (from scalepad_lm_get_deliverable or either deliverables list). Not a template id.

[ScalePad] COPY an existing template into a new account template — template in, template out. The source may be a ScalePad-provided template (available to every account) or one of your own custom templates, which makes this the standard way to get an editable copy of a ScalePad template you are not allowed to modify in place. Returns HTTP 201 with . There is NO request body, so the new template's name is derived rather than chosen; rename it with scalepad_lm_update_deliverable_template. The source template is unchanged. Do not confuse the three neighbours: this one copies a TEMPLATE to a TEMPLATE, scalepad_lm_create_deliverable_template_from_deliverable promotes a DELIVERABLE to a template, and scalepad_lm_create_deliverable_from_template instantiates a template as a CLIENT's deliverable (the only one of the three that takes a client id).

ParamTypeRequiredDefaultDescription
templateIdstringyesThe source TEMPLATE id to copy (from scalepad_lm_list_deliverable_templates). May be a ScalePad-provided or a custom template. Not a deliverable id.

[ScalePad] PERMANENTLY delete a deliverable template, along with its sections and components. Succeeds with HTTP 204 and no body (this tool returns ); 404 if it is missing or inaccessible. ScalePad documents no soft delete, restore or undo, so resolve and echo the exact template back to the user first — read it with scalepad_lm_get_deliverable_template. This is ACCOUNT-WIDE: the template disappears for every user, so anyone who builds QBRs from it loses that starting point. ScalePad does NOT document what happens to deliverables previously created from this template, so do not promise the user those documents are unaffected — treat it as unverified. Deliverables are separate records created by copy, so they are unlikely to vanish, but say so as an expectation rather than a fact.

ParamTypeRequiredDefaultDescription
templateIdstringyesThe deliverable template id to delete (from scalepad_lm_list_deliverable_templates). Confirm with the user before calling — the deletion is irreversible and affects the whole account.

[ScalePad] PERMANENTLY delete ONE section from a deliverable template, along with the components inside it. The template itself and its other sections survive. Succeeds with HTTP 204 and no body (this tool returns ). No undo, so echo the exact section back to the user first — list the current sections with scalepad_lm_get_deliverable_template. Note that scalepad_lm_update_deliverable_template canNOT do this: omitting a section from that patch leaves it unchanged rather than removing it, which is why this tool exists. This edits the TEMPLATE, so it changes what FUTURE deliverables built from it will contain; deliverables already created from the template are separate records and are not being edited here. To remove one component instead, use scalepad_lm_delete_deliverable_template_section_component.

ParamTypeRequiredDefaultDescription
sectionIdstringyesThe SECTION id to delete (from the sections array on scalepad_lm_get_deliverable_template). Confirm with the user before calling — its components go with it.
templateIdstringyesThe TEMPLATE id that owns the section — the parent, and the first path segment. Not a deliverable id.

[ScalePad] PERMANENTLY delete ONE component from one section of a deliverable template — the narrowest deletion in the template family. The section and the rest of the template survive. Succeeds with HTTP 204 and no body (this tool returns ); no undo, so echo the exact component back to the user first. The three ids are hierarchical and must be passed in DESCENDING order: TEMPLATE, then the section inside it, then the component inside that section — all three come from scalepad_lm_get_deliverable_template, and a component id belonging to a different section will not resolve. The first id is a template id, NOT a deliverable id: the identically-shaped deliverable tool is scalepad_lm_delete_deliverable_section_component, and passing one family's ids to the other is the mistake to avoid here. This changes what FUTURE deliverables built from the template contain, not existing ones.

ParamTypeRequiredDefaultDescription
componentIdstringyesThe COMPONENT id inside that section — the innermost target, the one actually deleted. Confirm with the user before calling.
sectionIdstringyesThe SECTION id inside that template — the middle level.
templateIdstringyesThe TEMPLATE id — the outermost parent, and the first path segment. Not a deliverable id.

[ScalePad] Get ONE deliverable template in full by its id, including all of its sections and the components inside them. The response is WRAPPED as {"deliverable_template": } — note the key is deliverable_template, not the deliverable key that the deliverable read returns, so a caller reusing the deliverable parsing path will find nothing. Works for both ScalePad-provided and custom templates. This is the read to call before scalepad_lm_update_deliverable_template, whose upsert semantics need the existing section and component ids to edit them rather than duplicate them, and before either delete-section tool.

ParamTypeRequiredDefaultDescription
templateIdstringyesThe deliverable template id to retrieve (from scalepad_lm_list_deliverable_templates).

[ScalePad] List the deliverable catalog components available for use in TEMPLATES, account-wide. Returns {data: [...]}, no paging envelope, no arguments. This is the template-authoring counterpart of scalepad_lm_list_client_deliverable_components, and the distinction matters: this list is account-wide and takes no client id, whereas the client version reflects what one specific client's vendor integrations make available. Because a template is not bound to a client, a component that is legal in a template can still be unavailable when that template is instantiated for a client whose integrations do not include it — so check the client list too before promising a client-specific result. Use this to pick legal component_key and component_type_configuration values for scalepad_lm_create_deliverable_template and scalepad_lm_update_deliverable_template.

[ScalePad] List every deliverable template available to the account — BOTH the ScalePad-provided templates that ship to all accounts AND the custom templates this account owns, in one combined response. Returns {data: [...]} with no paging envelope (no total_count, no next_cursor) and takes no arguments, so one call returns the complete set. Use this to find the templateId for scalepad_lm_create_deliverable_from_template (build a client's document) or scalepad_lm_create_deliverable_template_from_template (copy the template itself). The response mixes the two kinds together, so check a template's ownership before offering to edit or delete it — a ScalePad-provided template is not yours to change.

[ScalePad] Partially update a deliverable template — rename it, and upsert its sections and components. Returns HTTP 200 with , the updated template. This is a true PATCH with UPSERT semantics, not a replacement: only the fields you send are touched, an element WITH an id is updated, an element WITHOUT an id is CREATED, and anything omitted is left unchanged. So this is the only way to ADD a section or component to a template, and omission never removes one — deletion is scalepad_lm_delete_deliverable_template_section / _component. Read the template first with scalepad_lm_get_deliverable_template so you send existing ids and edit rather than duplicate. This is also the rename step after either create-from tool, since neither accepts a name. ScalePad does not document whether a ScalePad-provided template can be patched, so expect an error on one and prefer copying it first with scalepad_lm_create_deliverable_template_from_template.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body; every field is optional. name: the template's business-facing name — when provided must be non-empty and at most 255 characters. sections: an array with upsert semantics — include id to update an existing section, omit id to create a new one (section_key is then required), and omit a section entirely to leave it untouched. Per section: name (send an EMPTY STRING to revert to the section type's default name), summary (PLAIN TEXT here — not the ProseMirror summary_json that deliverable sections use), display_order, and components. Components upsert the same way — id to update, no id to create (component_key and component_type_configuration then required) — and a component's configuration object requires its data field when present. Template sections have NO include_cover_page or include_exec_summary; do not send them. Example: {"name":"Standard QBR v2","sections":[{"id":"sec_1","summary":"Updated budget narrative","display_order":2}]}.
templateIdstringyesThe deliverable template id to update (from scalepad_lm_list_deliverable_templates).

LM Deliverables

ToolPlanAccessSummary
scalepad_lm_create_client_deliverableProWriteCreate a NEW deliverable from scratch for one client, specifying its sections and components yourself.
scalepad_lm_create_deliverable_from_templateProWriteCreate a new DELIVERABLE for one client by copying a TEMPLATE — client-scoped, and the easiest way to start a document: the template supplies the whole section/component structure so the body only…
scalepad_lm_create_deliverable_share_general_linkProDestructiveCreate the general share link for a deliverable — a client-facing URL, protected by a generated password, that lets someone outside your account view the document.
scalepad_lm_delete_deliverableProDestructivePERMANENTLY delete an entire deliverable, INCLUDING every section and every component inside it.
scalepad_lm_delete_deliverable_sectionProDestructivePERMANENTLY delete ONE SECTION of a deliverable, INCLUDING every component inside that section.
scalepad_lm_delete_deliverable_section_componentProDestructivePERMANENTLY delete ONE COMPONENT from one section of a deliverable — the narrowest destructive action here: the section and its other components survive.
scalepad_lm_download_deliverable_pdfFreeRead-onlyExport ONE deliverable as the client-facing PDF that ScalePad renders server-side.
scalepad_lm_get_deliverableFreeRead-onlyGet ONE deliverable in full by its id, INCLUDING all of its sections and every component inside them — this is the tool that shows you the document's structure, and therefore the tool that gives you…
scalepad_lm_get_deliverable_presentationFreeRead-onlyGet a deliverable in its PRESENTATION form — the trimmed, render-ready view used to display it to a client.
scalepad_lm_get_deliverable_share_general_linkFreeRead-onlyRead the general share link for ONE deliverable — the client-facing URL that lets someone outside your account view the document.
scalepad_lm_list_client_deliverable_componentsFreeRead-onlyList the catalog COMPONENTS available to ONE specific client — the blocks you may place in a new or existing deliverable for that client.
scalepad_lm_list_client_deliverable_integrationsFreeRead-onlyList the vendor integrations ALREADY INTEGRATED for ONE specific client, with each one's configuration, status and vendor details.
scalepad_lm_list_client_deliverablesFreeRead-onlyList the deliverables belonging to ONE specific client, identified by the client id in the path.
scalepad_lm_list_deliverable_catalog_integrationsFreeRead-onlyList every deliverable integration available in the catalog ACCOUNT-WIDE — the vendor integrations that CAN be configured for clients.
scalepad_lm_list_deliverables_by_accountFreeRead-onlyList deliverables across the WHOLE account, every client together.
scalepad_lm_refresh_deliverable_sectionProDestructiveRe-pull live data into ONE SECTION of a deliverable — this refreshes the data of ALL components under that section at once, replacing each one's stored snapshot with current values from its source.
scalepad_lm_refresh_deliverable_section_componentProDestructiveRe-pull live data into ONE COMPONENT of one section of a deliverable — the narrowest refresh available: it replaces just that component's stored snapshot with current values from its source, leaving…
scalepad_lm_regenerate_deliverable_share_general_linkProDestructiveReplace a deliverable's general share link with a brand-new one.
scalepad_lm_revoke_deliverable_share_general_linkProDestructiveRevoke a deliverable's general share link, ending outside access to the document.
scalepad_lm_update_deliverableProWritePatch an existing deliverable — rename it, move it between Draft and Published, and upsert its sections and components.

[ScalePad] Create a NEW deliverable from scratch for one client, specifying its sections and components yourself. Returns HTTP 201 with (containing the new deliverable's id, sections and components). Use this when there is no suitable template; to build from a template instead — far less body to get right — use scalepad_lm_create_deliverable_from_template, and to turn a deliverable you already like into a reusable template afterwards use scalepad_lm_create_deliverable_template_from_deliverable. Discover the legal component_key / component_type_configuration values for THIS client first with scalepad_lm_list_client_deliverable_components, since a client's available components depend on which vendor integrations are set up for it. ScalePad documents no idempotency key for this operation, so never blind-retry it — re-check with scalepad_lm_list_client_deliverables first.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. Required: sections — an ARRAY, and it must be present even for a one-section document. Each section requires section_key (the section type's business key, e.g. "Risk", "Budget", "Roadmap") and display_order (int32; lower numbers appear first), plus components — a required ARRAY, where each component requires component_key (e.g. "Risk", "Budget", "Roadmap") and component_type_configuration (the type discriminator; one of the LifecycleManager, Vendor or Custom shapes). Optional per section: name (custom display name; when omitted the section type's default name is used), summary_json (this section's executive summary as a ProseMirror JSON document, root {"type":"doc","content":[]}, serialized to a STRING — structured JSON only, never HTML or Markdown), include_cover_page and include_exec_summary (booleans, both default TRUE when omitted, controlling what appears in the generated PDF). Optional per component: configuration, whose nested data object is required when configuration is supplied. Optional at the top level: name (the deliverable's own name). Example: {"name":"Q3 Business Review","sections":[{"section_key":"Budget","display_order":1,"summary_json":"{"type":"doc","content":[]}","components":[{"component_key":"Budget","component_type_configuration":{"type":"LifecycleManager"}}]}]}.
clientIdstringyesThe client id the new deliverable belongs to (a ScalePad client id).

[ScalePad] Create a new DELIVERABLE for one client by copying a TEMPLATE — client-scoped, and the easiest way to start a document: the template supplies the whole section/component structure so the body only needs an optional name. Returns HTTP 201 with . The source may be a ScalePad-provided template (available to every account) or a custom template your account owns; list both with scalepad_lm_list_deliverable_templates. Do not confuse this with the two template-producing tools that read the other direction: scalepad_lm_create_deliverable_template_from_deliverable promotes an existing DELIVERABLE into a template, and scalepad_lm_create_deliverable_template_from_template copies a TEMPLATE into another template. This tool is the only one of the three that takes a client id, and the only one of the three that accepts a request body.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. The vendor declares the body required but its only field is optional: name (nullable) — the new deliverable's name. Send to accept whatever name ScalePad derives from the template. Example: {"name":"Acme Q3 Business Review"}.
clientIdstringyesThe client id the new deliverable will be created FOR — the first path segment. This is a client id, not a template id.
templateIdstringyesThe TEMPLATE id to copy the structure FROM — the second path segment (from scalepad_lm_list_deliverable_templates). Either a ScalePad template or one of your account's custom templates.

[ScalePad] PERMANENTLY delete an entire deliverable, INCLUDING every section and every component inside it. This is the widest destructive action in the deliverables family — it destroys the whole document, not one part of it. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the deliverable is missing or inaccessible. ScalePad documents no soft delete, restore or undo, so read the document with scalepad_lm_get_deliverable and echo its name and client back to the user for explicit confirmation first. If the real intent is narrower, prefer the narrower tool: remove one part with scalepad_lm_delete_deliverable_section or scalepad_lm_delete_deliverable_section_component, take it out of the client's hands with scalepad_lm_revoke_deliverable_share_general_link, or simply set status back to Draft with scalepad_lm_update_deliverable. Preserve the structure for reuse with scalepad_lm_create_deliverable_template_from_deliverable BEFORE deleting.

ParamTypeRequiredDefaultDescription
deliverableIdstringyesThe deliverable id to delete. Confirm this with the user before calling — the deletion is irreversible and takes all sections and components with it.

[ScalePad] PERMANENTLY delete ONE SECTION of a deliverable, INCLUDING every component inside that section. The rest of the deliverable survives. Succeeds with HTTP 204 and no body (this tool returns ); 404 if either id is missing or inaccessible. There is no undo, so read the document with scalepad_lm_get_deliverable first and echo the section's name back to the user for confirmation — section ids are opaque and a wrong-but-valid id destroys the wrong section silently. If the data is merely out of date rather than unwanted, refresh it with scalepad_lm_refresh_deliverable_section instead of deleting. To drop a single block and keep the section, use scalepad_lm_delete_deliverable_section_component; to destroy the whole document, scalepad_lm_delete_deliverable.

ParamTypeRequiredDefaultDescription
deliverableIdstringyesThe DELIVERABLE id that owns the section — the first path segment.
sectionIdstringyesThe SECTION id to delete — the second path segment (from the sections array on scalepad_lm_get_deliverable). Every component inside it is deleted too. Confirm with the user first.

[ScalePad] PERMANENTLY delete ONE COMPONENT from one section of a deliverable — the narrowest destructive action here: the section and its other components survive. Succeeds with HTTP 204 and no body (this tool returns ); 404 if any id is missing or inaccessible. No undo, so read the document with scalepad_lm_get_deliverable first and confirm the exact block with the user. All three ids must belong to the same document in the order deliverable → section → component: transposing a section id and a component id can still resolve and delete something you did not mean to. If the block is only stale, refresh it with scalepad_lm_refresh_deliverable_section_component instead. To remove the whole section use scalepad_lm_delete_deliverable_section.

ParamTypeRequiredDefaultDescription
componentIdstringyesThe COMPONENT id to delete — the THIRD path segment (from that section's components array on scalepad_lm_get_deliverable). Confirm with the user first.
deliverableIdstringyesThe DELIVERABLE id that owns the section — the FIRST path segment.
sectionIdstringyesThe SECTION id that owns the component — the SECOND path segment (from the sections array on scalepad_lm_get_deliverable).

[ScalePad] Export ONE deliverable as the client-facing PDF that ScalePad renders server-side. Binary cannot cross MCP's JSON tool surface, so this tool does NOT return the file bytes: StackJack downloads the PDF, stores it, and returns a JSON envelope with a short-lived READ-ONLY download URL — {SasUrl, ContentType, SuggestedFilename, SizeBytes, ExpiresAt}. The URL is valid for about 30 minutes and then stops working, so fetch or hand it off promptly rather than saving it for later; re-run this tool to mint a fresh one. SuggestedFilename comes from the filename ScalePad supplies with the attachment. Note this is the MSP-side export of the document itself; it is unrelated to the client-facing share link (scalepad_lm_get_deliverable_share_general_link), and it does not publish, refresh or otherwise change the deliverable.

ParamTypeRequiredDefaultDescription
deliverableIdstringyesThe deliverable id to export as PDF (from scalepad_lm_list_client_deliverables or scalepad_lm_list_deliverables_by_account).

[ScalePad] Get ONE deliverable in full by its id, INCLUDING all of its sections and every component inside them — this is the tool that shows you the document's structure, and therefore the tool that gives you the section ids and component ids the delete/refresh tools require. Returns . A deliverable's status is documented as 'Draft' or 'Published' on the write side; the vendor also documents that a PUBLISHED deliverable is automatically moved back to 'Draft' whenever one of its components requires a new snapshot. For the render-ready view (client name, MSP branding, component output only) use scalepad_lm_get_deliverable_presentation; for the client-facing PDF use scalepad_lm_download_deliverable_pdf.

ParamTypeRequiredDefaultDescription
deliverableIdstringyesThe deliverable id to retrieve (from scalepad_lm_list_client_deliverables or scalepad_lm_list_deliverables_by_account).

[ScalePad] Get a deliverable in its PRESENTATION form — the trimmed, render-ready view used to display it to a client. This returns JSON, NOT a file: the response is {deliverable, branding}, where deliverable carries only what a renderer needs (deliverable name, client name, sections with their components' rendered output) and branding carries the MSP's own branding for the presentation header. It is deliberately narrower than scalepad_lm_get_deliverable, which returns the full editable structure including the configuration of every component. If you want the client-facing PDF file, that is scalepad_lm_download_deliverable_pdf — this tool never returns bytes or a download URL.

ParamTypeRequiredDefaultDescription
deliverableIdstringyesThe deliverable id to retrieve in presentation form (from scalepad_lm_list_client_deliverables or scalepad_lm_list_deliverables_by_account).

[ScalePad] List the catalog COMPONENTS available to ONE specific client — the blocks you may place in a new or existing deliverable for that client. CLIENT-SCOPED on purpose: the result covers both the built-in Lifecycle Manager components and any vendor-integration components that have been set up FOR THIS CLIENT, so two clients legitimately return different catalogs. Call this before scalepad_lm_create_client_deliverable or scalepad_lm_update_deliverable to learn the component_key and component_type_configuration values that will actually be accepted for that client. Returns {data: [...]} with no paging envelope. The TEMPLATE equivalent is account-wide, not client-scoped: scalepad_lm_list_deliverable_template_catalog_components.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe client id whose available deliverable components to list (a ScalePad client id).

[ScalePad] List the vendor integrations ALREADY INTEGRATED for ONE specific client, with each one's configuration, status and vendor details. This is the client-scoped 'what is actually wired up here' read — contrast scalepad_lm_list_deliverable_catalog_integrations, which takes no id and lists every integration the account COULD configure. Use this to explain why a component the account supports is unavailable for a particular client, or to check an integration's status before relying on its data in a deliverable. Returns {data: [...]} with no paging envelope. For the client's usable component blocks (rather than the integrations behind them) use scalepad_lm_list_client_deliverable_components.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe client id whose already-integrated deliverable integrations to list (a ScalePad client id).

[ScalePad] List the deliverables belonging to ONE specific client, identified by the client id in the path. This is the per-CLIENT list — for every deliverable in the account regardless of client, use scalepad_lm_list_deliverables_by_account instead, which takes no id. Returns {data: [...]} and NOTHING else: this envelope has no total_count and no next_cursor, unlike the account-wide list, so there is nothing to page through. NOTE: the vendor marks this operation DEPRECATED and names the account-wide list as its replacement; it still works, but prefer scalepad_lm_list_deliverables_by_account with filter[client.id] for new work — that route also pages and sorts, which this one cannot — and be aware ScalePad may retire this path. One difference that matters if you compare results between the two: this legacy route returns the RAW status values 'Draft' and 'Published', whereas the account-wide route maps a stored 'Draft' to 'Unpublished' in both its output and its filter[status] vocabulary. The same deliverable therefore reports a different status string depending on which list you asked. Hydrate any row with scalepad_lm_get_deliverable to see its sections and components.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe client id whose deliverables to list (a ScalePad client id, e.g. from the Core clients list). Returns only this client's deliverables.

[ScalePad] List every deliverable integration available in the catalog ACCOUNT-WIDE — the vendor integrations that CAN be configured for clients. Takes no id: this is the whole menu, not the subset in use. To see what is actually wired up for one client (with configuration, status and vendor details) use the client-scoped scalepad_lm_list_client_deliverable_integrations; to see the component blocks a specific client can place in a document use scalepad_lm_list_client_deliverable_components. Returns {data: [...]} with no paging envelope.

[ScalePad] List deliverables across the WHOLE account, every client together. Takes no client id in the path — that is the difference from scalepad_lm_list_client_deliverables, which is scoped to one client — and it is the vendor's stated replacement for that now-DEPRECATED per-client operation, so prefer this tool and narrow with filter[client.id] instead. Cursor-paginated: returns {data, total_count, next_cursor} — keep paging until next_cursor is null rather than until a page looks short, and deduplicate by deliverable id, since cursor scans are not atomic. Rows are BASIC info (name, status, client, updated_at), not the full document — hydrate one with scalepad_lm_get_deliverable to see its sections and components. VENDOR STATUS VOCABULARY SPLIT, recorded rather than normalized: filter[status] on THIS route takes Published or Unpublished, because the account-wide route maps a stored 'Draft' to 'Unpublished'; the legacy client-scoped route returns raw 'Draft'/'Published', and the write side (scalepad_lm_update_deliverable) accepts only 'Draft' or 'Published'. Filter with the route's own vocabulary, not the one you would write.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; never decode or manufacture one.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together and sent as filter[field] query keys. The documented fields are exactly client.id (eq|in — note the DOT), status (eq only; values Published or Unpublished on THIS route, NOT the Draft/Published vocabulary the update tool uses), created_by (eq|in), has_meeting (eq only; true or false) and name (eq|cont, where cont is a case-insensitive substring match — use it for free-form search). An omitted operator means eq, and 'in' takes a comma-separated list. Example: {"client.id":"eq:0d3e7d0k-241a-461r-av15-758a90d70283","status":"eq:Published","name":"cont:business review"}. Do not use an operator a field does not list, and do not borrow filters from another resource.
pageSizeintegernonullMaximum records per page, 1-200 (clamped to ScalePad's documented 200 platform cap). This operation's schema declares no default and no minimum, so omit it to take the server's own page size.
sortstringnonullSort expression; '-' prefix descends, '+' or no prefix ascends. Only three fields are documented for this endpoint: name, status and updated_at. Comma-separate for multiple keys (e.g. '-updated_at,name').

[ScalePad] Re-pull live data into ONE SECTION of a deliverable — this refreshes the data of ALL components under that section at once, replacing each one's stored snapshot with current values from its source. Use it when the underlying account data has moved on since the document was built and the whole section is stale. Succeeds with HTTP 204 and no body (this tool returns ); read the new values back with scalepad_lm_get_deliverable or scalepad_lm_get_deliverable_presentation. This does NOT delete anything and does not change the document's structure — only the data inside it. For a single stale block, use the narrower scalepad_lm_refresh_deliverable_section_component. Note that ScalePad documents a published deliverable returning to 'Draft' status when a component requires a new snapshot, so check status afterwards before assuming the client-facing view is still published.

ParamTypeRequiredDefaultDescription
deliverableIdstringyesThe DELIVERABLE id that owns the section — the first path segment.
sectionIdstringyesThe SECTION id to refresh — the second path segment. Section ids come from the sections array on scalepad_lm_get_deliverable. Every component under this section is refreshed.

[ScalePad] Re-pull live data into ONE COMPONENT of one section of a deliverable — the narrowest refresh available: it replaces just that component's stored snapshot with current values from its source, leaving the section's other components untouched. Succeeds with HTTP 204 and no body (this tool returns ); read the new values back with scalepad_lm_get_deliverable. This changes the data inside the block only — it deletes nothing and alters no structure. To refresh every component in the section in one call, use scalepad_lm_refresh_deliverable_section. All three path ids must belong to the same document, in the order deliverable → section → component; a wrong-but-valid id silently targets a different block. Note ScalePad documents a published deliverable moving back to 'Draft' when a component requires a new snapshot.

ParamTypeRequiredDefaultDescription
componentIdstringyesThe COMPONENT id to refresh — the THIRD path segment (from that section's components array on scalepad_lm_get_deliverable). Only this component is refreshed.
deliverableIdstringyesThe DELIVERABLE id that owns the section — the FIRST path segment.
sectionIdstringyesThe SECTION id that owns the component — the SECOND path segment (from the sections array on scalepad_lm_get_deliverable).

[ScalePad] Patch an existing deliverable — rename it, move it between Draft and Published, and upsert its sections and components. Returns HTTP 200 with the updated . This is a true PARTIAL update with UPSERT semantics, so it behaves unlike most StackJack update tools: only the fields you send are touched, a section or component sent WITH an id is updated, one sent WITHOUT an id is CREATED, and anything you omit entirely is left unchanged. That means omission never deletes — to remove a section or component you must call scalepad_lm_delete_deliverable_section or scalepad_lm_delete_deliverable_section_component. Two status vocabularies coexist in the vendor's own docs and neither is a transcription error: THIS body accepts only 'Draft' or 'Published', while the account-wide list's filter[status] documents Published and Unpublished. ScalePad also documents that a published deliverable moves itself back to 'Draft' when one of its components requires a new snapshot, so a status you set can legitimately change on its own.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body; every field is optional. name (nullable): the business-facing name — when supplied must be non-empty and at most 255 characters. status (nullable): must be either "Draft" or "Published" for this endpoint. sections (nullable array, upsert): each entry may carry id (update that existing section) or omit id (create a new one); section_key is REQUIRED when creating and optional when updating; name (send an EMPTY STRING to revert to the section type's default name); summary_json (ProseMirror JSON document serialized to a string — never HTML or Markdown); display_order (int32, lower first); include_cover_page and include_exec_summary (booleans, PDF inclusion). sections.components (nullable array, same upsert rule): id to update or omit to create; component_key and component_type_configuration are REQUIRED when creating, optional when updating; configuration is optional but its nested data object is required when configuration is present. Example: {"status":"Published","sections":[{"id":"sec_1","name":"","display_order":2}]}.
deliverableIdstringyesThe deliverable id to update (from scalepad_lm_list_client_deliverables or scalepad_lm_list_deliverables_by_account).

LM Goal Templates

ToolPlanAccessSummary
scalepad_lm_create_goal_templateProWriteCreate a new account-scoped Lifecycle Manager goal template.
scalepad_lm_delete_goal_templateProDestructivePERMANENTLY delete an account-owned Lifecycle Manager goal template.
scalepad_lm_get_goal_templateFreeRead-onlyGet ONE Lifecycle Manager goal template in full by its id.
scalepad_lm_list_goal_templatesFreeRead-onlyList every Lifecycle Manager goal template visible to the account, optionally narrowed by a title search.
scalepad_lm_update_goal_templateProWriteUpdate an existing account-owned goal template.

[ScalePad] Create a new account-scoped Lifecycle Manager goal template. Succeeds with HTTP 200 and — that id is what scalepad_lm_get_goal_template and scalepad_lm_create_goal_from_template take. The template is account-owned, so it becomes available to every client engagement in the account, not just one. ScalePad documents no idempotency-key header here, so never blind-retry — re-check with scalepad_lm_list_goal_templates first.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with a single required goal_template object. Inside it: title (REQUIRED string, the display name) and initiative_template_ids (REQUIRED array of initiative-template ids to attach — on CREATE the vendor says it "Must contain at least one value", which is stricter than the same field on scalepad_lm_update_goal_template where it "Can be empty"; that create/update divergence is the vendor's own). Optional: description (nullable string, the reusable description applied to goals created from this template) and categories (nullable array of label strings, max 50 characters each; omit to create the template without categories). Example: {"goal_template":{"title":"Annual security uplift","description":"Baseline hardening objectives","initiative_template_ids":["it_1","it_2"],"categories":["Security"]}}.

[ScalePad] PERMANENTLY delete an account-owned Lifecycle Manager goal template. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the template is missing or is a platform-provided template the account cannot delete. ScalePad documents no soft delete, restore or undo, so resolve and echo the exact template back to the user first — read it with scalepad_lm_get_goal_template. This destroys the BLUEPRINT only: goals already created from the template are separate records and are not affected, and the initiative templates it referenced are not deleted either.

ParamTypeRequiredDefaultDescription
goalTemplateIdstringyesThe goal template id to delete (an id from scalepad_lm_list_goal_templates). Confirm this with the user before calling — the deletion is irreversible.

[ScalePad] Get ONE Lifecycle Manager goal template in full by its id. Returns {goal_template: } — a single wrapped object, not a bare record and not an array. Use it to read the template's title, description, categories and its attached initiative_template_ids before creating a goal from it with scalepad_lm_create_goal_from_template, or before rewriting it with scalepad_lm_update_goal_template (which is a full replacement and needs the current values).

ParamTypeRequiredDefaultDescription
goalTemplateIdstringyesThe goal template id to retrieve — an id from scalepad_lm_list_goal_templates, or the goal_template_id returned by scalepad_lm_create_goal_template.

[ScalePad] List every Lifecycle Manager goal template visible to the account, optionally narrowed by a title search. Returns {data: [...]} — note this list has NO cursor envelope: unlike most ScalePad lists there is no total_count and no next_cursor, because the vendor declares no page_size and no cursor on this endpoint, so one call returns the whole matching set. There is no sort parameter either. Each row is a goal template: its id, title, reusable description, categories and attached initiative_template_ids. Hydrate one with scalepad_lm_get_goal_template, or create a goal from one with scalepad_lm_create_goal_from_template.

ParamTypeRequiredDefaultDescription
filtersJsonstringnonullJSON object of field -> "operator:value" filters, sent as filter[field] query keys. This endpoint documents exactly ONE filterable field: title, with the operators cont (case-insensitive substring match) and eq (exact match). Omit it to return every template visible to the account. Example: {"title":"cont:security"}. Do not borrow filter fields from another resource — nothing else is filterable here.

[ScalePad] Update an existing account-owned goal template. Only account-scoped templates can be updated — ScalePad's own platform-provided templates are read-only. Treat the payload as a REPLACEMENT, not a merge: read the current template with scalepad_lm_get_goal_template first and resend everything that should survive, because title and initiative_template_ids are both required on every call. Succeeds with HTTP 204 and no body (this tool returns ) — re-read the template to confirm. Editing a template does NOT retroactively change goals already created from it.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with a single required goal_template object. Required on every call: title (string, the updated display name) and initiative_template_ids (array of initiative-template ids to attach — here the vendor says it "Can be empty", unlike the CREATE contract which requires at least one value). Optional: description (nullable string) and categories (nullable array of label strings, max 50 characters each) — the vendor's rule for categories is spelled out and unusual, so follow it exactly: "Send the full set on every update: a provided list replaces the existing categories, and an empty list clears them. Omit the field to leave categories unchanged." Example: {"goal_template":{"title":"Annual security uplift","description":"Baseline hardening objectives","initiative_template_ids":["it_1"],"categories":["Security","Compliance"]}}.
goalTemplateIdstringyesThe goal template id to update (an id from scalepad_lm_list_goal_templates). Must be an account-owned template.

LM Goals

ToolPlanAccessSummary
scalepad_lm_attach_goal_initiativeProWriteLINK an existing INITIATIVE to a GOAL so the project counts toward that objective.
scalepad_lm_attach_goal_meetingProWriteLINK an existing MEETING to a GOAL, establishing that the objective will be discussed or reviewed at that meeting.
scalepad_lm_attach_initiative_goalProWriteLINK an existing GOAL to an INITIATIVE, aligning the project with the objective it supports.
scalepad_lm_attach_meeting_goalProWriteLINK an existing GOAL to a MEETING, adding the objective to that meeting's agenda as a discussion topic.
scalepad_lm_create_goalProWriteCreate a new Lifecycle Manager goal for a client from scratch.
scalepad_lm_create_goal_from_templateProWriteCreate a NEW goal for a client from an existing GOAL TEMPLATE, inheriting the template's description and its attached initiative templates.
scalepad_lm_delete_goalProDestructivePERMANENTLY delete a Lifecycle Manager goal that is no longer relevant.
scalepad_lm_detach_goal_initiativeProDestructiveUNLINK an INITIATIVE from a GOAL.
scalepad_lm_detach_goal_meetingProDestructiveUNLINK a MEETING from a GOAL, removing the objective from that meeting's agenda.
scalepad_lm_detach_initiative_goalProDestructiveUNLINK a GOAL from an INITIATIVE.
scalepad_lm_detach_meeting_goalProDestructiveUNLINK a GOAL from a MEETING, removing the objective from that meeting's agenda.
scalepad_lm_get_goalFreeRead-onlyGet ONE Lifecycle Manager goal in full by its id.
scalepad_lm_list_goal_initiativesFreeRead-onlyFor ONE GOAL, list the INITIATIVES aligned to it — the projects that contribute to achieving that objective.
scalepad_lm_list_goal_meetingsFreeRead-onlyFor ONE GOAL, list the MEETINGS aligned to it — the client discussions where that objective is reviewed.
scalepad_lm_list_goalsFreeRead-onlyList Lifecycle Manager goals across every client the caller can access.
scalepad_lm_list_initiative_goalsFreeRead-onlyFor ONE INITIATIVE, list the GOALS it is aligned to — the broader objectives that project supports.
scalepad_lm_list_meeting_goalsFreeRead-onlyFor ONE MEETING, list the GOALS on its agenda — the business objectives to be discussed or reviewed at that meeting.
scalepad_lm_update_goalProDestructiveUpdate a goal's core fields — title, description/rich text, status and target period — in one call.
scalepad_lm_update_goal_scheduleProWriteRe-target ONLY a goal's period — the year, half-year or quarter by which it should be achieved — leaving title, status and description untouched.
scalepad_lm_update_goal_statusProWriteChange ONLY a goal's workflow status, leaving its title, description, rich text and target period untouched.

[ScalePad] LINK an existing INITIATIVE to a GOAL so the project counts toward that objective. PARENT = the GOAL (first path segment); CHILD = the INITIATIVE. Both must already exist — this creates no records — and the initiative MUST belong to the SAME CLIENT as the goal or the call is rejected. Succeeds with HTTP 200 and no body (this tool returns ). ScalePad exposes this same link from the other end as scalepad_lm_attach_initiative_goal (POST initiatives//goals/), which takes the two ids in the OPPOSITE order and has the identical effect — pick whichever matches the id you already hold, but do not transpose the arguments. Verify with scalepad_lm_list_goal_initiatives.

ParamTypeRequiredDefaultDescription
goalIdstringyesThe GOAL id to attach TO — the parent, and the first path segment.
initiativeIdstringyesThe INITIATIVE id to attach — the child (from scalepad_lm_list_initiatives_v2). It must belong to the same client as the goal.

[ScalePad] LINK an existing MEETING to a GOAL, establishing that the objective will be discussed or reviewed at that meeting. PARENT = the GOAL (first path segment); CHILD = the MEETING. Both must already exist, and the meeting MUST belong to the SAME CLIENT as the goal. Succeeds with HTTP 200 and no body (this tool returns ). ScalePad exposes the same link from the other end as scalepad_lm_attach_meeting_goal (POST meetings//goals/), which takes the ids in the OPPOSITE order and has the identical effect. Verify with scalepad_lm_list_goal_meetings.

ParamTypeRequiredDefaultDescription
goalIdstringyesThe GOAL id to attach TO — the parent, and the first path segment.
meetingIdstringyesThe MEETING id to attach — the child (from the Lifecycle Manager meetings list). It must belong to the same client as the goal.

[ScalePad] LINK an existing GOAL to an INITIATIVE, aligning the project with the objective it supports. PARENT = the INITIATIVE (first path segment); CHILD = the GOAL. Both must already exist, and the goal should belong to the SAME CLIENT as the initiative. Succeeds with HTTP 200 and no body (this tool returns ). This is the MIRROR of scalepad_lm_attach_goal_initiative (POST goals//initiatives/): the two are distinct vendor endpoints with the identical effect and the ids in the OPPOSITE order — here the INITIATIVE comes first. Verify with scalepad_lm_list_initiative_goals.

ParamTypeRequiredDefaultDescription
goalIdstringyesThe GOAL id to attach — the child (the id field from scalepad_lm_list_goals). It should belong to the same client as the initiative.
initiativeIdstringyesThe INITIATIVE id to attach TO — the parent, and the first path segment.

[ScalePad] LINK an existing GOAL to a MEETING, adding the objective to that meeting's agenda as a discussion topic. PARENT = the MEETING (first path segment); CHILD = the GOAL. Both must already exist, and the goal MUST belong to the SAME CLIENT as the meeting. Succeeds with HTTP 200 and no body (this tool returns ). This is the MIRROR of scalepad_lm_attach_goal_meeting (POST goals//meetings/): two distinct vendor endpoints, identical effect, ids in the OPPOSITE order — here the MEETING comes first. Verify with scalepad_lm_list_meeting_goals.

ParamTypeRequiredDefaultDescription
goalIdstringyesThe GOAL id to attach — the child (the id field from scalepad_lm_list_goals). It must belong to the same client as the meeting.
meetingIdstringyesThe MEETING id to attach TO — the parent, and the first path segment.

[ScalePad] Create a new Lifecycle Manager goal for a client from scratch. Returns HTTP 200 with — that id is what every other goal tool takes. Only client_key is required; title, status and target_period are all optional, and status defaults to OnTrack when omitted. Note the vendor gap: this create body accepts only the plain-text description — there is NO description_json field here, unlike scalepad_lm_update_goal — so to give a new goal rich text, create it and then call scalepad_lm_update_goal with description_json. The body also takes NO link fields: attach initiatives and meetings afterwards with scalepad_lm_attach_goal_initiative / scalepad_lm_attach_goal_meeting. To start from a reusable template instead, use scalepad_lm_create_goal_from_template. ScalePad documents no idempotency key for this operation, so never blind-retry it — re-check with scalepad_lm_list_goals filtered on the client and title first.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. Required: client_key — the owning client, as {"id":"..."} or, when the id is unavailable, {"name":"..."} using the client's unique name. Optional (all nullable): title (string), description (plain text; this operation has no description_json counterpart), status (one of OnTrack, AtRisk, OffTrack, OnHold, Complete — defaults to OnTrack when omitted) and target_period (one of the three period shapes: {"type":"PeriodYear","year":2026} / {"type":"PeriodHalf","year":2026,"half":1} — half is 1 or 2 — / {"type":"PeriodQuarter","year":2026,"quarter":3} — quarter is 1-4; on this operation the type discriminator is itself optional and nullable, only year plus half/quarter are required). Example: {"client_key":{"id":"yqwe36dg-ahbc-48b2-k187-5h0d57c9i9cf"},"title":"Retire end-of-life servers","status":"OnTrack","target_period":{"type":"PeriodQuarter","year":2026,"quarter":4}}.

[ScalePad] Create a NEW goal for a client from an existing GOAL TEMPLATE, inheriting the template's description and its attached initiative templates. Takes a goal_template_id (from scalepad_lm_list_goal_templates) plus the target client, and returns HTTP 201 with — note the response key is goal_id here, while the from-scratch scalepad_lm_create_goal returns . Three neighbouring tools are easy to confuse: THIS one creates a GOAL from a GOAL template; scalepad_lm_apply_initiative_template applies an INITIATIVE template's contents onto an EXISTING initiative (replacing its budget, recurring costs and action items); scalepad_lm_duplicate_initiative_template merely copies a template to a new template. Not idempotent and no idempotency key is documented — do not blind-retry.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. Required: client_key — the client the new goal belongs to, as {"id":"..."} or {"name":"..."} (the client's unique name) when the id is unavailable. Optional and nullable: title, an override for the new goal's title; when omitted the server may fall back to the template's title or the default goal title (the vendor does not commit to which). Example: {"client_key":{"id":"yqwe36dg-ahbc-48b2-k187-5h0d57c9i9cf"},"title":"FY26 lifecycle refresh"}.
goalTemplateIdstringyesThe GOAL TEMPLATE id to create the goal from — goal_template_id from scalepad_lm_list_goal_templates or scalepad_lm_get_goal_template. This is NOT an initiative_template_id.

[ScalePad] PERMANENTLY delete a Lifecycle Manager goal that is no longer relevant. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the goal is missing or inaccessible. ScalePad documents no soft delete, restore or undo, so resolve and echo the exact goal back to the user first — read it with scalepad_lm_get_goal. When the intent is only to close the objective out, prefer scalepad_lm_update_goal_status with status Complete, which keeps the record and its history. To break a goal's link to an initiative or meeting WITHOUT destroying the goal, use scalepad_lm_detach_goal_initiative / scalepad_lm_detach_goal_meeting — those leave both records intact.

ParamTypeRequiredDefaultDescription
idstringyesThe goal id to delete (the id field from scalepad_lm_list_goals). Confirm this with the user before calling — the deletion is irreversible.

[ScalePad] UNLINK an INITIATIVE from a GOAL. PARENT = the GOAL (first path segment); CHILD = the INITIATIVE. This removes only the LINK — neither the goal nor the initiative is deleted, and the initiative continues to exist, just no longer contributing to that objective. Succeeds with HTTP 204 and no body (this tool returns ). The relationship removal has no undo, so echo the exact goal and initiative back to the user first; read the current links with scalepad_lm_list_goal_initiatives. The same unlink is available from the other end as scalepad_lm_detach_initiative_goal (DELETE initiatives//goals/), with the ids in the opposite order. If the intent is that the PROJECT should cease to exist, that is scalepad_lm_delete_initiative, not this tool.

ParamTypeRequiredDefaultDescription
goalIdstringyesThe GOAL id to detach FROM — the parent, and the first path segment.
initiativeIdstringyesThe INITIATIVE id to unlink — the child (an id from scalepad_lm_list_goal_initiatives). The initiative itself survives.

[ScalePad] UNLINK a MEETING from a GOAL, removing the objective from that meeting's agenda. PARENT = the GOAL (first path segment); CHILD = the MEETING. This removes only the LINK — neither the goal nor the meeting is deleted, and the meeting continues to exist. Succeeds with HTTP 204 and no body (this tool returns ). The relationship removal has no undo, so echo the exact goal and meeting back to the user first; read the current links with scalepad_lm_list_goal_meetings. The same unlink is available from the other end as scalepad_lm_detach_meeting_goal (DELETE meetings//goals/), with the ids in the opposite order.

ParamTypeRequiredDefaultDescription
goalIdstringyesThe GOAL id to detach FROM — the parent, and the first path segment.
meetingIdstringyesThe MEETING id to unlink — the child (an id from scalepad_lm_list_goal_meetings). The meeting itself survives.

[ScalePad] UNLINK a GOAL from an INITIATIVE. PARENT = the INITIATIVE (first path segment); CHILD = the GOAL. This removes only the LINK — neither entity is deleted, and the goal remains in the system, just no longer supported by that initiative. Succeeds with HTTP 204 and no body (this tool returns ). The relationship removal has no undo, so echo the exact initiative and goal back to the user first; read the current links with scalepad_lm_list_initiative_goals. This is the MIRROR of scalepad_lm_detach_goal_initiative (DELETE goals//initiatives/) — same effect, ids in the opposite order. To destroy the OBJECTIVE itself use scalepad_lm_delete_goal.

ParamTypeRequiredDefaultDescription
goalIdstringyesThe GOAL id to unlink — the child (an id from scalepad_lm_list_initiative_goals). The goal itself survives.
initiativeIdstringyesThe INITIATIVE id to detach FROM — the parent, and the first path segment.

[ScalePad] UNLINK a GOAL from a MEETING, removing the objective from that meeting's agenda. PARENT = the MEETING (first path segment); CHILD = the GOAL. This removes only the LINK — neither the meeting nor the goal is deleted, and the goal continues to exist independently of the meeting. Succeeds with HTTP 204 and no body (this tool returns ). The relationship removal has no undo, so echo the exact meeting and goal back to the user first; read the current links with scalepad_lm_list_meeting_goals. This is the MIRROR of scalepad_lm_detach_goal_meeting (DELETE goals//meetings/) — same effect, ids in the opposite order. To destroy the OBJECTIVE itself use scalepad_lm_delete_goal.

ParamTypeRequiredDefaultDescription
goalIdstringyesThe GOAL id to unlink — the child (an id from scalepad_lm_list_meeting_goals). The goal itself survives.
meetingIdstringyesThe MEETING id to detach FROM — the parent, and the first path segment.

[ScalePad] Get ONE Lifecycle Manager goal in full by its id. Returns the same record shape as scalepad_lm_list_goals — client {id, label}, id, title, description (deprecated plain text), description_json (ProseMirror document), status, period (the {type, year, half?, quarter?} discriminated object), record_created_at, record_updated_at, created_by, updated_by — but unwrapped, with no paging envelope. Use this to hydrate the bare ids returned by scalepad_lm_list_initiative_goals and scalepad_lm_list_meeting_goals, and to read the current title/status/target_period before calling scalepad_lm_update_goal (which is a replacement, not a merge).

ParamTypeRequiredDefaultDescription
idstringyesThe goal id to retrieve — the id field from scalepad_lm_list_goals, or any id from the goal_ids array of scalepad_lm_list_initiative_goals / scalepad_lm_list_meeting_goals.

[ScalePad] For ONE GOAL, list the INITIATIVES aligned to it — the projects that contribute to achieving that objective. Takes a GOAL id (the parent) and returns {initiative_ids: [...]} — bare ids ONLY, with no names, statuses, budgets or paging envelope; hydrate each with scalepad_lm_get_initiative. This is the goal-side view: scalepad_lm_list_initiative_goals is the SAME relationship read from the initiative side and returns {goal_ids: [...]} instead.

ParamTypeRequiredDefaultDescription
goalIdstringyesThe GOAL id whose aligned initiatives to list — the parent, and the first path segment (from scalepad_lm_list_goals).

[ScalePad] For ONE GOAL, list the MEETINGS aligned to it — the client discussions where that objective is reviewed. Takes a GOAL id (the parent) and returns {meeting_ids: [...]} — bare ids ONLY, with no titles, dates or paging envelope; hydrate each with the Lifecycle Manager meeting read tools. This is the goal-side view: scalepad_lm_list_meeting_goals is the SAME relationship read from the meeting side and returns {goal_ids: [...]} instead.

ParamTypeRequiredDefaultDescription
goalIdstringyesThe GOAL id whose aligned meetings to list — the parent, and the first path segment (from scalepad_lm_list_goals).

[ScalePad] List Lifecycle Manager goals across every client the caller can access. Cursor-paginated: returns {data[], total_count, next_cursor} — keep paging until next_cursor is null, not until a page looks short, and deduplicate by id because cursor scans are not atomic. Each row carries client {id, label}, id, title, description (plain text, DEPRECATED — a projection of the rich text), description_json (the ProseMirror rich-text document), status (OnTrack | AtRisk | OffTrack | OnHold | Complete), period (a discriminated object: {type:"PeriodYear",year} or {type:"PeriodHalf",year,half} or {type:"PeriodQuarter",year,quarter}), record_created_at, record_updated_at, created_by and updated_by. Two vendor spellings to expect: the goal timestamps are record_created_at / record_updated_at (NOT created_at / updated_at, which is what initiatives use), and created_by / updated_by are plain nullable user-id STRINGS, not objects. Use scalepad_lm_get_goal for one goal by id.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; never decode or manufacture one.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together and sent as filter[field] query keys. Documented fields for goals are exactly client.id (eq|in — note the DOT), title (eq|cont), status (eq|in; values AtRisk, Complete, OffTrack, OnHold, OnTrack), period.year (eq only), period.half (eq only) and period.quarter (eq only). An omitted operator means eq; cont is a case-insensitive substring match. Example: {"client.id":"eq:yqwe36dg-ahbc-48b2-k187-5h0d57c9i9cf","status":"in:OnTrack,AtRisk","period.year":"eq:2026"}. Do not use an operator a field does not list, and do not borrow filter keys from the initiative list — priority, scheduled, assigned_user_id, created_at and updated_at do NOT exist on goals.
pageSizeintegernonullMaximum records per page, clamped to ScalePad's documented 200 platform cap. This operation's schema declares no default, minimum or maximum of its own, so omit it to take the server's page size.
sortstringnonullSort expression; a leading '-' descends, '+' or no prefix ascends. Only four fields are documented for this endpoint: title, period.year, period.half and period.quarter. Example: -period.year. Do not sort on status, client or the record_* timestamps — they are not documented here.

[ScalePad] For ONE INITIATIVE, list the GOALS it is aligned to — the broader objectives that project supports. Takes an INITIATIVE id (the parent) and returns {goal_ids: [...]} — bare ids ONLY, with no titles, statuses, periods or paging envelope; hydrate each with scalepad_lm_get_goal. This is the initiative-side view of the same relationship scalepad_lm_list_goal_initiatives reads from the goal side (which returns {initiative_ids: [...]} instead).

ParamTypeRequiredDefaultDescription
initiativeIdstringyesThe INITIATIVE id whose aligned goals to list — the parent, and the first path segment (from scalepad_lm_list_initiatives_v2).

[ScalePad] For ONE MEETING, list the GOALS on its agenda — the business objectives to be discussed or reviewed at that meeting. Takes a MEETING id (the parent) and returns {goal_ids: [...]} — bare ids ONLY, with no titles, statuses or paging envelope; hydrate each with scalepad_lm_get_goal. This is the meeting-side view of the same relationship scalepad_lm_list_goal_meetings reads from the goal side (which returns {meeting_ids: [...]} instead). For the INITIATIVES on the same meeting's agenda use scalepad_lm_list_meeting_initiatives.

ParamTypeRequiredDefaultDescription
meetingIdstringyesThe MEETING id whose agenda goals to list — the parent, and the first path segment (from the Lifecycle Manager meetings list).

[ScalePad] Update a goal's core fields — title, description/rich text, status and target period — in one call. This is a REPLACEMENT, not a merge: title, status and target_period are all REQUIRED by the vendor schema, so read the current record with scalepad_lm_get_goal first and resend what should survive, or you will overwrite it. Succeeds with HTTP 204 and no body (this tool returns ); call scalepad_lm_get_goal to see the result. Prefer the narrow updaters when only one field changes: scalepad_lm_update_goal_status for status alone and scalepad_lm_update_goal_schedule for the target period alone (that one can also CLEAR the period, which this tool cannot — target_period is required here). Links are not editable here; use the attach/detach tools.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. Required: title (string), status (one of OnTrack, AtRisk, OffTrack, OnHold, Complete) and target_period (one of {"type":"PeriodYear","year":2026} / {"type":"PeriodHalf","year":2026,"half":1} / {"type":"PeriodQuarter","year":2026,"quarter":3}; the type discriminator is optional/nullable on this operation, year plus half/quarter are required). Optional and nullable: description_json — the rich-text description as a ProseMirror JSON document (root {"type":"doc","content":[]}) serialized to a STRING for this field; when omitted the server PRESERVES the existing document, so partial updates do not blank rich content — and description, the plain-text field, which the vendor marks DEPRECATED: sending it OVERWRITES any stored rich-text document with a minimal projection of the plain text, losing formatting and images. (The vendor is internally inconsistent here: the field table marks only description deprecated and calls description_json preferred, while the same operation's quirk list names both as deprecated. Follow the field-level contract and send description_json.) Example: {"title":"Retire end-of-life servers","status":"AtRisk","target_period":{"type":"PeriodQuarter","year":2026,"quarter":4},"description_json":"{"type":"doc","content":[]}"}.
idstringyesThe goal id to update (the id field from scalepad_lm_list_goals).

[ScalePad] Re-target ONLY a goal's period — the year, half-year or quarter by which it should be achieved — leaving title, status and description untouched. This is also the ONLY way to CLEAR a goal's target period: target_period is nullable here, while scalepad_lm_update_goal requires it. Succeeds with HTTP 204 and no body (this tool returns ); the new value appears as the period field on scalepad_lm_get_goal. Note the goal 'schedule' is a planning period only — it is not a fiscal quarter in the initiative sense; scalepad_lm_update_initiative_schedule takes a different body shape ({fiscal_quarter:{year,quarter}}).

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with the single optional, NULLABLE target_period — send null (or {"target_period":null}) to clear the goal's period entirely. When supplied it is one of three shapes: {"type":"PeriodYear","year":2026}, {"type":"PeriodHalf","year":2026,"half":1} (half is 1 = first half or 2 = second half), or {"type":"PeriodQuarter","year":2026,"quarter":3} (quarter is 1-4). Vendor inconsistency worth knowing: this operation reuses the RESPONSE-shaped period schema, in which the type discriminator is REQUIRED (and oddly marked read-only), whereas the create/update bodies use a request-shaped schema where type is optional — so always include type here. Example: {"target_period":{"type":"PeriodHalf","year":2026,"half":2}}.
idstringyesThe goal id whose target period changes (the id field from scalepad_lm_list_goals).

[ScalePad] Change ONLY a goal's workflow status, leaving its title, description, rich text and target period untouched. This is the narrow alternative to scalepad_lm_update_goal, whose body requires title and target_period as well and would overwrite them. Succeeds with HTTP 204 and no body (this tool returns ); read the new value back as the status field on scalepad_lm_get_goal. Not to be confused with scalepad_lm_update_initiative_status, whose status vocabulary is completely different (New/Proposed/Approved/InProgress/OnHold/Declined/Completed).

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with the single required string status. Accepted values, exactly as the vendor spells them: OnTrack, OffTrack, AtRisk, OnHold, Complete (Complete is the terminal 'achieved' state — there is no separate Cancelled or Archived status). Example: {"status":"OnTrack"}.
idstringyesThe goal id whose status changes (the id field from scalepad_lm_list_goals).

LM Hardware Lifecycles

ToolPlanAccessSummary
scalepad_lm_get_hardware_dashboardFreeRead-onlyGet the hardware dashboard rollup: {scope, as_of, total_assets, replacement_due, warranty_expired} — the counts of assets due (and soon due) for replacement and with expired (and soon expiring)…
scalepad_lm_get_hardware_overviewFreeRead-onlyGet the full overview of ONE hardware asset: display_title, category, client, location_name, os_version, cpu_model, ram, coverage_status, hardware_purchase_age, hardware_purchase_date,…
scalepad_lm_get_hardware_replacement_settingsFreeRead-onlyGet the hardware replacement BUDGET DEFAULTS by report asset type — returns {currency, values}, the per-asset-type replacement amounts that feed budget forecasting.
scalepad_lm_list_hardware_assetsFreeRead-onlyList hardware assets with client ownership and basic device fields — the broad, heavily filterable asset read.
scalepad_lm_list_hardware_lifecyclesFreeRead-onlyList the ACTIVE hardware lifecycle records — basic device information (model, serial number) plus the purchase-date and warranty-expiry metadata that replacement planning runs on.
scalepad_lm_search_hardware_attached_agreementsFreeRead-onlyFor ONE hardware asset, look up the AGREEMENTS (contracts) attached to it.
scalepad_lm_search_hardware_attached_initiativesFreeRead-onlyLook up which Lifecycle Manager INITIATIVES a single HARDWARE ASSET is attached to — the reverse of scalepad_lm_attach_initiative_assets.

[ScalePad] Get the hardware dashboard rollup: {scope, as_of, total_assets, replacement_due, warranty_expired} — the counts of assets due (and soon due) for replacement and with expired (and soon expiring) warranties. Scope is ACCOUNT-WIDE when no client filter is supplied, and the returned scope field tells you which you got, so read it rather than assuming. The only documented filter is filter[client_id] — UNDERSCORED, eq-only, and note that the contracts and notes lists spell the same concept as dotted filter[client.id] while the hardware ASSET list uses a plain client_id query parameter; a wrong spelling here silently yields the account-wide numbers instead of one client's. Aggregate counts only — for the underlying devices use scalepad_lm_list_hardware_assets or scalepad_lm_list_hardware_lifecycles.

ParamTypeRequiredDefaultDescription
filtersJsonstringnonullJSON object of field -> "operator:value" filters, sent as filter[field] query keys. Exactly ONE field is documented and it is eq-only: "client_id" (UNDERSCORED — not client.id). OMIT this entirely to get the authenticated account's dashboard. Example: {"client_id":"eq:a9b3f47d-7b39-4d1e-b320-fdd9b72b8ab5"}.

[ScalePad] Get the full overview of ONE hardware asset: display_title, category, client, location_name, os_version, cpu_model, ram, coverage_status, hardware_purchase_age, hardware_purchase_date, warranty_expiration_date, warranty_expires_in, the can_renew_hardware / can_link_to_initiative / can_link_to_agreement capability flags, data_sources, unique_key and asset_reference_id. This is a POST that only READS — the asset is identified by a body rather than a path id — and changes nothing, despite the vendor's own OpenAPI labelling the operation 'write — update/state change'; that label is a vendor misclassification, recorded rather than followed (the operationId is ApiPublicV1AssetsHardwareOverview and the documented purpose is 'Retrieve a hardware asset overview'). Unlike scalepad_lm_search_hardware_attached_agreements, whose body is otherwise identical, this operation ALSO accepts an optional client_id in the body to scope key resolution.

ParamTypeRequiredDefaultDescription
bodyJsonstringnonullJSON object body. REQUIRED: hardware_key, identifying the asset EITHER by "id" (its Core API identifier) OR by "unique_key" — provide one or the other, never both. When using unique_key, all THREE of its properties are required: serial_number, model and manufacturer. Optional/nullable: a top-level client_id (sibling of hardware_key, NOT inside it) which scopes key resolution to that client and verifies the resolved asset belongs to it — worth setting when resolving by unique_key, since serial numbers are only unique in practice, not by contract. Example: {"hardware_key":{"unique_key":{"serial_number":"ABCDEF123456","model":"OptiPlex 7090","manufacturer":"Dell"}},"client_id":"a9b3f47d-7b39-4d1e-b320-fdd9b72b8ab5"}.

[ScalePad] Get the hardware replacement BUDGET DEFAULTS by report asset type — returns {currency, values}, the per-asset-type replacement amounts that feed budget forecasting. These are configuration defaults, not per-device data. Client scoping uses the plain client_id QUERY PARAMETER (this tool's clientId argument), not a filter[...] key: supply it for one client's overrides, and OMIT it to get the ACCOUNT defaults — the two are different answers, so pass it deliberately. The forecast figures these defaults produce are read through the LM Budget & Forecast tools.

ParamTypeRequiredDefaultDescription
clientIdstringnonullOptional Core client id whose hardware replacement settings to return, sent as the plain client_id query parameter. OMIT to return the ACCOUNT defaults rather than a client's.

[ScalePad] List hardware assets with client ownership and basic device fields — the broad, heavily filterable asset read. Cursor-paginated: returns {data[], total_count, next_cursor} — keep paging until next_cursor is null, not until a page looks short, and deduplicate because cursor scans are not atomic. Scoping to one client here uses the plain client_id QUERY PARAMETER (this tool's clientId argument), NOT filter[client_id] and NOT filter[client.id] — this endpoint is the odd one out, and a filter-shaped attempt is silently ignored. Sixteen filter fields are documented and most of their names are SEPARATOR-FREE (assignedenduser, hasscalepadwarranty, installedsoftware, installedsoftwarecategory, integrationsources, unsupportedoperatingsystem, windows11cpucompatibility, warrantycoverage, initiativecount) while manufacturer.name on the same endpoint is dotted; use the exact spellings listed on the filtersJson argument, because a misspelled key returns unfiltered results rather than an error. For purchase and warranty DATES per device, use scalepad_lm_list_hardware_lifecycles instead.

ParamTypeRequiredDefaultDescription
clientIdstringnonullScope to one client. Sent as the plain client_id QUERY PARAMETER that this endpoint documents — do NOT also pass a client filter in filtersJson, because no client filter key exists here.
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; never decode or manufacture one.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together and sent as filter[field] query keys. The documented fields, with their ONLY permitted operators — note that most names have NO separators, which is ScalePad's own spelling: "age" (eq|gt|gte|in|lt|lte), "assignedenduser" (eq|in), "configuredbackup" (eq|in), "hasscalepadwarranty" (eq|in), "initiativecount" (eq|gt|gte|in|lt|lte), "installedsoftware" (cont|eq|in), "installedsoftwarecategory" (cont|eq|in), "integrationsources" (eq|in), "manufacturer.name" (cont|eq|in — DOTTED, unlike its neighbours), "memory" (eq|gt|gte|in|lt|lte), "processor" (cont|eq|in), "storage" (eq|gt|gte|in|lt|lte), "type" (eq|in), "unsupportedoperatingsystem" (eq|in), "warranty" (eq|in), "warrantycoverage" (eq|in), "windows11cpucompatibility" (eq|in). An omitted operator means eq. There is NO client filter here — use the clientId argument. Example: {"type":"eq:WORKSTATION","unsupportedoperatingsystem":"eq:true","manufacturer.name":"cont:Dell"}.
pageSizeintegernonullMaximum records per page, 1-200 (clamped to ScalePad's documented 200 platform cap). No default is declared; omit to take the server's own page size.
searchstringnonullFree-text search across hardware asset fields. Omit to return every accessible asset.
sortstringnonullSort expression: one or more lowercase snake_case fields, comma-separated, each optionally prefixed '+' (ascending) or '-' (descending). ScalePad publishes NO field allow-list beyond that syntax for this endpoint.

[ScalePad] List the ACTIVE hardware lifecycle records — basic device information (model, serial number) plus the purchase-date and warranty-expiry metadata that replacement planning runs on. Cursor-paginated: returns {data[], total_count, next_cursor} — page until next_cursor is null. Only TWO filters are documented and both are eq-ONLY: filter[client_id] (UNDERSCORED here — the contracts and notes lists spell the same concept as the dotted filter[client.id], and the hardware ASSET list takes client_id as a plain query parameter instead; a wrong spelling silently returns unscoped results) and filter[serial_number]. No sort parameter is documented. This read returns only ACTIVE lifecycle records, so a device absent here is not necessarily absent from scalepad_lm_list_hardware_assets, which is the wider inventory read.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page — the pointer used to fetch a given page. Omit for the first page.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together and sent as filter[field] query keys. Only two fields are documented, both eq-ONLY: "client_id" (UNDERSCORED on this endpoint — not client.id) and "serial_number". An omitted operator means eq. Example: {"client_id":"eq:a9b3f47d-7b39-4d1e-b320-fdd9b72b8ab5","serial_number":"eq:ABCDEF123456"}.
pageSizeintegernonullMaximum records per page, 1-200 (clamped to ScalePad's documented 200 platform cap). No default is declared; omit to take the server's own page size.

[ScalePad] For ONE hardware asset, look up the AGREEMENTS (contracts) attached to it. Returns {contract_ids: [...]} — bare ids ONLY, with no titles, costs or paging envelope; hydrate each one with scalepad_lm_get_contract. This is a POST that only READS (the asset is identified by a body rather than a path id) and changes nothing, despite the vendor's OpenAPI labelling the operation 'write — update/state change' — a vendor misclassification, recorded rather than followed. Unlike scalepad_lm_get_hardware_overview, this operation does NOT accept a client_id in the body: the hardware_key is all it takes. To change these links, use scalepad_lm_attach_contract_assets and scalepad_lm_bulk_delete_contract_assets, which work from the contract side. The initiative equivalent is scalepad_lm_search_hardware_attached_initiatives.

ParamTypeRequiredDefaultDescription
bodyJsonstringnonullJSON object body with a single required hardware_key identifying the asset EITHER by "id" (its Core API identifier) OR by "unique_key" — provide one or the other, never both. When using unique_key, all THREE of its properties are required: serial_number, model and manufacturer. This operation accepts NO client_id (its sibling scalepad_lm_get_hardware_overview does). Example: {"hardware_key":{"id":"a9b3f47d-7b39-4d1e-b320-fdd9b72b8ab5"}}.

[ScalePad] Look up which Lifecycle Manager INITIATIVES a single HARDWARE ASSET is attached to — the reverse of scalepad_lm_attach_initiative_assets. Returns {initiative_ids: [...]} — bare ids ONLY, with no names, statuses or paging envelope; hydrate each with scalepad_lm_get_initiative. Use it before retiring or replacing a device, to see which roadmap items already have it in scope. This is a READ despite being an HTTP POST: the vendor's own safety class says "write — update/state change" while its operation id and description say "Retrieve the initiatives attached to a hardware asset" — nothing is created or modified, and the POST body exists only because a hardware identifier is too structured for a query string. It takes exactly ONE asset per call (hardware_key, singular), so loop for a fleet.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with a single required hardware_key object — note SINGULAR: this endpoint looks up one device per call, unlike the initiative asset tools which take a hardware_keys array. Identify the device in either of two ways, and the vendor is explicit that you supply one or the other but NOT BOTH: id (the hardware asset's Core API identifier) or unique_key (used when the Core id is unavailable), an object whose three children are ALL required — serial_number, model (the model or display name) and manufacturer. Example: {"hardware_key":{"unique_key":{"serial_number":"5CD1234ABC","model":"EliteBook 840 G9","manufacturer":"HP"}}}. Or by id: {"hardware_key":{"id":"hw_1"}}.

LM Initiative Templates

ToolPlanAccessSummary
scalepad_lm_create_initiative_templateProWriteCreate a new account-scoped Lifecycle Manager initiative template with its budget, recurring costs and action-item checklist.
scalepad_lm_delete_initiative_templateProDestructivePERMANENTLY delete an account-scoped Lifecycle Manager initiative template.
scalepad_lm_duplicate_initiative_templateProWriteDuplicate an initiative template into a NEW editable account-scoped copy.
scalepad_lm_get_initiative_templateFreeRead-onlyGet ONE Lifecycle Manager initiative template in full by its id.
scalepad_lm_list_initiative_templatesFreeRead-onlyList every Lifecycle Manager initiative template available to the account — both ScalePad's platform-provided templates and the account's own, which the response distinguishes.
scalepad_lm_update_initiative_templateProDestructiveUpdate an account-scoped Lifecycle Manager initiative template's name, executive summary, budget, recurring costs and action items.

[ScalePad] Create a new account-scoped Lifecycle Manager initiative template with its budget, recurring costs and action-item checklist. Succeeds with HTTP 200 and . The template is account-owned, so it becomes available across every client engagement. ScalePad documents no idempotency-key header here, so never blind-retry — re-check with scalepad_lm_list_initiative_templates first. One asymmetry worth knowing before you build a workflow on it: tag_ids can be set HERE at creation but the vendor's update contract has no tag_ids field, so tags cannot be changed later through scalepad_lm_update_initiative_template.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with a single required initiative_template object. Required inside it: name (non-empty string), budget_line_items (array of one-time costs — REQUIRED as a field, pass [] if none), recurring_line_items (array of ongoing costs — REQUIRED as a field, pass [] if none) and action_items (array of plain STRINGS, the checklist pre-populated on initiatives created from this template — REQUIRED as a field, pass [] if none). Optional: executive_summary_json (nullable ProseMirror JSON document serialized to a string) and tag_ids (nullable array of initiative-tag ids the account owns; settable only at create — the update contract omits this field). Each budget_line_items entry: label (required, max 400 chars), cost_subunits (required int64 in the currency's MINOR unit — cents for USD, so $1,250.00 is 125000), cost_type (required, one of Fixed | PerAsset | PerUnit) and unit_count (optional int32, applied ONLY when cost_type is PerUnit). Each recurring_line_items entry takes the same four fields PLUS a required frequency (Monthly | Yearly). Example: {"initiative_template":{"name":"Firewall refresh","executive_summary_json":"{"type":"doc","content":[]}","budget_line_items":[{"label":"Appliance","cost_subunits":125000,"cost_type":"PerAsset"}],"recurring_line_items":[{"label":"Support","cost_subunits":4900,"cost_type":"Fixed","frequency":"Monthly"}],"action_items":["Scope the sites"]}}.

[ScalePad] PERMANENTLY delete an account-scoped Lifecycle Manager initiative template. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the template is missing or is a platform-provided template the account cannot delete. ScalePad documents no soft delete, restore or undo, so resolve and echo the exact template back to the user first — read it with scalepad_lm_get_initiative_template. This destroys the BLUEPRINT only: initiatives already created from it, or already updated by scalepad_lm_apply_initiative_template, keep their budget, recurring costs and action items. Goal templates that referenced this template through their initiative_template_ids are a separate resource and are not deleted — check scalepad_lm_list_goal_templates for dangling references.

ParamTypeRequiredDefaultDescription
initiativeTemplateIdstringyesThe initiative template id to delete (an id from scalepad_lm_list_initiative_templates). Confirm this with the user before calling — the deletion is irreversible.

[ScalePad] Duplicate an initiative template into a NEW editable account-scoped copy. Succeeds with HTTP 201 and — the id of the COPY, not the source. This is the intended way to get an editable version of one of ScalePad's platform-provided templates, which cannot be updated in place. The source template is left untouched, and the copy is independent from then on. The operation takes no body: the copy's name and contents come from the source, so rename it afterwards with scalepad_lm_update_initiative_template. Calling this twice creates TWO copies — there is no idempotency key, so check scalepad_lm_list_initiative_templates before retrying.

ParamTypeRequiredDefaultDescription
initiativeTemplateIdstringyesThe initiative template id to COPY — the source (an id from scalepad_lm_list_initiative_templates; a platform-provided template is a valid source). The new copy's id comes back in the response.

[ScalePad] Get ONE Lifecycle Manager initiative template in full by its id. Returns {initiative_template: } — a single wrapped object, not a bare record. Read it before scalepad_lm_update_initiative_template (which replaces the budget, recurring and action-item sets wholesale and therefore needs the current values) or before scalepad_lm_apply_initiative_template, so the user can see exactly what will be pushed onto the live initiative. All money is integer cost_subunits in the currency's minor unit.

ParamTypeRequiredDefaultDescription
initiativeTemplateIdstringyesThe initiative template id to retrieve — an id from scalepad_lm_list_initiative_templates, or the initiative_template_id returned by scalepad_lm_create_initiative_template or scalepad_lm_duplicate_initiative_template.

[ScalePad] List every Lifecycle Manager initiative template available to the account — both ScalePad's platform-provided templates and the account's own, which the response distinguishes. Cursor-paginated: returns {data[], total_count, next_cursor} — keep paging until next_cursor is null, not until a page is short, and deduplicate because cursor scans are not atomic and can skip or repeat records if templates change mid-walk. The vendor documents NO filters and NO sort on this endpoint, so paging is the only query surface; narrow the results client-side. Each row carries the template's name, executive summary, budget and recurring line items and action-item checklist; hydrate one with scalepad_lm_get_initiative_template.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; never decode or manufacture one.
pageSizeintegernonullMaximum records per page, 1-200 (clamped to ScalePad's documented 200 platform cap). The vendor's schema declares no default and no minimum for this endpoint, so omit it to take the server's own page size.

[ScalePad] Update an account-scoped Lifecycle Manager initiative template's name, executive summary, budget, recurring costs and action items. Only account-scoped templates can be updated — a platform-provided template is read-only, so duplicate it first with scalepad_lm_duplicate_initiative_template. This is a REPLACEMENT, not a merge: name, budget_line_items, recurring_line_items and action_items are all required on every call, and each collection replaces the stored set entirely (an empty array clears it). Read the current template with scalepad_lm_get_initiative_template first and resend what should survive. Succeeds with HTTP 204 and no body (this tool returns ). Editing a template does NOT retroactively change initiatives already created from or applied with it. Note the vendor's update contract has NO tag_ids field, so tags set at creation cannot be changed here.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with a single required initiative_template object. Required on every call: name (non-empty string), budget_line_items (REPLACEMENT set of one-time costs; [] clears all), recurring_line_items (REPLACEMENT set of ongoing costs; [] clears all) and action_items (REPLACEMENT checklist of plain STRINGS; [] clears all). Optional: executive_summary_json (nullable ProseMirror JSON document serialized to a string — pass null to CLEAR the existing summary). Each budget_line_items entry: label (required, max 400 chars), cost_subunits (required int64 in the currency's MINOR unit — cents for USD), cost_type (required, Fixed | PerAsset | PerUnit) and unit_count (optional int32, applied only when cost_type is PerUnit). Each recurring_line_items entry adds a required frequency (Monthly | Yearly). Example: {"initiative_template":{"name":"Firewall refresh 2027","executive_summary_json":null,"budget_line_items":[],"recurring_line_items":[{"label":"Support","cost_subunits":5900,"cost_type":"Fixed","frequency":"Monthly"}],"action_items":["Re-scope the sites"]}}.
initiativeTemplateIdstringyesThe initiative template id to update (an id from scalepad_lm_list_initiative_templates). Must be an account-scoped template.

LM Initiatives

ToolPlanAccessSummary
scalepad_lm_apply_initiative_templateProDestructivePush an initiative TEMPLATE's contents onto an EXISTING initiative.
scalepad_lm_attach_initiative_assetsProWriteLINK one or more HARDWARE ASSETS to an initiative, putting those devices in scope for the work (which is also what makes a PerAsset cost line scale).
scalepad_lm_attach_initiative_meetingProWriteLINK an existing MEETING to an INITIATIVE, aligning a client discussion with the implementation effort.
scalepad_lm_attach_initiative_opportunityProWriteLINK an EXISTING PSA opportunity to an initiative, connecting the roadmap item to revenue work already tracked in the PSA.
scalepad_lm_attach_meeting_initiativeProWriteLINK an existing INITIATIVE to a MEETING, putting the roadmap item on that meeting's agenda as a discussion topic.
scalepad_lm_create_initiativeProWriteCreate a new Lifecycle Manager initiative for a client — a program, project or operational effort on that client's roadmap.
scalepad_lm_create_initiative_opportunityProDestructiveCreate a BRAND NEW opportunity in the connected PSA and link it to this initiative.
scalepad_lm_create_initiative_ticketProDestructiveCreate a ticket in the connected PSA and link it to this initiative.
scalepad_lm_delete_initiativeProDestructivePERMANENTLY delete a Lifecycle Manager initiative that is no longer relevant.
scalepad_lm_delete_initiative_opportunityProDestructiveDETACH the PSA opportunity linked to an initiative.
scalepad_lm_delete_initiative_ticketProDestructiveDETACH the PSA ticket linked to an initiative.
scalepad_lm_detach_initiative_assetsProDestructiveUNLINK one or more HARDWARE ASSETS from an initiative, taking those devices out of scope for the work.
scalepad_lm_detach_initiative_meetingProDestructiveUNLINK a MEETING from an INITIATIVE.
scalepad_lm_detach_meeting_initiativeProDestructiveUNLINK an INITIATIVE from a MEETING, taking the roadmap item off that meeting's agenda.
scalepad_lm_download_initiative_pdfFreeRead-onlyExport ONE initiative as the client-facing PDF that ScalePad renders server-side, including its executive summary, budget, recurring costs and action items.
scalepad_lm_get_initiativeFreeRead-onlyGet ONE Lifecycle Manager initiative in full by its id, including its budget, recurring costs, status, priority, schedule and associated resources.
scalepad_lm_get_initiative_opportunityFreeRead-onlyGet the PSA OPPORTUNITY linked to an initiative.
scalepad_lm_get_initiative_ticketFreeRead-onlyGet the state of the PSA TICKET linked to an initiative.
scalepad_lm_list_initiative_meetingsFreeRead-onlyFor ONE INITIATIVE, list the MEETINGS aligned to it — the client discussions and reviews where that work is covered.
scalepad_lm_list_initiative_quotesFreeRead-onlyFor ONE INITIATIVE, list the QUOTES linked to it.
scalepad_lm_list_initiativesFreeRead-onlyList Lifecycle Manager initiatives across every client the caller can access.
scalepad_lm_list_initiatives_v2FreeRead-onlyList Lifecycle Manager initiatives across every client the caller can access — the CURRENT v2 endpoint, and the one to use for new work (the v1 scalepad_lm_list_initiatives is deprecated for removal…
scalepad_lm_list_meeting_initiativesFreeRead-onlyFor ONE MEETING, list the INITIATIVES attached to it — the roadmap items on that meeting's agenda.
scalepad_lm_update_initiativeProWriteUpdate an initiative's NAME and EXECUTIVE SUMMARY — and nothing else.
scalepad_lm_update_initiative_assigned_userProWriteSet the ScalePad user who OWNS an initiative.
scalepad_lm_update_initiative_budgetProDestructiveReplace an initiative's ONE-TIME investment plan — the budget_line_items set.
scalepad_lm_update_initiative_priorityProWriteSet ONE initiative's business priority, reflecting its strategic importance on the client's roadmap.
scalepad_lm_update_initiative_recurringProDestructiveReplace an initiative's RECURRING investment plan — the recurring_line_items set of monthly or yearly ongoing commitments.
scalepad_lm_update_initiative_scheduleProWriteSet the FISCAL QUARTER in which an initiative's resources are planned — the field that drives budget forecasting and the roadmap timeline.
scalepad_lm_update_initiative_statusProWriteMove ONE initiative through its business workflow by setting its status.

[ScalePad] Push an initiative TEMPLATE's contents onto an EXISTING initiative. The vendor's own description is explicit that this REPLACES the initiative's budget, recurring costs and action items with the template's — so any one-time lines, recurring lines or action items already on the target that the template does not contain are lost, and there is no undo. Read both sides first (scalepad_lm_get_initiative and scalepad_lm_get_initiative_template) and echo what will change before calling. Parent = the initiative being changed (the first path segment); the template is the source and is not modified. Succeeds with HTTP 200 and no body (this tool returns ) — re-read the initiative to see the result. This tool takes no body: everything applied comes from the template.

ParamTypeRequiredDefaultDescription
initiativeIdstringyesThe INITIATIVE id to apply the template TO — the target that gets overwritten, and the first path segment (an id from scalepad_lm_list_initiatives_v2).
initiativeTemplateIdstringyesThe INITIATIVE TEMPLATE id to apply — the source, which is read and not modified (an id from scalepad_lm_list_initiative_templates).

[ScalePad] LINK one or more HARDWARE ASSETS to an initiative, putting those devices in scope for the work (which is also what makes a PerAsset cost line scale). Note the vendor uses PUT here, while the matching detach is a POST to a /detach path. Succeeds with HTTP 204 and no body (this tool returns ). To see which initiatives a given device is already attached to, use scalepad_lm_search_hardware_attached_initiatives. Unlink with scalepad_lm_detach_initiative_assets — that removes the link only and never deletes the asset.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with a single required hardware_keys array — the assets to attach. Each entry identifies ONE device in either of two ways, and the vendor is explicit that you supply one or the other but NOT BOTH: id (the hardware asset's Core API identifier) or unique_key (used when the Core id is unavailable), an object whose three children are ALL required — serial_number, model (the model or display name) and manufacturer. Example: {"hardware_keys":[{"id":"hw_1"},{"unique_key":{"serial_number":"5CD1234ABC","model":"EliteBook 840 G9","manufacturer":"HP"}}]}.
initiativeIdstringyesThe INITIATIVE id to attach the assets TO — the parent, and the first path segment (an id from scalepad_lm_list_initiatives_v2).

[ScalePad] LINK an existing MEETING to an INITIATIVE, aligning a client discussion with the implementation effort. Parent = the initiative; child = the meeting. Both must already exist — this creates no records. The vendor uses PUT for this direction and returns HTTP 204 with no body (this tool returns ), whereas the mirror tool scalepad_lm_attach_meeting_initiative is a POST returning 200; both produce the SAME relationship, so use whichever side you already hold the parent id for and do not call both. Verify with scalepad_lm_list_initiative_meetings.

ParamTypeRequiredDefaultDescription
initiativeIdstringyesThe INITIATIVE id to attach TO — the parent, and the first path segment.
meetingIdstringyesThe MEETING id to attach — the child. It should belong to the same client as the initiative.

[ScalePad] LINK an EXISTING PSA opportunity to an initiative, connecting the roadmap item to revenue work already tracked in the PSA. This creates nothing in the PSA — both records must already exist. Its sibling scalepad_lm_create_initiative_opportunity does the opposite and creates a NEW opportunity; the two differ upstream by a single path segment (plural /opportunities/ here versus singular /opportunity there), so confirm which one the user means. Parent = the initiative (first path segment); child = the opportunity. Succeeds with HTTP 200 and no body (this tool returns ) — confirm with scalepad_lm_get_initiative_opportunity.

ParamTypeRequiredDefaultDescription
initiativeIdstringyesThe INITIATIVE id to attach the opportunity TO — the parent, and the first path segment (an id from scalepad_lm_list_initiatives_v2).
opportunityIdstringyesThe EXISTING PSA opportunity id to attach — the child. This must already exist in the connected PSA; nothing is created here.

[ScalePad] LINK an existing INITIATIVE to a MEETING, putting the roadmap item on that meeting's agenda as a discussion topic. Parent = the meeting; child = the initiative. Both must already exist, and the vendor requires the initiative to belong to the SAME CLIENT as the meeting. This direction is a POST returning HTTP 200 with no body (this tool returns ), whereas the mirror tool scalepad_lm_attach_initiative_meeting is a PUT returning 204; both produce the SAME relationship, so call only one. Verify with scalepad_lm_list_meeting_initiatives.

ParamTypeRequiredDefaultDescription
initiativeIdstringyesThe INITIATIVE id to attach — the child (an id from scalepad_lm_list_initiatives_v2). It must belong to the same client as the meeting.
meetingIdstringyesThe MEETING id to attach TO — the parent, and the first path segment.

[ScalePad] Create a new Lifecycle Manager initiative for a client — a program, project or operational effort on that client's roadmap. Succeeds with HTTP 200 and . The create body carries only the client, the name and the executive summary: status, priority, schedule, budget, recurring costs, assigned user, assets and every link are set AFTERWARDS through their own tools (scalepad_lm_update_initiative_status, _priority, _schedule, _budget, _recurring, _assigned_user, scalepad_lm_attach_initiative_assets, and the attach tools). To start from a blueprint instead, create the initiative here and then push a template onto it with scalepad_lm_apply_initiative_template. ScalePad documents no idempotency-key header, so never blind-retry — re-check with scalepad_lm_list_initiatives_v2 filtered on the client first.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. Required: client_key (the owning client, as {"id":"..."} or, when the id is unavailable, {"name":"..."} using the client's unique name — the vendor marks both children individually optional but one must identify the client) and name (the initiative's name). Optional: executive_summary_json (nullable, the summary as a ProseMirror JSON document serialized to a STRING) and executive_summary (nullable plain text, explicitly DEPRECATED by the vendor in favour of executive_summary_json — its own words: "using this field will overwrite rich text"). Example: {"client_key":{"id":"0d3e7d0k-241a-461r-av15-758a90d70283"},"name":"Firewall refresh","executive_summary_json":"{"type":"doc","content":[]}"}.

[ScalePad] Create a BRAND NEW opportunity in the connected PSA and link it to this initiative. Read that twice before calling: to link an opportunity that ALREADY EXISTS in the PSA, the tool is scalepad_lm_attach_initiative_opportunity — the two differ upstream by one path segment (singular /opportunity here versus plural /opportunities/ there), and choosing wrong creates a duplicate revenue record in the customer's PSA. Creation is QUEUED: this returns HTTP 202 Accepted with in a Pending state, not a finished record — poll scalepad_lm_get_initiative_opportunity until it reaches a terminal state. Never blind-retry a 202; re-read the link first, because a retry can create a second opportunity.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with a single required field_values array of PSA integration field key/value pairs that drive creation in the external PSA. Each entry requires key (the field key exactly as returned by the matching opportunity create-fields response — use the option's key, NOT its display label) and value (a string; the vendor says Select inputs should use the option value). Because the valid keys come from the PSA's own create-fields response, read that first rather than guessing field names. Example: {"field_values":[{"key":"Title","value":"Firewall refresh"},{"key":"StageId","value":"3"}]}.
initiativeIdstringyesThe initiative id the new opportunity should be created for and linked to (an id from scalepad_lm_list_initiatives_v2).

[ScalePad] Create a ticket in the connected PSA and link it to this initiative. Creation is QUEUED, so this returns HTTP 202 Accepted with in a Pending state rather than a finished ticket — poll scalepad_lm_get_initiative_ticket until the state reaches Created or Error. A 409 Conflict is a documented BUSINESS-STATE outcome here (surface it as such, not as a validation error): the usual cause is that the initiative already has a linked ticket, since it can hold only one. Never blind-retry a 202 or a 409 — read the link state first, because a retry can raise a second ticket in the customer's PSA.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with a single required field_values array (minimum ONE entry) of PSA integration field key/value pairs that drive ticket creation. Each entry requires key (the field key exactly as returned by the matching ticket-create-fields response — use the option's key, NOT its display label) and value (a string; for Select inputs the vendor says to use the option's id from that field's values list, not its label). The vendor states the Title key is ALWAYS required and represents the ticket subject. Because the remaining required keys come from the PSA's own create-fields response, read that first rather than guessing. Example: {"field_values":[{"key":"Title","value":"Replace aging firewall"},{"key":"BoardId","value":"12"}]}.
initiativeIdstringyesThe initiative id the new PSA ticket should be created for and linked to (an id from scalepad_lm_list_initiatives_v2).

[ScalePad] PERMANENTLY delete a Lifecycle Manager initiative that is no longer relevant. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the initiative is missing or inaccessible. ScalePad documents no soft delete, restore or undo, so resolve and echo the exact initiative back to the user first — read it with scalepad_lm_get_initiative. This takes the initiative's budget, recurring costs and roadmap position with it. When the intent is only to take the work off the active roadmap, prefer scalepad_lm_update_initiative_status with Declined or Completed, which keeps the record and its history. To break a single relationship without destroying the initiative, use the matching detach tool instead.

ParamTypeRequiredDefaultDescription
idstringyesThe initiative id to delete (an id from scalepad_lm_list_initiatives_v2). Confirm this with the user before calling — the deletion is irreversible.

[ScalePad] DETACH the PSA opportunity linked to an initiative. Despite the tool's 'delete' name and the DELETE verb, the vendor is explicit that the PSA opportunity itself is NOT deleted — only the Lifecycle-Manager-side LINK is removed, and the revenue record stays in the customer's PSA. Still treated as destructive because the relationship removal has no undo and the initiative loses its revenue attribution: echo the exact initiative and the linked opportunity back to the user first, reading it with scalepad_lm_get_initiative_opportunity. Succeeds with HTTP 204 and no body (this tool returns ); a 404 means nothing was linked. To relink later use scalepad_lm_attach_initiative_opportunity with the same opportunity id — do NOT use scalepad_lm_create_initiative_opportunity, which would create a duplicate.

ParamTypeRequiredDefaultDescription
initiativeIdstringyesThe initiative id whose PSA opportunity link should be removed (an id from scalepad_lm_list_initiatives_v2). Confirm with the user before calling.

[ScalePad] DETACH the PSA ticket linked to an initiative. Despite the tool's 'delete' name and the DELETE verb, the vendor is explicit that the ticket itself is NOT deleted from the external PSA — only the Lifecycle-Manager-side LINK is removed. It does have one further side effect the vendor calls out: any PENDING create job for the link is CANCELLED, so calling this while scalepad_lm_create_initiative_ticket is still Pending abandons that in-flight creation. Treated as destructive because neither the unlink nor the cancellation can be undone: echo the exact initiative and its ticket state back to the user first, reading it with scalepad_lm_get_initiative_ticket. Succeeds with HTTP 204 and no body (this tool returns ); a 404 means no ticket was linked.

ParamTypeRequiredDefaultDescription
initiativeIdstringyesThe initiative id whose linked PSA ticket should be detached (an id from scalepad_lm_list_initiatives_v2). Confirm with the user before calling — this also cancels any pending create job for the link.

[ScalePad] UNLINK one or more HARDWARE ASSETS from an initiative, taking those devices out of scope for the work. DESTRUCTIVE despite the POST verb and the additive-sounding path: the vendor expresses this removal as POST /initiatives//assets/detach, and it removes a whole SET of relationships in one call with no undo — echo the exact initiative and the exact device list back to the user first, and read the current scope with scalepad_lm_get_initiative. Only the LINKS are removed: every asset survives untouched in inventory, and the initiative itself survives too. Detaching assets can change what a PerAsset cost line totals. Succeeds with HTTP 200 and no body (this tool returns ).

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with a single required hardware_keys array — the assets to detach. Each entry identifies ONE device in either of two ways, and the vendor is explicit that you supply one or the other but NOT BOTH: id (the hardware asset's Core API identifier) or unique_key (used when the Core id is unavailable), an object whose three children are ALL required — serial_number, model and manufacturer. Confirm this list with the user before calling; every entry is an irreversible unlink. Example: {"hardware_keys":[{"id":"hw_1"}]}.
initiativeIdstringyesThe INITIATIVE id to detach the assets FROM — the parent, and the first path segment (an id from scalepad_lm_list_initiatives_v2).

[ScalePad] UNLINK a MEETING from an INITIATIVE. This removes only the LINK — neither the meeting nor the initiative is deleted, and both keep existing independently. The relationship removal has no undo, so echo the exact initiative and meeting back to the user first; read the current links with scalepad_lm_list_initiative_meetings. Succeeds with HTTP 204 and no body (this tool returns ). scalepad_lm_detach_meeting_initiative is the mirror of this tool and removes the SAME relationship from the meeting side — calling one is enough. To destroy the INITIATIVE itself use scalepad_lm_delete_initiative.

ParamTypeRequiredDefaultDescription
initiativeIdstringyesThe INITIATIVE id to detach FROM — the parent, and the first path segment.
meetingIdstringyesThe MEETING id to unlink — the child (an id from scalepad_lm_list_initiative_meetings). The meeting itself survives.

[ScalePad] UNLINK an INITIATIVE from a MEETING, taking the roadmap item off that meeting's agenda. This removes only the LINK — the initiative continues to exist and keeps its budget, status and every other relationship; the meeting survives too. The relationship removal has no undo, so echo the exact meeting and initiative back to the user first; read the current links with scalepad_lm_list_meeting_initiatives. Succeeds with HTTP 204 and no body (this tool returns ). scalepad_lm_detach_initiative_meeting is the mirror of this tool and removes the SAME relationship from the initiative side — calling one is enough. To destroy the INITIATIVE itself use scalepad_lm_delete_initiative.

ParamTypeRequiredDefaultDescription
initiativeIdstringyesThe INITIATIVE id to remove from the agenda — the child (an id from scalepad_lm_list_meeting_initiatives). The initiative itself survives.
meetingIdstringyesThe MEETING id to detach FROM — the parent, and the first path segment.

[ScalePad] Export ONE initiative as the client-facing PDF that ScalePad renders server-side, including its executive summary, budget, recurring costs and action items. Binary cannot cross MCP's JSON tool surface, so this tool does NOT return the file bytes: StackJack downloads the PDF, stores it, and returns a JSON envelope with a short-lived READ-ONLY download URL — {SasUrl, ContentType, SuggestedFilename, SizeBytes, ExpiresAt}. The URL is valid for about 30 minutes and then stops working, so fetch it or hand it off promptly rather than saving it for later; re-run this tool to mint a fresh one. This is the ONLY tool in this group that does not pass raw JSON straight through. It is a pure read: it renders a document and changes nothing about the initiative — it does not publish, share or alter status. For the whole client's roadmap rather than a single initiative, use the roadmap export tools instead.

ParamTypeRequiredDefaultDescription
initiativeIdstringyesThe initiative id to export as PDF (an id from scalepad_lm_list_initiatives_v2, or the id returned by scalepad_lm_create_initiative).

[ScalePad] Get ONE Lifecycle Manager initiative in full by its id, including its budget, recurring costs, status, priority, schedule and associated resources. Returns {initiative: } — a single wrapped object, not a bare record. Read this before any update: scalepad_lm_update_initiative replaces name plus executive summary, and the budget and recurring endpoints replace their whole line-item sets, so you need the current values to avoid dropping fields. All money is integer cost_subunits in the currency's minor unit (cents for USD).

ParamTypeRequiredDefaultDescription
idstringyesThe initiative id to retrieve — an id from scalepad_lm_list_initiatives_v2, or the id returned by scalepad_lm_create_initiative.

[ScalePad] Get the PSA OPPORTUNITY linked to an initiative. Returns {opportunity: }, and its state is what you POLL after scalepad_lm_create_initiative_opportunity: that create returns 202 Accepted with a Pending state, and this read reports Pending while the external PSA is still creating the record, then a terminal state once creation completes. A 404 here means NO opportunity is currently linked — that is the normal 'none yet' answer, not an error to retry. An initiative holds at most one linked opportunity, which is why the path is singular.

ParamTypeRequiredDefaultDescription
initiativeIdstringyesThe initiative id whose linked PSA opportunity to read (an id from scalepad_lm_list_initiatives_v2).

[ScalePad] Get the state of the PSA TICKET linked to an initiative. Returns — and this is the POLLING endpoint for scalepad_lm_create_initiative_ticket, which returns 202 Accepted with a Pending state: the vendor documents Pending while the ticket is still being created in the external PSA, then Created or Error as the terminal states. A 404 here means NO ticket is currently linked, which is the normal 'none yet' answer rather than a failure. An initiative holds at most one linked ticket, hence the singular path.

ParamTypeRequiredDefaultDescription
initiativeIdstringyesThe initiative id whose linked PSA ticket state to read (an id from scalepad_lm_list_initiatives_v2).

[ScalePad] For ONE INITIATIVE, list the MEETINGS aligned to it — the client discussions and reviews where that work is covered. Takes an INITIATIVE id, not a meeting id. Returns {meeting_ids: [...]} — bare ids ONLY, with no titles, dates or paging envelope; hydrate each one through the Lifecycle Manager meeting tools. This is the initiative side of the pair: the mirror view, listing the initiatives on one meeting's agenda, is scalepad_lm_list_meeting_initiatives.

ParamTypeRequiredDefaultDescription
initiativeIdstringyesThe INITIATIVE id whose aligned meetings to list (the parent — an id from scalepad_lm_list_initiatives_v2).

[ScalePad] For ONE INITIATIVE, list the QUOTES linked to it. Returns three top-level fields, all of them required by the vendor's schema: data[] (the linked quotes), status_availabilities (the status display metadata the UI renders quote states with) and has_quoter (a boolean telling you whether the ACCOUNT has ScalePad Quoter enabled at all). Read has_quoter before concluding anything from an empty data[] — with Quoter disabled, no quotes can exist regardless of the initiative. There is no paging envelope on this response. The quotes themselves live in the Quoter surface (scalepad_quoter_* tools); this tool only reports the linkage from the initiative's side.

ParamTypeRequiredDefaultDescription
initiativeIdstringyesThe INITIATIVE id whose linked quotes to list (an id from scalepad_lm_list_initiatives_v2).

[ScalePad] List Lifecycle Manager initiatives across every client the caller can access. DEPRECATED BY THE VENDOR and scheduled for removal on March 1, 2027 — prefer scalepad_lm_list_initiatives_v2 unless you specifically need a field v2 dropped. The two versions differ in CONTENT, not just shape: v2 DROPS executive_summary, executive_summary_json and created_by_user_id, and ADDS task_count, goal_count, meeting_count, ticket_link_state, has_opportunity and has_quote. So this v1 list is the only list that carries the executive summary inline (v2 callers read it per-initiative via scalepad_lm_get_initiative), while v2 is the only one with the rollup counts and link flags. Cursor-paginated: returns {data[], total_count, next_cursor} — keep paging until next_cursor is null, not until a page is short, and deduplicate by initiative id because cursor scans are not atomic. Filters are ANDed and every one requires an explicit operator prefix. NO SORTING: the vendor documents no sort parameter on this endpoint (unlike the goals list and scalepad_lm_list_budget_initiatives, which do), so results come back in the server's own order — sort client-side.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; never decode or manufacture one.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together and sent as filter[field] query keys. Documented fields and their ONLY permitted operators: client.id (eq|in — note the DOT), status (eq|in; values New, Proposed, Approved, InProgress, OnHold, Declined, Completed), priority (eq|in; values None, Low, Medium, High), scheduled (eq only), scheduled_period (eq only), assigned_user_id (eq|in), created_at (eq|gt|gte|lt|lte, ISO-8601) and updated_at (eq|gt|gte|lt|lte). Example: {"client.id":"eq:0d3e7d0k-241a-461r-av15-758a90d70283","status":"in:New,InProgress","updated_at":"gte:2026-01-01T00:00:00Z"}. Do not use an operator a field does not list, and do not borrow filters from another resource.
pageSizeintegernonullMaximum records per page, 1-200 (clamped to ScalePad's documented 200 platform cap). The vendor declares no default and no minimum here, so omit it to take the server's own page size.

[ScalePad] List Lifecycle Manager initiatives across every client the caller can access — the CURRENT v2 endpoint, and the one to use for new work (the v1 scalepad_lm_list_initiatives is deprecated for removal on March 1, 2027). v2 takes the SAME filter and paging surface as v1, but the RECORD CONTENT differs in both directions, so check what you need before choosing: v2 ADDS task_count, goal_count, meeting_count, ticket_link_state, has_opportunity and has_quote (rollups and link flags v1 has no equivalent for), and v2 DROPS executive_summary, executive_summary_json and created_by_user_id. If you need the executive summary, read it per-initiative with scalepad_lm_get_initiative rather than falling back to the deprecated v1 list. Cursor-paginated: returns {data[], total_count, next_cursor} — keep paging until next_cursor is null, not until a page is short, and deduplicate by initiative id because cursor scans are not atomic. NO SORTING: the vendor documents no sort parameter on this endpoint (unlike the goals list and scalepad_lm_list_budget_initiatives, which do), so results come back in the server's own order — sort client-side.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; never decode or manufacture one.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together and sent as filter[field] query keys. Documented fields and their ONLY permitted operators: client.id (eq|in — note the DOT), status (eq|in; values New, Proposed, Approved, InProgress, OnHold, Declined, Completed), priority (eq|in; values None, Low, Medium, High), scheduled (eq only), scheduled_period (eq only), assigned_user_id (eq|in), created_at (eq|gt|gte|lt|lte, ISO-8601) and updated_at (eq|gt|gte|lt|lte). Example: {"client.id":"eq:0d3e7d0k-241a-461r-av15-758a90d70283","priority":"in:High,Medium","scheduled":"eq:true"}. Do not use an operator a field does not list.
pageSizeintegernonullMaximum records per page, 1-200 (clamped to ScalePad's documented 200 platform cap). The vendor declares no default and no minimum here, so omit it to take the server's own page size.

[ScalePad] For ONE MEETING, list the INITIATIVES attached to it — the roadmap items on that meeting's agenda. Takes a MEETING id, not an initiative id. Returns {initiative_ids: [...]} — bare ids ONLY, with no names, statuses or paging envelope; hydrate each with scalepad_lm_get_initiative. This is the meeting side of the pair: the mirror view, listing the meetings aligned to one initiative, is scalepad_lm_list_initiative_meetings.

ParamTypeRequiredDefaultDescription
meetingIdstringyesThe MEETING id whose attached initiatives to list (the parent — from the Lifecycle Manager meetings list).

[ScalePad] Update an initiative's NAME and EXECUTIVE SUMMARY — and nothing else. This endpoint cannot touch status, priority, schedule, budget, recurring investments, assigned user, assets or links; each of those has its own tool (scalepad_lm_update_initiative_status, _priority, _schedule, _budget, _recurring, _assigned_user, and the attach/detach tools). name is required on every call, so read the current record with scalepad_lm_get_initiative first rather than sending a partial payload. Succeeds with HTTP 204 and no body (this tool returns ) — re-read the initiative to confirm.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. Required: name (the initiative's name — required on every call, so resend the existing value when only the summary is changing). Optional: executive_summary_json (nullable, the summary as a ProseMirror JSON document serialized to a STRING) and executive_summary (nullable plain text, explicitly DEPRECATED by the vendor — its own words: "Deprecated by 'executive_summary_json', using this field will overwrite rich text"). Example: {"name":"Firewall refresh — phase 2","executive_summary_json":"{"type":"doc","content":[]}"}.
idstringyesThe initiative id to update (an id from scalepad_lm_list_initiatives_v2).

[ScalePad] Set the ScalePad user who OWNS an initiative. This is the only way to change the assignee — scalepad_lm_update_initiative cannot touch it. Note the identifier is a ScalePad HUB USER id (an internal MSP staff member), not a client CONTACT id and not an email address. Succeeds with HTTP 204 and no body (this tool returns ). The vendor's contract marks assigned_user_id as required with no documented null form, so it does not describe a way to UNASSIGN an initiative through this endpoint. assigned_user_id is filterable on scalepad_lm_list_initiatives_v2.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with the single required string assigned_user_id — the ScalePad Hub user id of the staff member to assign. Not an email, and not a client contact id. Example: {"assigned_user_id":"0d3e7d0k-241a-461r-av15-758a90d70283"}.
initiativeIdstringyesThe initiative id whose owner changes (an id from scalepad_lm_list_initiatives_v2).

[ScalePad] Replace an initiative's ONE-TIME investment plan — the budget_line_items set. This is a WHOLESALE REPLACEMENT of every one-time line, not an append: read the current lines with scalepad_lm_get_initiative and resend the ones that should survive, or you will silently drop them. An empty array clears all one-time items. Ongoing costs are a separate set with a separate tool: scalepad_lm_update_initiative_recurring. Succeeds with HTTP 204 and no body (this tool returns ). All amounts are integer subunits, so a decimal like 1250.00 is wrong — send 125000 for $1,250.00.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with a single budget_line_items array — optional and nullable at the top level, and an EMPTY ARRAY clears every one-time item. Each entry requires: label (string, states what the line is for, max 400 characters), cost_subunits (int64, the cost in SUBUNITS of the account's currency — cents for USD, satoshis for Bitcoin, so $1,250.00 is 125000) and cost_type (one of Fixed, PerAsset, PerUnit — the scale factor: a flat cost, a cost per attached asset, or a cost per unit). Optional per entry: unit_count (int32, nullable, the number of units the per-unit cost applies to — the vendor applies it ONLY when cost_type is PerUnit). There is no currency field: the account's own currency applies. Example: {"budget_line_items":[{"label":"Firewall appliance","cost_subunits":125000,"cost_type":"PerAsset"},{"label":"Licences","cost_subunits":4500,"cost_type":"PerUnit","unit_count":25}]}.
idstringyesThe initiative id whose one-time investments are being replaced (an id from scalepad_lm_list_initiatives_v2).

[ScalePad] Set ONE initiative's business priority, reflecting its strategic importance on the client's roadmap. This is the only way to change priority — scalepad_lm_update_initiative cannot touch it. Purely a ranking signal: it does not schedule, approve or fund anything. Succeeds with HTTP 204 and no body (this tool returns ). The current value is the priority field on scalepad_lm_get_initiative, and priority is filterable on scalepad_lm_list_initiatives_v2.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with the single required string priority. Permitted values, exactly as the vendor spells them: None, Low, Medium, High. Note None is a real assignable value, not an instruction to omit the field. Example: {"priority":"High"}.
idstringyesThe initiative id whose priority changes (an id from scalepad_lm_list_initiatives_v2).

[ScalePad] Replace an initiative's RECURRING investment plan — the recurring_line_items set of monthly or yearly ongoing commitments. This is a WHOLESALE REPLACEMENT of every recurring line, not an append: read the current lines with scalepad_lm_get_initiative and resend the ones that should survive. An empty array clears all recurring items. One-time costs are a separate set with a separate tool: scalepad_lm_update_initiative_budget. Succeeds with HTTP 204 and no body (this tool returns ). All amounts are integer subunits, so send 4900 for $49.00.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with a single recurring_line_items array — optional and nullable at the top level, and an EMPTY ARRAY clears every recurring item. Each entry requires: label (string, states what the recurring line is for, max 400 characters), cost_subunits (int64, the cost in SUBUNITS of the account's currency — cents for USD, so $49.00 is 4900), cost_type (one of Fixed, PerAsset, PerUnit) and frequency (one of Monthly, Yearly — this field exists ONLY on recurring lines, not on one-time budget lines). Optional per entry: unit_count (int32, nullable, applied ONLY when cost_type is PerUnit). There is no currency field: the account's own currency applies. Example: {"recurring_line_items":[{"label":"Managed firewall support","cost_subunits":4900,"cost_type":"Fixed","frequency":"Monthly"},{"label":"Per-seat licence","cost_subunits":1200,"cost_type":"PerUnit","unit_count":40,"frequency":"Yearly"}]}.
idstringyesThe initiative id whose recurring investments are being replaced (an id from scalepad_lm_list_initiatives_v2).

[ScalePad] Set the FISCAL QUARTER in which an initiative's resources are planned — the field that drives budget forecasting and the roadmap timeline. This is the only way to change the schedule; scalepad_lm_update_initiative cannot touch it. Succeeds with HTTP 204 and no body (this tool returns ). Sending fiscal_quarter as null UNSCHEDULES the initiative, which is what moves it into the not-scheduled bucket that budget forecasts count separately (see the include_not_scheduled option on scalepad_lm_list_budget_initiatives). The quarter is a FISCAL quarter under the account's own fiscal calendar, so quarter 1 is not necessarily January-March.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with a single fiscal_quarter object. The field is optional AND nullable: pass null to clear the schedule and leave the initiative unscheduled. When supplied, BOTH children are required — year (integer, the calendar year the fiscal quarter belongs to) and quarter (integer, the fiscal quarter number). The vendor documents no numeric range for either, so it does not promise that quarter is limited to 1-4. Example: {"fiscal_quarter":{"year":2027,"quarter":2}}. To unschedule: {"fiscal_quarter":null}.
idstringyesThe initiative id to schedule or unschedule (an id from scalepad_lm_list_initiatives_v2).

[ScalePad] Move ONE initiative through its business workflow by setting its status. This is the only way to change status — scalepad_lm_update_initiative cannot touch it. Succeeds with HTTP 204 and no body (this tool returns ). Setting Declined or Completed is the reversible alternative to scalepad_lm_delete_initiative when work should leave the active roadmap but the record and its history should survive.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with the single required string status. Permitted values, exactly as the vendor spells them: New, Proposed, Approved, InProgress (one word, no space or underscore), OnHold (likewise), Declined, Completed. Example: {"status":"InProgress"}.
idstringyesThe initiative id whose status changes (an id from scalepad_lm_list_initiatives_v2).

LM Meeting Types

ToolPlanAccessSummary
scalepad_lm_create_meeting_typeProWriteCreate a new meeting type for the ACCOUNT — available to every client in it, not scoped to one.
scalepad_lm_delete_meeting_typeProDestructivePERMANENTLY delete a meeting type from the ACCOUNT.
scalepad_lm_list_meeting_typesFreeRead-onlyList every meeting type currently defined for the account.
scalepad_lm_update_meeting_typeProWriteRename a meeting type.

[ScalePad] Create a new meeting type for the ACCOUNT — available to every client in it, not scoped to one. Returns HTTP 200 with ; note the key is meeting_type_id, NOT the bare that the meeting create calls return, so do not read this response with the same field name. That value is what you pass as type to scalepad_lm_create_meeting_v2 / scalepad_lm_update_meeting_v2. Check scalepad_lm_list_meeting_types first — ScalePad documents no uniqueness constraint on the label and no idempotency-key header, so a blind retry can leave two identically-named types that are indistinguishable to a user picking one.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with the single required string label — a short human-friendly description of the kind of meeting this represents (the vendor's own wording). This is the only field the create accepts. Example: {"label":"Quarterly Business Review"}.

[ScalePad] PERMANENTLY delete a meeting type from the ACCOUNT. Succeeds with HTTP 204 and no body (this tool returns ); 404 if it is missing or inaccessible. This is account-wide and irreversible — ScalePad documents no soft delete, restore or undo — and the vendor does NOT document what happens to existing meetings that still reference the deleted type, so treat the effect on historical meetings as unknown rather than assuming they are left alone. Echo the exact type back to the user first, read the current set with scalepad_lm_list_meeting_types, and prefer scalepad_lm_update_meeting_type when the goal is merely to correct a name.

ParamTypeRequiredDefaultDescription
meetingTypeIdstringyesThe meeting type id to delete (the meeting_type_id from scalepad_lm_list_meeting_types). Confirm this with the user before calling — the deletion is irreversible and affects the whole account.

[ScalePad] List every meeting type currently defined for the account. Returns {data: [...]} — a plain array with NO paging envelope at all (no total_count, no next_cursor) and no filter, sort or page-size parameters, so a single call returns the complete set. This is the lookup that turns a human meeting-type name into the id that scalepad_lm_create_meeting_v2 and scalepad_lm_update_meeting_v2 require in their type field; call it before either of those. The deprecated v1 meeting tools do NOT use these ids — they take a fixed built-in enum instead.

[ScalePad] Rename a meeting type. Only its label is editable — there is no other field, so this is purely a rename and existing meetings keep pointing at the same type id. Succeeds with HTTP 204 and no body (this tool returns ); confirm with scalepad_lm_list_meeting_types. Two account-wide consequences worth stating to the user first: the new label appears on EVERY client's meetings of this type, and the DEPRECATED v1 meeting update reacts badly to renames — the vendor documents that if a meeting type was renamed, a v1 update silently defaults that meeting to the account's FIRST meeting type instead of failing. Prefer scalepad_lm_update_meeting_v2 after any rename.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with the single required string label — the new short human-friendly description. This replaces the existing label outright. Example: {"label":"Annual Business Review"}.
meetingTypeIdstringyesThe meeting type id to rename (the meeting_type_id from scalepad_lm_list_meeting_types).

LM Meetings

ToolPlanAccessSummary
scalepad_lm_add_meeting_attendee_usersProWriteAdd internal USERS (your own staff — technicians, account managers) as attendees of a meeting.
scalepad_lm_create_meetingProWriteDEPRECATED v1 meeting create (POST /v1/meetings) — the vendor has scheduled it for removal on March 1, 2027. Use scalepad_lm_create_meeting_v2 for new work; this tool exists so an existing v1…
scalepad_lm_create_meeting_v2ProWriteSchedule a new client meeting — the CURRENT create operation (POST /v2/meetings).
scalepad_lm_delete_meetingProDestructivePERMANENTLY delete a meeting that is no longer needed.
scalepad_lm_delete_meeting_attendee_usersProDestructiveREMOVE internal USERS from a meeting's attendee list.
scalepad_lm_get_meetingFreeRead-onlyGet ONE Lifecycle Manager meeting in full by its id.
scalepad_lm_list_meetingsFreeRead-onlyList Lifecycle Manager meetings across every client the caller can access.
scalepad_lm_update_meetingProWriteDEPRECATED v1 meeting update (PUT /v1/meetings/) — scheduled for removal on March 1, 2027. Use scalepad_lm_update_meeting_v2 for new work.
scalepad_lm_update_meeting_completion_statusProWriteMark ONE meeting completed or incomplete.
scalepad_lm_update_meeting_v2ProWriteUpdate a meeting's core information — the CURRENT update operation (PUT /v2/meetings/).

[ScalePad] Add internal USERS (your own staff — technicians, account managers) as attendees of a meeting. This is the STAFF side of attendance: to add a client-side person, use scalepad_lm_add_meeting_attendee_contacts instead, which takes contacts rather than users. Additive — existing attendees are untouched. Succeeds with HTTP 201 and returns , the ids of the users now attending. Each entry in the body identifies a user by id OR email, so you can add someone by work email without looking their id up first.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with the single required array user_keys — the users to add as attendees. Each element is an object with id OR email; exactly one of the two must be present (id when known, otherwise the user's unique email address). Note the field is user_keys, not user_ids — user_ids is what the RESPONSE returns. Example: {"user_keys":[{"email":"tech@example.com"},{"id":"usr_42"}]}.
idstringyesThe meeting id to add user attendees to (from scalepad_lm_list_meetings).

[ScalePad] DEPRECATED v1 meeting create (POST /v1/meetings) — the vendor has scheduled it for removal on March 1, 2027. Use scalepad_lm_create_meeting_v2 for new work; this tool exists so an existing v1 integration keeps working until then. Returns HTTP 200 with . The ONLY behavioral difference that matters is the type field's value space: v1 takes a FIXED ENUM of built-in meeting kinds, while v2 takes a meeting-type ID. A v1-created meeting is an ordinary meeting — every other tool here (get, list, delete, completion status, attendees, attach) works on it identically. ScalePad documents no idempotency-key header, so never blind-retry this.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. Required: client_key — the owning client, as {"id":"..."} or {"name":"..."} (the client's unique name). Optional/nullable: title (defaults to "Untitled Meeting"), type, starts_at, ends_at and agenda_json. CRITICAL — in v1, type is one of the fixed enum values BusinessReview, AnnualBusinessReview, Discovery, Onboarding, CheckIn, PhoneCall or ProjectMeeting (defaulting to BusinessReview when omitted); it is NOT a meeting-type id, which is what the v2 create expects under the same field name. agenda_json is a ProseMirror JSON document (root {"type":"doc","content":[]}) serialized to a STRING. Example: {"client_key":{"id":"0d3e7d0k-241a-461r-av15-758a90d70283"},"title":"Q3 Business Review","type":"BusinessReview","starts_at":"2026-09-30T15:00:00Z"}.

[ScalePad] Schedule a new client meeting — the CURRENT create operation (POST /v2/meetings). Prefer this over scalepad_lm_create_meeting, which is the deprecated v1 and is scheduled for removal on March 1, 2027. Returns HTTP 200 with ; pass that id to scalepad_lm_get_meeting, the attendee tools and the attach tools. Every body field except client_key is optional, so the smallest valid call names only the client and accepts the defaults (title becomes "Untitled Meeting"). ScalePad documents no idempotency-key header, so never blind-retry this — re-check with scalepad_lm_list_meetings filtered on the client first. Note the asymmetry with the v2 UPDATE, which requires both title and type.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. Required: client_key — the owning client, as {"id":"..."} or, when the id is unavailable, {"name":"..."} using the client's unique name. Optional/nullable: title (defaults to "Untitled Meeting"), type, starts_at and ends_at (date-times), and agenda_json. CRITICAL — in v2, type is the ID OF A MEETING TYPE (get one from scalepad_lm_list_meeting_types); the fixed enum names v1 accepts (BusinessReview, Discovery, …) are NOT valid here. agenda_json is the meeting agenda as a ProseMirror JSON document (root {"type":"doc","content":[]}) serialized to a STRING — pass structured JSON, never HTML or Markdown. Example: {"client_key":{"id":"0d3e7d0k-241a-461r-av15-758a90d70283"},"title":"Q3 Business Review","type":"mt_7","starts_at":"2026-09-30T15:00:00Z","ends_at":"2026-09-30T16:00:00Z","agenda_json":"{"type":"doc","content":[]}"}.

[ScalePad] PERMANENTLY delete a meeting that is no longer needed. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the meeting is missing or inaccessible. ScalePad documents no soft delete, restore or undo, so resolve and echo the exact meeting back to the user first — read it with scalepad_lm_get_meeting. When the intent is only to close out a meeting that happened, prefer scalepad_lm_update_meeting_completion_status with is_completed true, which keeps the record, its agenda and its history. To remove only a PERSON from the meeting use scalepad_lm_delete_meeting_attendee_users (or the contacts equivalent); to break a link to a goal, initiative or action item use the matching detach tool — all of those leave the meeting intact.

ParamTypeRequiredDefaultDescription
idstringyesThe meeting id to delete (from scalepad_lm_list_meetings). Confirm this with the user before calling — the deletion is irreversible.

[ScalePad] REMOVE internal USERS from a meeting's attendee list. This is a removal despite ScalePad expressing it as a POST to a "…/attendees/users/delete" path rather than an HTTP DELETE — the vendor's own operation is named MeetingAttendeeUserDelete, and it is flagged destructive here so removing a person always prompts for confirmation. It removes only the ATTENDANCE record: the users themselves, and the meeting, both survive. Because the body names a SET of users, echo the exact list back to the user before calling and read the current attendees with scalepad_lm_get_meeting first — there is no undo. Succeeds with HTTP 200 and no body (this tool returns ). To remove a client-side person instead, use scalepad_lm_delete_meeting_attendee_contacts; to delete the whole meeting, scalepad_lm_delete_meeting.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with the single required array user_keys — the users to REMOVE as attendees. Each element is an object with id OR email; exactly one of the two must be present. Confirm this exact set with the user before calling. Example: {"user_keys":[{"email":"tech@example.com"}]}.
idstringyesThe meeting id to remove user attendees from (from scalepad_lm_list_meetings).

[ScalePad] Get ONE Lifecycle Manager meeting in full by its id. The response is WRAPPED in a single-key envelope — {"meeting": } — so read through the meeting property rather than expecting fields at the root (the action-item read, by contrast, returns its fields unwrapped). This is the only way to see WHO is attending: the record carries user_attendee_ids[] (internal staff, ids only — hydrate them separately if you need names) and contact_attendees[] (the client-side people), where scalepad_lm_list_meetings gives you nothing but the user_attendees_count and contact_attendees_count totals. Note the asymmetry in those two field names — the staff side is ids, the contact side is objects. The agenda also lives here, as ProseMirror rich text. Related records hang off separate tools: scalepad_lm_list_meeting_action_items, scalepad_lm_list_meeting_goals and scalepad_lm_list_meeting_initiatives.

ParamTypeRequiredDefaultDescription
idstringyesThe meeting id to retrieve (from scalepad_lm_list_meetings, or the returned by a create call).

[ScalePad] List Lifecycle Manager meetings across every client the caller can access. Cursor-paginated: returns {data[], total_count, next_cursor} — keep paging until next_cursor is null, not until a page looks short, and deduplicate by meeting id because cursor scans are not atomic. Rows are meeting OVERVIEW records, not the full documents: attendees arrive here only as the COUNTS user_attendees_count and contact_attendees_count, so to learn WHO is attending you must hydrate the meeting with scalepad_lm_get_meeting, which returns user_attendee_ids[] and contact_attendees[]. VENDOR DOC BUG worth knowing before you trust a timestamp: on these overview rows the vendor describes record_created_at and record_updated_at as "the date and time when the GOAL was created/updated" — the prose is copy-pasted from ScalePad's goal schema. The field names are correct and they do refer to the MEETING; only the description is wrong, and it is reproduced here rather than silently corrected. This endpoint is also deliberately narrower than most Lifecycle Manager lists — the vendor documents exactly ONE filter field and NO sort at all, so there is no sort parameter here (do not expect the due_at/sort_rank vocabulary the action-item list accepts).

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; never decode or manufacture one.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, sent as filter[field] query keys. Exactly ONE field is documented for this endpoint: client.id (note the DOT), and it supports only the eq operator — no 'in' list, unlike the account-wide deliverables list. An omitted operator means eq. Example: {"client.id":"eq:0d3e7d0k-241a-461r-av15-758a90d70283"}. Do not borrow filter fields from another resource; an undocumented key is a 400.
pageSizeintegernonullMaximum records per page, 1-200 (clamped to ScalePad's documented 200 platform cap). This operation's schema declares no default and no minimum, so omit it to take the server's own page size.

[ScalePad] DEPRECATED v1 meeting update (PUT /v1/meetings/) — scheduled for removal on March 1, 2027. Use scalepad_lm_update_meeting_v2 for new work. Treat the body as a REPLACEMENT: title and type are both required, so read the meeting first with scalepad_lm_get_meeting and resend what should survive. Succeeds with HTTP 204 and no body (this tool returns ). Two v1-specific hazards. First, type is a FIXED ENUM here, not a meeting-type id. Second, the vendor documents a SILENT FALLBACK: if the account's meeting type was renamed, the update quietly defaults the meeting to the FIRST meeting type for the account instead of failing — so a v1 update can change a meeting's type to something you did not ask for, and you should verify with scalepad_lm_get_meeting afterwards. The v2 update has no such fallback.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. REQUIRED: title and type. CRITICAL — in v1, type is one of the fixed enum values BusinessReview, AnnualBusinessReview, Discovery, Onboarding, CheckIn, PhoneCall or ProjectMeeting; it is NOT a meeting-type id. If the type was renamed in the account, ScalePad silently defaults to the account's FIRST meeting type. Optional/nullable: starts_at, ends_at and agenda_json (a ProseMirror JSON document serialized to a STRING). Example: {"title":"Q3 Business Review","type":"BusinessReview","starts_at":"2026-09-30T15:00:00Z"}.
idstringyesThe meeting id to update (from scalepad_lm_list_meetings).

[ScalePad] Mark ONE meeting completed or incomplete. This is the only way to move a meeting's completion flag — neither scalepad_lm_update_meeting nor its v2 counterpart can touch it. Sending false reopens a meeting that was closed. Succeeds with HTTP 204 and no body (this tool returns ). Not to be confused with scalepad_lm_update_action_item_completion_status, which completes a TASK, or with the assessment-level completion tool — three different resources with parallel names.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body with the single required boolean is_completed: true marks the meeting completed, false marks it incomplete. Example: {"is_completed":true}.
idstringyesThe meeting id whose completion state changes (from scalepad_lm_list_meetings).

[ScalePad] Update a meeting's core information — the CURRENT update operation (PUT /v2/meetings/). Prefer this over scalepad_lm_update_meeting, the deprecated v1 scheduled for removal on March 1, 2027. Treat the body as a REPLACEMENT, not a merge: title and type are BOTH REQUIRED here even though the v2 CREATE marks them optional, so read the meeting first with scalepad_lm_get_meeting and resend everything that should survive — in particular the nullable starts_at, ends_at and agenda_json, which an omitted field can clear. Succeeds with HTTP 204 and no body (this tool returns ); call scalepad_lm_get_meeting to see the result. Completion state is NOT editable here — use scalepad_lm_update_meeting_completion_status. Attendees and linked goals/initiatives/action items are not editable here either.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. REQUIRED: title and type. CRITICAL — in v2, type is the ID OF A MEETING TYPE (from scalepad_lm_list_meeting_types), not one of v1's fixed enum names. Optional/nullable: starts_at, ends_at (date-times) and agenda_json (the agenda as a ProseMirror JSON document, root {"type":"doc","content":[]}, serialized to a STRING). Example: {"title":"Q3 Business Review","type":"mt_7","starts_at":"2026-09-30T15:00:00Z","ends_at":"2026-09-30T16:00:00Z","agenda_json":"{"type":"doc","content":[]}"}.
idstringyesThe meeting id to update (from scalepad_lm_list_meetings).

LM Notes

ToolPlanAccessSummary
scalepad_lm_create_noteProWriteCreate a new note for a client.
scalepad_lm_delete_noteProDestructivePERMANENTLY delete a note.
scalepad_lm_get_noteFreeRead-onlyGet ONE note's full details by its id.
scalepad_lm_list_notesFreeRead-onlyList Lifecycle Manager notes across every client the caller can access.
scalepad_lm_update_noteProDestructiveUpdate a note's title and description.
scalepad_lm_update_note_archive_statusProWriteArchive a note, or bring an archived note back to active.

[ScalePad] Create a new note for a client. Returns HTTP 200 with — that value is what the read tools expose as note_id. There is NO plain-text field on this resource: description_json is required and must be a ProseMirror JSON document, so a note cannot be written as plain text, HTML or Markdown. ScalePad documents no idempotency-key header for this operation, so never blind-retry it — re-check with scalepad_lm_list_notes filtered on filter[client.id] first.

ParamTypeRequiredDefaultDescription
bodyJsonstringnonullJSON object body. REQUIRED: client_key (the owning client, as {"id":"..."} or, when the id is unknown, {"name":"..."} using the client's unique name) and description_json — the note body as a ProseMirror JSON document (root {"type":"doc","content":[]}; nodes doc / paragraph / heading (level 1-4) / bulletList / orderedList / listItem / hardBreak, marks bold / italic / underline / strike / link with href) SERIALIZED TO A STRING for this field. Optional/nullable: title. Example: {"client_key":{"id":"0d3e7d0k-241a-461r-av15-758a90d70283"},"title":"Renewal call","description_json":"{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"Discussed the firewall refresh."}]}]}"}.

[ScalePad] PERMANENTLY delete a note. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the note is missing or inaccessible. ScalePad documents no soft delete, restore or undo for this operation, so resolve and echo the exact note back to the user first — read it with scalepad_lm_get_note. In almost every case where the goal is 'get this out of the way', the right call is scalepad_lm_update_note_archive_status with is_archived true instead: archiving is reversible and keeps the note's content and history, while this destroys both.

ParamTypeRequiredDefaultDescription
idstringyesThe note id to delete (note_id from scalepad_lm_list_notes). Confirm this with the user before calling — the deletion is irreversible; prefer archiving.

[ScalePad] Get ONE note's full details by its id. Returns the record directly (not wrapped in an envelope): note_id, title, description_json (the ProseMirror document as a string — description_json, is_archived, created_at and note_id are the guaranteed-present fields), is_archived, created_at, updated_at and linked_item, which names the entity the note hangs off. Run this before scalepad_lm_update_note: description_json is REQUIRED on update, so the current document is what you need in order to edit rather than replace the note's body.

ParamTypeRequiredDefaultDescription
idstringyesThe note id to retrieve — the note_id value from scalepad_lm_list_notes (the create call, confusingly, returns the same value as ).

[ScalePad] List Lifecycle Manager notes across every client the caller can access. Cursor-paginated: returns {data[], total_count, next_cursor} — keep paging until next_cursor is null, not until a page looks short, and deduplicate by note_id because cursor scans are not atomic. Each row carries note_id, title, description_json (the ProseMirror rich-text document, as a string), is_archived, created_at, updated_at and linked_item. Exactly TWO filter fields are documented, each eq-only: client.id (note the DOT — this endpoint spells it client.id, matching the contracts list, while the hardware dashboard and hardware lifecycles endpoints use the UNDERSCORED client_id; a wrong spelling is silently ignored rather than rejected) and is_archived. Archived notes are included by default, so pass is_archived eq:false to see only active ones. No sort parameter is documented here.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page; never decode or manufacture one.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together and sent as filter[field] query keys. Only two fields are documented, both eq-only: "client.id" (DOTTED — not client_id) and "is_archived" (true | false). An omitted operator means eq. Example: {"client.id":"eq:0d3e7d0k-241a-461r-av15-758a90d70283","is_archived":"eq:false"}. Do not borrow filter names from the hardware endpoints — they use different spellings.
pageSizeintegernonullMaximum records per page, 1-200 (clamped to ScalePad's documented 200 platform cap). No default is declared; omit to take the server's own page size.

[ScalePad] Update a note's title and description. description_json is REQUIRED, so this call always rewrites the note body — there is no way to change only the title, and there is no plain-text field to fall back on. Read the current document with scalepad_lm_get_note and send the edited version, or the existing body is replaced by whatever is supplied. Omitting the optional title clears it. Succeeds with HTTP 204 and no body (this tool returns ) — re-read with scalepad_lm_get_note to see the result. The archive flag is NOT editable here: use scalepad_lm_update_note_archive_status.

ParamTypeRequiredDefaultDescription
bodyJsonstringnonullJSON object body. REQUIRED: description_json — the full replacement note body as a ProseMirror JSON document (root {"type":"doc","content":[]}) SERIALIZED TO A STRING. Never HTML or Markdown, and never omitted. Optional/nullable: title (omitting it clears the existing title). Example: {"title":"Renewal call (updated)","description_json":"{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"Firewall refresh approved."}]}]}"}.
idstringyesThe note id to update (note_id from scalepad_lm_list_notes).

[ScalePad] Archive a note, or bring an archived note back to active. This is a REVERSIBLE boolean toggle in both directions — is_archived true archives, false unarchives — which is why it is deliberately NOT flagged destructive: nothing is lost either way, and the note keeps its id, body and history. It is the safe alternative to scalepad_lm_delete_note when the intent is only to get a note out of the active list. This is the only way to move the flag; scalepad_lm_update_note cannot touch it. Succeeds with HTTP 204 and no body (this tool returns ). The current state is the is_archived field on scalepad_lm_get_note, and archived notes are INCLUDED by default in scalepad_lm_list_notes unless filtered with filter[is_archived]=eq:false.

ParamTypeRequiredDefaultDescription
bodyJsonstringnonullJSON object body with the single required boolean is_archived: true archives the note, false brings an archived note back to active. Example: {"is_archived":true}.
idstringyesThe note id whose archive state changes (note_id from scalepad_lm_list_notes).

LM Opportunities

ToolPlanAccessSummary
scalepad_lm_get_opportunities_create_fieldsFreeRead-onlyMETADATA read: describe which fields a PSA OPPORTUNITY create would accept for one client, and whether creating one is even possible.
scalepad_lm_list_opportunitiesFreeRead-onlyList the PSA sales opportunities available for a client — the vendor's stated purpose is to let an MSP pick an EXISTING opportunity to link to an initiative.

[ScalePad] METADATA read: describe which fields a PSA OPPORTUNITY create would accept for one client, and whether creating one is even possible. Creates nothing. Use it to drive an opportunity-create form or to validate input before calling the initiative-opportunity create on the initiatives surface. Returns {data[], field_options[], ability, integration_url}: data and field_options are BOTH arrays of the same field descriptor, and the vendor gives them the identical description ("flat opportunity fields that the consumer should render") without documenting any difference between them — read them as equivalent rather than inventing one. Each descriptor is {field_key, parent_field_key, label, input_type, is_required}, where input_type is a union on its type field: Text (with validation {min_length, max_length}), Select (with options[{value, label}]), DateTime, Integer, Currency (with max_fraction_digits) or DataTypeReference (with data_type People or Member). ability is {has_psa_integration, organization_is_registered} — note the vendor says organization_is_registered HERE while the ticket equivalent says client_is_registered. integration_url is a direct link into the connected PSA. Note this call REQUIRES a client id, whereas scalepad_lm_get_tickets_create_fields takes one only optionally.

ParamTypeRequiredDefaultDescription
clientIdstringyesREQUIRED. The client whose PSA opportunity create fields should be returned — this operation's client_id query parameter is mandatory, so there is no account-wide form of this read.

[ScalePad] List the PSA sales opportunities available for a client — the vendor's stated purpose is to let an MSP pick an EXISTING opportunity to link to an initiative. Returns {data[]} only: no total_count, no next_cursor, no cursor paging, no filter[...] keys and no sort, just two plain query parameters. Each row is the overview {opportunity_id, title, description, status}; there is no amount, stage-probability, owner or close-date field on this schema, so do not promise those. The opportunity_id is the value an initiative-opportunity attach expects. If the account has no PSA integration connected, expect an empty data array rather than an error — confirm the integration state with scalepad_lm_get_opportunities_create_fields, whose ability.has_psa_integration flag reports it.

ParamTypeRequiredDefaultDescription
clientIdstringnonullOptional. The client whose opportunities should be listed. Unlike scalepad_lm_get_opportunities_create_fields, where the client is mandatory, this parameter is documented as optional — the vendor does not state what an omitted client returns, so pass one whenever you know it rather than relying on a whole-account default.
includeInactivebooleannonullOptional. Set true to include INACTIVE opportunities as well, but only where the connected PSA actually provides them (the vendor qualifies it exactly that way, so a PSA that does not expose inactive records will return none regardless). Omit or false to get the active set.

LM Roadmap Exports

ToolPlanAccessSummary
scalepad_lm_export_roadmap_csvFreeRead-onlyExport ONE client's initiative roadmap as a CSV file.
scalepad_lm_export_roadmap_pdfFreeRead-onlyExport ONE client's initiative roadmap as the client-facing PDF.
scalepad_lm_export_roadmap_spreadsheetFreeRead-onlyExport ONE client's initiative roadmap as an XLSX workbook.

[ScalePad] Export ONE client's initiative roadmap as a CSV file. A POST that only READS — the verb carries the export scope in the body and persists nothing (the vendor's own safety class is "POST command — generates export; no persistence documented"). Because text/csv cannot cross MCP's JSON tool surface, this tool does NOT return the file contents: StackJack downloads the CSV, stores it, and returns a JSON envelope with a short-lived READ-ONLY download URL — {SasUrl, ContentType, SuggestedFilename, SizeBytes, ExpiresAt}, valid for about 30 minutes. Re-run to mint a fresh URL. This shares its body schema exactly with scalepad_lm_export_roadmap_spreadsheet; the PDF export requires six ADDITIONAL fields, so a body built here will be rejected by scalepad_lm_export_roadmap_pdf.

ParamTypeRequiredDefaultDescription
bodyJsonstringnonullJSON object body. REQUIRED: client_id (a flat STRING — the client whose roadmap is exported; resolve a name with scalepad_lm_search_clients) and roadmap_download_payload. Inside roadmap_download_payload, THREE members are required: included_statuses (array of strings — the initiative statuses to include), included_priorities (array of strings — the initiative priorities to include) and should_include_unscheduled (boolean — whether unscheduled initiatives appear). One optional/nullable member: included_fiscal_quarter_range, and when supplied ALL of its nested fields are required — starting.year, starting.quarter, end.year, end.quarter (all int32; note the members are named "starting" and "end", not start/finish). Example: {"client_id":"0d3e7d0k-241a-461r-av15-758a90d70283","roadmap_download_payload":{"included_statuses":["New","InProgress"],"included_priorities":["High"],"should_include_unscheduled":true,"included_fiscal_quarter_range":{"starting":{"year":2026,"quarter":4},"end":{"year":2027,"quarter":3}}}}.

[ScalePad] Export ONE client's initiative roadmap as the client-facing PDF. A POST that only READS — it persists nothing (the vendor's own safety class is "POST command — generates export; no persistence documented"). Because binary cannot cross MCP's JSON tool surface, this tool does NOT return the file bytes: StackJack downloads the PDF, stores it, and returns a JSON envelope with a short-lived READ-ONLY download URL — {SasUrl, ContentType, SuggestedFilename, SizeBytes, ExpiresAt}, valid for about 30 minutes. Re-run to mint a fresh URL. This export's body is a SUPERSET of the CSV and spreadsheet body: it requires six additional layout switches, none of them optional, so reusing a CSV body here fails validation.

ParamTypeRequiredDefaultDescription
bodyJsonstringnonullJSON object body. REQUIRED: client_id (a flat STRING) and roadmap_download_payload. Inside roadmap_download_payload, NINE members are required — the three the CSV also needs, plus six PDF-only layout switches that the vendor marks required rather than optional: included_statuses (array of strings), included_priorities (array of strings), should_include_unscheduled (boolean), should_include_budgets (boolean — show each initiative's budget), should_include_assets (boolean — include each initiative's asset list), sorting_column (STRING naming the order initiatives appear in; the vendor documents no value list for it), should_include_cover_page (boolean), should_include_overview (boolean — include initiative overviews) and should_include_goals (boolean). One optional/nullable member: included_fiscal_quarter_range, and when supplied ALL of its nested fields are required — starting.year, starting.quarter, end.year, end.quarter (all int32). Example: {"client_id":"0d3e7d0k-241a-461r-av15-758a90d70283","roadmap_download_payload":{"included_statuses":["New","InProgress"],"included_priorities":["High","Medium"],"should_include_unscheduled":false,"should_include_budgets":true,"should_include_assets":false,"sorting_column":"StartDate","should_include_cover_page":true,"should_include_overview":true,"should_include_goals":true}}.

[ScalePad] Export ONE client's initiative roadmap as an XLSX workbook. A POST that only READS — it persists nothing (the vendor's own safety class is "POST command — generates export; no persistence documented"). Because an XLSX workbook cannot cross MCP's JSON tool surface, this tool does NOT return the file bytes: StackJack downloads the workbook, stores it, and returns a JSON envelope with a short-lived READ-ONLY download URL — {SasUrl, ContentType, SuggestedFilename, SizeBytes, ExpiresAt}, valid for about 30 minutes. Re-run to mint a fresh URL. ContentType comes back as the full OpenXML type (application/vnd.openxmlformats-officedocument.spreadsheetml.sheet), not a short "xlsx". The body schema is IDENTICAL to scalepad_lm_export_roadmap_csv — the two differ only in output format — while the PDF export requires six additional fields.

ParamTypeRequiredDefaultDescription
bodyJsonstringnonullJSON object body, identical in shape to scalepad_lm_export_roadmap_csv. REQUIRED: client_id (a flat STRING) and roadmap_download_payload. Inside roadmap_download_payload, THREE members are required: included_statuses (array of strings — the initiative statuses to include), included_priorities (array of strings) and should_include_unscheduled (boolean). One optional/nullable member: included_fiscal_quarter_range, and when supplied ALL of its nested fields are required — starting.year, starting.quarter, end.year, end.quarter (all int32). Example: {"client_id":"0d3e7d0k-241a-461r-av15-758a90d70283","roadmap_download_payload":{"included_statuses":["New"],"included_priorities":["High"],"should_include_unscheduled":true}}.

LM SaaS Management

ToolPlanAccessSummary
scalepad_lm_create_saas_enrollment_tokenProDestructiveCreate a SaaS Management enrollment token for ONE client.
scalepad_lm_get_saas_utilization_summaryFreeRead-onlyGet the SaaS-utilization summary metrics for ONE client: total_apps_used, unapproved_app_count, ai_app_count and total_active_time_ms (active time in MILLISECONDS, not seconds or hours — divide by…

[ScalePad] Create a SaaS Management enrollment token for ONE client. An enrolling RMM or Core agent presents the returned token to a separate device-enrollment endpoint to register a device under this client. Succeeds with HTTP 201 and returns {id, value, description, site_id, status, max_uses, remaining_uses, expires_at}. TREAT `value` AS A SECRET — it is a credential that enrolls devices into this client's tenant, so do not echo it into logs, tickets or chat transcripts beyond what is needed to hand it to the person doing the rollout. ScalePad documents no idempotency-key header for this operation AND no list or revoke operation in this API surface, so never blind-retry it: a repeated attempt mints an ADDITIONAL live token that cannot be cleaned up from here — only from the ScalePad console. max_uses and remaining_uses come back on the response but no request field controls them. A 400 most often means expires_at is not a valid calendar date.

ParamTypeRequiredDefaultDescription
bodyJsonstringnonullJSON object body. All three fields are OPTIONAL and nullable: description (a human-readable label — set one, since with no list endpoint it is the only way anyone will later tell tokens apart in the console), site_id (an optional site identifier to scope the token to), and expires_at (a DATE ONLY, format YYYY-MM-DD — NOT a date-time — stored as end-of-day UTC; OMITTING it mints a token that never expires until revoked, so supply one unless a permanent token is genuinely intended). Example: {"description":"Contoso HQ rollout","expires_at":"2026-12-31"}.
clientIdstringyesThe ScalePad client id the enrollment token is minted for (resolve a client name with scalepad_lm_search_clients).

[ScalePad] Get the SaaS-utilization summary metrics for ONE client: total_apps_used, unapproved_app_count, ai_app_count and total_active_time_ms (active time in MILLISECONDS, not seconds or hours — divide by 3,600,000 for hours). A flat object with no paging, no per-application breakdown, and no way to scope the reporting window: the endpoint exposes no date parameters at all, so the period is whatever ScalePad's SaaS Management reporting period is and cannot be narrowed from here. Requires the client to be enrolled in SaaS Management (see scalepad_lm_create_saas_enrollment_token). A 404 means the client is unknown or not owned by this tenant; a 402 means the tenant has no active SaaS Management subscription, which is a billing state rather than a permissions problem. Note this endpoint documents no 403.

ParamTypeRequiredDefaultDescription
clientIdstringyesThe ScalePad client id whose SaaS-utilization summary to retrieve (resolve a client name with scalepad_lm_search_clients).

LM Tickets

ToolPlanAccessSummary
scalepad_lm_get_tickets_create_fieldsFreeRead-onlyMETADATA read: describe which fields a PSA TICKET create would accept against the account's PRIMARY PSA — required and optional fields, valid values, validation hints and dependent child fields.

[ScalePad] METADATA read: describe which fields a PSA TICKET create would accept against the account's PRIMARY PSA — required and optional fields, valid values, validation hints and dependent child fields. Creates no ticket; the vendor's stated use is to drive a ticket-create form. Returns {data[], status, integration_url, field_options[]}: data and field_options are BOTH arrays of the same descriptor and the vendor gives them the identical description ("the field tree that the consumer should render for ticket creation") without documenting any difference — read them as equivalent rather than inventing one. Each descriptor is {key, label, input_type, is_required, values[{id, name}], validation {min_length, max_length, min_date_time, max_date_time}, child_options} where child_options holds nested options KEYED BY THE PARENT VALUE'S id, which is how dependent fields (board -> status, say) are expressed. Note the field-name drift against the opportunity equivalent: the identifier is key here but field_key on scalepad_lm_get_opportunities_create_fields, and selectable values live in a flat values[] array here rather than inside input_type. status is {has_psa_integration, client_is_registered, unavailable_message} — client_is_registered where opportunities say organization_is_registered, and unavailable_message explains why creation is blocked for this client when a PSA IS connected (null when creation is available or no guidance is needed). integration_url is a direct link into the connected PSA, null when no integration is connected.

ParamTypeRequiredDefaultDescription
clientIdstringnonullOptional. The client whose PSA ticket-create field availabilities should be returned. Unlike scalepad_lm_get_opportunities_create_fields — where client_id is REQUIRED — this operation documents it as optional; pass one whenever you know it, since per-client blocking is exactly what status.client_is_registered and status.unavailable_message report. The vendor does not document what an omitted client returns, so do not assume it yields an account-wide template.

Quoter Categories

ToolPlanAccessSummary
scalepad_quoter_create_categoryProWriteCreate a Quoter catalog category.
scalepad_quoter_delete_categoryProDestructiveDelete a Quoter catalog category by id.
scalepad_quoter_get_categoryFreeRead-onlyGet one Quoter catalog category by id (from scalepad_quoter_list_categories).
scalepad_quoter_list_categoriesFreeRead-onlyList Quoter catalog categories.
scalepad_quoter_update_categoryProWritePartially update a Quoter catalog category (HTTP PATCH).

[ScalePad] Create a Quoter catalog category. Returns 201 with the created category record (id, name, parent_category, parent_category_id, timestamps). Only name is required; a parent may be given either by name (parent_category) or by id (parent_category_id). ScalePad does not publish name lengths, uniqueness rules, or whether the two parent fields are mutually exclusive, so send just one of them and pass server validation through verbatim.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. Required: name (string). Optional: parent_category (the parent category's NAME), parent_category_id (the parent category's id from scalepad_quoter_list_categories). Example: {"name":"Network Switches","parent_category_id":"cat_123"}.

[ScalePad] Delete a Quoter catalog category by id. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the category is not found. ScalePad documents NO soft delete, restore, undo, cascade behavior, or dependency-conflict status for catalog deletes — treat this as irreversible, and never delete a parent category automatically in response to a 404 or an inferred orphan.

ParamTypeRequiredDefaultDescription
idstringyesThe Quoter category id to delete (from scalepad_quoter_list_categories). Confirm with the user first — items and quote lines referencing it may be affected in ways the vendor does not document.

[ScalePad] Get one Quoter catalog category by id (from scalepad_quoter_list_categories). Returns id, name, parent_category (the parent's name), parent_category_id, record_created_at, and record_updated_at. The only documented failure besides success is 404 — the vendor does not distinguish a deleted category from one this API key cannot see.

ParamTypeRequiredDefaultDescription
fieldsstringnonullComma-separated sparse-fieldset selector. This parameter NARROWS the response — omit it to return the full record. Documented fields: id, name, parent_category, parent_category_id, record_created_at, record_updated_at.
idstringyesThe Quoter category id to read (the id field from scalepad_quoter_list_categories). ScalePad publishes no id regex or maximum length for Quoter public ids.

[ScalePad] List Quoter catalog categories. Cursor-paginated: returns {data[], total_count, next_cursor} where next_cursor is null on the final page. Each category carries id, name, parent_category (the parent's NAME), parent_category_id, record_created_at, and record_updated_at; the response schema publishes no required array, so tolerate missing properties. The returned id is what you pass to scalepad_quoter_get_category, to filter[category_id] on scalepad_quoter_list_items, and as category.id when authoring quote line items with scalepad_quoter_create_quote_section_line_items.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page, and keep paging until next_cursor is null rather than until a page looks short. Cursor scans are not atomic — concurrent changes can skip or duplicate records, so deduplicate by id.
fieldsstringnonullComma-separated sparse-fieldset selector. This parameter NARROWS the response — omit it to return the full record. Documented fields: id, name, parent_category, parent_category_id, record_created_at, record_updated_at.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together, e.g. {"name":"cont:switch","parent_category_id":"eq:cat_123"}. Documented filters here: name (eq, cont), parent_category_id (eq), record_created_at (gt, lt), record_updated_at (gt, lt — useful for incremental polling). An omitted operator defaults to eq, but prefer the explicit operator each field documents.
pageSizeintegernonullMaximum records per page, 1-200. The Quoter catalog list schemas declare NO default page size (50 appears only as an example), so set this explicitly for predictable paging.
sortstringnonullComma-separated sort expression. This schema enumerates BOTH signs for every field, so always sign each one: +field ascends, -field descends. Sortable: id, name, parent_category_id, record_created_at, record_updated_at. Example: '-record_updated_at,+name'.

[ScalePad] Partially update a Quoter catalog category (HTTP PATCH). Every body property is optional; send only what changes. Returns 200 with the updated record. Unlike the newer quote line-item patch contract, this older catalog shape does NOT document null-as-clear semantics, nor whether an empty body is accepted, so there is no documented way to clear a parent relationship — surface whatever the server returns.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body; all properties optional. Accepts name, parent_category (parent's NAME), parent_category_id. Example: {"name":"Access Switches"}.
idstringyesThe Quoter category id to update (from scalepad_quoter_list_categories).

Quoter Contacts

ToolPlanAccessSummary
scalepad_quoter_create_contactProWriteCreate a Quoter contact with billing and optional shipping details.
scalepad_quoter_get_contactFreeRead-onlyGet one Quoter contact by id.
scalepad_quoter_list_contactsFreeRead-onlyList and filter Quoter contacts.
scalepad_quoter_update_contactProWritePartially update a Quoter contact (HTTP PATCH), backfilling the remote linkage first if needed (the same side effect as the fetch).

[ScalePad] Create a Quoter contact with billing and optional shipping details. Success is HTTP 200 (NOT 201 — that is the vendor's documented contract, and its 200 text carries the same "updated successfully" copy drift as the fetch) and returns the Contact record. Only the request fields listed below are persisted: response-only address properties such as normalized country/state names, is_eu, or coordinates are ignored if sent. Declares 400 on a malformed request, 401, and 422 on validation — including the rule that the same billing_email + client.id pair must not already exist.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body (CreateContactRequest). Required: billing_email, billing_first_name, billing_last_name, billing_organization (strings — the schema publishes no email format, length, or pattern for them), and billing_address, an object that REQUIRES country_code matching ^[A-Z]{2}$ and optionally takes address_line_1, address_line_2 (nullable), city, postal_code (nullable), state_prov_code (nullable). Optional: billing_mobile_phone, billing_work_phone, title, website; client (must reference an existing ScalePad client — when supplied, the persisted billing_organization is OVERWRITTEN with the resolved client name); shipping_address (same create-address schema, country_code still required); shipping_email, shipping_first_name, shipping_label, shipping_last_name, shipping_organization, shipping_phone. Example: {"billing_email":"ada@acme.test","billing_first_name":"Ada","billing_last_name":"Lovelace","billing_organization":"Acme","billing_address":{"country_code":"US","city":"Austin"},"client":{"id":"2220324"}}.

[ScalePad] Get one Quoter contact by id. Returns the Contact record (billing_* and shipping_* fields, client, title, website, normalized addresses, timestamps). One documented side effect to be aware of: if no local remote-system linkage exists for the contact, the fetch performs a remote lookup and BACKFILLS the resolved id — it does not change record_updated_at, so the operation is idempotent in practice, but it is not strictly free of writes. Declares 401, 404, and 422. A vendor copy-drift note: the 200 description reads "Contact updated successfully" even though this is a fetch.

ParamTypeRequiredDefaultDescription
fieldsstringnonullComma-separated sparse-fieldset selector. This parameter NARROWS the response — omit it to return the full record. Unlike the rest of Quoter, the allowed set is documented in the vendor's PROSE rather than in the schema enum: billing_address, billing_email, billing_first_name, billing_last_name, billing_mobile_phone, billing_organization, billing_work_phone, client, id, record_created_at, record_updated_at, shipping_address, shipping_email, shipping_first_name, shipping_label, shipping_last_name, shipping_organization, shipping_phone, title, website.
idstringyesThe Quoter contact id to read (the id field from scalepad_quoter_list_contacts). Contact ids are nullable in list responses — if the contact you want has no id, use scalepad_quoter_list_contacts with the filter[billing_email] + filter[client.id] tuple instead.

[ScalePad] List and filter Quoter contacts. Cursor-paginated: {data[], total_count, next_cursor}. Each contact carries id (NULLABLE — an unlinked contact can appear without one), billing_email, billing_first_name, billing_last_name, billing_organization, billing_address, billing_mobile_phone, billing_work_phone, the shipping_* equivalents (shipping_address, shipping_email, shipping_first_name, shipping_label, shipping_last_name, shipping_organization, shipping_phone), client, title, website, record_created_at, and record_updated_at. Response addresses are NORMALIZED and can include three address lines, country/state names and codes, is_eu, and geospatial coordinates — more than the write contract accepts. The response schema has no required array, so tolerate missing properties. When a row has no id, identify it with the tuple filter filter[billing_email]=eq:...&filter[client.id]=eq:... instead.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page, and keep paging until next_cursor is null. Cursor scans are not atomic, so deduplicate (by id, or by the billing_email + client.id tuple for unlinked rows).
fieldsstringnonullComma-separated sparse-fieldset selector. This parameter NARROWS the response — omit it to return the full record. Unlike the rest of Quoter, the allowed set is documented in the vendor's PROSE rather than in the schema enum: billing_address, billing_email, billing_first_name, billing_last_name, billing_mobile_phone, billing_organization, billing_work_phone, client, id, record_created_at, record_updated_at, shipping_address, shipping_email, shipping_first_name, shipping_label, shipping_last_name, shipping_organization, shipping_phone, title, website.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together. The canonical lookup is {"billing_email":"eq:john@example.com","client.id":"eq:2220324"}. Documented filters: id (eq), client.id (eq), billing_email (eq, cont), email (DEPRECATED alias for billing_email; eq, cont), first_name / last_name / phone / organization / address / city / postal_code (eq, cont — these map to the billing-side values), country / region (eq — billing country and region codes), record_created_at / record_updated_at (eq, gt, lt — note contacts accept eq on dates, unlike the catalog lists).
pageSizeintegernonullMaximum records per page, 1-200. The contact list schema declares NO default page size, so set this explicitly for predictable paging.
sortstringnonullComma-separated sort expression. Unlike the Quoter catalog lists, the sign is OPTIONAL here: no prefix or '+' ascends, '-' descends. Sortable: billing_first_name, billing_last_name, id, record_created_at, record_updated_at. Example: 'billing_last_name,-record_updated_at'.

[ScalePad] Partially update a Quoter contact (HTTP PATCH), backfilling the remote linkage first if needed (the same side effect as the fetch). Returns 200 with the updated Contact. No property is required. Two hard rules: (1) the body must NOT contain client at all — sending client, EVEN AS NULL, returns 422; and (2) if a changed billing_email combined with the contact's existing client.id would collide with another account contact, the request is rejected. The schema permits nulls but the vendor does not define whether every null clears or is ignored, so preserve the distinction between sending null and omitting a field and report the server result as-is. Declares 400, 401, 404, and 422.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body (UpdateContactRequest); no property is required, and client must be ABSENT entirely (even null returns 422). Accepts the nullable billing/shipping scalars matching create — billing_email, billing_first_name, billing_last_name, billing_organization, billing_mobile_phone, billing_work_phone, title, website, shipping_email, shipping_first_name, shipping_label, shipping_last_name, shipping_organization, shipping_phone — plus billing_address and shipping_address objects whose own fields are all optional and nullable here. Example: {"billing_work_phone":"+1-512-555-0100","billing_address":{"city":"Dallas"}}.
idstringyesThe Quoter contact id to update (from scalepad_quoter_list_contacts).

Quoter Item Groups

ToolPlanAccessSummary
scalepad_quoter_create_item_groupProWriteCreate a Quoter item group.
scalepad_quoter_create_item_group_assignmentProWriteAssign a catalog item to an item group by creating the join record.
scalepad_quoter_delete_item_groupProDestructiveDelete a Quoter item group by id.
scalepad_quoter_delete_item_group_assignmentProDestructiveRemove an item from an item group by deleting the join record.
scalepad_quoter_get_item_groupFreeRead-onlyGet one Quoter item group by id (from scalepad_quoter_list_item_groups).
scalepad_quoter_get_item_group_assignmentFreeRead-onlyGet one item-group-item assignment by its own id (from scalepad_quoter_list_item_group_assignments — this is the assignment's id, not the item id or the group id).
scalepad_quoter_list_item_group_assignmentsFreeRead-onlyList item-group-item assignments — the join records that place catalog items into item groups.
scalepad_quoter_list_item_groupsFreeRead-onlyList Quoter item groups.
scalepad_quoter_update_item_groupProWritePartially update a Quoter item group (HTTP PATCH).

[ScalePad] Create a Quoter item group. Returns 201 with the created record (id, name, timestamps). The body takes exactly one property — name — and creating the group does NOT add any items to it: follow up with scalepad_quoter_create_item_group_assignment once per item. Only 404 is declared besides success. The vendor publishes no name length or uniqueness rule.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. Required: name (string) — the only property this create schema accepts. Example: {"name":"Standard Workstation Bundle"}.

[ScalePad] Assign a catalog item to an item group by creating the join record. Returns 201 with the created assignment (id, item_group_id, item_id, timestamps). Both body fields are required and there is NO PATCH for assignments — to move an item to a different group, delete this assignment and create another. Only 404 is declared besides success; the vendor publishes no duplicate-assignment conflict status.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. Both properties are required: item_group_id (from scalepad_quoter_list_item_groups) and item_id (from scalepad_quoter_list_items). Example: {"item_group_id":"grp_1","item_id":"itm_9"}.

[ScalePad] Delete a Quoter item group by id. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the group is not found. ScalePad documents no soft delete, restore, undo, or cascade behavior, and does not say what happens to the assignments that still reference the group — treat this as irreversible and check membership with scalepad_quoter_list_item_group_assignments first.

ParamTypeRequiredDefaultDescription
idstringyesThe Quoter item group id to delete (from scalepad_quoter_list_item_groups). Confirm with the user first.

[ScalePad] Remove an item from an item group by deleting the join record. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the assignment is not found. This deletes only the LINK — neither the catalog item nor the group is removed. ScalePad documents no soft delete or restore for it, so recreate it with scalepad_quoter_create_item_group_assignment if you need it back.

ParamTypeRequiredDefaultDescription
idstringyesThe assignment record's own id (from scalepad_quoter_list_item_group_assignments) — NOT an item id or item group id.

[ScalePad] Get one Quoter item group by id (from scalepad_quoter_list_item_groups). Returns id, name, record_created_at, and record_updated_at — nothing else; the group's membership is read via scalepad_quoter_list_item_group_assignments with filter[item_group_id]. The only documented failure besides success is 404.

ParamTypeRequiredDefaultDescription
fieldsstringnonullComma-separated sparse-fieldset selector. This parameter NARROWS the response — omit it to return the full record. Documented fields: id, name, record_created_at, record_updated_at.
idstringyesThe Quoter item group id to read (the id field from scalepad_quoter_list_item_groups).

[ScalePad] Get one item-group-item assignment by its own id (from scalepad_quoter_list_item_group_assignments — this is the assignment's id, not the item id or the group id). Returns id, item_group_id, item_id, record_created_at, and record_updated_at. The only documented failure besides success is 404.

ParamTypeRequiredDefaultDescription
fieldsstringnonullComma-separated sparse-fieldset selector. This parameter NARROWS the response — omit it to return the full record. Documented fields: id, item_group_id, item_id, record_created_at, record_updated_at.
idstringyesThe assignment record's own id (the id field from scalepad_quoter_list_item_group_assignments) — NOT an item id or item group id.

[ScalePad] List item-group-item assignments — the join records that place catalog items into item groups. Cursor-paginated: {data[], total_count, next_cursor}. Each assignment carries id, item_group_id, item_id, record_created_at, and record_updated_at. Filter by item_group_id to read a group's membership, or by item_id to find every group an item belongs to.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page, and keep paging until next_cursor is null. Cursor scans are not atomic, so deduplicate by id.
fieldsstringnonullComma-separated sparse-fieldset selector. This parameter NARROWS the response — omit it to return the full record. Documented fields: id, item_group_id, item_id, record_created_at, record_updated_at.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together, e.g. {"item_group_id":"eq:grp_1"}. Documented filters here: item_group_id (eq), item_id (eq), record_created_at (gt, lt), record_updated_at (gt, lt).
pageSizeintegernonullMaximum records per page, 1-200. The Quoter catalog list schemas declare NO default page size, so set this explicitly for predictable paging.
sortstringnonullComma-separated sort expression. This schema enumerates BOTH signs, so always sign each field: +field ascends, -field descends. Sortable: id, item_group_id, item_id, record_created_at, record_updated_at.

[ScalePad] List Quoter item groups. Cursor-paginated: {data[], total_count, next_cursor}. An item group is deliberately thin — each record carries only id, name, record_created_at, and record_updated_at; the items inside it are exposed through the separate assignment resource (scalepad_quoter_list_item_group_assignments filtered by item_group_id), not inlined here.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page, and keep paging until next_cursor is null. Cursor scans are not atomic, so deduplicate by id.
fieldsstringnonullComma-separated sparse-fieldset selector. This parameter NARROWS the response — omit it to return the full record. Documented fields: id, name, record_created_at, record_updated_at.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together, e.g. {"name":"cont:bundle"}. Documented filters here: name (eq, cont), record_created_at (gt, lt), record_updated_at (gt, lt).
pageSizeintegernonullMaximum records per page, 1-200. The Quoter catalog list schemas declare NO default page size, so set this explicitly for predictable paging.
sortstringnonullComma-separated sort expression. This schema enumerates BOTH signs, so always sign each field: +field ascends, -field descends. Sortable: id, name, record_created_at, record_updated_at.

[ScalePad] Partially update a Quoter item group (HTTP PATCH). The only mutable property is name, and it is optional — the vendor does not say whether an empty body is accepted, nor document null-as-clear semantics for this older catalog shape. Returns 200 with the updated record; 404 if the group is not found. Membership is not editable here — use the assignment tools.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body; the only accepted property is name (optional). Example: {"name":"Premium Workstation Bundle"}.
idstringyesThe Quoter item group id to update (from scalepad_quoter_list_item_groups).

Quoter Item Options

ToolPlanAccessSummary
scalepad_quoter_create_item_optionProWriteCreate a configurable option on a catalog item.
scalepad_quoter_create_item_option_valueProWriteCreate a selectable value under a Quoter item option.
scalepad_quoter_delete_item_optionProDestructiveDelete a Quoter item option by id.
scalepad_quoter_delete_item_option_valueProDestructiveDelete a Quoter item-option value by id.
scalepad_quoter_get_item_optionFreeRead-onlyGet one Quoter item option by id (from scalepad_quoter_list_item_options).
scalepad_quoter_get_item_option_valueFreeRead-onlyGet one Quoter item-option value by id (from scalepad_quoter_list_item_option_values).
scalepad_quoter_list_item_option_valuesFreeRead-onlyList Quoter item-option VALUES — the individual selectable choices under an option, each with its own pricing.
scalepad_quoter_list_item_optionsFreeRead-onlyList Quoter item options — the configurable option definitions attached to catalog items.
scalepad_quoter_update_item_optionProWritePartially update a Quoter item option (HTTP PATCH).
scalepad_quoter_update_item_option_valueProWritePartially update a Quoter item-option value (HTTP PATCH).

[ScalePad] Create a configurable option on a catalog item. Returns 201 with the created option record. item_id is required and CANNOT be changed later — the PATCH schema does not accept it — so create the option against the right item the first time. Add its selectable values afterwards with scalepad_quoter_create_item_option_value. Only 404 is declared besides success.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. Required: item_id (from scalepad_quoter_list_items — not accepted on update) and name (string). Optional: description and extended_description (strings), allow_multiple_values (boolean — whether the buyer may pick more than one value), required (boolean — whether a value must be chosen), sort_order (integer display position). Example: {"item_id":"itm_9","name":"Warranty term","required":true,"sort_order":1}.

[ScalePad] Create a selectable value under a Quoter item option. Returns 201 with the created record. item_option_id is required and CANNOT be changed later (the PATCH schema does not accept it), so create the value under the right option the first time. Note that unlike the catalog item schema, the official value schema publishes NO enum for cost_type or pricing_scheme — do not assume the item-level enums apply; send what the Quoter UI shows and pass server validation through verbatim. Only 404 is declared besides success.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. Required: item_option_id (from scalepad_quoter_list_item_options — not accepted on update) and name (string). Optional: code (string), cost_decimal and price_decimal (decimal STRINGS), cost_type (string — the official schema publishes no enum for this field), pricing_scheme (string — likewise no published enum), sort_order (integer display position). Example: {"item_option_id":"opt_1","name":"3 years","price_decimal":"199.00","sort_order":2}.

[ScalePad] Delete a Quoter item option by id. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the option is not found. ScalePad documents no soft delete, restore, undo, or cascade behavior, and does not state what happens to the option's values — treat this as irreversible and enumerate the values with scalepad_quoter_list_item_option_values first if you need to preserve them.

ParamTypeRequiredDefaultDescription
idstringyesThe Quoter item option id to delete (from scalepad_quoter_list_item_options). Confirm with the user first.

[ScalePad] Delete a Quoter item-option value by id. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the value is not found. ScalePad documents no soft delete, restore, or undo — treat this as irreversible. Deleting the last value of a required option leaves that option with nothing to select, and the vendor does not document how quoting behaves in that state.

ParamTypeRequiredDefaultDescription
idstringyesThe Quoter item-option value id to delete (from scalepad_quoter_list_item_option_values). Confirm with the user first.

[ScalePad] Get one Quoter item option by id (from scalepad_quoter_list_item_options). Returns id, item_id, name, description, extended_description, allow_multiple_values, required, sort_order, and timestamps. The option's selectable values are a separate resource — list them with scalepad_quoter_list_item_option_values filtered by item_option_id. The only documented failure besides success is 404.

ParamTypeRequiredDefaultDescription
fieldsstringnonullComma-separated sparse-fieldset selector. This parameter NARROWS the response — omit it to return the full record. Documented fields: allow_multiple_values, description, extended_description, id, item_id, name, record_created_at, record_updated_at, required, sort_order.
idstringyesThe Quoter item option id to read (the id field from scalepad_quoter_list_item_options).

[ScalePad] Get one Quoter item-option value by id (from scalepad_quoter_list_item_option_values). Returns id, item_id, item_option_id, name, code, cost_decimal, cost_type, price_decimal, pricing_scheme, sort_order, and timestamps; money values are decimal STRINGS. The only documented failure besides success is 404.

ParamTypeRequiredDefaultDescription
fieldsstringnonullComma-separated sparse-fieldset selector. This parameter NARROWS the response — omit it to return the full record. Documented fields: code, cost_decimal, cost_type, id, item_id, item_option_id, name, price_decimal, pricing_scheme, record_created_at, record_updated_at, sort_order.
idstringyesThe Quoter item-option value id to read (the id field from scalepad_quoter_list_item_option_values).

[ScalePad] List Quoter item-option VALUES — the individual selectable choices under an option, each with its own pricing. Cursor-paginated: {data[], total_count, next_cursor}. Each value carries id, item_id, item_option_id, name, code, cost_decimal, cost_type, price_decimal, pricing_scheme, sort_order (integer), record_created_at, and record_updated_at. Money values are decimal STRINGS. Filter by item_option_id for one option's choices, or by item_id for every value across an item.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page, and keep paging until next_cursor is null. Cursor scans are not atomic, so deduplicate by id.
fieldsstringnonullComma-separated sparse-fieldset selector. This parameter NARROWS the response — omit it to return the full record. Documented fields: code, cost_decimal, cost_type, id, item_id, item_option_id, name, price_decimal, pricing_scheme, record_created_at, record_updated_at, sort_order.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together, e.g. {"item_option_id":"eq:opt_1"}. Documented filters here: item_option_id (eq), item_id (eq), code (eq, cont), name (eq, cont), record_created_at (gt, lt), record_updated_at (gt, lt).
pageSizeintegernonullMaximum records per page, 1-200. The Quoter catalog list schemas declare NO default page size, so set this explicitly for predictable paging.
sortstringnonullComma-separated sort expression. This schema enumerates BOTH signs, so always sign each field: +field ascends, -field descends. Sortable: id, name, sort_order, record_created_at, record_updated_at. Use '+sort_order' to read values in their configured display order.

[ScalePad] List Quoter item options — the configurable option definitions attached to catalog items. Cursor-paginated: {data[], total_count, next_cursor}. Each option carries id, item_id, name, description, extended_description, allow_multiple_values, required, sort_order (integer), record_created_at, and record_updated_at. Filter by item_id to read one item's options; the option id is what you pass to filter[item_option_id] on scalepad_quoter_list_item_option_values.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page, and keep paging until next_cursor is null. Cursor scans are not atomic, so deduplicate by id.
fieldsstringnonullComma-separated sparse-fieldset selector. This parameter NARROWS the response — omit it to return the full record. Documented fields: allow_multiple_values, description, extended_description, id, item_id, name, record_created_at, record_updated_at, required, sort_order.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together, e.g. {"item_id":"eq:itm_9"}. Documented filters here: item_id (eq), name (eq, cont), record_created_at (gt, lt), record_updated_at (gt, lt).
pageSizeintegernonullMaximum records per page, 1-200. The Quoter catalog list schemas declare NO default page size, so set this explicitly for predictable paging.
sortstringnonullComma-separated sort expression. This schema enumerates BOTH signs, so always sign each field: +field ascends, -field descends. Sortable: id, name, sort_order, record_created_at, record_updated_at. Use '+sort_order' to read options in their configured display order.

[ScalePad] Partially update a Quoter item option (HTTP PATCH). Every property is optional and item_id is NOT accepted — an option cannot be reassigned to a different catalog item. Returns 200 with the updated record; 404 if the option is not found. This older catalog shape documents neither null-as-clear semantics nor whether an empty body is accepted.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body; all properties optional, and item_id is NOT accepted. Accepts name, description, extended_description, allow_multiple_values (boolean), required (boolean), sort_order (integer). Example: {"required":false,"sort_order":2}.
idstringyesThe Quoter item option id to update (from scalepad_quoter_list_item_options).

[ScalePad] Partially update a Quoter item-option value (HTTP PATCH). Every property is optional and item_option_id is NOT accepted — a value cannot be moved to a different option. Returns 200 with the updated record; 404 if the value is not found. Null-as-clear semantics and the acceptability of an empty body are undocumented for this older catalog shape.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body; all properties optional, and item_option_id is NOT accepted. Accepts name, code, cost_decimal and price_decimal (decimal STRINGS), cost_type (no published enum), pricing_scheme (no published enum), sort_order (integer). Example: {"price_decimal":"249.00"}.
idstringyesThe Quoter item-option value id to update (from scalepad_quoter_list_item_option_values).

Quoter Item Tiers

ToolPlanAccessSummary
scalepad_quoter_create_item_tierProWriteCreate a volume pricing tier on a catalog item.
scalepad_quoter_delete_item_tierProDestructiveDelete a Quoter item pricing tier by id.
scalepad_quoter_get_item_tierFreeRead-onlyGet one Quoter item pricing tier by id (from scalepad_quoter_list_item_tiers).
scalepad_quoter_list_item_tiersFreeRead-onlyList Quoter item pricing tiers (volume price breaks).
scalepad_quoter_update_item_tierProWritePartially update a Quoter item pricing tier (HTTP PATCH).

[ScalePad] Create a volume pricing tier on a catalog item. Returns 201 with the created tier. Only item_id is required — every pricing property is optional — and item_id CANNOT be changed later (the PATCH schema does not accept it). Tiers are meaningful only on an item whose pricing_scheme is tiered_volume or tiered_stepped (see scalepad_quoter_update_item). The official schema publishes no enum for cost_type and no overlap or ordering validation for lower_boundary, so pass server validation through verbatim. Only 404 is declared besides success.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. Required: item_id (from scalepad_quoter_list_items — not accepted on update). Optional: lower_boundary (INTEGER quantity at which the tier begins), price_decimal and cost_decimal (decimal STRINGS), cost_type (string — the official schema publishes no enum for it). Example: {"item_id":"itm_9","lower_boundary":10,"price_decimal":"449.00"}.

[ScalePad] Delete a Quoter item pricing tier by id. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the tier is not found. ScalePad documents no soft delete, restore, or undo — treat this as irreversible. Removing a break from a tiered item changes how quantities in that range are priced, and the vendor does not document the resulting gap behavior.

ParamTypeRequiredDefaultDescription
idstringyesThe Quoter item tier id to delete (from scalepad_quoter_list_item_tiers). Confirm with the user first.

[ScalePad] Get one Quoter item pricing tier by id (from scalepad_quoter_list_item_tiers). Returns id, item_id, lower_boundary, price_decimal, cost_decimal, cost_type, and timestamps; money values are decimal STRINGS. The only documented failure besides success is 404.

ParamTypeRequiredDefaultDescription
fieldsstringnonullComma-separated sparse-fieldset selector. This parameter NARROWS the response — omit it to return the full record. Documented fields: cost_decimal, cost_type, id, item_id, lower_boundary, price_decimal, record_created_at, record_updated_at.
idstringyesThe Quoter item tier id to read (the id field from scalepad_quoter_list_item_tiers).

[ScalePad] List Quoter item pricing tiers (volume price breaks). Cursor-paginated: {data[], total_count, next_cursor}. Each tier carries id, item_id, lower_boundary (the integer quantity at which the tier starts applying), price_decimal, cost_decimal, cost_type, record_created_at, and record_updated_at; money values are decimal STRINGS. Filter by item_id and sort by +lower_boundary to read one item's price breaks in order.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page, and keep paging until next_cursor is null. Cursor scans are not atomic, so deduplicate by id.
fieldsstringnonullComma-separated sparse-fieldset selector. This parameter NARROWS the response — omit it to return the full record. Documented fields: cost_decimal, cost_type, id, item_id, lower_boundary, price_decimal, record_created_at, record_updated_at.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together, e.g. {"item_id":"eq:itm_9"}. Documented filters here are only: item_id (eq), record_created_at (gt, lt), record_updated_at (gt, lt). There is no documented filter on lower_boundary or price.
pageSizeintegernonullMaximum records per page, 1-200. The Quoter catalog list schemas declare NO default page size, so set this explicitly for predictable paging.
sortstringnonullComma-separated sort expression. This schema enumerates BOTH signs, so always sign each field: +field ascends, -field descends. Sortable: id, lower_boundary, record_created_at, record_updated_at — note that name is NOT sortable here (a tier has no name). Use '+lower_boundary' to read the breaks in ascending quantity order.

[ScalePad] Partially update a Quoter item pricing tier (HTTP PATCH). Every property is optional and item_id is NOT accepted — a tier cannot be reassigned to a different item. Returns 200 with the updated record; 404 if the tier is not found. Null-as-clear semantics and whether an empty body is accepted are undocumented for this older catalog shape.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body; all properties optional, and item_id is NOT accepted. Accepts lower_boundary (integer), price_decimal and cost_decimal (decimal STRINGS), cost_type (no published enum). Example: {"lower_boundary":25,"price_decimal":"429.00"}.
idstringyesThe Quoter item tier id to update (from scalepad_quoter_list_item_tiers).

Quoter Items

ToolPlanAccessSummary
scalepad_quoter_create_itemProWriteCreate a Quoter catalog item.
scalepad_quoter_delete_itemProDestructiveDelete a Quoter catalog item by id.
scalepad_quoter_get_itemFreeRead-onlyGet one Quoter catalog item by id (from scalepad_quoter_list_items).
scalepad_quoter_list_itemsFreeRead-onlyList Quoter catalog items.
scalepad_quoter_update_itemProWritePartially update a Quoter catalog item (HTTP PATCH).

[ScalePad] Create a Quoter catalog item. Returns 201 with the created item record. Two documented contract traps: (1) the machine-readable required array is ["category_id","name"] while the prose calls BOTH category (a name) and category_id required and says they cannot be sent together — follow the validator, send category_id, and do not send category; (2) recurring_interval here is spelled monthly|quarterly|semi_annually|annually, which differs from the quote-line spelling (semi_annual, annual). The vendor publishes no decimal regex, precision, or numeric range for the money fields, so pass server validation through verbatim. Only 404 is declared besides success.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body (ItemCreateRequest). Required: name (string) and category_id (from scalepad_quoter_list_categories — send this rather than the category name field). Optional associations: manufacturer_id (or the mutually exclusive manufacturer name), supplier_id (or the mutually exclusive supplier name — these reference Quoter suppliers from scalepad_quoter_list_suppliers, not SupplierSync feeds). Optional identifiers/text: code (the MPN), sku, description (HTML), internal_note, quantity_help_tip. Optional flags: allow_decimal_quantities, restrict_discounting, show_option_prices, recurring, taxable (defaults TRUE when omitted). Optional pricing: pricing_scheme (per_unit|flat|tiered_volume|tiered_stepped|percentage; defaults per_unit — price_decimal applies to flat and per_unit), price_decimal / cost_decimal / percentage_price_decimal / weight_decimal (decimal STRINGS), cost_type (amount|percentage; required when a cost is supplied, defaults amount, and allowed only with flat, per_unit, or percentage pricing), percentage_price_category_ids (string array — required when percentage pricing is selected and allowed only then), recurring_interval (monthly|quarterly|semi_annually|annually — requires recurring: true and is required when recurring is true). Example: {"name":"48-port Switch","category_id":"cat_1","price_decimal":"499.00","pricing_scheme":"per_unit"}.

[ScalePad] Delete a Quoter catalog item by id. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the item is not found. ScalePad documents NO soft delete, restore, undo, cascade behavior, or dependency-conflict status — treat this as irreversible, and be aware that the item's options, option values, tiers, and group assignments all reference it while the vendor does not state what happens to them.

ParamTypeRequiredDefaultDescription
idstringyesThe Quoter item id to delete (from scalepad_quoter_list_items). Confirm with the user first.

[ScalePad] Get one Quoter catalog item by id (from scalepad_quoter_list_items). Returns the full item record — name, code (MPN), sku, description (HTML), internal_note, quantity_help_tip, category/category_id, manufacturer/manufacturer_id, supplier/supplier_id, cost_decimal, price_decimal, percentage_price_decimal, weight_decimal, cost_type, pricing_scheme, percentage_price_category_ids, allow_decimal_quantities, restrict_discounting, show_option_prices, recurring, recurring_interval, taxable, and timestamps. Money values are decimal STRINGS. The only documented failure besides success is 404.

ParamTypeRequiredDefaultDescription
fieldsstringnonullComma-separated sparse-fieldset selector. This parameter NARROWS the response — omit it to return the full record. Documented fields: allow_decimal_quantities, category, category_id, code, cost_decimal, cost_type, description, id, internal_note, manufacturer, manufacturer_id, name, percentage_price_category_ids, percentage_price_decimal, price_decimal, pricing_scheme, quantity_help_tip, record_created_at, record_updated_at, recurring, recurring_interval, restrict_discounting, show_option_prices, sku, supplier, supplier_id, taxable, weight_decimal.
idstringyesThe Quoter item id to read (the id field from scalepad_quoter_list_items).

[ScalePad] List Quoter catalog items. Cursor-paginated: {data[], total_count, next_cursor}. Each item carries id, name, code (the MPN shown in the Quoter UI — the vendor says it should be unique but publishes no uniqueness error), sku, description (HTML), internal_note, quantity_help_tip, category and category_id, manufacturer and manufacturer_id, supplier and supplier_id (these reference Quoter supplier records from scalepad_quoter_list_suppliers, NOT SupplierSync feed objects), the decimal-string money fields cost_decimal / price_decimal / percentage_price_decimal / weight_decimal, cost_type, pricing_scheme, percentage_price_category_ids, the flags allow_decimal_quantities / restrict_discounting / show_option_prices / recurring / taxable, recurring_interval, and record_created_at / record_updated_at. The schema publishes no required array, so tolerate missing properties. An item id is what you pass to filter[item_id] on scalepad_quoter_list_item_options, scalepad_quoter_list_item_tiers, and scalepad_quoter_list_item_group_assignments.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page, and keep paging until next_cursor is null rather than until a page looks short. Cursor scans are not atomic, so deduplicate by id.
fieldsstringnonullComma-separated sparse-fieldset selector. This parameter NARROWS the response — omit it to return the full record. Documented fields: allow_decimal_quantities, category, category_id, code, cost_decimal, cost_type, description, id, internal_note, manufacturer, manufacturer_id, name, percentage_price_category_ids, percentage_price_decimal, price_decimal, pricing_scheme, quantity_help_tip, record_created_at, record_updated_at, recurring, recurring_interval, restrict_discounting, show_option_prices, sku, supplier, supplier_id, taxable, weight_decimal.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together, e.g. {"category_id":"eq:cat_1","name":"cont:switch"}. Documented filters here: category_id (eq), manufacturer_id (eq), supplier_id (eq), code (eq — exact MPN match), sku (eq), name (eq, cont), record_created_at (gt, lt), record_updated_at (gt, lt — useful for incremental polling). There is no free-text search across all fields; use name with cont.
pageSizeintegernonullMaximum records per page, 1-200. The Quoter catalog list schemas declare NO default page size (50 appears only as an example), so set this explicitly for predictable paging.
sortstringnonullComma-separated sort expression. This schema enumerates BOTH signs, so always sign each field: +field ascends, -field descends. Sortable: id, name, record_created_at, record_updated_at. Example: '-record_updated_at'.

[ScalePad] Partially update a Quoter catalog item (HTTP PATCH). Every body property is optional — including name — so send only what changes. Returns 200 with the updated record. This older catalog shape does NOT document null-as-clear semantics or whether an empty body is accepted, so there is no documented way to clear an association; report whatever the server returns. The same dependent-field rules as create still apply (cost_type only with flat/per_unit/percentage pricing, percentage_price_category_ids only with percentage pricing, recurring_interval only with recurring: true), and recurring_interval keeps the item spelling semi_annually/annually.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body (ItemUpdateRequest); all properties optional. Accepts the same fields as create: name, category_id (or the mutually exclusive category name), manufacturer_id / manufacturer, supplier_id / supplier, code, sku, description (HTML), internal_note, quantity_help_tip, allow_decimal_quantities, restrict_discounting, show_option_prices, recurring, taxable, pricing_scheme (per_unit|flat|tiered_volume|tiered_stepped|percentage), price_decimal / cost_decimal / percentage_price_decimal / weight_decimal (decimal strings), cost_type (amount|percentage), percentage_price_category_ids, recurring_interval (monthly|quarterly|semi_annually|annually). Example: {"price_decimal":"549.00"}.
idstringyesThe Quoter item id to update (from scalepad_quoter_list_items).

Quoter Manufacturers

ToolPlanAccessSummary
scalepad_quoter_create_manufacturerProWriteCreate a Quoter manufacturer.
scalepad_quoter_delete_manufacturerProDestructiveDelete a Quoter manufacturer by id.
scalepad_quoter_get_manufacturerFreeRead-onlyGet one Quoter manufacturer by id (from scalepad_quoter_list_manufacturers).
scalepad_quoter_list_manufacturersFreeRead-onlyList Quoter manufacturers.
scalepad_quoter_update_manufacturerProWritePartially update a Quoter manufacturer (HTTP PATCH).

[ScalePad] Create a Quoter manufacturer. Returns 201 with the created record (id, name, timestamps). The body takes exactly one property — name. ScalePad publishes no name length or uniqueness rule and no conflict status, so check scalepad_quoter_list_manufacturers with filter[name] first if you need to avoid duplicates. Only 404 is declared besides success.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. Required: name (string) — the only property this create schema accepts. Example: {"name":"Dell"}.

[ScalePad] Delete a Quoter manufacturer by id. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the manufacturer is not found. ScalePad documents NO soft delete, restore, undo, cascade behavior, or dependency-conflict status, and does not state what happens to catalog items and quote lines that still reference it — treat this as irreversible and check usage with scalepad_quoter_list_items using filter[manufacturer_id] first.

ParamTypeRequiredDefaultDescription
idstringyesThe Quoter manufacturer id to delete (from scalepad_quoter_list_manufacturers). Confirm with the user first.

[ScalePad] Get one Quoter manufacturer by id (from scalepad_quoter_list_manufacturers). Returns id, name, record_created_at, and record_updated_at — the record has no other documented properties. The only documented failure besides success is 404.

ParamTypeRequiredDefaultDescription
fieldsstringnonullComma-separated sparse-fieldset selector. This parameter NARROWS the response — omit it to return the full record. Documented fields: id, name, record_created_at, record_updated_at.
idstringyesThe Quoter manufacturer id to read (the id field from scalepad_quoter_list_manufacturers).

[ScalePad] List Quoter manufacturers. Cursor-paginated: {data[], total_count, next_cursor}. Each manufacturer carries only id, name, record_created_at, and record_updated_at. The id is what you pass as manufacturer_id when creating a catalog item (scalepad_quoter_create_item), as manufacturer when authoring quote line items (scalepad_quoter_create_quote_section_line_items), and to filter[manufacturer_id] on scalepad_quoter_list_items.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page, and keep paging until next_cursor is null. Cursor scans are not atomic, so deduplicate by id.
fieldsstringnonullComma-separated sparse-fieldset selector. This parameter NARROWS the response — omit it to return the full record. Documented fields: id, name, record_created_at, record_updated_at.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together, e.g. {"name":"cont:dell"}. Documented filters here are only: name (eq, cont), record_created_at (gt, lt), record_updated_at (gt, lt).
pageSizeintegernonullMaximum records per page, 1-200. The Quoter catalog list schemas declare NO default page size, so set this explicitly for predictable paging.
sortstringnonullComma-separated sort expression. This schema enumerates BOTH signs, so always sign each field: +field ascends, -field descends. Sortable: id, name, record_created_at, record_updated_at. Example: '+name'.

[ScalePad] Partially update a Quoter manufacturer (HTTP PATCH). The only mutable property is name, and it is optional; the vendor does not say whether an empty body is accepted and documents no null-as-clear semantics for this older catalog shape. Returns 200 with the updated record; 404 if the manufacturer is not found. Renaming does not re-point any item — items reference the manufacturer by id.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body; the only accepted property is name (optional). Example: {"name":"Dell Technologies"}.
idstringyesThe Quoter manufacturer id to update (from scalepad_quoter_list_manufacturers).

Quoter SupplierSync Datafeeds

ToolPlanAccessSummary
scalepad_quoter_list_datafeed_supplier_itemsFreeRead-onlyLook up SupplierSync item data for one or more manufacturer part numbers (MPNs) — the live distributor price and stock projection behind Quoter.

[ScalePad] Look up SupplierSync item data for one or more manufacturer part numbers (MPNs) — the live distributor price and stock projection behind Quoter. Cursor-paginated: {data[], total_count, next_cursor}. Each item carries required identity/source fields id, semantic_item_id, supplier_id and supplier_name (the feed, resolvable via scalepad_quoter_list_datafeed_suppliers); required product fields category, mpn, name, sku, weight_decimal, price_amount_decimal; required tax flags taxable and supplier_default_taxable; required record_created_at / record_updated_at; and a required warehouses[] array where each entry has id, name, and quantity — that is where per-location stock lives. The MPN filter is MANDATORY: this endpoint returns 400 without it. Results cover SupplierSync sources ONLY and exclude native distributor integrations. No manufacturer filter, supplier filter, sorting, or sparse fieldset is documented for this endpoint, which is why this tool exposes none. Declares 400 and 422 in addition to 200.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque (base64) next_cursor from the previous page. Omit for the first page, and keep paging until next_cursor is null. Cursor scans are not atomic, so deduplicate by id.
filtersJsonstringyesREQUIRED JSON filter object. This endpoint documents exactly one filter and it must be present or the API returns 400: mpn, whose value must match ^cont:.+$ — the operator is always cont, and several MPNs are passed as one comma-separated value. Single: {"mpn":"cont:AW30004"}. Several: {"mpn":"cont:AW30004,AW30005"}.
pageSizeintegernonullMaximum records per page, 1-200, DEFAULT 100 — the two SupplierSync data-feed lists are the only Quoter lists that declare a default.

Quoter Suppliers

ToolPlanAccessSummary
scalepad_quoter_create_supplierProWriteCreate a Quoter catalog supplier.
scalepad_quoter_delete_supplierProDestructiveDelete a Quoter catalog supplier by id.
scalepad_quoter_get_supplierFreeRead-onlyGet one Quoter catalog supplier by id (from scalepad_quoter_list_suppliers).
scalepad_quoter_list_datafeed_suppliersFreeRead-onlyList the SupplierSync data feeds configured for the account — a READ-ONLY projection, richer than the editable Quoter supplier records at scalepad_quoter_list_suppliers, and not creatable or editable…
scalepad_quoter_list_suppliersFreeRead-onlyList Quoter catalog suppliers — the editable vendor records, NOT the SupplierSync feeds (for those use scalepad_quoter_list_datafeed_suppliers).
scalepad_quoter_update_supplierProWritePartially update a Quoter catalog supplier (HTTP PATCH).

[ScalePad] Create a Quoter catalog supplier. Returns 201 with the created record (id, name, timestamps). The body takes exactly one property — name. This does NOT configure a SupplierSync data feed: feed suppliers (with url, default_taxable, and field_mapping) are set up in SupplierSync and are read-only through this API. ScalePad publishes no name length or uniqueness rule and no conflict status. Only 404 is declared besides success.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body. Required: name (string) — the only property this create schema accepts. Example: {"name":"Ingram Micro"}.

[ScalePad] Delete a Quoter catalog supplier by id. Succeeds with HTTP 204 and no body (this tool returns ); 404 if the supplier is not found. ScalePad documents NO soft delete, restore, undo, cascade behavior, or dependency-conflict status, and does not state what happens to catalog items and quote lines that still reference it — treat this as irreversible and check usage with scalepad_quoter_list_items using filter[supplier_id] first. This deletes only the Quoter catalog record; it does not remove a SupplierSync feed.

ParamTypeRequiredDefaultDescription
idstringyesThe Quoter supplier id to delete (from scalepad_quoter_list_suppliers). Confirm with the user first.

[ScalePad] Get one Quoter catalog supplier by id (from scalepad_quoter_list_suppliers). Returns id, name, record_created_at, and record_updated_at — the record has no other documented properties, and in particular no URL or tax flag (those belong to the richer SupplierSync feed projection returned by scalepad_quoter_list_datafeed_suppliers). The only documented failure besides success is 404.

ParamTypeRequiredDefaultDescription
fieldsstringnonullComma-separated sparse-fieldset selector. This parameter NARROWS the response — omit it to return the full record. Documented fields: id, name, record_created_at, record_updated_at.
idstringyesThe Quoter supplier id to read (the id field from scalepad_quoter_list_suppliers).

[ScalePad] List the SupplierSync data feeds configured for the account — a READ-ONLY projection, richer than the editable Quoter supplier records at scalepad_quoter_list_suppliers, and not creatable or editable through this API. Cursor-paginated: {data[], total_count, next_cursor}. Each feed supplier carries required id, name, url, default_taxable, record_created_at, record_updated_at, and field_mapping — the mapping names for category, manufacturer, MPN, product name, price, supplier SKU, weight, and warehouses (each warehouse mapping requiring name and quantity), with the taxable mapping optional. The supplier_id on a SupplierSync item from scalepad_quoter_list_datafeed_supplier_items refers to one of these feeds. Declares 400 and 422 in addition to 200. NOTE: this list documents NO filters, which is why this tool takes none.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque (base64) next_cursor from the previous page. Omit for the first page, and keep paging until next_cursor is null. Cursor scans are not atomic, so deduplicate by id.
pageSizeintegernonullMaximum records per page, 1-200, DEFAULT 100 — the two SupplierSync data-feed lists are the only Quoter lists that declare a default.
sortstringnonullComma-separated sort expression. This is the ONE Quoter list whose regex REQUIRES a direction prefix on every field — a bare field name is rejected. Allowed values: +name, -name, +record_created_at, -record_created_at, +record_updated_at, -record_updated_at. Example: '+name' or '-record_updated_at,+name'.

[ScalePad] List Quoter catalog suppliers — the editable vendor records, NOT the SupplierSync feeds (for those use scalepad_quoter_list_datafeed_suppliers). Cursor-paginated: {data[], total_count, next_cursor}. Each supplier carries only id, name, record_created_at, and record_updated_at. The id is what you pass as supplier_id when creating a catalog item (scalepad_quoter_create_item), as supplier when authoring quote line items, and to filter[supplier_id] on scalepad_quoter_list_items.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page, and keep paging until next_cursor is null. Cursor scans are not atomic, so deduplicate by id.
fieldsstringnonullComma-separated sparse-fieldset selector. This parameter NARROWS the response — omit it to return the full record. Documented fields: id, name, record_created_at, record_updated_at.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together, e.g. {"name":"cont:ingram"}. Documented filters here are only: name (eq, cont), record_created_at (gt, lt), record_updated_at (gt, lt).
pageSizeintegernonullMaximum records per page, 1-200. The Quoter catalog list schemas declare NO default page size, so set this explicitly for predictable paging.
sortstringnonullComma-separated sort expression. This schema enumerates BOTH signs, so always sign each field: +field ascends, -field descends. Sortable: id, name, record_created_at, record_updated_at. Example: '+name'.

[ScalePad] Partially update a Quoter catalog supplier (HTTP PATCH). The only mutable property is name, and it is optional; the vendor does not say whether an empty body is accepted and documents no null-as-clear semantics for this older catalog shape. Returns 200 with the updated record; 404 if the supplier is not found. Renaming does not re-point any item — items reference the supplier by id.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body; the only accepted property is name (optional). Example: {"name":"Ingram Micro US"}.
idstringyesThe Quoter supplier id to update (from scalepad_quoter_list_suppliers).

Quotes & Authoring

ToolPlanAccessSummary
scalepad_quoter_create_line_itemProWriteCreate a single STANDALONE line item in an existing quote (POST /v1/line-items) — the older flat contract, which is a DIFFERENT operation from scalepad_quoter_create_quote_section_line_items.
scalepad_quoter_create_quoteProWriteCreate a DRAFT quote from a template, identifying or creating its primary contact.
scalepad_quoter_create_quote_section_line_itemsProWriteAdd a BATCH of line items to one SECTION of a draft quote and recompute line, section, and quote totals — one call creates SEVERAL line items, which is why this tool is plural and its body is a…
scalepad_quoter_create_quote_sectionsProWriteAdd a BATCH of sections to a draft quote — one call creates SEVERAL sections, which is why this tool is plural and its body is a top-level JSON ARRAY, not a single object.
scalepad_quoter_get_quoteFreeRead-onlyFetch one full quote revision (the QuoteFetchResponse that every authoring write also returns).
scalepad_quoter_list_quote_templatesFreeRead-onlyList quote templates — the starting points for a new draft.
scalepad_quoter_list_quotesFreeRead-onlyList quote revisions.
scalepad_quoter_publish_quoteProDestructivePublish a draft quote, making it LIVE and sendable.
scalepad_quoter_update_quote_section_line_itemProWritePatch ONE existing line item in a draft quote's section and recompute the affected totals.

[ScalePad] Create a single STANDALONE line item in an existing quote (POST /v1/line-items) — the older flat contract, which is a DIFFERENT operation from scalepad_quoter_create_quote_section_line_items. Distinguishing facts: the target quote is named by quote_id IN THE BODY (there is no path parameter and no section id at all), category is a plain STRING rather than a object, prices and quantity are NUMBERS rather than decimal strings, and the response is the small flat LineItem record (id, the request-like fields, record_created_at, record_updated_at) rather than the full QuoteFetchResponse. Its definition documents neither draft-state protection nor total recomputation, and the only declared failure is 404 for a missing referenced record. Prefer the section-scoped batch tool for real quote authoring; use this one only when you specifically need the flat contract. Do not merge the two input models. One vendor inconsistency to expect: the request types unit_cost as a number while the response types it as a string.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body (the flat LineItemCreateRequest). Required: quote_id (the quote to add the line to — carried in the BODY, not the URL), category (a plain STRING, not an object or id), name (string), quantity (a NUMBER, not a decimal string). Optional: description, manufacturer, part_number, supplier, supplier_sku (strings or null), recurring and taxable (booleans or null), unit_cost and unit_price (NUMBERS or null). Example: {"quote_id":"quot_abc","category":"Hardware","name":"Switch","quantity":2,"unit_price":499}.

[ScalePad] Create a DRAFT quote from a template, identifying or creating its primary contact. Returns 201 with the full QuoteFetchResponse; the new draft has no sections yet, and content blocks, attachments, custom-field values, totals, and contact-sourced addresses may be empty or null. Add sections next with scalepad_quoter_create_quote_sections. Failure semantics: 400 malformed JSON, 404 the referenced template or client was not found, 422 validation (a past expiration, a currency not enabled on the account, or an owner-email lookup failure). Note a documented vendor inconsistency on unknown owners — the page shows ERR_OWNER_EMAIL_NOT_FOUND with 422 in one place and ERR_OWNER_NOT_FOUND with 404 language in another; report whichever code and status the server actually returns rather than normalizing them.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body (CreateQuoteRequest). Required: contact {email (email format), client_id} — client_id is a ScalePad CORE client UUID from scalepad_core_list_clients, NOT a quot_/qtpl_-prefixed Quoter id, and the email+client pair reuses an existing account contact or creates one; and template_id (from scalepad_quoter_list_quote_templates, normally qtpl_...). Optional: name (max 75 chars), comments (plain-text internal sales notes), internal_notes (private, never shown to the contact), cover_page_title (max 100), cover_page_subtitle (max 125), cover_page_content (HTML body), currency_iso (three-letter ISO 4217 code that must be enabled on the account; omit for the account default), custom_number (max 250; null lets publish assign the system number), expired_at (RFC 3339 date-time, must not be in the past; null defers to the account expiration window at publish), owner (must be an active user in the same account; omit for the API-key user), tax_codes[] (each {code, rate_decimal} where rate_decimal is a decimal string — "13.00" means 13% — plus an optional nullable registration number). Example: {"template_id":"qtpl_abc","contact":{"email":"ada@acme.test","client_id":"2220324"},"name":"Q3 Refresh"}.

[ScalePad] Add a BATCH of line items to one SECTION of a draft quote and recompute line, section, and quote totals — one call creates SEVERAL line items, which is why this tool is plural and its body is a top-level JSON ARRAY. Returns 201 with the full QuoteFetchResponse including the recomputed totals. This is the section-scoped authoring contract: it uses decimal STRINGS, resolves category/manufacturer/supplier by nested objects, enforces draft state (409 on a published quote), and returns the whole quote. Do NOT confuse it with scalepad_quoter_create_line_item, which is the flat standalone catalog-style create that takes quote_id in the BODY, a plain category STRING, and numeric prices. Validation accumulates across the whole batch: a 422 returns EVERY failure with line_items[i].field locations (e.g. ERR_LINE_ITEM_CATEGORY_REQUIRED, ERR_LINE_ITEM_QUANTITY_INVALID, ERR_LINE_ITEM_DISCOUNT_EXCEEDS_SUBTOTAL), so treat errors[] as a collection. Also 400 on malformed JSON or a missing path value, and 404 if the quote or section is not found. No batch size limit is published.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesTop-level JSON ARRAY of line-item objects. Per item, required: category {id, name?} (resolution is by id ONLY — a name hint is accepted but ignored), name (max 100 chars), quantity_decimal (decimal string strictly greater than zero; fractional quantities are supported), unit_price_decimal (non-negative decimal string), and taxable (boolean — schema-required, though the vendor description contradictorily says it defaults to false when omitted, so always send it). Optional: code (max 50), sku (max 50), description (HTML), unit_cost_decimal (non-negative decimal string; omitting it creates a cost-less line whose margin equals price), manufacturer and supplier (id-only resolution), discount {input_decimal, input_type} where input_type is amount or percentage (an amount may not exceed quantity x unit price, a percentage is 0-100, and the decimal is non-negative), and recurring_interval (monthly|quarterly|semi_annual|annual, or null for one-time — a non-null value MUST match the parent quote's interval, and a one-time quote rejects any non-null interval). Example: [{"category":{"id":"cat_1"},"name":"Switch","quantity_decimal":"2","unit_price_decimal":"499.00","taxable":true}].
quoteIdstringyesThe draft quote id, normally shaped quot_... (from scalepad_quoter_create_quote or scalepad_quoter_list_quotes).
sectionIdstringyesThe section id to add the line items to, normally shaped qsec_... (from the sections[] array of scalepad_quoter_get_quote or of the scalepad_quoter_create_quote_sections response).

[ScalePad] Add a BATCH of sections to a draft quote — one call creates SEVERAL sections, which is why this tool is plural and its body is a top-level JSON ARRAY, not a single object. Returns 201 with the full QuoteFetchResponse; each new section comes back with line_items: null until you populate it via scalepad_quoter_create_quote_section_line_items. Requires a draft: 409 if the quote is not a draft, 404 if the quote is not found, 400 on malformed JSON, 422 on validation. ScalePad publishes no minItems, maxItems, or payload ceiling for the array, and does not say whether an empty array is accepted.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesTop-level JSON ARRAY of section objects. Each object has only name — an optional nullable string of at most 200 characters; an empty string is explicitly allowed. Example: [{"name":"Hardware & Services"},{"name":"Recurring"}].
quoteIdstringyesThe draft quote id to add sections to, normally shaped quot_... (from scalepad_quoter_create_quote or scalepad_quoter_list_quotes).

[ScalePad] Fetch one full quote revision (the QuoteFetchResponse that every authoring write also returns). Carries id, draft and primary booleans (inspect BOTH to understand revision state — do not infer it from the id), revision (nullable string counter), number and custom_number, name, currency_iso, pricing_split_order (none|one_time_first|recurring_first), expired_at, comments, internal_notes, template_id, nullable client {id,name} and owner {id,email,first_name,last_name}, contacts[] (currently at most one primary contact), normalized billing_address and shipping_address, attachments[] {id,filename,size_bytes,url}, content_blocks {introductory,closing}, cover_page, custom_fields {quote_creation,quote_acceptance}, sections[] (each with its line_items[] and section totals), totals, and record timestamps. All money values are decimal STRINGS, and several fields deliberately distinguish null from "0" and "0.00". A 404 means the quote was not found; a 422 here specifically means the quote uses an external tax provider and cannot currently be retrieved through this endpoint.

ParamTypeRequiredDefaultDescription
quoteIdstringyesThe quote revision id to fetch, normally shaped quot_... (the id field from scalepad_quoter_list_quotes, or the id returned by scalepad_quoter_create_quote).

[ScalePad] List quote templates — the starting points for a new draft. Cursor-paginated: {data[], total_count, next_cursor}. Each template carries id (normally shaped qtpl_...), slug, title, record_created_at, and record_updated_at; all five are schema-required here. The id is what you pass as template_id to scalepad_quoter_create_quote.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page, and keep paging until next_cursor is null. Cursor scans are not atomic, so deduplicate by id.
filtersJsonstringnonullJSON object of filters. This list documents exactly ONE filter: title, with the cont operator only — {"title":"cont:managed services"}. No other filter key is published for templates.
pageSizeintegernonullMaximum records per page, 1-200. The template list schema declares NO default page size, so set this explicitly for predictable paging.
sortstringnonullComma-separated sort expression over signed values: +field ascends, -field descends. Sortable: id, record_created_at, record_updated_at, title. Example: '+title'.

[ScalePad] List quote revisions. Cursor-paginated: {data[], total_count, next_cursor}. data[] is a FLATTENED list projection, not the full fetch payload — it carries identity/state (id, uuid, revision, number, custom_number, name, draft, primary, stage, flagged, currency_iso, tags), lifecycle timestamps (record_created_at, record_updated_at, expired_at, won_at, email_first_sent_at, email_last_sent_at, email_status), client/contact/owner fields (client, billing and shipping name/organization/address, owner_id, owner_first_name, owner_last_name), external deal ids (autotask_opportunity_id, connectwise_opportunity_id, halo_opportunity_id, hubspot_deal_id, kaseya_opportunity_id, lm_initiative_id, pipedrive_deal_id, quickbooks_invoice_id, salesforce_opportunity_id, xero_invoice_id, zoho_deal_id), and decimal-STRING money groups for annual/monthly/quarterly/semi_annual/one_time/upfront (cost, discount, discounted subtotal, margin, subtotal, tax total, total) plus shipping_decimal. Most money and integration fields are nullable and the schema declares no required array, so tolerate missing fields. Use scalepad_quoter_get_quote for the full payload with sections and line items. Both draft and primary revisions can appear — read the draft and primary booleans rather than inferring state from the id.

ParamTypeRequiredDefaultDescription
cursorstringnonullOpaque next_cursor from the previous page. Omit for the first page, and keep paging until next_cursor is null rather than until a page looks short. Cursor scans are not atomic — concurrent changes can skip or duplicate records, so deduplicate by id.
filtersJsonstringnonullJSON object of field -> "operator:value" filters, ANDed together, e.g. {"stage":"in:draft,published","client.id":"eq:2220324"}. Documented filters: client.id (eq), custom_number (cont), draft (eq, boolean), primary (eq, boolean), email_status (eq; bounced|clicked|deferred|delivered|dropped|opened|processed|reported_as_spam|sent|unsubscribed), id (in, comma-separated ids), lm_initiative_id (eq — links a quote to a Lifecycle Manager initiative), name (eq, cont), recurring_interval (eq; the list schema publishes no enum for it), stage (in, comma-separated: draft|expired|lost|published|sent-clicked|sent-delivered|sent-opened|sent-pending|sent-undeliverable|won-accepted|won-fulfilled|won-ordered), uuid (eq), and the date filters expired_at / record_created_at / record_updated_at / won_at (gt, lt only — no eq).
pageSizeintegernonullMaximum records per page, 1-200. The quote list schema declares NO default page size, so set this explicitly for predictable paging.
sortstringnonullComma-separated sort expression over signed values: +field ascends, -field descends. Sortable: expired_at, id, name, record_created_at, record_updated_at, won_at. Example: '-record_created_at'.

[ScalePad] Publish a draft quote, making it LIVE and sendable. Takes NO request body. Returns 200 with the full QuoteFetchResponse; publishing assigns the quote number and, when expired_at is null, resolves it from the account default expiration window. Confirm with the user before calling: the published ScalePad API surface contains NO unpublish operation and no quote DELETE, so this transition is effectively irreversible. Failure semantics: 404 quote not found; 409 the quote is not a draft or is already published; 422 publish validation (documented examples include ERR_QUOTE_HAS_NO_LINE_ITEMS and ERR_EXPIRED_AT_IN_PAST); and 503 ERR_COULD_NOT_FETCH_ACCOUNT_INFO, a temporary upstream failure the vendor explicitly documents as safe to retry.

ParamTypeRequiredDefaultDescription
quoteIdstringyesThe draft quote id to publish, normally shaped quot_... (from scalepad_quoter_create_quote or scalepad_quoter_list_quotes with filter[draft]=eq:true). Verify the draft's line items and totals with scalepad_quoter_get_quote first.

[ScalePad] Patch ONE existing line item in a draft quote's section and recompute the affected totals. Returns 200 with the full QuoteFetchResponse (totals are recomputed only if a calculation field changed). Every body field is optional, but an empty body is explicitly REJECTED with 422. Nested objects and recurring_interval are tri-state: omitted means no change, null clears where clearing is allowed, and a populated value applies. Requires a draft: 409 on a published quote; 404 if the quote, section, or line item is not found; 400 on malformed JSON or a missing path value; 422 for field validation, with locations reported as line_items[0].field.

ParamTypeRequiredDefaultDescription
bodyJsonstringyesJSON object body; all properties optional but is rejected with 422. Accepts: name (max 100), code (max 50), sku (max 50), description (HTML), quantity_decimal (decimal string greater than zero), unit_price_decimal and unit_cost_decimal (non-negative decimal strings), taxable (boolean), category (CANNOT be cleared — null, , or an empty id all return 422; resolution is by id), manufacturer or null to clear ( or an empty id is invalid), supplier or null to clear (same rule), discount {input_decimal, input_type} or null to clear ( is invalid; amount may not exceed quantity x unit price, percentage is 0-100), and recurring_interval (monthly|quarterly|semi_annual|annual, or null to make the line one-time — an empty string is invalid and a non-null value must match the parent quote). Example: {"quantity_decimal":"3","discount":null}.
lineItemIdstringyesThe line item id to patch, normally shaped litm_... (from sections[].line_items[] in scalepad_quoter_get_quote).
quoteIdstringyesThe draft quote id, normally shaped quot_... (from scalepad_quoter_list_quotes).
sectionIdstringyesThe section id containing the line item, normally shaped qsec_... (from sections[] in scalepad_quoter_get_quote).